热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【图像处理】FFmpeg解码H264及swscale缩放详解

http:blog.csdn.netgubenpeiyuanarticledetails19548019主题FFmpeg本文概要:本文介绍著名开源音视频编解码库ffm

本文概要:

        本文介绍著名开源音视频编解码库ffmpeg如何解码h264码流,比较详细阐述了其h264码流输入过程,解码原理,解码过程。同时,大部分应用环境下,以原始码流视频大小展示并不是最佳方式,因此,开发者不仅仅需要对视频流解码,并且需要缩放图像以展示于不同窗体下。

        综上,本文除介绍ffmpeg解码h264,同时阐述如何使用swscale缩放视频流。       

        文章使用的开发环境Ubuntu12.04.。交流邮箱:leoluopy@gmail.com。 转载请注明出处 CSDN--固本培元。

ffmpeg介绍:

          FFmpeg是一个开源免费跨平台的视频和 音频流 方案,属于自由 软件 ,采用LGPL或GPL许可证(依据你选择的组件)。它提供了录制、转换以及流化音视频的完整解决方案。它包含了非常先进的音频/视频编解码库libavcodec,为了保证高可移植性和编解码质量,libavcodec里很多codec都是从头开发的。

开始解码

         好了,不多说了。直接上工程和代码吧。(注意在链接工程时,引用库有连接顺序,因为他们有相互依赖关系,如果缺少将不能通过编译。)

        

        需要连接的库: VS代码如下

#pragma comment (lib,"..\\FFMPEG_lib\\avformat.lib")
#pragma comment (lib,"..\\FFMPEG_lib\\avutil.lib")
#pragma comment (lib,"..\\FFMPEG_lib\\swscale.lib")
#pragma comment (lib,"..\\FFMPEG_lib\\avcodec.lib")
#pragma comment (lib,"..\\FFMPEG_lib\\avdevice.lib")
#pragma comment (lib,"..\\FFMPEG_lib\\avfilter.lib")           需要的头文件:

#include "libavcodec\\avcodec.h"
#include "libswscale/swscale.h"           环境初始化代码:(参考了api-example.c)ubuntu上使用的ffmpeg版本是0.6

avcodec_init(); //首先,main函数中一开始会去调用avcodec_init()函数,该函数的作用是初始化libavcodec,而我们在使用avcodec编解码库时,该函数必须被调用。avcodec_register_all();//注册所有的编解码器(codecs),解析器(parsers)以及码流过滤器(bitstream filters)。当然我们也可以使用个别的注册函数来注册我们所要支持的格式。AVCodec *codec;AVCodecContext *c= NULL;int frame, size, got_picture, len;FILE *fin, *fout;AVFrame *picture,*dst_picture;uint8_t inbuf[INBUF_SIZE + FF_INPUT_BUFFER_PADDING_SIZE], *inbuf_ptr;char buf[1024];/* set end of buffer to 0 (this ensures that no overreading happens for damaged mpeg streams) */memset(inbuf + INBUF_SIZE, 0, FF_INPUT_BUFFER_PADDING_SIZE);printf("Video decoding\n");/* find the mpeg1 video decoder */codec = avcodec_find_decoder(CODEC_ID_H264);if (!codec){fprintf(stderr, "codec not found\n");exit(1);}c= avcodec_alloc_context();picture= avcodec_alloc_frame();if(codec->capabilities&CODEC_CAP_TRUNCATED){c->flags|= CODEC_FLAG_TRUNCATED; /* we dont send complete frames */
}/* for some codecs, such as msmpeg4 and mpeg4, width and heightMUST be initialized there because these info are not availablein the bitstream *//* open it */if (avcodec_open(c, codec) <0){fprintf(stderr, "could not open codec\n");exit(1);}           avcodec_init和avcodec_register_all初始化了相关的解码器&#xff0c;申请了解码需要的空间等。

          其他解码需要具备的是AVcontext、AVCodec、以及AVFrame。

          AVContext是解码需要的环境&#xff0c;其中存储了比如长宽&#xff0c;编码器算法&#xff0c;位图格式等信息。

         AVCondec就是你所选择的的编解码器了&#xff0c;使用枚举来索引&#xff0c;申请空间后与解码函数配合使用。

         AVFrame与AVPicture比较像&#xff0c;都存储解码后的位图信息。

