1);
avcodec_init和avcodec_register_all初始化了相关的解码器,申请了解码需要的空间等。
其他解码需要具备的是AVcontext、AVCodec、以及AVFrame。
AVContext是解码需要的环境,其中存储了比如长宽,编码器算法,位图格式等信息。
AVCondec就是你所选择的的编解码器了,使用枚举来索引,申请空间后与解码函数配合使用。
AVFrame与AVPicture比较像,都存储解码后的位图信息。
avcodec_decode_video需要输入参数,AVContext,AVFrame,数据首地址以及数据长度。同时传入一个int指针用于记录解码返回的解码成功帧数。
len记录本次解码消耗的字节数。
len = avcodec_decode_video(c, picture, &got_picture,
inbuf_ptr, size);
注意:在解码过程中不要清理contxt环境,以及解码器,如果有必要字节流空间有保存意义,因为,264传输过程中,有PTS以及DTS之分,播放时间以及解码时间如果不一致,可能导致,先到数据需要存储后到达他解码时间时解码。
同时,h264码流分IPB帧,只有I帧是比较全面的图像信息。如果在解码I帧完成后,清空解码环境context,后续解码将持续返回错误信息,直至下一个I帧出现。作者亲测,望看到此文的朋友在做解码时不会再走这条弯路。
自此,解码部分阐述完毕。
利用ffmpeg进行图像数据格式的转换以及图片的缩放应用中,主要用到了swscale.h文件中的三个函数,分别是:
struct SwsContext *sws_getContext(int srcW, int srcH, enum AVPixelFormat srcFormat,
int dstW, int dstH, enum AVPixelFormat dstFormat,
int flags, SwsFilter *srcFilter,
SwsFilter *dstFilter, const double *param);
int sws_scale(struct SwsContext *c, const uint8_t *const srcSlice[],
const int srcStride[], int srcSliceY, int srcSliceH,
uint8_t *const dst[], const int dstStride[]);
void sws_freeContext(struct SwsContext *swsContext);
sws_getContext 函数可以看做是初始化函数,它的参数定义分别为:
int srcW,int srcH 为原始图像数据的高和宽;
int dstW,int dstH 为输出图像数据的高和宽;
enum AVPixelFormat srcFormat 为输入和输出图片数据的类型;eg:AV_PIX_FMT_YUV420、PAV_PIX_FMT_RGB24;
int flags 为scale算法种类;eg:SWS_BICUBIC、SWS_BICUBLIN、SWS_POINT、SWS_SINC;
SwsFilter *srcFilter ,SwsFilter *dstFilter,const double *param 可以不用管,全为NULL即可;
sws_scale 函数则为执行函数,它的参数定义分别为:
struct SwsContext *c 为sws_getContext函数返回的值;
const uint8_t *const srcSlice[],uint8_t *const dst[] 为输入输出图像数据各颜色通道的buffer指针数组;
const int srcStride[],const int dstStride[] 为输入输出图像数据各颜色通道每行存储的字节数数组;
int srcSliceY 为从输入图像数据的第多少列开始逐行扫描,通常设为0;
int srcSliceH 为需要扫描多少行,通常为输入图像数据的高度;
sws_freeContext 函数为结束函数,它的参数即为sws_getContext函数返回的值;
做一个实际缩放YUV420函数打包实例如下:
int ScaleImg(AVCodecContext *pCodecCtx,AVFrame *src_picture,AVFrame *dst_picture,int nDstH ,int nDstW )
int i ;
int nSrcStride[3];
int nDstStride[3];
int nSrcH = pCodecCtx->height;
int nSrcW = pCodecCtx->width;
struct SwsContext* m_pSwsContext;
uint8_t *pSrcBuff[3] = {src_picture->data[0],src_picture->data[1], src_picture->data[2]};
nSrcStride[0] = nSrcW ;
nSrcStride[1] = nSrcW/2 ;
nSrcStride[2] = nSrcW/2 ;
dst_picture->linesize[0] = nDstW;
dst_picture->linesize[1] = nDstW / 2;
dst_picture->linesize[2] = nDstW / 2;
printf("nSrcW%d\n",nSrcW);
m_pSwsContext = sws_getContext(nSrcW, nSrcH, PIX_FMT_YUV420P,
nDstW, nDstH, PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL, NULL, NULL);
if (NULL == m_pSwsContext)
printf("ffmpeg get context error!\n");
exit (-1);
sws_scale(m_pSwsContext, src_picture->data,src_picture->linesize, 0, pCodecCtx->height,dst_picture->data,dst_picture->linesize);
printf("line0:%d line1:%d line2:%d\n",dst_picture->linesize[0] ,dst_picture->linesize[1] ,dst_picture->linesize[2]);
sws_freeContext(m_pSwsContext);
return 1 ;
函数很简单,申请环境初始指针,后缩放即可。读到此文的朋友,这个函数可以直接拷贝使用哟。如果有疑问可以留言或者邮件:leoluopy@gmail.com
下面有一个缩放图像的效果图:
目的位图的空间申请:
注意:上面的缩放函数如果直接使用而在没有解码成功或者没有申请目的位图空间时,将报段错误。
原因:没有解码成功,位图源地址将是指向空的地址,目的位图地址同样。
申请目的位图的方式:
dst_picture = avcodec_alloc_frame();
if (!dst_picture){
return ;
if(avpicture_alloc((AVPicture *)dst_picture, c->pix_fmt,c->width*2, c->height*2)<0){
printf("dst_picture allocate failed\n");
exit(1);
初始化后即可以用于缩放了。
图像的内存Dump方法:
上文已经比较详细的阐述了ffmpeg的解码以及缩放原理及流程,然而在实际运用环境中,无论是从实时播放或者是从历史回放来看,我们需要的是像素位图,而不是ffmpeg的结构体。那么怎么转换呢?下文介绍了相关的内容。
作为实际运用环境中,像素位图格式,笔者使用的是比较常用的YUV格式。
编解码图像格式
承接上文继续,在大部分现场环境下,为了节省传送带宽以及提高系统工作效率,视频原始位图格式以及解码后展示格式一般使用YUV420。
其中YV12以及IYUV是最常见的格式。
简单的说,YUV格式又分为平面格式以及打包格式。他们各有利弊。本文使用的是YUV平面格式即三通道分别使用不同入口地址存储,这样的好处是,与ffmpeg解码接口方面,AVFrame中数据结构可以便捷拷贝图像信息。
YV12:
这里做简单的叙述:详细可以参考:
http://blog.csdn.net/searchsun/article/details/2443867
如何Dump
好了,对YUV格式又初步了解后,下面介绍如果导出这像素位图数据。ffmpeg将图像的亮度以及色差信息保存在AVFrame中的data[0]、data[1]、data[2]中。
详细参考:
AVFrame和AVPicture对比:
http://yul100887.blog.163.com/blog/static/200336135201211143525930/
所有操作均是针对这三个指针展开的,如下:
pgm_save(picture->data[0], picture->linesize[0], //Y
c->width, c->height, outfilename);
pgm_save(picture->data[1], picture->linesize[1],
c->width/2, c->height/2, outfilename); //U
pgm_save(picture->data[2], picture->linesize[2],
c->width/2, c->height/2, outfilename); //V
void pgm_save(unsigned char *buf,int wrap, int xsize,int ysize,char *filename)
FILE *f;
int i;
f=fopen(filename,"ab+");
for(i=0;i<ysize;i++)
fwrite(buf + i * wrap, 1, xsize, f );
fclose(f);
代码对YUV三个像素通道分开dump,读者可以根据自己的需要对函数进行包装。data[0] 是亮度信息入口指针,同时传入图像长宽,以及存储内存区域行宽。 data[1] data[2] 类似。
最后需要注意的是:linesize总是容易被遗忘,livesize[0]=height ,livesize[1]=height/2 ,livesize[2]=height /2,
此文到此结束,感谢阅读。
---------------------leoluopy
参考文章:
使用ffmpeg进行图像格式转换及缩放
http://blog.csdn.net/skys_broyal/article/details/10337147
AVFrame和AVPicture对比:
http://yul100887.blog.163.com/blog/static/200336135201211143525930/
from:http://blog.csdn.net/gubenpeiyuan/article/details/19548019
【Android RTMP】音频数据采集编码 ( FAAC 音频编码参数设置 | FAAC 编码器创建 | 获取编码器参数 | 设置 AAC 编码规格 | 设置编码器输入输出参数 )
【Android RTMP】音频数据采集编码 ( FAAC 音频编码参数设置 | FAAC 编码器创建 | 获取编码器参数 | 设置 AAC 编码规格 | 设置编码器输入输出参数 )
【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV -> RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )
【Android FFMPEG 开发】FFMPEG AVFrame 图像格式转换 YUV -> RGBA ( 获取 SwsContext | 初始化图像数据存储内存 | 图像格式转换 )
【Android FFMPEG 开发】FFMPEG 音视频同步 ( 音视频同步方案 | 视频帧 FPS 控制 | H.264 编码 I / P / B 帧 | PTS | 音视频同步 )(一)
【Android FFMPEG 开发】FFMPEG 音视频同步 ( 音视频同步方案 | 视频帧 FPS 控制 | H.264 编码 I / P / B 帧 | PTS | 音视频同步 )(一)
【Android FFMPEG 开发】FFMPEG 音视频同步 ( 音视频同步方案 | 视频帧 FPS 控制 | H.264 编码 I / P / B 帧 | PTS | 音视频同步 )(二)
【Android FFMPEG 开发】FFMPEG 音视频同步 ( 音视频同步方案 | 视频帧 FPS 控制 | H.264 编码 I / P / B 帧 | PTS | 音视频同步 )(二)
FFmpeg 开发(03):FFmpeg + OpenSLES 实现音频解码播放
本文将利用 FFmpeg 对一个 Mp4 文件的音频流进行解码,然后使用 libswresample 将解码后的 PCM 音频数据转换为目标格式的数据,最后利用 OpenSLES 进行播放。
项目源码FFmpeg开发文档
像素格式转换
像素格式描述了像素数据存储所用的格式。定义了像素在内存中的编码方式。FFmpeg支持的像素格式主要是rbg和yuv两种,具体可以在结构体AVPixelFormat中看到
* Pixel format.