PS封装笔记
MPEG-2PS封装解析
1. 背景了解
ISOIEC 13818-1标准,MPEG-2中定义了两种复合信息流:传送流(TS:TransportStream)和节目流(PS:ProgramStream)
现在主要用于安防和广电领域,苹果全系列产品也在使用(HLS)。
PS主要应用在安防领域,因为GB28181-11规定了码流必须是RTP+PS,而且有存储属性;
TS主要应用在广电领域,因为TS的易于恢复的特性,苹果HLS协议的码流封装格式也是TS
2. 主要区别
- TS流的包结构是固定的,而PS流是可变的;所以,TS可以在任意位置恢复播放,PS必须保证完整性
- TS用于数据流传输,PS既有存储属性也有实时流传输的属性
- PS支持在多个层次加入私有数据,方便解码,拖动和减少延时
3. 封装格式
TS和PS流都是基于PES包进行的二次封装,总体封装结构如下:
3.1 TS层结构
3.2 PS层结构
PS层主要由PS header,PS system header,PS system map加上PES packets组成。
对于包含IDR帧的PS包的内容为:“PS包起始码(0x00 00 00 01 ba)” + “系统头” + “PSM” +“PES header” + “PES payload”
对于包含非IDR帧的PS包的内容为:“PS包起始码(0x00 00 00 01 ba)” +“PES header” + “PES payload”解封装思路:
PS header:字段含义如下:
system_clock_reference_base 和 system_clock_reference_extension
这两个字段暂时没太明白,iso13818-1介绍如下,猜测是为了同步编解码两端的时钟频率而设置的字段,可能是历史上的应用场景,但到今天作用已经不大了(有清楚的朋友希望可以讲解一下,感谢。):
查阅了ffmpeg代码,发现组包时system_clock_reference_extension直接被设置为0;而system_clock_reference_base是以采样间隔为单位逐步累加的。
gastatic int put_pack_header(AVFormatContext *ctx, uint8_t *buf, int64_t timestamp) { MpegMuxContext *s = ctx->priv_data; PutBitContext pb; init_put_bits(&pb, buf, 128); put_bits32(&pb, PACK_START_CODE); if (s->is_mpeg2) put_bits(&pb, 2, 0x1); else put_bits(&pb, 4, 0x2); put_bits(&pb, 3, (uint32_t)((timestamp >> 30) & 0x07)); put_bits(&pb, 1, 1); put_bits(&pb, 15, (uint32_t)((timestamp >> 15) & 0x7fff)); put_bits(&pb, 1, 1); put_bits(&pb, 15, (uint32_t)((timestamp) & 0x7fff)); put_bits(&pb, 1, 1); if (s->is_mpeg2) /* clock extension */ put_bits(&pb, 9, 0); // 此处直接设置为0 put_bits(&pb, 1, 1); put_bits(&pb, 22, s->mux_rate); put_bits(&pb, 1, 1); if (s->is_mpeg2) { put_bits(&pb, 1, 1); put_bits(&pb, 5, 0x1f); /* reserved */ put_bits(&pb, 3, 0); /* stuffing length,默认不填充stuffing byte */ } flush_put_bits(&pb); return put_bits_ptr(&pb) - pb.buf; } // scr base计算方式如下,是一个以采样间隔为单位的值,感觉ffmpeg跟iso13818-1文档所说的计算方式并不一样,可能今天的应用场景改变已经偏大 scr += s->packet_size * 90000LL / (s->mux_rate * 50LL); size = put_pack_header(ctx, buf_ptr, scr); s->last_scr = scr;program_mux_rate
表示当前码率的字段,单位是50 bytes/second,不能为0。
pack_stuffing_length
A 3 bit integer specifying the number of stuffing bytes which follow this field
stuffing_byte (pack_stuffing_length大于0才存在)
pack_stuffing_length字段指定的填充字节,固定为0xFF
PS system header:用来描述VCD和DVD信息的,在今天作用已经不大,不做详细解释,组包流程可以参考ffmpeg代码(put_system_header(…))
PSM(PS system map):在固定包头和系统头之后,只有关键帧的时候,才会存在,提供了es流的描述信息,以及各es流之间的关系
主要字段名称 字段长度(bits) 功能描述 program_stream_map_length 16 该字段之后,PSM还有多长,单位byte。 最大值1018 (0x3FA) program_stream_info_length 16 该字段之后,自定义复合流的长度,一般是0。 但可以用这个字段定制私有数据。
海康用该字段 定义了BASIC信息、DEVICE信息、加密信息elementary_stream_info_length 16 该字段之后,视频流信息、音频流信息、私有数据信息三者的总长度 CRC_32 32 循环冗余校验 私有数据部分不多做解释,私有数据解析参考:海康PSM流解析 关于elementary_stream_info,标准流一般只解析音视频类型等信息,参考ffmpeg代码:
static long mpegps_psm_parse(MpegDemuxContext *m, AVIOContext *pb) { int psm_length, ps_info_length, es_map_length; psm_length = avio_rb16(pb); avio_r8(pb); avio_r8(pb); ps_info_length = avio_rb16(pb); /* skip program_stream_info 无私有数据,直接跳过 */ avio_skip(pb, ps_info_length); /*es_map_length = */avio_rb16(pb); /* Ignore es_map_length, trust psm_length */ es_map_length = psm_length - ps_info_length - 10; /* at least one es available? */ while (es_map_length >= 4) { unsigned char type = avio_r8(pb); unsigned char es_id = avio_r8(pb); uint16_t es_info_length = avio_rb16(pb); /* remember mapping from stream id to stream type */ m->psm_es_type[es_id] = type; /* skip program_stream_info */ avio_skip(pb, es_info_length); // 只解析上边三个信息, 剩下的不解析直接跳过(默认无私有数据) es_map_length -= 4 + es_info_length; } avio_rb32(pb); /* crc32 */ return 2 + psm_length; }3.3 PES层结构
TS流和PS流都是由PES包经过某种逻辑结构组织而成
PES包可以被用来完成从TS流转换到PS流的转换
PES包的大小有时也可以大于TS包(一个PES包可能会被分装在多个TS包中)
如上图所示,PES包由3部分组成:
- 固定头
- packet_start_code_prefix:PES起始码固定为0x000001
- stream_id:规定了基本流的号码和类型。0x(c0-df)指音频,0x(e0-ef)为视频,0xbf 私有,0xbe 填充
- PES Packet length:说明了从固定头之后,还有多少个字节属于本PES包(TS流该字段可能为0,但PS不可能)
- 可选头**(stream_id是特定值的话,才会有这个字段)**
从固定头信息中只能知道PES包的开始和总长度,但PES header长度是可变的,怎么知道header和payload的分界呢
optional fields:可选填充域,TS流要固定包大小,需要这个字段。
- 负载(即ES流)
3.4 ES流如何封装为PES
没有特别严格的方式,一般就是一帧音频或者视频数据封装为一个PES packet,然后N个PES packet封装为一个PS packet,用工具解析一下萤石的录像文件:
更多推荐













所有评论(0)