解码&#xff1a;

         avcodec_decode_video需要输入参数&#xff0c;AVContext&#xff0c;AVFrame&#xff0c;数据首地址以及数据长度。同时传入一个int指针用于记录解码返回的解码成功帧数。

         len记录本次解码消耗的字节数。 

len &#61; avcodec_decode_video(c, picture, &got_picture,inbuf_ptr, size);          注意&#xff1a;在解码过程中不要清理contxt环境&#xff0c;以及解码器&#xff0c;如果有必要字节流空间有保存意义&#xff0c;因为&#xff0c;264传输过程中&#xff0c;有PTS以及DTS之分&#xff0c;播放时间以及解码时间如果不一致&#xff0c;可能导致&#xff0c;先到数据需要存储后到达他解码时间时解码。

          同时&#xff0c;h264码流分IPB帧&#xff0c;只有I帧是比较全面的图像信息。如果在解码I帧完成后&#xff0c;清空解码环境context&#xff0c;后续解码将持续返回错误信息&#xff0c;直至下一个I帧出现。作者亲测&#xff0c;望看到此文的朋友在做解码时不会再走这条弯路。

          自此&#xff0c;解码部分阐述完毕。

缩放&#xff1a;

           

利用ffmpeg进行图像数据格式的转换以及图片的缩放应用中&#xff0c;主要用到了swscale.h文件中的三个函数&#xff0c;分别是&#xff1a;

    

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 函数可以看做是初始化函数&#xff0c;它的参数定义分别为&#xff1a; 

      int srcW&#xff0c;int srcH 为原始图像数据的高和宽&#xff1b;

      int dstW&#xff0c;int dstH 为输出图像数据的高和宽&#xff1b;

      enum AVPixelFormat srcFormat 为输入和输出图片数据的类型&#xff1b;eg&#xff1a;AV_PIX_FMT_YUV420、PAV_PIX_FMT_RGB24&#xff1b;

      int flags 为scale算法种类&#xff1b;eg&#xff1a;SWS_BICUBIC、SWS_BICUBLIN、SWS_POINT、SWS_SINC&#xff1b;

      SwsFilter *srcFilter &#xff0c;SwsFilter *dstFilter&#xff0c;const double *param 可以不用管&#xff0c;全为NULL即可&#xff1b;

   sws_scale 函数则为执行函数&#xff0c;它的参数定义分别为&#xff1a;

      struct SwsContext *c 为sws_getContext函数返回的值&#xff1b;

      const uint8_t *const srcSlice[]&#xff0c;uint8_t *const dst[] 为输入输出图像数据各颜色通道的buffer指针数组&#xff1b;

      const int srcStride[]&#xff0c;const int dstStride[] 为输入输出图像数据各颜色通道每行存储的字节数数组&#xff1b;     

      int srcSliceY 为从输入图像数据的第多少列开始逐行扫描&#xff0c;通常设为0&#xff1b;

      int srcSliceH 为需要扫描多少行&#xff0c;通常为输入图像数据的高度&#xff1b;

   sws_freeContext 函数为结束函数&#xff0c;它的参数即为sws_getContext函数返回的值&#xff1b;

         做一个实际缩放YUV420函数打包实例如下&#xff1a;

