今天到月末了,才发我这个月的第一篇文章,因为这个月前三周一直在看ffmpeg的libavcodec和libavformat两个库源码。实验室要做一个“小传大”的软件,就是android手机或平板电脑的屏幕包括操作等全映射到电脑或电视上去。这个首先想到的就是用TS串流来做,一来是符合标准规范,音视频同步方便;二来是接收端非常简单,普通能播放网络串流的播放器都可以胜任,大大降低开发难度。于是我就开始看ffmpeg的libav库,如下是我的小体会。

ffmpeg库的框架非常漂亮,接口函数在几个主要的头文件中,如avformat.h,avcodec.h等,内部静态函数虽然调用层次非常纷繁复杂,但是命名规范,层次也非常清晰,看起来只要自己不乱,那是非常爽的,这个必须赞一下先哈。如果只是编解码的话,只要调用avcodec.h里面的接口函数就足够了,关于编码有两个结构体,AVCodecContext和AVCodec。AVCodecContext结构体里面的成员是编解码器的一些参数,codec_type,codec_id,width,height,bit_rate,time_base,pix_fmt这些,其中前两个在avcodec_alloc_context3(codec)函数初始化的时候自动赋值,其实也就是用的其参数AVCodec *codec里面设置的值;后面的几个参数要用户自己赋值。AVCodec结构体是由函数avcodec_find_encode(CODEC_ID)来初始化的,这一步通过用户传入的参数CODEC_ID,比如AV_CODEC_ID_MJPEG,不仅把type和id赋了值(这俩就是在AVCodecContext初始化时传入的值),而且把encode等这些函数指针也都赋了值,指向了相应的编解码函数。当做完两个结构体的初始化后,就可以编码了。但是yuv文件和编完码后的码流存在哪里?这就用到ffmpeg的AVFrame和AVPacket两个结构体。有时候也可以用AVPicture来存yuv裸数据,毕竟AVPicture只是AVFrame的子集,我就一直用AVFrame了。AVFrame结构体里面除了width和height以及linesize后,就是一个8个元素的指针数组:data[8],我不知道为啥要给8个,即使是BGRA格式也只用4个哈,这就不管了,现在用YUV420P只占用3个,即data[0]->Y,data[1]->U,data[2]->V。好了,初始化工作完成后,调用avcodec_encode_video2函数就妥了,然后直接从AVPacket结构体里取编成你设置好的格式的码流就行了。

关键要说的不是上面的这些,因为我3个星期才看出这点玩意就太山寨了。我要把yuv编成ts流,当初不知道,直接上面的步骤只是把CODEC_ID设为AV_CODEC_ID_MPEG2TS,结果各种报错,当时我世界观就踏了,到处发贴问,但都石沉大海,木有人回应,不知道是大家都不做TS流编码还是怎么着。当我再回过头仔细看源码并且看TS的标准18030之后,才发现是怎么一回事。TS流就是个容器,可以包mpeg2或h264等ES流,所以如果要编码TS流就要经过两步,第一步是编码成mpeg2或h264,第二步封装成TS流,后者就是libavformat要干的事儿,这就引出了下面的结构体:AVFormatContext,AVOutputFormat和AVStream。AVFormatContext结构体是标准的始祖,太全了,包罗万象,用avformat_alloc_context()初始化,捎带把AVOutputFormat也初始化了,这个结构体里面的write_header,write_packet,interleave_packet这些函数太重要了,但也就是这里出现了大的问题,我后面说明。AVStream结构体就是保存各种流,如果有音频和视频那么就是两个流。

方是时,正当我高兴的以为做出来了的时候,才发现,av_interleaved_write_frame()函数里一个参数是AVIOContext,就是这个结构体太坑了,给TA初始化的时候要添加文件名,也就是说必须是硬盘上的文件才可以,而我要把yuv做成TS流的目的就是要实时打成RTP包发出去,TA让我先存到硬盘上,再从硬盘上读取到内存中打包发送是怎么个意思??还怎么实时性?!我第一个想到的就是直接从AVIOContext结构体里面把那个指向编玩码的流指针找出来,这样就可以解决,但是又当我欣喜的时候,我才发现,这个AVIOContext里面的buffer指针指向的根本就不是封装好的TS流!!!我再一次进去扒代码,才发现,经过了层层的函数调用后,TA的过程我描述如下:

编好的mpeg2或h264 ES流文件存在AVPacket *pkt中,pkt作为输入传入av_interleaved_write_frame()函数,在里面新实例了一个AVPacket *opkt的局部结构体,当调用mpegts_write_packet_internal()函数的时候,输入参数已经是opkt了,写上TS的头,写上opkt的部分长数据,写入文件,free掉buf指针,再写上TS body的数据,再写入部分opkt数据,写入文件,free掉buf指针,。。。不断循环操作,直到写完为止。这样,这个局部TS流文件指针就压根没有!!如果改库的话在AVIOContext结构体里面新建一个TS流指针,把上面的文件操作全部赋给这个指针,我最后也是可以得到的,但是改库的代价和不可预期的错误太大,我稚嫩的肩膀不能hold住。。。。但临近月末,啥都做不出来太没有工作量了,于是我换了一种简单的思路先凑和解决了,就是yuv编码成jpg然后直接udp传,接收端我用QT写了个界面,实时把接到的图片显示出来。虽然做出来了,但是以后音视频同步还是个问题,我还是认为应该用TS流来做,但具体怎么做我要歇几天以后靠灵感了~也期待大家给点灵感~~:-)

转载请注明: 转自http://blog.csdn.net/littlethunder/article/details/9164929

最新文章

  1. Threadlocal使用Case
  2. C# 动态修改Config
  3. WebLogic 8.1 部署问题记录
  4. jQuery10种不同动画效果的响应式全屏遮罩层
  5. 能源项目xml文件 -- springMVC-servlet.xml -- default-lazy-init
  6. ios uiwebview 上几个技巧
  7. 重识JavaScript 之 数据类型的相互转换
  8. android 入门 006(sqlite增删改查)
  9. 三个特殊资源目录 /res/xml /res/raw 和 /assets
  10. cf B. Two Heaps
  11. BZOJ 1610: [Usaco2008 Feb]Line连线游戏
  12. Akka(29): Http:Server-Side-Api,Low-Level-Api
  13. MAVEN 打包JAR
  14. iOS开发基础-图片切换(3)之属性列表
  15. 必须知道的Linux内核常识详解
  16. 文件 "c:\Program Files\Microsoft SQL Server\MSSQL10.SQLEXPRESS\MSSQL\DATA\ttt.mdf" 已压缩,但未驻留在只读数据库或文件组中。必须将此文件解压缩。 CREATE DATABASE 失败。无法创建列出的某些文件名。请查看相关错误。 (.Net SqlClient Data Provider)
  17. How Tomcat works — 二、tomcat启动(1)
  18. MySQL中间件之ProxySQL(3):Admin管理接口
  19. 10.13 Django随笔
  20. unity shader base pass and additional pass

热门文章

  1. Windows WDDM显卡驱动框架及GPUView工具的使用(1)
  2. phpstorm安装laravel-ide-helper实现自动完成、代码提示和跟踪
  3. ubuntu eclipse android搭建
  4. elasticsearch集群搭建实例
  5. iPhone、iPad、iPadMini界面设计标准
  6. [LeetCode] ZigZag Conversion [9]
  7. Visual Studio 2015 & C#6.0 试用报告,持续更新。
  8. javascript 学习总结(二)Array数组
  9. 读书笔记—CLR via C#异常和状态管理
  10. Memcached在.Net中的基本操作