织梦CMS - 轻松建站从此开始!

罗索

FFMpeg分析详细分析

落鹤生 发布于 2013-03-22 16:38 点击:次 
与其说是分析,不如说是学习,只是看在自己第一次写系列文章的份上,给足自己面子,取个有"深度"的题目!如有人被题目所蒙骗进来,还望见谅!
TAG:

与其说是分析,不如说是学习,只是看在自己第一次写系列文章的份上,给足自己面子,取个有"深度"的题目!如有人被题目所蒙骗进来,还望见谅!

URLProtocol,URLContext和ByteIOContext是FFMpeg操作文件(即I/O,包括网络数据流)的结构,这几个结构现实 的功能类似于C++的多态继承吧,C++的多态是通过子类继承实现,而FFMpeg的“多态”是通过静态对像现实。这部分的代码非常值得C程序借鉴,我是 说,如果你要在C里实现类似C++多态性的功能;比如当你要区分你老婆和情人之间的不同功能时。

     好了,先来看一下这三个struct的定义吧

  1. typedef struct URLProtocol { 
  2.  
  3.     const char *name;        //Rotocol名称  
  4.     int (*url_open)(URLContext *h, const char *url, int flags); //open函数指针对象,以下类似 
  5.     int (*url_read)(URLContext *h, unsigned char *buf, int size);   
  6.     int (*url_write)(URLContext *h, unsigned char *buf, int size); 
  7.  
  8.     int64_t (*url_seek)(URLContext *h, int64_t pos, int whence); 
  9.  
  10.     int (*url_close)(URLContext *h); 
  11.  
  12.     struct URLProtocol *next; //指向下一个URLProtocol,具体说明见备注1 
  13.  
  14.     int (*url_read_pause)(URLContext *h, int pause); 
  15.  
  16.     int64_t (*url_read_seek)(URLContext *h, int stream_index,int64_t timestamp, int flags); 
  17.  
  18.     int (*url_get_file_handle)(URLContext *h); 
  19.  
  20. } URLProtocol; 

备注1:FFMpeg所有的Protol类型都用这个变量串成一个链表,表头为avio.c里的URLProtocol *first_protocol = NULL;

每个文件类似都有自己的一个URLProtocol静态对象,如libavformat/file.c里

  1. URLProtocol file_protocol = { 
  2.     "file"
  3.     file_open, 
  4.     file_read, 
  5.     file_write, 
  6.     file_seek, 
  7.     file_close, 
  8.     .url_get_file_handle = file_get_handle, 
  9. }; 

再通过av_register_protocol()将他们链接成链表。在FFMpeg中所有的URLProtocol对像值都在编译时确定。

  1. typedef struct URLContext { 
  2. #if LIBAVFORMAT_VERSION_MAJOR >= 53 
  3.     const AVClass *av_class; ///< information for av_log(). Set by url_open(). 
  4. #endif 
  5.     struct URLProtocol *prot;
  6. //指向具体的I/0类型,在运行时通过文件URL确定,如是file类型时就是file_protocol           
  7.     int flags; 
  8.     int is_streamed;
  9. /**< true if streamed (no seek possible), default = false */ 
  10.     int max_packet_size;
  11. /**< if non zero, the stream is packetized with this max packet size */ 
  12.     void *priv_data;//指向具体的I/O句柄 
  13.     char *filename; /**< specified URL */ 
  14. } URLContext; 

不同于URLProtocol对象值在编译时确定,URLContext对象值是在运行过程中根据输入的I/O类型动态确定的。这一动一静组合起到了C++的多态继承一样的作用。URLContext像是基类,为大家共同所有,而URLProtocol像是子类部分。

  1. typedef struct { 
  2.     unsigned char *buffer; 
  3.     int buffer_size; 
  4.     unsigned char *buf_ptr, *buf_end; 
  5.     void *opaque; 
  6.     int (*read_packet)(void *opaque, uint8_t *buf, int buf_size); 
  7.     int (*write_packet)(void *opaque, uint8_t *buf, int buf_size); 
  8.     int64_t (*seek)(void *opaque, int64_t offset, int whence); 
  9.     int64_t pos; /**< position in the file of the current buffer */ 
  10.     int must_flush; /**< true if the next seek should flush */ 
  11.     int eof_reached; /**< true if eof reached */ 
  12.     int write_flag;  /**< true if open for writing */ 
  13.     int is_streamed; 
  14.     int max_packet_size; 
  15.     unsigned long checksum; 
  16.     unsigned char *checksum_ptr; 
  17.     unsigned long (*update_checksum)(unsigned long checksum
  18. const uint8_t *buf, unsigned int size); 
  19.     int error;         ///< contains the error code or 0 if no error happened 
  20.     int (*read_pause)(void *opaque, int pause); 
  21.     int64_t (*read_seek)(void *opaque, int stream_index, 
  22.                          int64_t timestamp, int flags); 
  23. } ByteIOContext; 

