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层的解析基本完成。
http://blog.chinaunix.net/uid-26000296-id-3454725.html

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

  1. ffmpeg mplayer x264 代码重点详解 详细分析

    ffmpeg和mplayer中求平均值得方法 1 ordinary c language level #define avg2(a,b) ((a+b+1)>>1) #define avg4 ...

  2. MPEG2 TS 总汇

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

  3. 结合python使用ffmpeg将批量ts文件合成mp4。

    一.获取ts文件,如下图所示. 二.安装 ffmpeg 并配置到环境变量 linux: apt-get install ffmpeg libavcodec-extra windows: 1.进入 ht ...

  4. ffmpeg命令分析【详细分析合集】

    ffmpeg命令分析[内容包括]-vf/ac/b:v/r/re/segment/t/ss/output_ts_offset/vn/acc/print/yuv420p/yuv封装mp4/FFmpeg硬件 ...

  5. MPEG2 TS与ISMA的比较

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

  6. python爬虫+ffmpeg批量下载ts文件,解密合并成mp4

    标题 python爬虫+ffmpeg批量下载ts文件,解密合并成mp4 文章目录 标题 前言 一.分析目标 二.寻找url规律 三.写代码 总结 前言 (第一次写博客,写的不好请见谅哈~~) 目标是大 ...

  7. ffmpeg 视频转ts切片 生成m3u8视频播放列表

    近期做视频点播,要求将视频文件切片成ts文件.经搜索得到以下两个命令,可完成这个任务. 一  首先将视频文件转为视频编码h264,音频编码aac格式的mp4文件       1.可以预先使用ffpro ...

  8. cmd合并多个ts文件,ffmpeg快速转ts为mp4文件,通过m3u8合并文件

    1.如何合并多个ts文件 进入目录执行如下cmd命令即可 copy /b *.ts new.ts 2.如何将ts转为mp4 ffmpeg下载 官网:https://ffmpeg.org/ github ...

  9. ffmpeg合并m3u8 ts key文件 解决Invalid data found when processing input错误

    首先我先说明一下我的使用场景和错误原因: 我使用IDM从网站中批量下载ts文件,然后下载对应的一个.m3u8文件(ts文件列表和加密信息的描述)和一个key.key文件(秘钥).以上文件全部保存在同一 ...

最新文章

  1. win10安装虚拟机提示主IP地址显示网络信息不可用
  2. Java项目:在线点餐系统(java+Springboot+Maven+mybatis+Vue+mysql+Redis)
  3. Linux中mmap与munmap函数系统调用
  4. ElasticSearch 2 (38) - 信息聚合系列之结束与思考
  5. 待处理,待学习(每日更新)
  6. 定义列表的特点html,HTML的列表表格表单知识点
  7. 【深度学习】 - MobileNet使用的可分离卷积
  8. java int 和 long比较大小会_解析java的addExact()与multiplyExact()
  9. Moodle网站档案的结构
  10. 【Elasticsearch】es一个奇怪的问题 is_write_index fasle的时候还在写入 索引滚动无效
  11. HALCON 20.11:深度学习笔记(2)
  12. db_mysql.so_MySQL登陆时提示找不到到libmysqlclient.so.15
  13. Nginx一个server主机上80、433http、https共存
  14. 用react-custom-scrollbars插件美化 滚动条
  15. 切片器可以设置日期格式?_切片器——一个筛选神器
  16. 二十:让行内元素在div中垂直居中
  17. VS2019 编译 paho-mqtt-cpp 遇到的问题
  18. libvirt零知识学习4 —— libvirt源码编译安装(2)
  19. Linux traceroute no reply
  20. python情人节之玫瑰花与表白方式

热门文章

  1. hive 莫名其名爆出无法识别hadoop 版本
  2. 产品经理必懂的技术知识
  3. 二进制 算法相关的题目
  4. 李永乐(四)初等变换、初等矩阵、分块矩阵——笔记
  5. taskkill /im test.exe /f
  6. 图解 | 后端程序员,你把React理解到这一步就够了!
  7. 科技创新让工作更加清晰:这款工作计划管理软件如此强大
  8. 历届数据挖掘课程中参加kaggle、天池、数据城堡、datacastle等互联网数据挖掘竞赛资源
  9. 使用LayUI实现AJAX分页
  10. 初探串口输出六轴陀螺仪