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

来源:互联网 发布:4412linux内核移植步骤 编辑:程序博客网 时间:2024/04/27 21:47

参考:  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 0
原创粉丝点击