ByteIOContext是URLContext和URLProtocol 一个扩展,也是FFMpeg提供给用户的接口,URLContext 和URLProtocol对用户是透明,我们所有的操作是通过ByteIOContext现实。这几个struct的相关的关键函数有:

  1. int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, 
  2.                        AVInputFormat *fmt, 
  3.                        int buf_size, 
  4.                        AVFormatParameters *ap) 
  5.     int url_fopen(ByteIOContext **s, const char *filename, int flags) 
  6.     { 
  7.          url_open(URLContext **puc, const char *filename, int flags) 
  8.          { 
  9.                URLProtocol *up; 
  10.                //根据filename确定up 
  11. url_open_protocol (URLContext **puc, struct URLProtocol *up, const char *filename, int flags) 
  12.                { 
  13. //初始化URLContext对像,并通过 up->url_open()将I/O打开将I/O fd赋值给URLContext的priv_data对像 
  14.                } 
  15.          } 
  16.          url_fdopen(ByteIOContext **s, URLContext *h) 
  17.          { 
  18.                //初始化ByteIOContext 对像 
  19.          } 
  20.     } 

我们先看一下音视频播放器的大概结构(个人想法,不保证正确):1、数据源输入(Input)->2、文件格式解析器(Demux)->3、 音视频解码(Decoder)->4、颜色空间转换(仅视频)->5、渲染输出(Render Output)。前一篇介绍的几个struct是数据源输入模块里的内容,哪么这一帖所讲的就是第二个模块即文件格式解析器里用到的内容。

      AVInputFormat、AVOutputFormat与URLProtocol类似,每一种文件类型都有一个AVInputFormat和 AVOutputFormat静态对像并通过av_register_input_format和av_register_output_format函 数链成一个表,其表头在utils.c:

 

/** head of registered input format linked list */

AVInputFormat *first_iformat = NULL;

/** head of registered output format linked list */

AVOutputFormat *first_oformat = NULL;

 

AVInputFormat和AVOutputFormat的定义分别在avformat.h,代码很长,不贴出来浪费空间了。

当程序运行时,AVInputFormat对像的

  1. int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, 
  2.                        AVInputFormat *fmt, 
  3.                        int buf_size, 
  4.                        AVFormatParameters *ap) 
  5.      fmt = av_probe_input_format(pd, 0);//返回该文件的AVInputFormat类型 

至于AVOutputFormat嘛,下次再说吧,晚安!

   AVFormatContext在FFMpeg里是一个非常重要的的结构,是其它输入、输出相关信息的一个容器,需要注意的是其中两个成员:

  struct AVInputFormat *iformat;//数据输入格式
 struct AVOutputFormat *oformat;//数据输出格式
这两个成员不能同时赋值,即AVFormatContext不能同时做为输入、输出格式的容器。AVFormatContext和AVIContext、FLVContext等XXXContext之间像前面讲的 URLContext和 URLProtocol的关系一样,是一种"多态"关系,即AVFormatContext 对像记录着运行时大家共有的信息,而各个XXXContext记录自己文件格式的信息,如AVIContext、FLVContext等。 AVInputFormat->priv_data_size记录相对应的XXXContext的大小,该值大小在编译时静态确定。 AVFormatContext的void *priv_data记录XXXContext指针。
AVFormatContext对像的初始化主要在 AVInputFormat的read_header函数中进行,read_header是个函数指针,指向
具体的文件类型的read_header,如flv_read_header(),avi_read_header()等,AVFormatContext、 AVInputFormat和XXXContext组成一起共同完成数据输入模块,可以出来粗鲁的认为,AVFormatContext是一个类容 器,AVInputFormat是这个类的操作函数集合,XXXContext代表该类的私有数据对像。AVFormatContext还有个重要的成 员 AVStream *streams[MAX_STREAMS];也是在read_header里初始化,这个等会儿再讲。

 前几篇说的都还是数据源文件格式解析部分,哪么解析完后呢,读出的数据流保存在哪呢?正是现在讲的AVStream对像,在AVInputFormat 的read_header中初始化AVFormatContext对像时,他会解析出该输入文件有哪些类型的数据流,并初始化 AVFormatContext的AVStream *streams[MAX_STREAMS];一个AVStream代表一个流对像,如音频流、视频流,nb_streams记录流对像个数。主版本号大 于53时MAX_STREAMS为100,小于53为20。AVStream也是个容器,其

