相关博客列表:

FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析

FFMPEG内存操作(二)从内存中读取数及数据格式的转换

FFmpeg内存操作(三)内存转码器

在雷神的《最简单的基于FFmpeg的内存读写例子(内存播放器)》中,它是设计回调函数从输入文件中读取数据。与FFMPEG 官方给出的avio_reading.c不同的是,雷神给的例子是当需要数据的时候,回调函数才去从输入文件读取数据,而avio_reading.c 则是直接全部数据读取到内存中待后面处理。

在我的这个实例中,我是将读取到的输入文件解码成YUV420P数据格式。同时可以通过设置不同的数据格式和尺寸实现输出图像的拉伸缩小或是数据格式的装换。

  1. /*=============================================================================
  2. #     FileName: ffmpeg_mem_read.c
  3. #         Desc: an example of ffmpeg read from memory
  4. #       Author: licaibiao
  5. #   LastChange: 2017-03-21
  6. =============================================================================*/
  7. #include <stdio.h>
  8. #define __STDC_CONSTANT_MACROS
  9. #include "avcodec.h"
  10. #include "avformat.h"
  11. #include "swscale.h"
  12. //#define QUARTER_SHOW
  13. charchar *input_name = "cuc60anniversary_start.ts";
  14. FILEFILE *fp_open = NULL;
  15. int read_buffer(voidvoid *opaque, uint8_t *buf, int buf_size){
  16. if(!feof(fp_open)){
  17. int true_size=fread(buf,1,buf_size,fp_open);
  18. return true_size;
  19. }else{
  20. return -1;
  21. }
  22. }
  23. int main(int argc, char* argv[])
  24. {
  25. AVFormatContext *pFormatCtx;
  26. AVCodecContext  *pCodecCtx;
  27. AVCodec         *pCodec;
  28. AVIOContext     *avio;
  29. AVFrame         *pFrame;
  30. AVFrame         *pFrameYUV;
  31. AVPacket        *packet;
  32. struct SwsContext *img_convert_ctx;
  33. int             videoindex;
  34. int             i;
  35. int             ret;
  36. int             got_picture;
  37. int             y_size;
  38. unsigned char   *aviobuffer;
  39. FILE            *fp_yuv;
  40. //fp_open = fopen(argv[1],"rb+");
  41. fp_open = fopen(input_name, "rb+");
  42. fp_yuv  = fopen("output.yuv", "wb+");
  43. av_register_all();
  44. pFormatCtx = avformat_alloc_context();
  45. aviobuffer=(unsigned charchar *)av_malloc(32768);
  46. avio = avio_alloc_context(aviobuffer, 32768,0,NULL,read_buffer,NULL,NULL);
  47. /* Open an input stream and read the header. */
  48. pFormatCtx->pb = avio;
  49. if(avformat_open_input(&pFormatCtx,NULL,NULL,NULL)!=0){
  50. printf("Couldn't open input stream.\n");
  51. return -1;
  52. }
  53. /* Read packets of a media file to get stream information. */
  54. if(avformat_find_stream_info(pFormatCtx,NULL)<0){
  55. printf("Couldn't find stream information.\n");
  56. return -1;
  57. }
  58. videoindex = -1;
  59. for(i=0; i<pFormatCtx->nb_streams; i++) {
  60. if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){
  61. videoindex=i;
  62. break;
  63. }
  64. }
  65. if(videoindex==-1){
  66. printf("Didn't find a video stream.\n");
  67. return -1;
  68. }
  69. av_dump_format(pFormatCtx, 0, input_name, 0);
  70. /* Find a registered decoder with a matching codec ID */
  71. pCodecCtx = pFormatCtx->streams[videoindex]->codec;
  72. pCodec    = avcodec_find_decoder(pCodecCtx->codec_id);
  73. if(pCodec==NULL){
  74. printf("Codec not found.\n");
  75. return -1;
  76. }
  77. /* Initialize the AVCodecContext to use the given AVCodec */
  78. if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){
  79. printf("Could not open codec.\n");
  80. return -1;
  81. }
  82. pFrame    = av_frame_alloc();
  83. pFrameYUV = av_frame_alloc();
  84. uint8_t *out_buffer=(uint8_t *)av_malloc(avpicture_get_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height));
  85. avpicture_fill((AVPicture *)pFrameYUV, out_buffer, AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height);
  86. /*  Allocate and return an SwsContext. */
  87. /* srcW:源图像的宽
  88. * srcH:源图像的高
  89. * srcFormat:源图像的像素格式
  90. * dstW:目标图像的宽
  91. * dstH:目标图像的高
  92. * dstFormat:目标图像的像素格式
  93. * flags:设定图像拉伸使用的算法
  94. */
  95. #ifndef QUARTER_SHOW
  96. img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
  97. #else
  98. img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt, pCodecCtx->width/2, pCodecCtx->height/2, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
  99. printf("out frame  width = %d, height = %d \n", pCodecCtx->width/2,pCodecCtx->height/2);
  100. #endif
  101. packet = (AVPacket *)av_malloc(sizeof(AVPacket));
  102. while(av_read_frame(pFormatCtx, packet) >= 0){
  103. if(packet->stream_index == videoindex){
  104. ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture, packet);
  105. if(ret < 0){
  106. printf("Decode Error.\n");
  107. return -1;
  108. }
  109. if(got_picture){
  110. /* Scale the image slice in srcSlice and put the resulting scaled slice in the image in dst. 图像处理函数 */
  111. /* c             the scaling context previously created with  sws_getContext()
  112. * srcSlice      the array containing the pointers to the planes of the source slice
  113. * srcStride     the array containing the strides for each plane of
  114. * srcSliceY     the position in the source image of the slice to process, that is the number (counted starting from
  115. zero) in the image of the first row of the slice 输入图像数据的第多少列开始逐行扫描,通常设为0
  116. * srcSliceH     the height of the source slice, that is the number of rows in the slice 为需要扫描多少行,通常为输入图像数据的高度
  117. * dst           the array containing the pointers to the planes of the destination image
  118. * dstStride     the array containing the strides for each plane of the destination image
  119. */
  120. sws_scale(img_convert_ctx, (const uint8_t* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
  121. #ifndef QUARTER_SHOW
  122. // printf("pFrameYUV->height   = %d\n ",pFrameYUV->height);
  123. // printf("pFrameYUV->width    = %d\n ", pFrameYUV->width);
  124. // printf("pFrameYUV->pkt_size = %d\n ",pFrameYUV->pkt_size);
  125. y_size = pCodecCtx->width*pCodecCtx->height;
  126. fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y
  127. fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U
  128. fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V
  129. #else
  130. for(i=0; i<pCodecCtx->height/2; i++){
  131. fwrite(pFrameYUV->data[0]+pCodecCtx->width * i ,1,pCodecCtx->width/2,fp_yuv);
  132. }
  133. for(i=0; i<pCodecCtx->height/2; i = i + 2){
  134. fwrite(pFrameYUV->data[1]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
  135. }
  136. for(i=0; i<pCodecCtx->height/2; i = i + 2 ){
  137. fwrite(pFrameYUV->data[2]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
  138. }
  139. #endif
  140. }
  141. }
  142. av_free_packet(packet);
  143. }
  144. sws_freeContext(img_convert_ctx);
  145. fclose(fp_yuv);
  146. fclose(fp_open);
  147. av_free(out_buffer);
  148. av_free(pFrameYUV);
  149. avcodec_close(pCodecCtx);
  150. avformat_close_input(&pFormatCtx);
  151. return 0;
  152. }

正常操作是将输入文件解码成YUV数据。

当我们对sws_getContext  设置不同参数的时候,我们可以得到不同的输出数据。在这里需要注意几点:

(1)out_buffer 的大小要跟着sws_getContext  参数的设置而改变,如果out_buffer分配得比输出数据小,会出现内存溢出问题。

(2)sws_scale 函数中,我们一般设置从输入数据的第0行开始扫描,扫描的高度(也就是行数)一般是输入数据的高度。

(3)得到的YUV数据是储存在pFrameYUV->data 的三个分量里,需要分开读取。

举例:

在sws_getContext 中设置输出格式的宽和高都是输入格式的一半。正常的显示应该是图像变为了原来的1/4大小。如果我们还是按原图尺寸提取输出数据:

  1. y_size = pCodecCtx->width*pCodecCtx->height;
  2. fwrite(pFrameYUV->data[0],1,y_size,fp_yuv);    //Y
  3. fwrite(pFrameYUV->data[1],1,y_size/4,fp_yuv);  //U
  4. fwrite(pFrameYUV->data[2],1,y_size/4,fp_yuv);  //V

得到的图像将会是:

如上图,图像显示是正常的,但同时也提取了很多的无用数据,实际图像并没有缩小到原来的1/4

所以我们需要自己对输出数据再重组:

  1. for(i=0; i<pCodecCtx->height/2; i++){
  2. fwrite(pFrameYUV->data[0]+pCodecCtx->width * i ,1,pCodecCtx->width/2,fp_yuv);
  3. }
  4. for(i=0; i<pCodecCtx->height/2; i = i + 2){
  5. fwrite(pFrameYUV->data[1]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
  6. }
  7. for(i=0; i<pCodecCtx->height/2; i = i + 2 ){
  8. fwrite(pFrameYUV->data[2]+pCodecCtx->width * i/4 ,1,pCodecCtx->width/4,fp_yuv);
  9. }

分别重新提取了Y U V 三个分量上的数据,得到图像如下:

在读取输出数据的时候,如果发现读取的输出数据显示有问题,最好是使用UltraEdit 工具对某帧数据进行分析,先确认输出的一帧数据是否正常,然后才进行视频格式的转换。比如出现下面的这种情况

从图像中大概能分析出来,Y分量数据是正常的,图像右半边的UV分量可能丢失。使用UltraEdit查看原始数据,如下图。我们可以看到从地址0xe150开始有一段是全0的数据,因此我们可以到我们的程序中去检查是否UV分量读取错误。

总结:我们直接使用sws_getContext和sws_scale 可以直接对图像进行拉伸和缩放,同时可以进行数据格式的转换,只需要设置sws_getContext 就可以了,非常简单。但是需要注意,输出的数据需要我们自己重新组合,否则读取到的输出数据很有可能出错。

from:http://blog.csdn.net/li_wen01/article/details/64904586

FFMPEG内存操作(二)从内存中读取数及数据格式的转换的更多相关文章

  1. jvm大局观之内存管理篇(二):当java中new一个对象,背后发生了什么

    https://zhuanlan.zhihu.com/p/257863129?utm_source=ZHShareTargetIDMore 番茄番茄我是西瓜 那是我日夜思念深深爱着的人啊~ 已关注   ...

  2. c# 外挂操作(内存操作)(内存读写取窗口句柄移动窗口取模块地址)工具类

    来源于网上  参考 https://www.cnblogs.com/fuhua/p/5877781.html 等众多文章 详情取看我第二个例子封装功能较多 https://www.cnblogs.co ...

  3. openCV(二)---iOS中使用openCV的图片格式转换

    可以实现将UIImage和IplImage类型实现相互转换 //由于OpenCV主要针对的是计算机视觉方面的处理,因此在函数库中,最重要的结构体是IplImage结构. - (IplImage *)C ...

  4. python从excel中读取数据传给其他函数使用

    首先安装xlrd库 pip install xlrd 方法1: 表格内容如下: 场景描述,读取该表格A列数据,然后打印出数据 代码何解析如下: import xlrd #引入xlrd库 def exc ...

  5. FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析

    相关博客列表 : FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析 FFMPEG内存操作(二)从内存中读取数及数据格式的转换 FFmpeg内存操作(三)内存转码器 在F ...

  6. FFmpeg内存操作(三)内存转码器

    相关博客列表 : FFMPEG内存操作(一) avio_reading.c 回调读取数据到内存解析 FFMPEG内存操作(二)从内存中读取数及数据格式的转换 FFmpeg内存操作(三)内存转码器 本文 ...

  7. 重磅硬核 | 一文聊透对象在 JVM 中的内存布局,以及内存对齐和压缩指针的原理及应用

    欢迎关注公众号:bin的技术小屋 大家好,我是bin,又到了每周我们见面的时刻了,我的公众号在1月10号那天发布了第一篇文章<从内核角度看IO模型的演变>,在这篇文章中我们通过图解的方式以 ...

  8. java 如何从配置文件(.properties)中读取内容

    1.如何创建.properties文件 很简单,建立一个txt文件,并把后缀改成.properties即可 2.将.properties文件拷入src的根目录下 3..properties文件内容格式 ...

  9. php中读取文件内容的几种方法。(file_get_contents:将文件内容读入一个字符串)

    php中读取文件内容的几种方法.(file_get_contents:将文件内容读入一个字符串) 一.总结 php中读取文件内容的几种方法(file_get_contents:将文件内容读入一个字符串 ...

随机推荐

  1. windows下php配置redis

    方法/步骤 1.使用phpinfo()函数查看PHP的版本信息,这会决定扩展文件版本   2.根据PHP版本号,编译器版本号和CPU架构, 选择php_redis-2.2.5-5.5-ts-vc11- ...

  2. 基于jquery的bootstrap在线文本编辑器插件Summernote (转)

    Summernote是一个基于jquery的bootstrap超级简单WYSIWYG在线编辑器.Summernote非常的轻量级,大小只有30KB,支持Safari,Chrome,Firefox.Op ...

  3. 问题:今天测试模块一直出现一个问题?module 'subprocess' has no attribute 'Popen'

    原因:我起的名字用了模块本身的名字,这个地方一定要切记

  4. Javascript对数组的操作--转载

    在jquery中处理JSON数组的情况中遍历用到的比较多,但是用添加移除这些好像不是太多. 今天试过json[i].remove(),json.remove(i)之后都不行,看网页的DOM对象中好像J ...

  5. 【学员管理系统】0x03 老师信息管理功能

    [学员管理系统]0x03 老师信息管理功能 老师信息管理相比于学生信息管理又多了一点,因为我们的数据结构中老师表和班级表是通过teacher2class表进行多对多关联的. 写在前面 项目详细需求参见 ...

  6. 超轻量级、高性能C日志库--EasyLogger

    [ 声明:版权全部,欢迎转载.请勿用于商业用途. 联系信箱:armink.ztl@gmail.com] EasyLogger 1. 介绍 EasyLogger 是一款超轻量级(ROM<1.6K, ...

  7. fedora25 安装sublime text3

    fedora 25安装使用 sublime text 3 安装 sublime text 3 fedora 需要选择 tarball 版本.下载后将 sublime text 3 解压后放到 opt ...

  8. 【深度学习】ubuntu16.04下安装opencv3.4.0

    1.首先安装一些编译工具 # 安装编译工具 sudo apt-get install build-essential # 安装依赖包 sudo apt-get install cmake git li ...

  9. jQuery对象转成DOM对象:

    jQuery对象转成DOM对象: 两种转换方式将一个jQuery对象转换成DOM对象:[index]和.get(index); (1)jQuery对象是一个数据对象,可以通过[index]的方法,来得 ...

  10. python基础14 ---函数模块4(configparser模块)

    configparser模块 一.configparser模块 1.什么是configparser模块:configparser模块操作配置文件,配置文件的格式与windows ini和linux的c ...