ffmpeg 解mpegts

原地址已经找不到了,暂时不能贴上来。


本文主要的分析内容是 demuxer流程 
1. Demuxer 相关 
1.1. ffmpeg 中选择 demuxer 的过程 
(1)Input Method

av_open_input_file()

-> avio_open()

--> url_open()

---> url_alloc

---> url_connect

     在 av_open_input_file() 函数中首先调用 avio_open() 来判断参数中给定的视频流是文件格式(file)还是网络流格式(rtsp、tcp、http),并调用相应的url_open 函数来打开流。在url_open()函数中, url_alloc()为格式探测函数(查找出是哪个协议,url_alloc_for_protocol()将URLContext结构体填充满);url_connect()-->uc->prot->url_open() 为相应的流打开函数,uc->prot->url_open() 为函数指针,存储选中格式(即url_alloc()探测出来的函数格式)的open函数,以TCP的url_open()为例,会先建立Socket连接。格式判断方法比较简单:判断文件名中是否有“:”,若有,再判断具体是哪种流;没有的话按照文件格式来处理。 格式探测完毕并打开流之后(将 uc->is_connected 设置为1,意味与本地文件或非本地资源建立起连接),avio_open ()函数结束。

(2)Create Buffer for Input

->avio_open()

--> url_fdopen()

---> url_get_max_packet_size() 

---> ffio_init_context()

avio_open()接下来执行 url_fdopen(), 在此过程首先初始化一个Buffer,接着通过 URLContext 数据结构(不同的格式会有不同的读取包的方式),函数ffio_init_context()来初始化AVIOContext , AVIOContext 是 AVFormatContext 的成员,其成员包括流属性,已经对流的读取、写入、定位等函数指针。经过初始化此结构程序便可以读取实际的数据包来做进一步分析了。 将URLProtocol中的url_read、url_write、url_seek写到AVIOContext(),之后就主要使用这个结构体。

(3) Choose 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 。 
    下面以 mpegts 为例,具体选择 demuxer 的过程。先调用av_probe_input_buffer ()->av_probe_input_format2()来确定,在 mpegts 中是调用libavformat/mpegts.c 中的 mpegts_probe-->analyze(), 具体原理是读取一定数量的包(此处是 2048Byte ,计算下来应该是 10 个包 204*10=2040 ),判断 0x47 出现的次数( 0x47 是 mpegts 的同步位),根据出现次数来计算分数。 如果匹配的话,分数接近满分,故而选择。目前,TS包的大小有三种标准:188,192, 204,其中188的标准用得最多。

(4)Protocol of FFMPEG Support

FFMPEG支持网络协议有Contat、PIPE、gopher、http、RTMP(network及扩展t、e、te、s)、Md5、MMSt、MMSh(Over http)、rtmp、rtp、Tcp、Udp。(在全工程中查找URLProtocol得到结果)

typedef struct URLProtocol {

    const char *name;

    int (*url_open)(URLContext *h, const char *url, int flags);

    int (*url_read)(URLContext *h, unsigned char *buf, int size);

    int (*url_write)(URLContext *h, const unsigned char *buf, int size);

    int64_t (*url_seek)(URLContext *h, int64_t pos, int whence);

    int (*url_close)(URLContext *h);

    struct URLProtocol *next;

    int (*url_read_pause)(URLContext *h, int pause);

    int64_t (*url_read_seek)(URLContext *h, int stream_index,

                             int64_t timestamp, int flags);

    int (*url_get_file_handle)(URLContext *h);

    int priv_data_size;

    const AVClass *priv_data_class;

    int flags;

} URLProtocol;

1.2. TS结构分析

(1) PSI 

TS流中PSIProgram Specific Infromation,节目特定信息)组成:

节目关联表(PATProgram Associate Table

条件接收表(CATCondition Accept Table

节目映射表(PMTProgram Mapped Table

网络信息表(NITNetwork Information Table

(2) SI

TS流中的SIService Information,服务信息)组成:

l 业务群关联表(BATBevy Associate Table

l 业务描述表(SDTXX Describe Table

(3) PID and TID

PID信息:

PAT_PID = 0x0000

SDT_PID = 0x0011

Table信息:

#define PAT_TID   0x00

#define PMT_TID   0x02

#define SDT_TID   0x42

(4) Stream_type类型表:

#define STREAM_TYPE_VIDEO_MPEG1     0x01

#define STREAM_TYPE_VIDEO_MPEG2     0x02

#define STREAM_TYPE_AUDIO_MPEG1     0x03

#define STREAM_TYPE_AUDIO_MPEG2     0x04

#define STREAM_TYPE_PRIVATE_SECTION 0x05

#define STREAM_TYPE_PRIVATE_DATA    0x06

#define STREAM_TYPE_AUDIO_AAC       0x0f

#define STREAM_TYPE_AUDIO_AAC_LATM  0x11

#define STREAM_TYPE_VIDEO_MPEG4     0x10

#define STREAM_TYPE_VIDEO_H264      0x1b

#define STREAM_TYPE_VIDEO_VC1       0xea

#define STREAM_TYPE_VIDEO_DIRAC     0xd1

#define STREAM_TYPE_AUDIO_AC3       0x81

#define STREAM_TYPE_AUDIO_DTS       0x8a

1.3. PSI 分析

av_open_input_file()->av_open_input_stream ()-->read_header()

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

      选择完毕 demuxer (format)之后,下一步打开视频流并分析。主要分析 PSI 信息,即包含的频道及各个频道关联的具体视频或音频流的信息等。具体需调用read_header函数指针。 以 mpegts 为例,实际调用的函数为 mpegts_read_header(),此过程主要是分析TS 流中有哪些视频流、音频流、字幕流及私有数据流(主要通过分析 ts 流中的 pat sdt 表等信息得出)。

在mpegts_read_header()中,首先通过 mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1)和mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1),挂接 sdt 以及 pat 的处理函数,即 sdt 和 pat 是最初始的信息,在 pat 中才能得到 pmt 的 pid 以便进一步分析。

其次,在handle_packets 中读取一个包然后,调用 handle_packet() 处理一个188字节的packet。处理过程中调用 write_section_data, 接收 pat 等信息,当一个完整的 pat 接收完毕后,调用 tss->section_cb()来解析,此处 section_cb 便是通过 mpegts_open_section_filter 挂接的 pat_cb( 以 pat 为例, sdt 分析方式一样 ) 。分析过程得到 pmt 的 pid 并通过 mpegts_open_section_filter(… pmt_pid, pmt_cb… ); 将 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 包并解析实际的媒体数据。 

PS:MpegTSFilter  *pid[NB_PID_MAX]    而NB_PID_MAZ == 8192

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值