介绍: 
MPEG的系统层编码为不同的应用场景设计了两种格式: 
TS(Transport Stream) 和PS(Program Stream),
它们两者之间不具有层级关系,
在逻辑上,它们两者都是由PES(Packetized Elementary Stream)包组成的,
所以可以很方便地实现相互转换.
 
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 that is used for 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 sooner or 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 (== 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 bytes to get 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 */
  •     /* first do a scaning to get 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
  •     } 
  •   } 
  • }
  • 而8192,是2^13=8192(PID)的最大数目,
    为什么会有PES和Section的区分,更详细的可以参考ISO/IEC-13818-1.
     
     
    挂载上了两种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 *= 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 *= 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_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 (+ 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 whether filter has been closed */
  •         if (!ts->pids[pid])
  •           return 0;
  •       }
  •       p += len;
  •       if (< 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;
  • }
  • write_section_data()函数:
      反复收集buffer中的数据,指导完成相关Section的重组过程,
      然后调用之前注册的两个section_cb.
     
     
    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层的解析基本完成。

    相关文章:

    • 2022-12-23
    • 2022-12-23
    • 2022-12-23
    • 2022-12-23
    • 2022-12-23
    • 2021-12-03
    • 2021-11-01
    • 2021-11-03
    猜你喜欢
    • 2022-12-23
    • 2022-12-23
    • 2022-12-23
    • 2021-05-27
    • 2021-10-22
    • 2022-01-05
    • 2021-07-24
    相关资源
    相似解决方案