介绍: 
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. for (x=i=0; i < size-3; i++)
  14. {
  15. if ((buf[i] == 0x47) && !(buf[i+1] & 0x80) && (buf[i+3] & 0x30))
  16. {
  17. stat[x]++;
  18. if (stat[x] > best_score)
  19. {
  20. best_score= stat[x];
  21. if (index)
  22. *index= x;
  23. }
  24. }
  25. x++;
  26. if (x == packet_size)
  27. x= 0;
  28. }
  29. return best_score;
  30. }
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. /* pids */
  4. #define PAT_PID 0x0000
  5. #define SDT_PID 0x0011
  6. /* table ids */
  7. #define PAT_TID 0x00
  8. #define PMT_TID 0x02
  9. #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\n");
  29. ts->raw_packet_size = TS_PACKET_SIZE;
  30. }
  31. ts->stream = s;
  32. ts->auto_guess = 0;
  33. if (s->iformat == &ff_mpegts_demuxer)
  34. {
  35. /* normal demux */
  36. /* first do a scaning to get all the services */
  37. if (avio_seek(pb, pos, SEEK_SET) < 0)
  38. {
  39. av_log(s, AV_LOG_ERROR, "Unable to seek back to the start\n");
  40. }
  41. /*
  42. * 挂载了两个Section类型的过滤器,
  43. * 其实在TS的两种负载中,section是PES的元数据,
  44. * 只有先解析了section,才能进一步解析PES数据,因此先挂上section的过滤器。
  45. */
  46. mpegts_open_section_filter(ts, SDT_PID, sdt_cb, ts, 1);
  47. mpegts_open_section_filter(ts, PAT_PID, pat_cb, ts, 1);
  48. /*
  49. */
  50. handle_packets(ts, s->probesize / ts->raw_packet_size);
  51. /* if could not find service, enable auto_guess */
  52. ts->auto_guess = 1;
  53. av_dlog(ts->stream, "tuning done\n");
  54. s->ctx_flags |= AVFMTCTX_NOHEADER;
  55. }
  56. else
  57. {
  58. ...
  59. }
  60. avio_seek(pb, pos, SEEK_SET);
  61. return 0;
  62. fail:
  63. return -1;
  64. }
  1. MpegTSFilter *mpegts_open_section_filter(MpegTSContext* ts,
  2. unsigned int pid,
  3. SectionCallback* section_cb,
  4. void* opaque,
  5. int check_crc)
  6. {
  7. MpegTSFilter *filter;
  8. MpegTSSectionFilter *sec;
  9. av_dlog(ts->stream, "Filter: pid=0x%x\n", pid);
  10. if (pid >= NB_PID_MAX || ts->pids[pid])
  11. return NULL;
  12. filter = av_mallocz(sizeof(MpegTSFilter));
  13. if (!filter)
  14. return NULL;
  15. ts->pids[pid] = filter;
  16. filter->type = MPEGTS_SECTION;
  17. filter->pid = pid;
  18. filter->last_cc = -1;
  19. sec = &filter->u.section_filter;
  20. sec->section_cb = section_cb;
  21. sec->opaque = opaque;
  22. sec->section_buf= av_malloc(MAX_SECTION_SIZE);
  23. sec->check_crc = check_crc;
  24. if (!sec->section_buf)
  25. {
  26. av_free(filter);
  27. return NULL;
  28. }
  29. return filter;
  30. }
对于这部分代码,需要分析数据结构的定义:
依次为:
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. if (adaptation_field_control=='01' ||
  17. adaptation_field_control=='11' )
  18. {
  19. for (i=0;i<N;i++)
  20. {
  21. data_byte                     8                bslbf
  22. }
  23. }
  24. }
而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. ts->stop_parse = 0;
  7. packet_num = 0;
  8. for ( ; ; )
  9. {
  10. packet_num++;
  11. if (nb_packets != 0 && packet_num >= nb_packets ||
  12. ts->stop_parse > 1)
  13. {
  14. ret = AVERROR(EAGAIN);
  15. break;
  16. }
  17. if (ts->stop_parse > 0)
  18. break;
  19. ret = read_packet(s, packet, ts->raw_packet_size);
  20. if (ret != 0)
  21. return ret;
  22. ret = handle_packet(ts, packet);
  23. if (ret != 0)
  24. return ret;
  25. }
  26. return 0;
  27. }
它的代码结构很简单:
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层的解析基本完成。