int ScaleImg(AVCodecContext *pCodecCtx,AVFrame *src_picture,AVFrame *dst_picture,int nDstH ,int nDstW )
{
int i ;
int nSrcStride[3];
int nDstStride[3];
int nSrcH &#61; pCodecCtx->height;
int nSrcW &#61; pCodecCtx->width;
struct SwsContext* m_pSwsContext;uint8_t *pSrcBuff[3] &#61; {src_picture->data[0],src_picture->data[1], src_picture->data[2]};nSrcStride[0] &#61; nSrcW ;
nSrcStride[1] &#61; nSrcW/2 ;
nSrcStride[2] &#61; nSrcW/2 ;dst_picture->linesize[0] &#61; nDstW;
dst_picture->linesize[1] &#61; nDstW / 2;
dst_picture->linesize[2] &#61; nDstW / 2;printf("nSrcW%d\n",nSrcW);m_pSwsContext &#61; sws_getContext(nSrcW, nSrcH, PIX_FMT_YUV420P,
nDstW, nDstH, PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL, NULL, NULL);if (NULL &#61;&#61; 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 ;
}         函数很简单&#xff0c;申请环境初始指针&#xff0c;后缩放即可。读到此文的朋友&#xff0c;这个函数可以直接拷贝使用哟。如果有疑问可以留言或者邮件&#xff1a;leoluopy&#64;gmail.com

        下面有一个缩放图像的效果图&#xff1a;


目的位图的空间申请&#xff1a;

       注意&#xff1a;上面的缩放函数如果直接使用而在没有解码成功或者没有申请目的位图空间时&#xff0c;将报段错误。

      原因&#xff1a;没有解码成功&#xff0c;位图源地址将是指向空的地址&#xff0c;目的位图地址同样。 
  

      申请目的位图的方式&#xff1a;

dst_picture &#61; 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方法&#xff1a;

        上文已经比较详细的阐述了ffmpeg的解码以及缩放原理及流程&#xff0c;然而在实际运用环境中&#xff0c;无论是从实时播放或者是从历史回放来看&#xff0c;我们需要的是像素位图&#xff0c;而不是ffmpeg的结构体。那么怎么转换呢&#xff1f;下文介绍了相关的内容。

        作为实际运用环境中&#xff0c;像素位图格式&#xff0c;笔者使用的是比较常用的YUV格式。

        编解码图像格式

          承接上文继续&#xff0c;在大部分现场环境下&#xff0c;为了节省传送带宽以及提高系统工作效率&#xff0c;视频原始位图格式以及解码后展示格式一般使用YUV420。

          其中YV12以及IYUV是最常见的格式。

          简单的说&#xff0c;YUV格式又分为平面格式以及打包格式。他们各有利弊。本文使用的是YUV平面格式即三通道分别使用不同入口地址存储&#xff0c;这样的好处是&#xff0c;与ffmpeg解码接口方面&#xff0c;AVFrame中数据结构可以便捷拷贝图像信息。

         YV12&#xff1a;


         IYUV 




          这里做简单的叙述&#xff1a;详细可以参考&#xff1a;


          http://blog.csdn.net/searchsun/article/details/2443867


     如何Dump

           好了&#xff0c;对YUV格式又初步了解后&#xff0c;下面介绍如果导出这像素位图数据。ffmpeg将图像的亮度以及色差信息保存在AVFrame中的data[0]、data[1]、data[2]中。

          详细参考&#xff1a;

AVFrame和AVPicture对比&#xff1a;

http://yul100887.blog.163.com/blog/static/200336135201211143525930/

           所有操作均是针对这三个指针展开的&#xff0c;如下&#xff1a;

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&#61;fopen(filename,"ab&#43;"); for(i&#61;0;i{
fwrite(buf &#43; i * wrap, 1, xsize, f );
}
fclose(f);
}
                代码对YUV三个像素通道分开dump&#xff0c;读者可以根据自己的需要对函数进行包装。data[0] 是亮度信息入口指针&#xff0c;同时传入图像长宽&#xff0c;以及存储内存区域行宽。 data[1]  data[2] 类似。

                最后需要注意的是&#xff1a;linesize总是容易被遗忘&#xff0c;livesize[0]&#61;height ,livesize[1]&#61;height/2 ,livesize[2]&#61;height /2, 
                

               此文到此结束&#xff0c;感谢阅读。

                                                                        ---------------------leoluopy

参考文章&#xff1a;

使用ffmpeg进行图像格式转换及缩放

http://blog.csdn.net/skys_broyal/article/details/10337147

