TS(Transport Stream): 是将具有一个或多个独立时间基的一个或多个节目(包括音频和视频)组成一个流, 组成同一个节目的基本流(如一个视频流,多个音频流)的PES包有一个共用的时间基。 TS的包长标准为188bytes. 从上面的定义可以分成三层来看TS/PS。 ES层 : 由单独的音频(如mp3),视频流(如h.264)组成基本的ES(Elementary Stream)。 PES层 : 将基本的ES按一定的规则(如H.264以AU)进行封装,并打上时间戳,组成PES。 TS/PS层: 将PES包进行切分后再封装成188bytes大小的TS包, 同时还将一些节目信息也封装成TS包(称为section), 两者共同组成TS层。
从上面的总结,TS/PS总体上来说,是一种封装格式,用来承载数据。 所以FFmpeg 将TS/PS的解析文件定义在libavformat/mpegts.c文件中 将音频,视频的解码定义在libavcodec/mpeg12.c文件中
下面来看FFmpeg是如何进行TS的demuxer的。 1. MPEG2-TS的demuxer函数
- AVInputFormat ff_mpegts_demuxer = {
- "mpegts",
- NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
- sizeof(MpegTSContext),
- mpegts_probe,
- mpegts_read_header,
- mpegts_read_packet,
- mpegts_read_close,
- read_seek,
- mpegts_get_pcr,
- .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT,
- #ifdef USE_SYNCPOINT_SEARCH
- .read_seek2 = read_seek2,
- #endif
- };
2. 解析流中的TS格式
- /*
- * 出现3种格式,主要原因是:
- * TS标准是 188Bytes;
- * 日本标准是192Bytes的DVH-S格式;
- * 第三种的 204Bytes则是在188Bytes的基础上,加上16Bytes的FEC(前向纠错).
-
*/
- #define TS_PACKET_SIZE 188
- #define TS_DVHS_PACKET_SIZE 192
- #define TS_FEC_PACKET_SIZE 204
- #define TS_MAX_PACKET_SIZE 204
-
//< maximum score, half of thatis usedfor file-extension-based detection
- #define AVPROBE_SCORE_MAX 100
- /*
- * 函数功能:
- * 分析流中是三种TS格式的哪一种
-
*/
- static int mpegts_probe(AVProbeData*p)
-
{
- #define CHECK_COUNT 10
- const int size= p->buf_size;
- int score, fec_score, dvhs_score;
- int check_count= size/ TS_FEC_PACKET_SIZE;
- if (check_count< CHECK_COUNT)
- return -1;
- score = analyze(p->buf, TS_PACKET_SIZE*check_count, TS_PACKET_SIZE, NULL)
-
* CHECK_COUNT / check_count;
- dvhs_score= analyze(p->buf, TS_DVHS_PACKET_SIZE*check_count, TS_DVHS_PACKET_SIZE, NULL)
-
* CHECK_COUNT / check_count;
- fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE*check_count, TS_FEC_PACKET_SIZE, NULL)
-
* CHECK_COUNT / check_count;
- /*
- * we need a clear definition for the returned score ,
- * otherwise things will become messy sooneror later
- */
- if (score> fec_score&& score> dvhs_score&& score> 6)
- return AVPROBE_SCORE_MAX + score - CHECK_COUNT;
- else if(dvhs_score> score&& dvhs_score> fec_score&& dvhs_score> 6)
- return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT;
- else if(fec_score> 6)
- return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT;
- else
- return -1;
- }
- /*
- * 函数功能:
- * 在size大小的buf中,寻找满足特定格式,长度为packet_size的
- * packet的个数;
- * 显然,返回的值越大越可能是相应的格式(188/192/204)
-
*/
- static int analyze(const uint8_t*buf,int size, int packet_size,int*index){
- int stat[TS_MAX_PACKET_SIZE];
- int i;
- int x=0;
- int best_score=0;
- memset(stat, 0, packet_size*sizeof(int));
-
- for (x=i=0; i< size-3; i++)
- {
- if ((buf[i]== 0x47)&&!(buf[i+1]& 0x80)&& (buf[i+3]& 0x30))
- {
- stat[x]++;
-
- if (stat[x]> best_score)
- {
- best_score= stat[x];
- if (index)
- *index= x;
- }
- }
- x++;
- if (x== packet_size)
- x= 0;
- }
-
- return best_score;
- }
buf[i] == 0x47 其中的sync_byte固定为0x47,即上面的. !(buf[i+1] & 0x80) 由于transport_error_indicator为1的TS Packet实际有错误, 表示携带的数据无意义, 这样的Packet显然没什么意义. buf[i+3] & 0x30 对于adaptation_field_control, 如果取值为0x00,则表示为未来保留,现在不用.
这就是MPEG TS的侦测过程.
3. MPEG2-TS头解析
- #define NB_PID_MAX 8192
- #define MAX_SECTION_SIZE 4096
-
-
/* pids*/
- #define PAT_PID 0x0000
- #define SDT_PID 0x0011
-
-
/* table ids*/
- #define PAT_TID 0x00
- #define PMT_TID 0x02
- #define SDT_TID 0x42
-
/*
- * 函数功能:
- *
- */
-
int mpegts_read_header(AVFormatContext*s, AVFormatParameters*ap)
-
{
- /*
- * MpegTSContext , 是为了解码不同容器格式所使用的私有数据,
- * 只有在相应的诸如mpegts.c文件才可以使用的.
- * 这样,增加了这个库的模块化.
-
*/
- MpegTSContext *ts = s->priv_data;
- AVIOContext *pb = s->pb;
- uint8_t buf[8*1024];
- int len;
- int64_t pos;
- /* read the first 8*1024 bytestoget packet size*/
- pos = avio_tell(pb); // 获取buf的当前位置
- len = avio_read(pb, buf, sizeof(buf));// 从pb->opaque中读取sizeof(buf)个字节到buf
- if (len!= sizeof(buf))
- goto fail;
- /*
- * 获得TS包的实际长度
-
*/
- ts->raw_packet_size= get_packet_size(buf, sizeof(buf));
- if (ts->raw_packet_size<= 0)
- {
- av_log(s, AV_LOG_WARNING,"Could not detect TS packet size, defaulting to non-FEC/DVHS\n");
- ts->raw_packet_size= TS_PACKET_SIZE;
- }
- ts->stream= s;
- ts->auto_guess= 0;
-
- if (s->iformat==&ff_mpegts_demuxer)
- {
- /* normal demux*/
- /* firstdo a scaningtoget all the services*/
- if (avio_seek(pb, pos, SEEK_SET)< 0)
- {
- av_log(s, AV_LOG_ERROR,"Unable to seek back to the start\n");
- }
- /*
- * 挂载了两个Section类型的过滤器,
- * 其实在TS的两种负载中,section是PES的元数据,
- * 只有先解析了section,才能进一步解析PES数据,因此先挂上section的过滤器。
-
*/
- mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
- mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
- /*
- */
- handle_packets(ts, s->probesize/ ts->raw_packet_size);
- /* if could not find service, enable auto_guess*/
- ts->auto_guess= 1;
- av_dlog(ts->stream,"tuning done\n");
- s->ctx_flags|= AVFMTCTX_NOHEADER;
- }
- else
- {
- ...
- }
- avio_seek(pb, pos, SEEK_SET);
- return 0;
- fail:
- return -1;
- }
- MpegTSFilter *mpegts_open_section_filter(MpegTSContext* ts,
- unsigned int pid,
- SectionCallback* section_cb,
- void* opaque,
- int check_crc)
-
{
- MpegTSFilter *filter;
- MpegTSSectionFilter *sec;
- av_dlog(ts->stream,"Filter: pid=0x%x\n", pid);
- if (pid>= NB_PID_MAX|| ts->pids[pid])
- return NULL;
- filter = av_mallocz(sizeof(MpegTSFilter));
- if (!filter)
- return NULL;
- ts->pids[pid]=filter;
- filter->type= MPEGTS_SECTION;
- filter->pid= pid;
- filter->last_cc=-1;
- sec = &filter->u.section_filter;
- sec->section_cb= section_cb;
- sec->opaque= opaque;
- sec->section_buf= av_malloc(MAX_SECTION_SIZE);
- sec->check_crc= check_crc;
- if (!sec->section_buf)
- {
- av_free(filter);
- return NULL;
- }
- return filter;
- }
struct MpegTSContext; | V struct MpegTSFilter; | V +--------------+---------------+ | | V V MpegTSPESFilter MpegTSSectionFilter
就是struct MpegTSContext;中有NB_PID_MAX(8192)个TS的Filter, 而每个struct MpegTSFilter 可能是 PES 的Filter 或者是 Section的Filter。
为什么NB_PID_MAX 是 8192, 需要看TS的语法结构(ISO/IEC 138138-1 page 19):
- Syntax No. of bits Mnemonic
- transport_packet(){
- sync_byte 8 bslbf
- transport_error_indicator 1 bslbf
- payload_unit_start_indicator 1 bslbf
- transport_priority 1 bslbf
- PID 13 uimsbf
- transport_scrambling_control 2 bslbf
- adaptation_field_control 2 bslbf
- continuity_counter 4 uimsbf
- if (adaptation_field_control=='10'||
- adaptation_field_control=='11')
- {
- adaptation_field()
- }
-
- if (adaptation_field_control=='01'||
- adaptation_field_control=='11')
- {
- for (i=0;i<N;i++)
- {
- data_byte 8 bslbf
- }
- }
- }
挂载上了两种section过滤器,如下: ========================================================================= PID |Section Name |Callback ========================================================================= SDT_PID(0x0011) |ServiceDescriptionTable|sdt_cb | | PAT_PID(0x0000) |ProgramAssociationTable|pat_cb ========================================================================= 设计成回调函数,是为了在后面使用。
4. MPEG2-TS的包处理
-
int handle_packets(MpegTSContext*ts,int nb_packets)
-
{
- AVFormatContext *s = ts->stream;
- uint8_t packet[TS_PACKET_SIZE];
- int packet_num, ret;
-
- ts->stop_parse= 0;
- packet_num = 0;
- for (;; )
- {
- packet_num++;
-
- if (nb_packets!= 0&& packet_num>= nb_packets||
- ts->stop_parse> 1)
- {
- ret = AVERROR(EAGAIN);
- break;
- }
- if (ts->stop_parse> 0)
- break;
-
- ret = read_packet(s, packet, ts->raw_packet_size);
- if (ret!= 0)
- return ret;
- ret = handle_packet(ts, packet);
- if (ret!= 0)
- return ret;
- }
-
- return 0;
- }
它的代码结构很简单: handle_packets() | +->read_packet() | +->handle_packet() | +->write_section_data() read_packet(), 很简单, 就是去找sync_byte(0x47), handle_packet(),是真正处理数据的地方.它的代码如下:
- /*
- * 功能: handle one TS packet
-
*/
-
int handle_packet(MpegTSContext*ts,const uint8_t *packet)
-
{
- AVFormatContext *s = ts->stream;
- MpegTSFilter *tss;
- int len, pid, cc, expected_cc, cc_ok, afc, is_start;
- const uint8_t *p, *p_end;
- int64_t pos;
-
/* 获取该包的PID */
- pid = AV_RB16(packet+ 1)& 0x1fff;
- if (pid&& discard_pid(ts, pid))
- return 0;
- /*
- * 是否是PES或者Section的开头
- * 即syntax element: payload_unit_start_indicator
-
*/
- is_start = packet[1]& 0x40;
- tss = ts->pids[pid];
- /*
- * ts->auto_guess此时为0,因此不考虑下面的代码
-
*/
- if (ts->auto_guess&& tss== NULL&& is_start)
- {
- add_pes_stream(ts, pid,-1);
- tss = ts->pids[pid];
- }
- if (!tss)
- return 0;
- /*
- * continuity check (currently not used)
- * 虽然检查,但不利用检查的结果
-
*/
- cc = (packet[3]& 0xf);
- expected_cc = (packet[3]& 0x10)? (tss->last_cc+ 1)& 0x0f : tss->last_cc;
- cc_ok = (tss->last_cc< 0)|| (expected_cc== cc);
- tss->last_cc= cc;
- /*
- * 解析 adaptation_field_control 语法元素
- * =======================================================
- * 00 | Reserved for future use by ISO/IEC
- * 01 | No adaptation_field, payload only
- * 10 | Adaptation_field only, no payload
- * 11 | Adaptation_field follwed by payload
- * =======================================================
-
*/
- afc = (packet[3]>> 4)& 3;
- p = packet + 4;
- if (afc== 0)/* reserved value*/
- return 0;
- if (afc== 2)/* adaptation field only*/
- return 0;
- if (afc== 3)
- {
- /*
- * 跳过 adapation field
- * p[0]对应的语法元素为: adaptation_field_length
-
*/
- p += p[0]+ 1;
- }
- /*
- * if past the end of packet, ignore
- * p已近到达TS包中的有效负载的地方
-
*/
- p_end = packet + TS_PACKET_SIZE;
- if (p>= p_end)
- return 0;
- pos = avio_tell(ts->stream->pb);
- ts->pos47= pos% ts->raw_packet_size;
- if (tss->type== MPEGTS_SECTION)
- {
- /*
- * 针对Section, 第一个字节对应的语法元素为:pointer_field(见2.4.4.1),
- * 它表示在当前TS包中,从pointer_field开始到第一个section的第一个字节间的字节数。
- * 当TS包中有至少一个section的起始时,
- * payload_unit_start_indicator = 1 且 TS负载的第一个字节为pointer_field;
- * pointer_field = 0x00时,表示section的起始就在这个字节之后;
- * 当TS包中没有section的起始时,
- * payload_unit_start_indicator = 0 且 TS负载中没有pointer_field;
-
*/
- if (is_start)
- {
- /* pointer field present*/
- len =*p++;
- if (p+len > p_end)
- return 0;
- if (len&& cc_ok)
- {
- /*
- * write remaining section bytes
- * TS包的负载部分由Section A的End部分和Section B的Start组成,
- * 先把Section A的End部分写入
-
*/
- write_section_data(s, tss, p,len, 0);
- /* check whetherfilter has been closed*/
- if (!ts->pids[pid])
- return 0;
- }
- p +=len;
- if (p< p_end)
- {
- /*
- * 再将Section B的Start部分写入
-
*/
- write_section_data(s, tss, p, p_end- p, 1);
- }
- }
- else
- {
-
/* TS包负载仅是一个Section的中间部分部分,将其写入*/
- if (cc_ok)
- {
- write_section_data(s, tss, p, p_end- p, 0);
- }
- }
- }
- else
- {
- int ret;
- /*
- * 如果是PES类型,直接调用其Callback,
- * 但显然,只有Section部分解析完成后才可能解析PES
-
*/
- // Note: The position here points actually behind the current packet.
- if ((ret= tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start,
- pos - ts->raw_packet_size))< 0)
- return ret;
- }
- return 0;
- }
5. 节目指定信息的解析
- /*
- * PAT(Program Association Table) 节目相关表
- * 提供了节目号与PID值的对应关系
- * 见ISO/IEC 13818-1 2.4.4.3 Table 2-30
-
*/
- void pat_cb(MpegTSFilter *filter,const uint8_t*section,int section_len);
- /*
- * PMT(Program Map Table) 节目映射表
- * 提供了节目号与组成节目的元素之间的映射关系--或者称为"节目定义"
- * 见ISO/IEC 13818-1 2.4.4.8 Table 2-33
-
*/
- void pmt_cb(MpegTSFilter *filter,const uint8_t*section,int section_len);
- /*
- * SDT(Transport Stream Description Table) TS描述表
- * 用于定义TS描述子的表
- * 见ISO/IEC 13818-1 2.4.4.12 Table 2-36
-
*/
- void sdt_cb(MpegTSFilter *filter,const uint8_t*section,int section_len)
6. 解析PES包
- /*
- * 见ISO/IEC 13818-1 2.4.3.6 Table 2-21
-
*/
-
int mpegts_push_data(MpegTSFilter*filter,
- const uint8_t* buf,
- int buf_size,
- int is_start,
- int64_t pos);
至此,整个TS层的解析基本完成