Chinaunix首页 | 论坛 | 博客
  • 博客访问: 233091
  • 博文数量: 50
  • 博客积分: 1793
  • 博客等级: 上尉
  • 技术积分: 393
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-22 23:28
文章分类
文章存档

2012年(7)

2011年(17)

2010年(26)

我的朋友

分类: LINUX

2011-03-22 18:46:33

最近在看ffmpeg源码,希望多了解下底层的东西,mplayer源码太过庞大,不利于快速的分析关键代码。ffmpeg恰好满足需要。

主要的分析内容包括如下两个方面:demuxer相关,解码器选取相关
1
demuxer相关
1.1 ffmpeg
中选择demuxer的过程
av_open_input_file
**->ff_probe_input_buffer**-->av_probe_input_format2(***)-->read_probe(), 其中read_probe是函数指针,不同的demuxer基本都会实现自己的read_probe,举例来说,mpegts对应的函数名是 mpegts_probe,根据read_probe的返回值来判断,选择得分(返回值)最大的demuxer

   
其中在av_open_input_file函数中首先调用url_fopen**)来判断参数中给定的视频流是 文件格式 file)还是网络流格式(rtsprtp等),并调用相应的open函数来打开流,其中url_alloc(**)为格式探测函数,url_connect(**)—>uc->prot->url_open(**)为相应的流打开函数,其中uc->prot->url_open为函数指针,存储选中格式的open函数。格式判断方法比较简单:判断文件名中是否有,有再判断具体是哪种流,没有的话按照文件格式来处理。 格式探测完毕并打开流之后(将uc->is_connected设置为1,url_open函数结束。接下来执行url_fdopen(**),在此过程中通过URLContext数据结构(不同的格式会有不同的读取包的方式)来初始化ByteIOContextByteIOContext AVFormatContext的成员,其成员包括流属性,已经对流的读取、写入、定位等函数指针。经过初始化此结构程序便可以读取实际的数据包来做进一步分析了。
   
   
下面以mpegts为例,看具体选择demuxer的过程。过程通过调用read_probe()来确定,在mpegts中是调用 libavformat/mpegts.c中的mpegts_probe**-->analyze(***),具体原理是读取一定数量的包(此 处是2048Byte,计算下来应该是10个包 204*10=2040),判断0x47出现的次数(0x47mpegts的同步位),根据出现次数来计算分数(具体代码分析可参考引用的其他文章)。 如果匹配的话,肯定是满分,故而选择。
1.2psi
分析
    
选择完毕demuxerformat)之后,下一步打开视频流并分析。主要分析PSI信息,即包含的频道及各个频道关联的具体视频或音频流的信息等。具体过程如下:
av_open_input_stream
**-->read_header(), 其中read_header也是函数指针,在此处我们还以mpegts为例,实际调用的函数为mpegts_read_header**),此过程主要 是分析ts流中有哪些视频流、音频流等参数(主要通过分析ts流中的pat sdt表等信息得出),调用过程为

mpegts_read_header(***)-->handle_packets(**)-->handle_packet(**)

其中在mpegts_read_header**)中首先通过pegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1); mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);挂接sdt以及pat的处理函数,即sdtpat是最初始的信息,在pat中才能得到pmtpid以便进一步分析。在handle_packets中读取一个包然后调用handle_packet(**)处理。处理过程中调用write_section_data

接收pat等信息,当一个完整的pat接收完毕后,调用tss->section_cb(tss1, tss->section_buf, tss->section_h_size);来解析,此处section_cb便是通过mpegts_open_section_filter挂接的pat_cb(pat为例,sdt分析方式一样)。分析过程得到pmtpid并通过mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);pmt的解析函数挂接上来,之后便可以读取pmt包并处理了。通过分析pmt便可以得到具体的媒体流并通过add_pes_stream(ts, pid, pcr_pid);函数来建立各个媒体流的信息,也得到了此ts中包含的媒体流的数量和id,在pmt_cb中还有一个很重要的函数是mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc);此函数通过mpegts_find_stream_type(st, pes->stream_type, MISC_types);可以定位到每个媒体流的具体解码器(主要是存储codec_id,其他在后面初始化),之后解码的时候便可以通过codec_id来选择具体的解码器了。增加流的操作中会调用tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);来挂接实际的解析pes的函数,之后便可以接收具体的pes包并解析实际的媒体数据。以上内容并不保证准确,需进一步确认。

经过此过程,便得到了基本的流信息(video audio 数量及相应的ID),在读包后分析ID即可相应处理,达到解复用的目的了。分析参数得到流信息的过程描述如下:主要是两个表PATPMT,其中PAT 储的频道信息,一个频道便有一个PMT,而PMT存储的是此频道由哪些流组成(视频流 音频流 以及相应的PID),以及流的具体格式(h264等),通过分析完每个PMT便得到了总的视频流及频道等信息。具体的关于sdt和pat、pmt的介绍可参照网上信息或查阅标准文档。

 

2、解码器相关

2.1解码器选择:

         通过如上分析,已经得到了解码器的codec_id,根据codec_id选择即可

 

 

待续。。。

转载请注明!
阅读(2820) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~