void *priv_data;//

成员变量指向具体的Stream类型对像,如AVIStream。其

AVCodecContext *actx;//记录具体的编解容器,这个下面会讲

也在这读头文件信息里初始化。

主要相关的函数有

  1. int av_open_input_file(AVFormatContext **ic_ptr, const char *filename, 
  2.                        AVInputFormat *fmt, 
  3.                       int buf_size, 
  4.                        AVFormatParameters *ap) 
  5.     av_open_input_stream(AVFormatContext **ic_ptr,ByteIOContext *pb
  6. const char *filename,AVInputFormat *fmt, AVFormatParameters *ap) 
  7.     { 
  8.          fmt.read_header()//调用具体的AVInputFormat的read_header,如avi_read_header 
  9.          { 
  10.                //根据文件头信息初始化AVStream *streams及AVStream里的 
  11.                //void *priv_data和AVCodecContext *actx;成员对像 
  12.          }         
  13.     } 

他们之间的关系和URLProtocol、URLContext之间是一样的,AVCodecContext动态的记录一个解码器的上下文信息,而 AVCodec是每个解码器都会拥有一个自己的静态对像,并通过avcodec_register()函数注册成一个链表,表头在utils.c里定义

static AVCodec *first_avcodec = NULL;

AVCodecContext的enum CodecID codec_id成员记录者当前数据流的Codec,void *priv_data记录具体Codec所对应的上下文信息对像的指针,如MsrleContext。这三个结合起来现实数据解码的作用。我们可以傻逼的 认为AVCodecContext是这个解码模块的容器类,Codec是操作函数集合,类似MsrleContext的就是操作数据对像。

他们之间关系的确立:

每一个解码类型都会有自己的Codec静态对像,Codec的int priv_data_size记录该解码器上下文的结构大小,如MsrleContext。这些都是编译时确定的,程序运行时通过 avcodec_register_all()将所有的解码器注册成一个链表。在av_open_input_stream()函数中调用 AVInputFormat的read_header()中读文件头信息时,会读出数据流的CodecID,即确定了他的解码器Codec。

  1. typedef struct AVPicture { 
  2.     uint8_t *data[4]; 
  3.     int linesize[4];       ///< number of bytes per line 
  4. } AVPicture; 
  5.  
  6. typedef struct AVFrame 
  7.    uint8_t *data[4]; // 有多重意义,其一用NULL 来判断是否被占用 
  8.    int linesize[4]; 
  9.    uint8_t *base[4]; // 有多重意义,其一用NULL 来判断是否分配内存 
  10.    //......其他的数据 
  11. } AVFrame; 

从定义上可知,AVPicture是AVFrame的一个子集,他们都是数据流在编解过程中用来保存数据缓存的对像,从int av_read_frame(AVFormatContext *s, AVPacket *pkt)函数看,从数据流读出的数据首先是保存在AVPacket里,也可以理解为一个AVPacket最多只包含一个AVFrame,而一个 AVFrame可能包含好几个AVPacket,AVPacket是种数据流分包的概念。记录一些音视频相关的属性值,如pts,dts等,定义如下:

  1. typedef struct AVPacket {    
  2.     int64_t pts;    
  3.     int64_t dts; 
  4.     uint8_t *data; 
  5.     int   size; 
  6.     int   stream_index; 
  7.     int   flags;    
  8.     int   duration; 
  9.     void  (*destruct)(struct AVPacket *); 
  10.     void  *priv; 
  11.     int64_t pos; //< byte position in stream, -1 if unknown    
  12.     int64_t convergence_duration; 
  13. } AVPacket; 

 

(fmms)
本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201303/16562.html]
本文出处:open-open 作者:fmms 原文
顶一下
(1)
25%
踩一下
(3)
75%
------分隔线----------------------------
发表评论
请自觉遵守互联网相关的政策法规,严禁发布色情、暴力、反动的言论。
评价:
表情:
用户名: 验证码:点击我更换图片
栏目列表
将本文分享到微信
织梦二维码生成器
推荐内容