图解FFMPEG打开媒体的函数avformat_open_input&avformat_find_stream_info

参考:  http://jiya.io/archives.html 

http://blog.csdn.net/woshinia/article/category/1270366

http://my.oschina.net/mavericsoung/blog/139265     ffmpeg中MPEG2 TS 流解码的流程分析


1 int avformat_open_input(AVFormatContext **ps, const char *filename,
                        AVInputFormat *fmt, AVDictionary **options)

2 接口功能

  1. 创建AVFormatContext结构并填充其中的关键字段
  2. 打开一个指定URI,初始化输入模块。
  3. 解析多媒体文件或流的头信息,为每个流创建AVStream结构。

3 流程分析

  1. 如果s为空,则调用avformat_alloc_context方法为AVFormatContext结构体(以下简称s)分配内存,并使用默认值设置,具体的默认设置是av_format_context_class。
    注:AVFormatContext这个结构体描述了一个媒体文件或媒体流的构成和基本信息。

  2. 如果传入的AVInputFormat类型形参fmt不为空,即判断输入格式是否在调用该方法之前已经探明,
    如果是则直接设置s的iformat成员为该指针。如果不是则需要在后续步骤中分析输入格式。

  3. AVDictionary相关设置暂不分析。

  4. 调用init_input,初始化输入。
    4.1 判断s的pb成员是否为空,pb成员是AVIOContext类型的,AVIOContext是用于IO操作的结构体,其内部还包含缓冲区。
    这里可以这么理解,如果该指针不为空,说明可以直接通过该指针进一步调用IO方法,而如果该指针为空,对于一个流输入来说,说明尚未建立网络层连接,则无法进行IO操作。
    4.2 如果pb成员不为空,那么判断iformat成员是否为空,不为空则调用av_probe_input_buffer去分析输入格式,不为空该方法调用返回。
    4.3 如果pb成员为空,通过avio_open2去建立连接,并设置pb的读写方法指针。

URLProtocol ff_librtmp_protocol = {
         .name                = "rtmp",
         .url_open            = rtmp_open,
         .url_read            = rtmp_read,
         .url_write           = rtmp_write,    
         .url_close           = rtmp_close,    
         .url_read_pause      = rtmp_read_pause,    
         .url_read_seek       = rtmp_read_seek,    
         .url_get_file_handle = rtmp_get_file_handle,   
         .priv_data_size      = sizeof(LibRTMPContext),    
         .priv_data_class     = &librtmp_class,    
         .flags               = URL_PROTOCOL_FLAG_NETWORK,
};  

4.4 如果iformat成员为空,调用av_probe_input_buffer方法去初始化iformat,流程如下:
4.4.1 通过avio_read进一步调用pb的read方法从网络层读取数据。
例如:接收一个RTMP协议的输入,read方法实际指向rtmp_read,读取第一帧数据到pb的buffer中。仔细看librtmp中RTMP_Read接口,它会判断是否是第一帧,如果是第一帧数据,会向返回的数据中添加一个flv头。
4.4.2 根据读到数据探测输入的格式。具体的猜测方法是这样的,通过av_iformat_next接口不断获取已注册的fmt结构体指针,如果该fmt存在read_probe方法,则调用该方法,分析文件格式。
例如:判断flv格式的方法是flv_probe,它就是比对前三个字节。
4.4.3 判断得到文件格式后,s的iformat成员就不会空了,例如flv:

AVInputFormat ff_flv_demuxer = {
           .name           = "flv",    
           .long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),    
           .priv_data_size = sizeof(FLVContext),    
           .read_probe     = flv_probe,   
           .read_header    = flv_read_header,    
           .read_packet    = flv_read_packet,    
           .read_seek      = flv_read_seek,    
           .read_close     = flv_read_close,    
           .extensions     = "flv",    
           .priv_class     = &class,
};
  1. 设置duration和start_time。

  2. 根据需要,为iformat成员的priv_data分配内存。

  3. 读入一些数据分析该输入是否包含ID3(MP3相关)的头信息。
    如果不包含ID3数据,并不会改变pb->buffer的buf_ptr指针,即数据开始指针。

  4. 如果read_header方法指针不为空,则调用该方法,具体以flv_read_header方法分析:
    8.1 根据flv格式,跳过前4个字节,读取8 bit的流信息位。
    8.2 如果存在视频,则创建视频流。即s的streams指针。并设置codec_type,设置pts info。音频类似。
    8.3 最后移动缓冲区指针,相当于跳过了flv的前13(9+4)个字节。

  5. ID3内容处理。

  6. queue_attached_pictures尚未弄懂。

4  avformat_open_input流程图


5  avformat_find_stream_info流程图


6 TSContext


7. parse_packet()的作用:

util.c中 parse_packet() -> av_parser_parse2() -> AVCodecParserContext::parser->parser_parse 会把一个PES(包含多帧),分解成一帧一帧;
             第一帧会使用PES包头中的DTS/PTS, 第二帧的DTS/PTS会通过compute_pkt_fields()函数,在第一帧DTS/PTS的基础上面,加duration。
数据流:
TS => PES => ES               =>                            PES => decode
                                 parse_packet() 组帧                           解码

8. packet_buffer 和 raw_packet_buffer 区别

packet_buffer 和 raw_packet_buffer都是AVFormatContext 结构成员
raw_packet_buffer :主要是直接从demuxer读出的包,它会包含格式信息,这个信息,对探测文件格式非常重要, 所以探测文件格式时,探测数据是直接从raw_packet_buffer 取得.
这个包是先于parser和decoder,只有当文件格式探测到后,才不需要使用这个缓冲,但它的缓冲数据需要,转移到packet_buffer上

packet_buffer:这里的packet是通过av_read_frame ,将av_read_packet的packet最终parser成一帧,然后放到packet_buffer中,也就是说packet_buffer是一帧帧的ES数据,它并没有format的信息,这个packet已经有pts,等信息

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值