ffmpeg对mpeg2-TS详细解析相关推荐

  1. 数字电视之TS流解析

    做了这么久的TV方案,对数字电视也很了解,今天来总结一下,MPEG-2 TS流解析的细节: TS流中有两种标识符,一种是包标识符,一种是表标识符.具有相同PID的不用信息表由表标识符table Id来 ...

  2. TS流解析 ffmpeg

    ffmpeg关于mpegts码流解析部分: 1.  首先来看main函数 通过av_register_all()来注册所有的编解码器.解复用器(这里只用到mpegts_demuxer).注册所使用的协 ...

  3. MPEG2 TS 总汇

    MPEG2 TS的基本格式,其中包括PES,PS和TS,以及相关字段的介绍.那么作为一种传输流,TS将内容进行打包/复用,让其媒体内容变成TS传输,并最终在解码端解码.简单来看,TS是一个传输层的协议 ...

  4. TS流解析【PCR】自己的总结

    http://www.cnblogs.com/ztteng/articles/3166025.html http://blog.csdn.net/liuhongxiangm/article/detai ...

  5. TS流解析 二 *****

    1.TS格式介绍 TS:全称为MPEG2-TS.TS即"Transport Stream"的缩写.它是分包发送的,每一个包长为188字节(还有192和204个字节的包).包的结构为 ...

  6. 图像视频信息库改直播服务器,短视频直播系统,开发流程详细解析

    原标题:短视频直播系统,开发流程详细解析 短视频直播系统的开发也分Android端和iOS端,不同端口对应不同的开发方式,使用不同的开发协议,与直播系统的开发流程相同,也是分五个步骤,今天就给大家详细 ...

  7. Linux操作系统PS命令详细 解析

    http://blog.chinaunix.net/space.php?uid=20564848&do=blog&id=74654 Linux操作系统PS命令详细 解析 要对系统中进程 ...

  8. HDMI EDID详细解析——C代码实现

    继上一篇<HDMI EDID详细解析> https://blog.csdn.net/cfl927096306/article/details/108017501 现在用C代码来实现解析HD ...

  9. MPEG2 TS与ISMA的比较

    简单的比较如下: MPEG-2 TS/UDP方式将媒体数据,包括视频.音频和其他数据封装成MPEG-2 TS格式,再承载在UDP和IP协议之上,其优点是能够承载不同编码标准的媒体数据,并且视频和音频数 ...

  10. DophinScheduler ui部分 核心代码详细解析——底层逻辑与具体实现

    2021SC@SDUSC 文章目录 一.前端 1.\_test\_ 1.Counter.js 2.test.spec.js 2.build 3.node_modules 4.src 一.前端 1._t ...

最新文章

  1. 解读Go语言的2018:怎么就在中国火成这样了?
  2. 旷视孙剑团队提出Anchor DETR:基于Transformer的目标检测新网络
  3. 类的笔记整理__7-10__
  4. jvm类加载、初始化
  5. 单臂路由的配置及应用:
  6. [仙吕·一半儿] 题画《翠林和鸣》
  7. windows常见软件库
  8. “约见”面试官系列之常见面试题第十六篇之http(建议收藏)
  9. 工业机器人用铸铁牌号_锻钢牌号表示方法你了解么?
  10. CODE[VS]1012 最大公约数和最小公倍数问题
  11. Java中string中hashcode_Java String中的hashCode实现
  12. Dubbo介绍前篇------单一应用框架、垂直应用框架、分布式应用框架、流动计算框架,及RPC的简介
  13. ExtJS4.2学习(13)基于表格的扩展插件---rowEditing
  14. html 抽奖机 代码,JS实现转动随机数抽奖特效代码
  15. Python requests+BeautifulSoup 采集 安居客_新房信息
  16. 安全加密 - 加密算法 - 摘要算法 - 秘钥交换协议 - 量子加密
  17. QQ钱包的接入及其设计分析
  18. 拥有百万粉丝的大牛讲述学Android的历程程。看看你缺了哪些?
  19. 持续集成与持续部署(五)01-TravisCI——使用简介-Travis CI 只支持 Github,提供的是持续集成服务 配置项目的.travis.yml文件
  20. Linux查看端口占用情况的命令

热门文章

  1. 最好用的python音频库之一:pydub的中文文档(含API)
  2. 你需要来自XX的权限才能对此文件夹进行更改”的解决方法
  3. Oracle设计规范!
  4. TCN论文及代码解读总结
  5. 模糊c均值聚类算法的c++实现
  6. 风投把钱砸在了这十家初创公司
  7. windows下更换MySql数据库数据文件夹位置
  8. 有道云笔记新版上线 深化本土应用合作
  9. Windows10安装WAMP遇到的问题
  10. PHP sha256WithRsa加解密