AVFrame和AVPicture对比&#xff1a;

http://yul100887.blog.163.com/blog/static/200336135201211143525930/


转:https://www.cnblogs.com/huty/p/8518768.html



推荐阅读
  • 大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记
    本文介绍了大数据Hadoop生态(20)MapReduce框架原理OutputFormat的开发笔记,包括outputFormat接口实现类、自定义outputFormat步骤和案例。案例中将包含nty的日志输出到nty.log文件,其他日志输出到other.log文件。同时提供了一些相关网址供参考。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文讨论了如何使用GStreamer来删除H264格式视频文件中的中间部分,而不需要进行重编码。作者提出了使用gst_element_seek(...)函数来实现这个目标的思路,并提到遇到了一个解决不了的BUG。文章还列举了8个解决方案,希望能够得到更好的思路。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了Linux系统中正则表达式的基础知识,包括正则表达式的简介、字符分类、普通字符和元字符的区别,以及在学习过程中需要注意的事项。同时提醒读者要注意正则表达式与通配符的区别,并给出了使用正则表达式时的一些建议。本文适合初学者了解Linux系统中的正则表达式,并提供了学习的参考资料。 ... [详细]
  • 成功安装Sabayon Linux在thinkpad X60上的经验分享
    本文分享了作者在国庆期间在thinkpad X60上成功安装Sabayon Linux的经验。通过修改CHOST和执行emerge命令,作者顺利完成了安装过程。Sabayon Linux是一个基于Gentoo Linux的发行版,可以将电脑快速转变为一个功能强大的系统。除了作为一个live DVD使用外,Sabayon Linux还可以被安装在硬盘上,方便用户使用。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 第四章高阶函数(参数传递、高阶函数、lambda表达式)(python进阶)的讲解和应用
    本文主要讲解了第四章高阶函数(参数传递、高阶函数、lambda表达式)的相关知识,包括函数参数传递机制和赋值机制、引用传递的概念和应用、默认参数的定义和使用等内容。同时介绍了高阶函数和lambda表达式的概念,并给出了一些实例代码进行演示。对于想要进一步提升python编程能力的读者来说,本文将是一个不错的学习资料。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 31.项目部署
    目录1一些概念1.1项目部署1.2WSGI1.3uWSGI1.4Nginx2安装环境与迁移项目2.1项目内容2.2项目配置2.2.1DEBUG2.2.2STAT ... [详细]
  • This article discusses the efficiency of using char str[] and char *str and whether there is any reason to prefer one over the other. It explains the difference between the two and provides an example to illustrate their usage. ... [详细]
  • Android工程师面试准备及设计模式使用场景
    本文介绍了Android工程师面试准备的经验,包括面试流程和重点准备内容。同时,还介绍了建造者模式的使用场景,以及在Android开发中的具体应用。 ... [详细]
  • Asp.net Mvc Framework 七 (Filter及其执行顺序) 的应用示例
    本文介绍了在Asp.net Mvc中应用Filter功能进行登录判断、用户权限控制、输出缓存、防盗链、防蜘蛛、本地化设置等操作的示例,并解释了Filter的执行顺序。通过示例代码,详细说明了如何使用Filter来实现这些功能。 ... [详细]
  • Python使用Pillow包生成验证码图片的方法
    本文介绍了使用Python中的Pillow包生成验证码图片的方法。通过随机生成数字和符号,并添加干扰象素,生成一幅验证码图片。需要配置好Python环境,并安装Pillow库。代码实现包括导入Pillow包和随机模块,定义随机生成字母、数字和字体颜色的函数。 ... [详细]
  • 本文讨论了在shiro java配置中加入Shiro listener后启动失败的问题。作者引入了一系列jar包,并在web.xml中配置了相关内容,但启动后却无法正常运行。文章提供了具体引入的jar包和web.xml的配置内容,并指出可能的错误原因。该问题可能与jar包版本不兼容、web.xml配置错误等有关。 ... [详细]
author-avatar
暴躁的玩具
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有