ffmpeg ts section 表及pes表解释
1. sdt 表解释
例如下面的调用栈
0 in sdt_cb of libavformat/mpegts.c:2650
1 in write_section_data of libavformat/mpegts.c:466
2 in handle_packet of libavformat/mpegts.c:2819
3 in handle_packets of libavformat/mpegts.c:2984
4 in mpegts_read_header of libavformat/mpegts.c:3102
5 in avformat_open_input of libavformat/utils.c:609
6 in main of main.c:31
static void sdt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len)
1. section, section_len 信息是如何获得的?
在write_secton_data()中调用
tss->section_cb(tss1, cur_section_buf, tss->section_h_size)
其中 cur_section_buf 是指向当前section的数据指针
tss->section_h_size 是 从section 数据中读取的
tss->section_h_size = (AV_RB16(cur_section_buf + 1) & 0xfff) + 3;
只所以加3,是因为第1字节为tableID,第2,3字节是section的长度.
另:tss1 是tsfilter, tss是sectionfilter.
MpegTSSectionFilter *tss = &tss1->u.section_filter; // tss1是tsfilter, tss是sectionFilter, tss1更大一些.
2. sdt 表中有什么内容
sdt表,其pid是0x11,其tableID 是0x42, 后跟12bits 长度,后面还有16bits ts_ID(传输流id) 及3个bytes版本号,sec_num,last_sec_num为表头
后面一个16bits onID(origial network id),一个8bits val, 然后进入循环
后面一个16bits sid(服务id),一个8bits val, 一个16bits 长度再次进入循环
后面一个8bits tag, 一个8bits 长度.
如果tag 是0x48, 则后面数据包含一个8bits服务类型,提供商名称和服务名称.
onID,ts_ID,sid 构成3级定位
若存在该服务,则可以创建一个av_new_program(fmt_ctx,sid), 其传人参数是sid, 作为节目ID,并给这个program添加meta_data.
av_dict_set(&program->metadata, "service_name", name, 0);
av_dict_set(&program->metadata, "service_provider", provider_name, 0);
2. pat表解释
pat表,其pid是0x0,其tableID 是0x0, 后跟12bits 长度,后面还有16bits ts_ID(为传输流ID) 及3个bytes版本号,sec_num,last_sec_num为表头
后面进入循环:
16bits sid(服务id), 16bits pmt_pid
这里的sid 就是节目ID, 与sdt表中的sid 是一样的,都是节目ID或者说服务ID
每一个sid,都可以创建一个新节目av_new_program(fctx,sid),
这样program 就加了一个,一个流中(由ts_ID标识)可以有多个program(节目)
根据pmt_pid,可以创作一个Pmt filter, mpegts_open_section_filter(ts, pmt_pid, pmt_cb, ts, 1);
就能过滤出pmt表了. 其中第一个ts 是上下文, 第二个ts是pmt_cb的参数.
每一个sid,pmt_pid构成pat表中的一项
pat 表的另一个作用是在 struct MpegTSContext 中添加sid, 并把pmt_pid加入其中
prg = add_program(ts, sid); //ts中有一个program 数组, ts->prg, 每个program包含一个ID数组及stream数组
add_pid_to_program(prg, pmt_pid);
3. pmt表解释
pmt表,其pid是pat表找到的,其tableID 是0x2, 后跟12bits 长度,后面还有16bits ID(为sid) 及3个bytes版本号,sec_num,last_sec_num为表头
这里的sid就是节目ID,和前面的sdt,pat中sid是一致的.每个表描述节目的一部分特性. sdt描述了服务名称,pat描述了pmt_pid,pmt中描述了播出流属性
一个节目,是包含节目ID和多个播出流pid, 例如音频流pid,视频流pid
首先,把当前流的pid(就是pmt表本身的PID) 加到program结构中
表头后面跟:
13bits pcr_pid, 把pcr_pid加到program结构中
12bits 节目信息长度,可能值为0, 后面进行循环,获取PMT项
获取8bits 流类型(stream_type), 13bits pid(这是基础流es的PID),12bits 基础流信息长度(可能为0)+基础流信息
例1: 1b e1 00 f0 00
0x1b(视频流,这是h264类型)+0x100(pid) ; e(1110),f(111)中的1都是1填充位
由stream_type 判定是否是pes 流, 除了0x13不能当pes流, 0x86有时候不能当pes流(配合另一参数),其它都可以作为基础流类型.
根据pid,pcr_pcd 可以创建一个pes 流,这就是基础流. 加上基础流过滤器,就可以过滤分析基础流了.
tss = mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);
其中ts 是上下文,pid是基础流id, mpegts_push_data 是回调函数,pes是回调函数的参数
然后在format 上创建一个新的stream
avformat_new_stream()
stream_type 就是codec_tag
stream 里有codec_param(stream_type,codec_type,codec_id), 有codec, 有time_base
看下面条目,就是定义0x1b流类型是视频h264
{ 0x1b, AVMEDIA_TYPE_VIDEO, AV_CODEC_ID_H264 },
0x81 是audio,ac3类型
{ 0x81, AVMEDIA_TYPE_AUDIO, AV_CODEC_ID_AC3 },
在 struct MpegTSContext 中可以添加该stream
pes = add_pes_stream(ts, pid, pcr_pid) 其中会打开Pes 过滤器 mpegts_open_pes_filter(ts, pid, mpegts_push_data, pes);
st = avformat_new_stream(pes->stream, NULL); //创建了一个stream, 但codec给的是空, 一个stream包含codec,codecpar,internal结构指针
st->id = pes->pid;
if (pes && !pes->stream_type) mpegts_set_stream_info(st, pes, stream_type, prog_reg_desc);
add_pid_to_program(prg, pid);
例2: 81 E1 01 F0 06 0A 04 65 6E 67 00
0x81(音频流,这是ac3类型)+0x101(pid)+6bytes 基础流信息,0A 为tag(这是描述语言类型的), 长度为4,内容为eng
4. pes 表头解释
固定的00 00 01开始加stream_id(8bits)+len(16bits), byte+标志位+长度(8bits) 构成9字节表头,
若flags & 0xc0==0xc0, 表示后跟pts(5B),dts(5B), 在后面是负载
参考: ffmpeg4.4 代码