您的位置:首页 > 运维架构

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

2015-01-10 20:46 549 查看
参考: 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(***FormatContext **ps, const char *filename,

***InputFormat *fmt, ***Dictionary **options)

2 接口功能

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

3 流程分析

如果s为空,则调用avformat_alloc_context方法为***FormatContext结构体(以下简称s)分配内存,并使用默认值设置,具体的默认设置是av_format_context_class。

注:***FormatContext这个结构体描述了一个媒体文件或媒体流的构成和基本信息。

如果传入的***InputFormat类型形参fmt不为空,即判断输入格式是否在调用该方法之前已经探明,

如果是则直接设置s的iformat成员为该指针。如果不是则需要在后续步骤中分析输入格式。

***Dictionary相关设置暂不分析。

调用init_input,初始化输入。

4.1 判断s的pb成员是否为空,pb成员是***IOContext类型的,***IOContext是用于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:

***InputFormat 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,
};


设置duration和start_time。

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

读入一些数据分析该输入是否包含ID3(MP3相关)的头信息。

如果不包含ID3数据,并不会改变pb->buffer的buf_ptr指针,即数据开始指针。

如果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)个字节。

ID3内容处理。

queue_attached_pictures尚未弄懂。

4 avformat_open_input流程图



5 avformat_find_stream_info流程图



6 TSContext



7. parse_packet()的作用:

util.c中 parse_packet() -> av_parser_parse2() -> ***CodecParserContext::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都是***FormatContext 结构成员

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,等信息
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: