• ffmpeg解析TS流(转)


    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函数
    1. AVInputFormat ff_mpegts_demuxer = { 
    2.     "mpegts", 
    3.     NULL_IF_CONFIG_SMALL("MPEG-2 transport stream format"),
    4.     sizeof(MpegTSContext),
    5.     mpegts_probe,
    6.     mpegts_read_header,
    7.     mpegts_read_packet, 
    8.     mpegts_read_close, 
    9.     read_seek,
    10.     mpegts_get_pcr,
    11.     .flags = AVFMT_SHOW_IDS|AVFMT_TS_DISCONT, 
    12. #ifdef USE_SYNCPOINT_SEARCH 
    13.     .read_seek2 = read_seek2, 
    14. #endif 
    15. };
     
    2. 解析流中的TS格式
    1. /*
    2.  * 出现3种格式,主要原因是:
    3.  * TS标准是 188Bytes;
    4.  * 日本标准是192Bytes的DVH-S格式;
    5.  * 第三种的 204Bytes则是在188Bytes的基础上,加上16Bytes的FEC(前向纠错).
    6.  */
    7. #define TS_PACKET_SIZE 188
    8. #define TS_DVHS_PACKET_SIZE 192
    9. #define TS_FEC_PACKET_SIZE 204
    10. #define TS_MAX_PACKET_SIZE 204
    11. //< maximum score, half of that is used for file-extension-based detection
    12. #define AVPROBE_SCORE_MAX 100
     

    1. /*
    2.  * 函数功能:
    3.  * 分析流中是三种TS格式的哪一种
    4.  */
    5. static int mpegts_probe(AVProbeData *p)
    6. {
    7. #define CHECK_COUNT 10
    8.   const int size= p->buf_size;
    9.   int score, fec_score, dvhs_score;
    10.   int check_count= size / TS_FEC_PACKET_SIZE;
    11.   if (check_count < CHECK_COUNT)
    12.       return -1;
    13.   score     = analyze(p->buf, TS_PACKET_SIZE *check_count, TS_PACKET_SIZE , NULL) 
    14.               * CHECK_COUNT / check_count;
    15.   dvhs_score= analyze(p->buf, TS_DVHS_PACKET_SIZE*check_count, TS_DVHS_PACKET_SIZE, NULL)
    16.               * CHECK_COUNT / check_count;
    17.   fec_score = analyze(p->buf, TS_FEC_PACKET_SIZE *check_count, TS_FEC_PACKET_SIZE , NULL)
    18.               * CHECK_COUNT / check_count;
    19.   /* 
    20.    * we need a clear definition for the returned score ,
    21.    * otherwise things will become messy sooner or later
    22.    */
    23.   if (score > fec_score && score > dvhs_score && score > 6) 
    24.     return AVPROBE_SCORE_MAX + score - CHECK_COUNT;
    25.   else if(dvhs_score > score && dvhs_score > fec_score && dvhs_score > 6) 
    26.     return AVPROBE_SCORE_MAX + dvhs_score - CHECK_COUNT;
    27.   else if(fec_score > 6) 
    28.     return AVPROBE_SCORE_MAX + fec_score - CHECK_COUNT;
    29.   else 
    30.     return -1;
    31. }
     
    1. /*
    2.  * 函数功能:
    3.  * 在size大小的buf中,寻找满足特定格式,长度为packet_size的
    4.  * packet的个数;
    5.  * 显然,返回的值越大越可能是相应的格式(188/192/204)
    6.  */
    7. static int analyze(const uint8_t *buf, int size, int packet_size, int *index){
    8.   int stat[TS_MAX_PACKET_SIZE];
    9.   int i;
    10.   int x=0;
    11.   int best_score=0;
    12.   memset(stat, 0, packet_size*sizeof(int));
    13.     
    14.   for (x=i=0; i < size-3; i++)
    15.   {
    16.     if ((buf[i] == 0x47) && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30))
    17.     {
    18.       stat[x]++;
    19.             
    20.       if (stat[x] > best_score)
    21.       {
    22.         best_score= stat[x];
    23.         if (index) 
    24.           *index= x;
    25.       }
    26.     }
    27.     x++;
    28.     if (x == packet_size) 
    29.       x= 0; 
    30.   }
    31.     
    32.   return best_score;
    33. }
     
    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头解析
    1. #define NB_PID_MAX 8192
    2. #define MAX_SECTION_SIZE 4096
    3.         
    4. /* pids */
    5. #define PAT_PID 0x0000
    6. #define SDT_PID 0x0011
    7.         
    8. /* table ids */
    9. #define PAT_TID 0x00
    10. #define PMT_TID 0x02
    11. #define SDT_TID 0x42

    1. /*
    2.  * 函数功能:
    3.  * 
    4.  */
    5. int mpegts_read_header(AVFormatContext *s, AVFormatParameters *ap)
    6. {
    7.   /*
    8.    * MpegTSContext , 是为了解码不同容器格式所使用的私有数据,
    9.    * 只有在相应的诸如mpegts.c文件才可以使用的.
    10.    * 这样,增加了这个库的模块化.
    11.    */
    12.   MpegTSContext *ts = s->priv_data;
    13.   AVIOContext *pb = s->pb;
    14.   uint8_t buf[8*1024];
    15.   int len;
    16.   int64_t pos;
    17.   /* read the first 8*1024 bytes to get packet size */
    18.   pos = avio_tell(pb);                   // 获取buf的当前位置
    19.   len = avio_read(pb, buf, sizeof(buf)); // 从pb->opaque中读取sizeof(buf)个字节到buf
    20.   if (len != sizeof(buf))
    21.     goto fail;
    22.   /* 
    23.    * 获得TS包的实际长度
    24.    */
    25.   ts->raw_packet_size = get_packet_size(buf, sizeof(buf));
    26.   if (ts->raw_packet_size <= 0) 
    27.   {
    28.     av_log(s, AV_LOG_WARNING, "Could not detect TS packet size, defaulting to non-FEC/DVHS ");
    29.     ts->raw_packet_size = TS_PACKET_SIZE;
    30.   }
    31.   ts->stream = s; 
    32.   ts->auto_guess = 0;
    33.   
    34.   if (s->iformat == &ff_mpegts_demuxer) 
    35.   {
    36.     /* normal demux */
    37.     /* first do a scaning to get all the services */
    38.     if (avio_seek(pb, pos, SEEK_SET) < 0)
    39.     {
    40.       av_log(s, AV_LOG_ERROR, "Unable to seek back to the start ");
    41.     }
    42.     /*
    43.      * 挂载了两个Section类型的过滤器,
    44.      * 其实在TS的两种负载中,section是PES的元数据,
    45.      * 只有先解析了section,才能进一步解析PES数据,因此先挂上section的过滤器。
    46.      */
    47.     mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
    48.     mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
    49.     /*
    50.      */ 
    51.     handle_packets(ts, s->probesize / ts->raw_packet_size);
    52.     /* if could not find service, enable auto_guess */
    53.     ts->auto_guess = 1;
    54.     av_dlog(ts->stream, "tuning done ");
    55.     s->ctx_flags |= AVFMTCTX_NOHEADER;
    56.   } 
    57.   else 
    58.   {
    59.     ...
    60.   }
    61.   avio_seek(pb, pos, SEEK_SET); 
    62.   return 0;
    63. fail:
    64.   return -1;
    65. }

    1. MpegTSFilter *mpegts_open_section_filter(MpegTSContext* ts, 
    2.                                          unsigned int pid,
    3.                                          SectionCallback* section_cb, 
    4.                                          void* opaque,
    5.                                          int check_crc)
    6.   MpegTSFilter *filter;
    7.   MpegTSSectionFilter *sec; 
    8.   av_dlog(ts->stream, "Filter: pid=0x%x ", pid);
    9.   if (pid >= NB_PID_MAX || ts->pids[pid])
    10.     return NULL;
    11.   filter = av_mallocz(sizeof(MpegTSFilter));
    12.   if (!filter)
    13.     return NULL;
    14.   ts->pids[pid] = filter;
    15.   filter->type = MPEGTS_SECTION;
    16.   filter->pid = pid; 
    17.   filter->last_cc = -1;
    18.   sec = &filter->u.section_filter;
    19.   sec->section_cb = section_cb;
    20.   sec->opaque = opaque;
    21.   sec->section_buf= av_malloc(MAX_SECTION_SIZE);
    22.   sec->check_crc = check_crc;
    23.   if (!sec->section_buf) 
    24.   {
    25.     av_free(filter);
    26.     return NULL;
    27.   }
    28.   return filter;
    29. }
    对于这部分代码,需要分析数据结构的定义:
    依次为:
     
        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):
    1. Syntax                          No. of bits         Mnemonic
    2. transport_packet(){ 
    3.   sync_byte                        8                 bslbf
    4.   transport_error_indicator        1                 bslbf
    5.   payload_unit_start_indicator     1                 bslbf
    6.   transport_priority               1                 bslbf
    7.   PID                              13                uimsbf
    8.   transport_scrambling_control     2                 bslbf
    9.   adaptation_field_control         2                 bslbf
    10.   continuity_counter               4                 uimsbf
    11.   if (adaptation_field_control=='10' || 
    12.       adaptation_field_control=='11' )
    13.   { 
    14.         adaptation_field() 
    15.   } 
    16.        
    17.   if (adaptation_field_control=='01' || 
    18.       adaptation_field_control=='11' ) 
    19.   { 
    20.     for (i=0;i<N;i++)
    21.     { 
    22.       data_byte                     8                bslbf
    23.     } 
    24.   } 
    25. }
    而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的包处理
     

    1. int handle_packets(MpegTSContext *ts, int nb_packets)
    2. {
    3.   AVFormatContext *s = ts->stream;
    4.   uint8_t packet[TS_PACKET_SIZE];
    5.   int packet_num, ret;
    6.      
    7.   ts->stop_parse = 0;
    8.   packet_num = 0;
    9.   for ( ; ; ) 
    10.   {
    11.     packet_num++;
    12.     
    13.     if (nb_packets != 0 && packet_num >= nb_packets ||
    14.         ts->stop_parse > 1) 
    15.     {
    16.       ret = AVERROR(EAGAIN);
    17.       break;
    18.     }
    19.     if (ts->stop_parse > 0)
    20.       break;
    21.         
    22.     ret = read_packet(s, packet, ts->raw_packet_size);
    23.     if (ret != 0)
    24.       return ret;
    25.     ret = handle_packet(ts, packet);
    26.     if (ret != 0)
    27.       return ret;
    28.   } 
    29.   
    30.   return 0; 
    31. }
     
    它的代码结构很简单:
    handle_packets()
        |
        +->read_packet()
        |
        +->handle_packet()
            |
            +->write_section_data()
        
    read_packet(),  很简单, 就是去找sync_byte(0x47),
    handle_packet(),是真正处理数据的地方.它的代码如下:
    1. /* 
    2.  * 功能: handle one TS packet 
    3.  */
    4. int handle_packet(MpegTSContext *ts, const uint8_t *packet)
    5. {
    6.   AVFormatContext *s = ts->stream;
    7.   MpegTSFilter *tss;
    8.   int len, pid, cc, expected_cc, cc_ok, afc, is_start;
    9.   const uint8_t *p, *p_end;
    10.   int64_t pos;
    11.   /* 获取该包的PID */
    12.   pid = AV_RB16(packet + 1) & 0x1fff;
    13.   if (pid && discard_pid(ts, pid))
    14.      return 0;
    15.   /* 
    16.    * 是否是PES或者Section的开头
    17.    * 即syntax element: payload_unit_start_indicator 
    18.    */
    19.   is_start = packet[1] & 0x40;
    20.   tss = ts->pids[pid];
    21.   /* 
    22.    * ts->auto_guess此时为0,因此不考虑下面的代码
    23.    */
    24.   if (ts->auto_guess && tss == NULL && is_start) 
    25.   {
    26.     add_pes_stream(ts, pid, -1);
    27.     tss = ts->pids[pid];
    28.   }
    29.   if (!tss)
    30.     return 0;
    31.   /* 
    32.    * continuity check (currently not used) 
    33.    * 虽然检查,但不利用检查的结果
    34.    */
    35.   cc = (packet[3] & 0xf);
    36.   expected_cc = (packet[3] & 0x10) ? (tss->last_cc + 1) & 0x0f : tss->last_cc;
    37.   cc_ok = (tss->last_cc < 0) || (expected_cc == cc);
    38.   tss->last_cc = cc;
    39.   /* 
    40.    * 解析 adaptation_field_control 语法元素
    41.    * =======================================================
    42.    * 00 | Reserved for future use by ISO/IEC
    43.    * 01 | No adaptation_field, payload only
    44.    * 10 | Adaptation_field only, no payload
    45.    * 11 | Adaptation_field follwed by payload
    46.    * =======================================================
    47.    */ 
    48.   afc = (packet[3] >> 4) & 3;
    49.   p = packet + 4;
    50.   if (afc == 0) /* reserved value */
    51.     return 0; 
    52.   if (afc == 2) /* adaptation field only */ 
    53.     return 0;
    54.   if (afc == 3) 
    55.   {
    56.     /* 
    57.      * 跳过 adapation field 
    58.      * p[0]对应的语法元素为: adaptation_field_length
    59.      */
    60.     p += p[0] + 1;
    61.   }
    62.   /* 
    63.    * if past the end of packet, ignore 
    64.    * p已近到达TS包中的有效负载的地方
    65.    */
    66.   p_end = packet + TS_PACKET_SIZE;
    67.   if (p >= p_end)
    68.     return 0;
    69.   pos = avio_tell(ts->stream->pb);
    70.   ts->pos47= pos % ts->raw_packet_size;
    71.   if (tss->type == MPEGTS_SECTION) 
    72.   {
    73.     /*
    74.      * 针对Section, 第一个字节对应的语法元素为:pointer_field(见2.4.4.1),
    75.      * 它表示在当前TS包中,从pointer_field开始到第一个section的第一个字节间的字节数。
    76.      * 当TS包中有至少一个section的起始时,
    77.      *    payload_unit_start_indicator = 1 且 TS负载的第一个字节为pointer_field;
    78.      *    pointer_field = 0x00时,表示section的起始就在这个字节之后;
    79.      * 当TS包中没有section的起始时, 
    80.      *    payload_unit_start_indicator = 0 且 TS负载中没有pointer_field;
    81.      */
    82.     if (is_start) 
    83.     {
    84.       /* pointer field present */
    85.       len = *p++;
    86.       if (p + len > p_end)
    87.         return 0;
    88.       if (len && cc_ok) 
    89.       {
    90.         /* 
    91.          * write remaining section bytes 
    92.          * TS包的负载部分由Section A的End部分和Section B的Start组成,
    93.          * 先把Section A的End部分写入
    94.          */
    95.         write_section_data(s, tss, p, len, 0);
    96.         /* check whether filter has been closed */
    97.         if (!ts->pids[pid])
    98.           return 0;
    99.       }
    100.       p += len;
    101.       if (p < p_end) 
    102.       { 
    103.         /*
    104.          * 再将Section B的Start部分写入
    105.          */
    106.         write_section_data(s, tss, p, p_end - p, 1);
    107.       }
    108.     } 
    109.     else 
    110.     {
    111.       /* TS包负载仅是一个Section的中间部分部分,将其写入*/
    112.       if (cc_ok) 
    113.       {
    114.         write_section_data(s, tss, p, p_end - p, 0);
    115.       }
    116.     }
    117.   } 
    118.   else 
    119.   {
    120.     int ret;
    121.     /* 
    122.      * 如果是PES类型,直接调用其Callback,
    123.      * 但显然,只有Section部分解析完成后才可能解析PES
    124.      */
    125.     // Note: The position here points actually behind the current packet.
    126.     if ((ret = tss->u.pes_filter.pes_cb(tss, p, p_end - p, is_start,
    127.         pos - ts->raw_packet_size)) < 0)
    128.       return ret;
    129.   }
    130.   return 0;
    131. }
    write_section_data()函数:
      反复收集buffer中的数据,指导完成相关Section的重组过程,
      然后调用之前注册的两个section_cb.
     
     
    5. 节目指定信息的解析

    1. /*
    2.  * PAT(Program Association Table) 节目相关表
    3.  * 提供了节目号与PID值的对应关系
    4.  * 见ISO/IEC 13818-1 2.4.4.3 Table 2-30
    5.  */
    6. void pat_cb(MpegTSFilter *filter, const uint8_t *section, int section_len);
    7. /*
    8.  * PMT(Program Map Table) 节目映射表
    9.  * 提供了节目号与组成节目的元素之间的映射关系--或者称为"节目定义"
    10.  * 见ISO/IEC 13818-1 2.4.4.8 Table 2-33
    11.  */
    12. void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len);
    13. /*
    14.  * SDT(Transport Stream Description Table) TS描述表
    15.  * 用于定义TS描述子的表
    16.  * 见ISO/IEC 13818-1 2.4.4.12 Table 2-36
    17.  */
    18. void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
     
    6. 解析PES包
    1. /* 
    2.  * 见ISO/IEC 13818-1 2.4.3.6 Table 2-21
    3.  */
    4. int mpegts_push_data(MpegTSFilter* filter,
    5.                      const uint8_t* buf, 
    6.                      int buf_size, 
    7.                      int is_start,
    8.                      int64_t pos);
     
    至此,整个TS层的解析基本完成。
    提高技能如同提升自信心。
  • 相关阅读:
    AJAX 基础知识
    jQuery知识点总结
    css基础应用总结
    javascript 总结
    找回密码-博客园
    centerOS 7 安装MySql
    java leetcode TreeNode类、ListNode类的实现
    iOS StatusBar状态栏文字颜色更改
    使用Jmeter压力测试工具测试
    安装node.js
  • 原文地址:https://www.cnblogs.com/chims-liu-touch/p/9264999.html
Copyright © 2020-2023  润新知