SRS 代码分析【mpeg-ts解析】
2017-12-10 15:29
579 查看
SRS 代码分析【mpeg-ts解析】
1.SrsTsContext的decode接口定义如下:
首先创建SrsTsPacket,接着调用packet->decode().
SrsTsPacket的定义
这里最主要关系的是payload_unit_start_indicator,PID,adaptation_field_control,continuity_counter
payload_unit_start_indicator
这个位标志为1,指的是一个包的启示,因为ts包只有188个字节,对于一个PES包的话往往大于188字节,因此一个PES包往往要拆成多个TS包,为了识别收到的TS包属于另一个PES包,起始位表示新的一个PES包或者PSI包等到来了。
2.SrsTsPacket的decode接口定义如下:
然后根据adaption_field_control字段判断有没有payload,adaptionfield
adaption_field_control字段的说明如下:
adaptation_field_control的值如下表描述
存在adaption自适应字段的场合,会接着创建SrsTsAdaptionField,并调用该类decode方法去解析各个字段。
自适应字段我们主要关注的是adaptation field length和PCR,这里重点讲解他们的主要用处:
adaptationfield length指的是自适应字段的长度,也就是,从discontinuity indicator 到adaptation field最后的长度,也就是从第6字节(包含第6字节)开始算到最后。
PCR
这个值是系统的时间戳,在PES层时间戳是PTS与DTS,这里要注意与PCR,PTS,DTS的概念,可能会让人模糊。PCR是TS层的时间戳,PTS与DTS是PES的时间戳,PCR在PES层相当于DTS,TS不需要考虑PTS。为啥不需要,这里就要讲下,PTS的具体概念。详细的在ISO-13818-1上有,详细到可以看到你吐。其实实际中不需要考虑这么多。我简单的讲吧。在ES流中,依次组成图像帧序为I1P4B2B3P7B5B6I10B8B9的,这里,I、P、B分别指I帧,P帧,B帧。具体意义可以参考H264的相关基本概念,对于I、P帧而言,PES的图像帧序为I1P4B2B3P7B5B6I10B8B9,应该P4比B2、B3在先,但显示时P4一定
要比B2、B3在后,这就必须重新排序。在PTS/DTS时间标志指引下,将P4提前插入数据流,经过缓存器重新排序,重建视频帧序 I1B2B3P4B5B6P7B8B9I10。显然,PTS/DTS是表明确定事件或确定信息,并以专用时标形态确定事件或信息的开始时刻。说到这里,PTS,与DTS的概念应该明白了。但是为啥TS层不需要呢,因为TS层只是负责传输,你知道解码的时间在什么位置,确保传输的TS包不是延迟太久就可以了,具体的显示细节交给PES层去做。
存在payload的场合,会接着根据pid类型判断包的类型,有PAT,PMT,PES,以及Resolved。
if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth)
下面是pid类型定义的枚举
PID program id
节目标示符,一个13位的无符号整数。作用如下表描述。
3.PAT包的处理,PAT包的pid为0x00
PAT表中定义的字段说明如下
unsigned table_id : 8; //固定为0x00 ,标志是该表是PAT
unsigned section_syntax_indicator : 1; //段语法标志位,固定为1
unsigned zero : 1; //0
unsigned reserved_1 : 2; // 保留位
unsigned section_length : 12; //表示这个字节后面有用的字节数,包括CRC32
unsigned transport_stream_id : 16; //该传输流的ID,区别于一个网络中其它多路复用的流
unsigned reserved_2 : 2;// 保留位
unsigned version_number : 5; //范围0-31,表示PAT的版本号
unsigned current_next_indicator : 1; //发送的PAT是当前有效还是下一个PAT有效
unsigned section_number : 8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
unsigned last_section_number : 8; //最后一个分段的号码
std::vector<TS_PAT_Program> program;
unsigned reserved_3 : 3; // 保留位
unsigned network_PID : 13; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID
unsigned CRC_32 : 32; //CRC32校验码
解析PAT包对应的类为SrsTsPayloadPAT,该类继承自SrsTsPayloadPSI
由于PAT表和PMT表的读取过程存在相同的处理,相同的部分被封装到基类SrsTsPayloadPSI的decode函数中;基类SrsTsPayloadPSI中有纯虚函数psi_decode,不同的部分在子类的psi_decode中去实现。
SrsTsPayloadPSI的头文件定义如下
SrsTsPayloadPAT没有重写decode方法,实际还是调用父类SrsTsPayloadPSI的decode方法,定义如下
SrsTsPayloadPSI的decode函数会调用子类的psi_decode函数,这里调用的是SrsTsPayloadPAT的psi_decode函数,定义如下:
psi_decode函数将解析的节目信息存放在SrsTsPayloadPATProgram,该类定义如下:
unsigned
number :16; //节目号
unsigned program_map_PID :13; //节目映射表的PID,每个节目对应一个
SrsTsPayloadPAT::psi_decode函数中每解析一个SrsTsPayloadPATProgram都会调用SrsTsContext::set方法更新节目映射表。后面在解析PMT数据时会用到更新的pid。
4.PMT包的处理,PMT包的pid在解析PAT表时通过SrsTsContext::set进行了记录。
SrsTsPacket::decode中调用下面的方法去判断包中数据是否为PMT
如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表。
PMT表中包含的数据如下:
(1) 当前频道中包含的所有Video数据的PID
(2) 当前频道中包含的所有Audio数据的PID
(3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)
只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video
PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。
PMT表字段说明如下:
unsigned table_id : 8; //固定为0x02, 表示PMT表
unsigned section_syntax_indicator : 1; //固定为0x01
unsigned zero : 1; //0x01
unsigned reserved_1 : 2; //0x03
unsigned section_length : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
unsigned program_number : 16;// 指出该节目对应于可应用的Program map PID
unsigned reserved_2 : 2; //0x03
unsigned version_number : 5; //指出TS流中Program map section的版本号
unsigned current_next_indicator : 1; //当该位置1时,当前传送的Program map section可用;
//当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。
unsigned section_number : 8; //固定为0x00
unsigned last_section_number : 8; //固定为0x00
unsigned reserved_3 : 3; //0x07
unsigned PCR_PID : 13; //指明TS包的PID值,该TS包含有PCR域,
//该PCR值对应于由节目号指定的对应节目。
//如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
unsigned reserved_4 : 4; //预留为0x0F
unsigned program_info_length : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。
std::vector<TS_PMT_Stream> PMT_Stream; //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定
unsigned reserved_5 : 3; //0x07
unsigned reserved_6 : 4; //0x0F
unsigned CRC_32 : 32;
解析PMT包对应的类为SrsTsPayloadPMT,该类也是继承自SrsTsPayloadPSI
SrsTsPayloadPMT也实现了基类的纯虚函数psi_decode用来解析PMT数据,定义如下:
字段说明
unsigned stream_type : 8; //指示特定PID的节目元素包的类型。音视频编码格式。
unsigned elementary_PID : 13; //该域指示TS包的PID值。这些TS包含有相关的节目元素
unsigned ES_info_length : 12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数
unsigned descriptor;
媒体节目信息的解析如下:
4.PES包的处理,PES包的pid在解析PMT表时通过SrsTsContext::set进行了记录。
SrsTsPacket::decode中调用下面的方法去判断包中数据是否为PES
解析PES包对应的类为SrsTsPayloadPES,该类头文件中定义了PES包中的字段。
SrsTsPayloadPES的decode函数会解析上述字段。定义如下:
pts,dts也会被解析记录到msg中。
发送视频时帧会超过188字节,因此一帧数据会被拆成多个PES包发送,因此接收到PES包的数据时就需要根据continuity_counter将解析后的数据重新组装成一个视频帧。
1.SrsTsContext的decode接口定义如下:
int SrsTsContext::decode(SrsBuffer* stream, ISrsTsHandler* handler) { int ret = ERROR_SUCCESS; // parse util EOF of stream. // for example, parse multiple times for the PES_packet_length(0) packet. while (!stream->empty()) { SrsTsPacket* packet = new SrsTsPacket(this); SrsAutoFree(SrsTsPacket, packet); SrsTsMessage* msg = NULL; if ((ret = packet->decode(stream, &msg)) != ERROR_SUCCESS) { srs_error("mpegts: decode ts packet failed. ret=%d", ret); return ret; } if (!msg) { continue; } SrsAutoFree(SrsTsMessage, msg); if ((ret = handler->on_ts_message(msg)) != ERROR_SUCCESS) { srs_error("mpegts: handler ts message failed. ret=%d", ret); return ret; } } return ret; }
首先创建SrsTsPacket,接着调用packet->decode().
SrsTsPacket的定义
class SrsTsPacket { public: // 1B /** * The sync_byte is a fixed 8-bit field whose value is '0100 0111' (0x47). Sync_byte emulation in the choice of * values for other regularly occurring fields, such as PID, should be avoided. */ int8_t sync_byte; //8bits // 2B /** * The transport_error_indicator is a 1-bit flag. When set to '1' it indicates that at least * 1 uncorrectable bit error exists in the associated Transport Stream packet. This bit may be set to '1' by entities external to * the transport layer. When set to '1' this bit shall not be reset to '0' unless the bit value(s) in error have been corrected. */ int8_t transport_error_indicator; //1bit /** * The payload_unit_start_indicator is a 1-bit flag which has normative meaning for * Transport Stream packets that carry PES packets (refer to 2.4.3.6) or PSI data (refer to 2.4.4). * * When the payload of the Transport Stream packet contains PES packet data, the payload_unit_start_indicator has the * following significance: a '1' indicates that the payload of this Transport Stream packet will commence(start) with the first byte * of a PES packet and a '0' indicates no PES packet shall start in this Transport Stream packet. If the * payload_unit_start_indicator is set to '1', then one and only one PES packet starts in this Transport Stream packet. This * also applies to private streams of stream_type 6 (refer to Table 2-29). * * When the payload of the Transport Stream packet contains PSI data, the payload_unit_start_indicator has the following * significance: if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value * shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field. If the * Transport Stream packet does not carry the first byte of a PSI section, the payload_unit_start_indicator value shall be '0', * indicating that there is no pointer_field in the payload. Refer to 2.4.4.1 and 2.4.4.2. This also applies to private streams of * stream_type 5 (refer to Table 2-29). * * For null packets the payload_unit_start_indicator shall be set to '0'. * * The meaning of this bit for Transport Stream packets carrying only private data is not defined in this Specification. */ int8_t payload_unit_start_indicator; //1bit /** * The transport_priority is a 1-bit indicator. When set to '1' it indicates that the associated packet is * of greater priority than other packets having the same PID which do not have the bit set to '1'. The transport mechanism * can use this to prioritize its data within an elementary stream. Depending on the application the transport_priority field * may be coded regardless of the PID or within one PID only. This field may be changed by channel specific encoders or * decoders. */ int8_t transport_priority; //1bit /** * The PID is a 13-bit field, indicating the type of the data stored in the packet payload. PID value 0x0000 is * reserved for the Program Association Table (see Table 2-25). PID value 0x0001 is reserved for the Conditional Access * Table (see Table 2-27). PID values 0x0002 - 0x000F are reserved. PID value 0x1FFF is reserved for null packets (see * Table 2-3). */ SrsTsPid pid; //13bits // 1B /** * This 2-bit field indicates the scrambling mode of the Transport Stream packet payload. * The Transport Stream packet header, and the adaptation field when present, shall not be scrambled. In the case of a null * packet the value of the transport_scrambling_control field shall be set to '00' (see Table 2-4). */ SrsTsScrambled transport_scrambling_control; //2bits /** * This 2-bit field indicates whether this Transport Stream packet header is followed by an * adaptation field and/or payload (see Table 2-5). * * ITU-T Rec. H.222.0 | ISO/IEC 13818-1 decoders shall discard Transport Stream packets with the * adaptation_field_control field set to a value of '00'. In the case of a null packet the value of the adaptation_field_control * shall be set to '01'. */ SrsTsAdaptationFieldType adaption_field_control; //2bits /** * The continuity_counter is a 4-bit field incrementing with each Transport Stream packet with the * same PID. The continuity_counter wraps around to 0 after its maximum value. The continuity_counter shall not be * incremented when the adaptation_field_control of the packet equals '00'(reseverd) or '10'(adaptation field only). * * In Transport Streams, duplicate packets may be sent as two, and only two, consecutive Transport Stream packets of the * same PID. The duplicate packets shall have the same continuity_counter value as the original packet and the * adaptation_field_control field shall be equal to '01'(payload only) or '11'(both). In duplicate packets each byte of the original packet shall be * duplicated, with the exception that in the program clock reference fields, if present, a valid value shall be encoded. * * The continuity_counter in a particular Transport Stream packet is continuous when it differs by a positive value of one * from the continuity_counter value in the previous Transport Stream packet of the same PID, or when either of the nonincrementing * conditions (adaptation_field_control set to '00' or '10', or duplicate packets as described above) are met. * The continuity counter may be discontinuous when the discontinuity_indicator is set to '1' (refer to 2.4.3.4). In the case of * a null packet the value of the continuity_counter is undefined. */ uint8_t continuity_counter; //4bits private: SrsTsAdaptationField* adaptation_field; SrsTsPayload* payload; ...... }字段的说明
字段 | Value | bits | |
sync_byte | 0x47 | 8 | bslbf |
transport_error_indicator | 如果这个流中包含了一个无法修复的错误,由解调器设置,以告诉多路解调器,该包存在一个无法纠正的错误 | 1 | bslbf |
payload_unit_start_indicator | 1 表示是 PES 数据或 PSI数据的开始部分,否则为零. | 1 | bslbf |
transport_priority | 1 意思是在相同 PID 的数据包中含有更高的优先权. | 1 | bslbf |
PID | PID 为13 比特字段,指示包有效载荷中存储的数据类型,也就是包的标识号。 | 13 | uimsbf |
transport_scrambling_control | 此2 比特字段指示传输流包有效载荷的加扰方式。 | 2 | bslbf |
adaptation_field_control | 此2 比特字段指示此传输流包头是否后随自适应字段和/或有效载荷 | 2 | bslbf |
continuity_counter | 包递增计数器,continuity_counter 为4 比特字段,随着具有相同PID 的每个传输流包而增加 | 4 | uimsbf |
|
payload_unit_start_indicator
这个位标志为1,指的是一个包的启示,因为ts包只有188个字节,对于一个PES包的话往往大于188字节,因此一个PES包往往要拆成多个TS包,为了识别收到的TS包属于另一个PES包,起始位表示新的一个PES包或者PSI包等到来了。
2.SrsTsPacket的decode接口定义如下:
int SrsTsPacket::decode(SrsBuffer* stream, SrsTsMessage** ppmsg) { int ret = ERROR_SUCCESS; int pos = stream->pos(); // 4B ts packet header. if (!stream->require(4)) { ret = ERROR_STREAM_CASTER_TS_HEADER; srs_error("ts: demux header failed. ret=%d", ret); return ret; } sync_byte = stream->read_1bytes(); if (sync_byte != 0x47) { ret = ERROR_STREAM_CASTER_TS_SYNC_BYTE; srs_error("ts: sync_bytes must be 0x47, actual=%#x. ret=%d", sync_byte, ret); return ret; } int16_t pidv = stream->read_2bytes(); transport_error_indicator = (pidv >> 15) & 0x01; payload_unit_start_indicator = (pidv >> 14) & 0x01; transport_priority = (pidv >> 13) & 0x01; pid = (SrsTsPid)(pidv & 0x1FFF); int8_t ccv = stream->read_1bytes(); transport_scrambling_control = (SrsTsScrambled)((ccv >> 6) & 0x03); adaption_field_control = (SrsTsAdaptationFieldType)((ccv >> 4) & 0x03); continuity_counter = ccv & 0x0F; // TODO: FIXME: create pids map when got new pid. srs_info("ts: header sync=%#x error=%d unit_start=%d priotiry=%d pid=%d scrambling=%d adaption=%d counter=%d", sync_byte, transport_error_indicator, payload_unit_start_indicator, transport_priority, pid, transport_scrambling_control, adaption_field_control, continuity_counter); // optional: adaptation field if (adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth) { srs_freep(adaptation_field); adaptation_field = new SrsTsAdaptationField(this); if ((ret = adaptation_field->decode(stream)) != ERROR_SUCCESS) { srs_error("ts: demux af faield. ret=%d", ret); return ret; } srs_verbose("ts: demux af ok."); } // calc the user defined data size for payload. int nb_payload = SRS_TS_PACKET_SIZE - (stream->pos() - pos); // optional: payload. if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth) { if (pid == SrsTsPidPAT) { // 2.4.4.3 Program association Table srs_freep(payload); payload = new SrsTsPayloadPAT(this); } else { SrsTsChannel* channel = context->get(pid); if (channel && channel->apply == SrsTsPidApplyPMT) { // 2.4.4.8 Program Map Table srs_freep(payload); payload = new SrsTsPayloadPMT(this); } else if (channel && (channel->apply == SrsTsPidApplyVideo || channel->apply == SrsTsPidApplyAudio)) { // 2.4.3.6 PES packet srs_freep(payload); payload = new SrsTsPayloadPES(this); } else { // left bytes as reserved. stream->skip(nb_payload); } } if (payload && (ret = payload->decode(stream, ppmsg)) != ERROR_SUCCESS) { srs_error("ts: demux payload failed. ret=%d", ret); return ret; } } return ret; }首先该函数完成payload_unit_start_indicator,PID,adaptation_field_control,continuity_counter等字段的解析。
然后根据adaption_field_control字段判断有没有payload,adaptionfield
adaption_field_control字段的说明如下:
enum SrsTsAdaptationFieldType { // Reserved for future use by ISO/IEC SrsTsAdaptationFieldTypeReserved = 0x00, // No adaptation_field, payload only SrsTsAdaptationFieldTypePayloadOnly = 0x01, // Adaptation_field only, no payload SrsTsAdaptationFieldTypeAdaptionOnly = 0x02, // Adaptation_field followed by payload SrsTsAdaptationFieldTypeBoth = 0x03, };
adaptation_field_control的值如下表描述
存在adaption自适应字段的场合,会接着创建SrsTsAdaptionField,并调用该类decode方法去解析各个字段。
if (adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth)
自适应字段我们主要关注的是adaptation field length和PCR,这里重点讲解他们的主要用处:
adaptationfield length指的是自适应字段的长度,也就是,从discontinuity indicator 到adaptation field最后的长度,也就是从第6字节(包含第6字节)开始算到最后。
PCR
这个值是系统的时间戳,在PES层时间戳是PTS与DTS,这里要注意与PCR,PTS,DTS的概念,可能会让人模糊。PCR是TS层的时间戳,PTS与DTS是PES的时间戳,PCR在PES层相当于DTS,TS不需要考虑PTS。为啥不需要,这里就要讲下,PTS的具体概念。详细的在ISO-13818-1上有,详细到可以看到你吐。其实实际中不需要考虑这么多。我简单的讲吧。在ES流中,依次组成图像帧序为I1P4B2B3P7B5B6I10B8B9的,这里,I、P、B分别指I帧,P帧,B帧。具体意义可以参考H264的相关基本概念,对于I、P帧而言,PES的图像帧序为I1P4B2B3P7B5B6I10B8B9,应该P4比B2、B3在先,但显示时P4一定
要比B2、B3在后,这就必须重新排序。在PTS/DTS时间标志指引下,将P4提前插入数据流,经过缓存器重新排序,重建视频帧序 I1B2B3P4B5B6P7B8B9I10。显然,PTS/DTS是表明确定事件或确定信息,并以专用时标形态确定事件或信息的开始时刻。说到这里,PTS,与DTS的概念应该明白了。但是为啥TS层不需要呢,因为TS层只是负责传输,你知道解码的时间在什么位置,确保传输的TS包不是延迟太久就可以了,具体的显示细节交给PES层去做。
存在payload的场合,会接着根据pid类型判断包的类型,有PAT,PMT,PES,以及Resolved。
if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth)
下面是pid类型定义的枚举
enum SrsTsPid { // Program Association Table(see Table 2-25). SrsTsPidPAT = 0x00, // Conditional Access Table (see Table 2-27). SrsTsPidCAT = 0x01, // Transport Stream Description Table SrsTsPidTSDT = 0x02, // Reserved SrsTsPidReservedStart = 0x03, SrsTsPidReservedEnd = 0x0f, // May be assigned as network_PID, Program_map_PID, elementary_PID, or for other purposes SrsTsPidAppStart = 0x10, SrsTsPidAppEnd = 0x1ffe, // null packets (see Table 2-3) SrsTsPidNULL = 0x01FFF, };类型说明
PID program id
节目标示符,一个13位的无符号整数。作用如下表描述。
3.PAT包的处理,PAT包的pid为0x00
PAT表中定义的字段说明如下
unsigned table_id : 8; //固定为0x00 ,标志是该表是PAT
unsigned section_syntax_indicator : 1; //段语法标志位,固定为1
unsigned zero : 1; //0
unsigned reserved_1 : 2; // 保留位
unsigned section_length : 12; //表示这个字节后面有用的字节数,包括CRC32
unsigned transport_stream_id : 16; //该传输流的ID,区别于一个网络中其它多路复用的流
unsigned reserved_2 : 2;// 保留位
unsigned version_number : 5; //范围0-31,表示PAT的版本号
unsigned current_next_indicator : 1; //发送的PAT是当前有效还是下一个PAT有效
unsigned section_number : 8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段
unsigned last_section_number : 8; //最后一个分段的号码
std::vector<TS_PAT_Program> program;
unsigned reserved_3 : 3; // 保留位
unsigned network_PID : 13; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID
unsigned CRC_32 : 32; //CRC32校验码
解析PAT包对应的类为SrsTsPayloadPAT,该类继承自SrsTsPayloadPSI
class SrsTsPayloadPAT : public SrsTsPayloadPSI { public: // 2B /** * This is a 16-bit field which serves as a label to identify this Transport Stream from any other * multiplex within a network. Its value is defined by the user. */ uint16_t transport_stream_id; //16bits // 1B /** * reverved value, must be '1' */ int8_t const3_value; //2bits /** * This 5-bit field is the version number of the whole Program Association Table. The version number * shall be incremented by 1 modulo 32 whenever the definition of the Program Association Table changes. When the * current_next_indicator is set to '1', then the version_number shall be that of the currently applicable Program Association * Table. When the current_next_indicator is set to '0', then the version_number shall be that of the next applicable Program * Association Table. */ int8_t version_number; //5bits /** * A 1-bit indicator, which when set to '1' indicates that the Program Association Table sent is * currently applicable. When the bit is set to '0', it indicates that the table sent is not yet applicable and shall be the next * table to become valid. */ int8_t current_next_indicator; //1bit // 1B /** * This 8-bit field gives the number of this section. The section_number of the first section in the * Program Association Table shall be 0x00. It shall be incremented by 1 with each additional section in the Program * Association Table. */ uint8_t section_number; //8bits // 1B /** * This 8-bit field specifies the number of the last section (that is, the section with the highest * section_number) of the complete Program Association Table. */ uint8_t last_section_number; //8bits // multiple 4B program data. std::vector<SrsTsPayloadPATProgram*> programs; ...... }
由于PAT表和PMT表的读取过程存在相同的处理,相同的部分被封装到基类SrsTsPayloadPSI的decode函数中;基类SrsTsPayloadPSI中有纯虚函数psi_decode,不同的部分在子类的psi_decode中去实现。
SrsTsPayloadPSI的头文件定义如下
class SrsTsPayloadPSI : public SrsTsPayload { public: // 1B /** * This is an 8-bit field whose value shall be the number of bytes, immediately following the pointer_field * until the first byte of the first section that is present in the payload of the Transport Stream packet (so a value of 0x00 in * the pointer_field indicates that the section starts immediately after the pointer_field). When at least one section begins in * a given Transport Stream packet, then the payload_unit_start_indicator (refer to 2.4.3.2) shall be set to 1 and the first * byte of the payload of that Transport Stream packet shall contain the pointer. When no section begins in a given * Transport Stream packet, then the payload_unit_start_indicator shall be set to 0 and no pointer shall be sent in the * payload of that packet. */ int8_t pointer_field; public: // 1B /** * This is an 8-bit field, which shall be set to 0x00 as shown in Table 2-26. */ SrsTsPsiId table_id; //8bits // 2B /** * The section_syntax_indicator is a 1-bit field which shall be set to '1'. */ int8_t section_syntax_indicator; //1bit /** * const value, must be '0' */ int8_t const0_value; //1bit /** * reverved value, must be '1' */ int8_t const1_value; //2bits /** * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the number * of bytes of the section, starting immediately following the section_length field, and including the CRC. The value in this * field shall not exceed 1021 (0x3FD). */ uint16_t section_length; //12bits public: // the specified psi info, for example, PAT fields. public: // 4B /** * This is a 32-bit field that contains the CRC value that gives a zero output of the registers in the decoder * defined in Annex A after processing the entire section. * @remark crc32(bytes without pointer field, before crc32 field) */ int32_t CRC_32; //32bits ...... }
SrsTsPayloadPAT没有重写decode方法,实际还是调用父类SrsTsPayloadPSI的decode方法,定义如下
int SrsTsPayloadPSI::decode(SrsBuffer* stream, SrsTsMessage** /*ppmsg*/) { int ret = ERROR_SUCCESS; /** * When the payload of the Transport Stream packet contains PSI data, the payload_unit_start_indicator has the following * significance: if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value * shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field. If the * Transport Stream packet does not carry the first byte of a PSI section, the payload_unit_start_indicator value shall be '0', * indicating that there is no pointer_field in the payload. Refer to 2.4.4.1 and 2.4.4.2. This also applies to private streams of * stream_type 5 (refer to Table 2-29). */ if (packet->payload_unit_start_indicator) { if (!stream->require(1)) { ret = ERROR_STREAM_CASTER_TS_PSI; srs_error("ts: demux PSI failed. ret=%d", ret); return ret; } pointer_field = stream->read_1bytes(); } // to calc the crc32 char* ppat = stream->data() + stream->pos(); int pat_pos = stream->pos(); // atleast 3B for all psi. if (!stream->require(3)) { ret = ERROR_STREAM_CASTER_TS_PSI; srs_error("ts: demux PSI failed. ret=%d", ret); return ret; } // 1B table_id = (SrsTsPsiId)stream->read_1bytes(); // 2B int16_t slv = stream->read_2bytes(); section_syntax_indicator = (slv >> 15) & 0x01; const0_value = (slv >> 14) & 0x01; const1_value = (slv >> 12) & 0x03; section_length = slv & 0x0FFF; // no section, ignore. if (section_length == 0) { srs_warn("ts: demux PAT ignore empty section"); return ret; } if (!stream->require(section_length)) { ret = ERROR_STREAM_CASTER_TS_PSI; srs_error("ts: demux PAT section failed. ret=%d", ret); return ret; } // call the virtual method of actual PSI. if ((ret = psi_decode(stream)) != ERROR_SUCCESS) { return ret; } // 4B if (!stream->require(4)) { ret = ERROR_STREAM_CASTER_TS_PSI; srs_error("ts: demux PSI crc32 failed. ret=%d", ret); return ret; } CRC_32 = stream->read_4bytes(); // verify crc32. int32_t crc32 = srs_crc32_mpegts(ppat, stream->pos() - pat_pos - 4); if (crc32 != CRC_32) { ret = ERROR_STREAM_CASTER_TS_CRC32; srs_error("ts: verify PSI crc32 failed. ret=%d", ret); return ret; } // consume left stuffings if (!stream->empty()) { int nb_stuffings = stream->size() - stream->pos(); char* stuffing = stream->data() + stream->pos(); // all stuffing must be 0xff. // TODO: FIXME: maybe need to remove the following. for (int i = 0; i < nb_stuffings; i++) { if ((uint8_t)stuffing[i] != 0xff) { srs_warn("ts: stuff is not 0xff, actual=%#x", stuffing[i]); break; } } stream->skip(nb_stuffings); } return ret; }关于pointfiled的说明:当TS包带有PSI数据并且payload_unit_start_indicator为1时,表示TS包带有PSI部分的第一个字节,即第一个字节带有指针pointer_field,为0表示TS包不带有一个PSI部分的第一个字节,即在有效净荷中没有指针pointer_field。
SrsTsPayloadPSI的decode函数会调用子类的psi_decode函数,这里调用的是SrsTsPayloadPAT的psi_decode函数,定义如下:
int SrsTsPayloadPAT::psi_decode(SrsBuffer* stream) { int ret = ERROR_SUCCESS; // atleast 5B for PAT specified if (!stream->require(5)) { ret = ERROR_STREAM_CASTER_TS_PAT; srs_error("ts: demux PAT failed. ret=%d", ret); return ret; } int pos = stream->pos(); // 2B transport_stream_id = stream->read_2bytes(); // 1B int8_t cniv = stream->read_1bytes(); const3_value = (cniv >> 6) & 0x03; version_number = (cniv >> 1) & 0x1F; current_next_indicator = cniv & 0x01; // TODO: FIXME: check the indicator. // 1B section_number = stream->read_1bytes(); // 1B last_section_number = stream->read_1bytes(); // multiple 4B program data. int program_bytes = section_length - 4 - (stream->pos() - pos); for (int i = 0; i < program_bytes; i += 4) { SrsTsPayloadPATProgram* program = new SrsTsPayloadPATProgram(); if ((ret = program->decode(stream)) != ERROR_SUCCESS) { return ret; } // update the apply pid table. packet->context->set(program->pid, SrsTsPidApplyPMT); programs.push_back(program); } // update the apply pid table. packet->context->set(packet->pid, SrsTsPidApplyPAT); packet->context->on_pmt_parsed(); return ret; }
psi_decode函数将解析的节目信息存放在SrsTsPayloadPATProgram,该类定义如下:
class SrsTsPayloadPATProgram { public: // 4B /** * Program_number is a 16-bit field. It specifies the program to which the program_map_PID is * applicable. When set to 0x0000, then the following PID reference shall be the network PID. For all other cases the value * of this field is user defined. This field shall not take any single value more than once within one version of the Program * Association Table. */ int16_t number; // 16bits /** * reverved value, must be '1' */ int8_t const1_value; //3bits /** * program_map_PID/network_PID 13bits * network_PID - The network_PID is a 13-bit field, which is used only in conjunction with the value of the * program_number set to 0x0000, specifies the PID of the Transport Stream packets which shall contain the Network * Information Table. The value of the network_PID field is defined by the user, but shall only take values as specified in * Table 2-3. The presence of the network_PID is optional. */ int16_t pid; //13bits ...... }字段说明
unsigned
number :16; //节目号
unsigned program_map_PID :13; //节目映射表的PID,每个节目对应一个
SrsTsPayloadPAT::psi_decode函数中每解析一个SrsTsPayloadPATProgram都会调用SrsTsContext::set方法更新节目映射表。后面在解析PMT数据时会用到更新的pid。
packet->context->set(program->pid, SrsTsPidApplyPMT)
void SrsTsContext::set(int pid, SrsTsPidApply apply_pid, SrsTsStream stream) { SrsTsChannel* channel = NULL; if (pids.find(pid) == pids.end()) { channel = new SrsTsChannel(); channel->context = this; pids[pid] = channel; } else { channel = pids[pid]; } channel->pid = pid; channel->apply = apply_pid; channel->stream = stream; }
4.PMT包的处理,PMT包的pid在解析PAT表时通过SrsTsContext::set进行了记录。
SrsTsPacket::decode中调用下面的方法去判断包中数据是否为PMT
SrsTsChannel* channel = context->get(pid); if (channel && channel->apply == SrsTsPidApplyPMT) { // 2.4.4.8 Program Map Table srs_freep(payload); payload = new SrsTsPayloadPMT(this); }
PMT表的描述
如果一个TS流中含有多个频道,那么就会包含多个PID不同的PMT表。PMT表中包含的数据如下:
(1) 当前频道中包含的所有Video数据的PID
(2) 当前频道中包含的所有Audio数据的PID
(3) 和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)
只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video
PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。
PMT表字段说明如下:
unsigned table_id : 8; //固定为0x02, 表示PMT表
unsigned section_syntax_indicator : 1; //固定为0x01
unsigned zero : 1; //0x01
unsigned reserved_1 : 2; //0x03
unsigned section_length : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
unsigned program_number : 16;// 指出该节目对应于可应用的Program map PID
unsigned reserved_2 : 2; //0x03
unsigned version_number : 5; //指出TS流中Program map section的版本号
unsigned current_next_indicator : 1; //当该位置1时,当前传送的Program map section可用;
//当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。
unsigned section_number : 8; //固定为0x00
unsigned last_section_number : 8; //固定为0x00
unsigned reserved_3 : 3; //0x07
unsigned PCR_PID : 13; //指明TS包的PID值,该TS包含有PCR域,
//该PCR值对应于由节目号指定的对应节目。
//如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
unsigned reserved_4 : 4; //预留为0x0F
unsigned program_info_length : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。
std::vector<TS_PMT_Stream> PMT_Stream; //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定
unsigned reserved_5 : 3; //0x07
unsigned reserved_6 : 4; //0x0F
unsigned CRC_32 : 32;
解析PMT包对应的类为SrsTsPayloadPMT,该类也是继承自SrsTsPayloadPSI
/** * the PMT payload of PSI ts packet. * 2.4.4.8 Program Map Table, hls-mpeg-ts-iso13818-1.pdf, page 64 * The Program Map Table provides the mappings between program numbers and the program elements that comprise * them. A single instance of such a mapping is referred to as a "program definition". The program map table is the * complete collection of all program definitions for a Transport Stream. This table shall be transmitted in packets, the PID * values of which are selected by the encoder. More than one PID value may be used, if desired. The table is contained in * one or more sections with the following syntax. It may be segmented to occupy multiple sections. In each section, the * section number field shall be set to zero. Sections are identified by the program_number field. */ class SrsTsPayloadPMT : public SrsTsPayloadPSI { public: // 2B /** * program_number is a 16-bit field. It specifies the program to which the program_map_PID is * applicable. One program definition shall be carried within only one TS_program_map_section. This implies that a * program definition is never longer than 1016 (0x3F8). See Informative Annex C for ways to deal with the cases when * that length is not sufficient. The program_number may be used as a designation for a broadcast channel, for example. By * describing the different program elements belonging to a program, data from different sources (e.g. sequential events) * can be concatenated together to form a continuous set of streams using a program_number. For examples of applications * refer to Annex C. */ uint16_t program_number; //16bits // 1B /** * reverved value, must be '1' */ int8_t const1_value0; //2bits /** * This 5-bit field is the version number of the TS_program_map_section. The version number shall be * incremented by 1 modulo 32 when a change in the information carried within the section occurs. Version number refers * to the definition of a single program, and therefore to a single section. When the current_next_indicator is set to '1', then * the version_number shall be that of the currently applicable TS_program_map_section. When the current_next_indicator * is set to '0', then the version_number shall be that of the next applicable TS_program_map_section. */ int8_t version_number; //5bits /** * A 1-bit field, which when set to '1' indicates that the TS_program_map_section sent is * currently applicable. When the bit is set to '0', it indicates that the TS_program_map_section sent is not yet applicable * and shall be the next TS_program_map_section to become valid. */ int8_t current_next_indicator; //1bit // 1B /** * The value of this 8-bit field shall be 0x00. */ uint8_t section_number; //8bits // 1B /** * The value of this 8-bit field shall be 0x00. */ uint8_t last_section_number; //8bits // 2B /** * reverved value, must be '1' */ int8_t const1_value1; //3bits /** * This is a 13-bit field indicating the PID of the Transport Stream packets which shall contain the PCR fields * valid for the program specified by program_number. If no PCR is associated with a program definition for private * streams, then this field shall take the value of 0x1FFF. Refer to the semantic definition of PCR in 2.4.3.5 and Table 2-3 * for restrictions on the choice of PCR_PID value. */ int16_t PCR_PID; //13bits // 2B int8_t const1_value2; //4bits /** * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the * number of bytes of the descriptors immediately following the program_info_length field. */ std::vector<char> program_info_desc; //[program_info_length]bytes // array of TSPMTESInfo. std::vector<SrsTsPayloadPMTESInfo*> infos; ...... }
SrsTsPayloadPMT也实现了基类的纯虚函数psi_decode用来解析PMT数据,定义如下:
int SrsTsPayloadPMT::psi_decode(SrsBuffer* stream) { int ret = ERROR_SUCCESS; // atleast 9B for PMT specified if (!stream->require(9)) { ret = ERROR_STREAM_CASTER_TS_PMT; srs_error("ts: demux PMT failed. ret=%d", ret); return ret; } // 2B program_number = stream->read_2bytes(); // 1B int8_t cniv = stream->read_1bytes(); const1_value0 = (cniv >> 6) & 0x03; version_number = (cniv >> 1) & 0x1F; current_next_indicator = cniv & 0x01; // 1B section_number = stream->read_1bytes(); // 1B last_section_number = stream->read_1bytes(); // 2B int16_t ppv = stream->read_2bytes(); const1_value1 = (ppv >> 13) & 0x07; PCR_PID = ppv & 0x1FFF; // 2B int16_t pilv = stream->read_2bytes(); const1_value2 = (pilv >> 12) & 0x0F; /** * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the * number of bytes of the descriptors immediately following the program_info_length field. */ uint16_t program_info_length = pilv & 0xFFF; if (program_info_length > 0) { if (!stream->require(program_info_length)) { ret = ERROR_STREAM_CASTER_TS_PMT; srs_error("ts: demux PMT program info failed. ret=%d", ret); return ret; } program_info_desc.resize(program_info_length); stream->read_bytes(&program_info_desc[0], program_info_length); } // [section_length] - 4(CRC) - 9B - [program_info_length] int ES_EOF_pos = stream->pos() + section_length - 4 - 9 - program_info_length; while (stream->pos() < ES_EOF_pos) { SrsTsPayloadPMTESInfo* info = new SrsTsPayloadPMTESInfo(); infos.push_back(info); if ((ret = info->decode(stream)) != ERROR_SUCCESS) { return ret; } // update the apply pid table switch (info->stream_type) { case SrsTsStreamVideoH264: case SrsTsStreamVideoMpeg4: packet->context->set(info->elementary_PID, SrsTsPidApplyVideo, info->stream_type); break; case SrsTsStreamAudioAAC: case SrsTsStreamAudioAC3: case SrsTsStreamAudioDTS: case SrsTsStreamAudioMp3: packet->context->set(info->elementary_PID, SrsTsPidApplyAudio, info->stream_type); break; default: srs_warn("ts: drop pid=%#x, stream=%#x", info->elementary_PID, info->stream_type); break; } } // update the apply pid table. packet->context->set(packet->pid, SrsTsPidApplyPMT); return ret; }psi_decode函数将解析的媒体流信息存放在SrsTsPayloadPMTESInfo,该类定义如下:
class SrsTsPayloadPMTESInfo { public: // 1B /** * This is an 8-bit field specifying the type of program element carried within the packets with the PID * whose value is specified by the elementary_PID. The values of stream_type are specified in Table 2-29. */ SrsTsStream stream_type; //8bits // 2B /** * reverved value, must be '1' */ int8_t const1_value0; //3bits /** * This is a 13-bit field specifying the PID of the Transport Stream packets which carry the associated * program element. */ int16_t elementary_PID; //13bits // (2+x)B /** * reverved value, must be '1' */ int8_t const1_value1; //4bits std::vector<char> ES_info; //[ES_info_length] bytes. ...... }
字段说明
unsigned stream_type : 8; //指示特定PID的节目元素包的类型。音视频编码格式。
unsigned elementary_PID : 13; //该域指示TS包的PID值。这些TS包含有相关的节目元素
unsigned ES_info_length : 12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数
unsigned descriptor;
媒体节目信息的解析如下:
int SrsTsPayloadPMTESInfo::decode(SrsBuffer* stream) { int ret = ERROR_SUCCESS; // 5B if (!stream->require(5)) { ret = ERROR_STREAM_CASTER_TS_PMT; srs_error("ts: demux PMT es info failed. ret=%d", ret); return ret; } stream_type = (SrsTsStream)stream->read_1bytes(); int16_t epv = stream->read_2bytes(); const1_value0 = (epv >> 13) & 0x07; elementary_PID = epv & 0x1FFF; int16_t eilv = stream->read_2bytes(); const1_value1 = (eilv >> 12) & 0x0f; /** * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the number * of bytes of the descriptors of the associated program element immediately following the ES_info_length field. */ int16_t ES_info_length = eilv & 0x0FFF; if (ES_info_length > 0) { if (!stream->require(ES_info_length)) { ret = ERROR_STREAM_CASTER_TS_PMT; srs_error("ts: demux PMT es info data failed. ret=%d", ret); return ret; } ES_info.resize(ES_info_length); stream->read_bytes(&ES_info[0], ES_info_length); } return ret; }
4.PES包的处理,PES包的pid在解析PMT表时通过SrsTsContext::set进行了记录。
SrsTsPacket::decode中调用下面的方法去判断包中数据是否为PES
if (channel && (channel->apply == SrsTsPidApplyVideo || channel->apply == SrsTsPidApplyAudio)) { // 2.4.3.6 PES packet srs_freep(payload); payload = new SrsTsPayloadPES(this); }
解析PES包对应的类为SrsTsPayloadPES,该类头文件中定义了PES包中的字段。
class SrsTsPayloadPES : public SrsTsPayload { public: // 3B /** * The packet_start_code_prefix is a 24-bit code. Together with the stream_id that follows it * constitutes a packet start code that identifies the beginning of a packet. The packet_start_code_prefix is the bit string * '0000 0000 0000 0000 0000 0001' (0x000001). */ int32_t packet_start_code_prefix; //24bits // 1B /** * In Program Streams, the stream_id specifies the type and number of the elementary stream as defined by the * stream_id Table 2-18. In Transport Streams, the stream_id may be set to any valid value which correctly describes the * elementary stream type as defined in Table 2-18. In Transport Streams, the elementary stream type is specified in the * Program Specific Information as specified in 2.4.4. */ // @see SrsTsPESStreamId, value can be SrsTsPESStreamIdAudioCommon or SrsTsPESStreamIdVideoCommon. uint8_t stream_id; //8bits // 2B /** * A 16-bit field specifying the number of bytes in the PES packet following the last byte of the * field. A value of 0 indicates that the PES packet length is neither specified nor bounded and is allowed only in * PES packets whose payload consists of bytes from a video elementary stream contained in Transport Stream packets. */ uint16_t PES_packet_length; //16bits // 1B /** * 2bits const '10' */ int8_t const2bits; //2bits /** * The 2-bit PES_scrambling_control field indicates the scrambling mode of the PES packet * payload. When scrambling is performed at the PES level, the PES packet header, including the optional fields when * present, shall not be scrambled (see Table 2-19). */ int8_t PES_scrambling_control; //2bits /** * This is a 1-bit field indicating the priority of the payload in this PES packet. A '1' indicates a higher * priority of the payload of the PES packet payload than a PES packet payload with this field set to '0'. A multiplexor can * use the PES_priority bit to prioritize its data within an elementary stream. This field shall not be changed by the transport * mechanism. */ int8_t PES_priority; //1bit /** * This is a 1-bit flag. When set to a value of '1' it indicates that the PES packet header is * immediately followed by the video start code or audio syncword indicated in the data_stream_alignment_descriptor * in 2.6.10 if this descriptor is present. If set to a value of '1' and the descriptor is not present, alignment as indicated in * alignment_type '01' in Table 2-47 and Table 2-48 is required. When set to a value of '0' it is not defined whether any such * alignment occurs or not. */ int8_t data_alignment_indicator; //1bit /** * This is a 1-bit field. When set to '1' it indicates that the material of the associated PES packet payload is * protected by copyright. When set to '0' it is not defined whether the material is protected by copyright. A copyright * descriptor described in 2.6.24 is associated with the elementary stream which contains this PES packet and the copyright * flag is set to '1' if the descriptor applies to the material contained in this PES packet */ int8_t copyright; //1bit /** * This is a 1-bit field. When set to '1' the contents of the associated PES packet payload is an original. * When set to '0' it indicates that the contents of the associated PES packet payload is a copy. */ int8_t original_or_copy; //1bit // 1B /** * This is a 2-bit field. When the PTS_DTS_flags field is set to '10', the PTS fields shall be present in * the PES packet header. When the PTS_DTS_flags field is set to '11', both the PTS fields and DTS fields shall be present * in the PES packet header. When the PTS_DTS_flags field is set to '00' no PTS or DTS fields shall be present in the PES * packet header. The value '01' is forbidden. */ int8_t PTS_DTS_flags; //2bits /** * A 1-bit flag, which when set to '1' indicates that ESCR base and extension fields are present in the PES * packet header. When set to '0' it indicates that no ESCR fields are present. */ int8_t ESCR_flag; //1bit /** * A 1-bit flag, which when set to '1' indicates that the ES_rate field is present in the PES packet header. * When set to '0' it indicates that no ES_rate field is present. */ int8_t ES_rate_flag; //1bit /** * A 1-bit flag, which when set to '1' it indicates the presence of an 8-bit trick mode field. When * set to '0' it indicates that this field is not present. */ int8_t DSM_trick_mode_flag; //1bit /** * A 1-bit flag, which when set to '1' indicates the presence of the additional_copy_info field. * When set to '0' it indicates that this field is not present. */ int8_t additional_copy_info_flag; //1bit /** * A 1-bit flag, which when set to '1' indicates that a CRC field is present in the PES packet. When set to * '0' it indicates that this field is not present. */ int8_t PES_CRC_flag; //1bit /** * A 1-bit flag, which when set to '1' indicates that an extension field exists in this PES packet * header. When set to '0' it indicates that this field is not present. */ int8_t PES_extension_flag; //1bit // 1B /** * An 8-bit field specifying the total number of bytes occupied by the optional fields and any * stuffing bytes contained in this PES packet header. The presence of optional fields is indicated in the byte that precedes * the PES_header_data_length field. */ uint8_t PES_header_data_length; //8bits // 5B /** * Presentation times shall be related to decoding times as follows: The PTS is a 33-bit * number coded in three separate fields. It indicates the time of presentation, tp n (k), in the system target decoder of a * presentation unit k of elementary stream n. The value of PTS is specified in units of the period of the system clock * frequency divided by 300 (yielding 90 kHz). The presentation time is derived from the PTS according to equation 2-11 * below. Refer to 2.7.4 for constraints on the frequency of coding presentation timestamps. */ // ===========1B // 4bits const // 3bits PTS [32..30] // 1bit const '1' // ===========2B // 15bits PTS [29..15] // 1bit const '1' // ===========2B // 15bits PTS [14..0] // 1bit const '1' int64_t pts; // 33bits // 5B /** * The DTS is a 33-bit number coded in three separate fields. It indicates the decoding time, * td n (j), in the system target decoder of an access unit j of elementary stream n. The value of DTS is specified in units of * the period of the system clock frequency divided by 300 (yielding 90 kHz). */ // ===========1B // 4bits const // 3bits DTS [32..30] // 1bit const '1' // ===========2B // 15bits DTS [29..15] // 1bit const '1' // ===========2B // 15bits DTS [14..0] // 1bit const '1' int64_t dts; // 33bits // 6B /** * The elementary stream clock reference is a 42-bit field coded in two parts. The first * part, ESCR_base, is a 33-bit field whose value is given by ESCR_base(i), as given in equation 2-14. The second part, * ESCR_ext, is a 9-bit field whose value is given by ESCR_ext(i), as given in equation 2-15. The ESCR field indicates the * intended time of arrival of the byte containing the last bit of the ESCR_base at the input of the PES-STD for PES streams * (refer to 2.5.2.4). */ // 2bits reserved // 3bits ESCR_base[32..30] // 1bit const '1' // 15bits ESCR_base[29..15] // 1bit const '1' // 15bits ESCR_base[14..0] // 1bit const '1' // 9bits ESCR_extension // 1bit const '1' int64_t ESCR_base; //33bits int16_t ESCR_extension; //9bits // 3B /** * The ES_rate field is a 22-bit unsigned integer specifying the rate at which the * system target decoder receives bytes of the PES packet in the case of a PES stream. The ES_rate is valid in the PES * packet in which it is included and in subsequent PES packets of the same PES stream until a new ES_rate field is * encountered. The value of the ES_rate is measured in units of 50 bytes/second. The value 0 is forbidden. The value of the * ES_rate is used to define the time of arrival of bytes at the input of a P-STD for PES streams defined in 2.5.2.4. The * value encoded in the ES_rate field may vary from PES_packet to PES_packet. */ // 1bit const '1' // 22bits ES_rate // 1bit const '1' int32_t ES_rate; //22bits // 1B /** * A 3-bit field that indicates which trick mode is applied to the associated video stream. In cases of * other types of elementary streams, the meanings of this field and those defined by the following five bits are undefined. * For the definition of trick_mode status, refer to the trick mode section of 2.4.2.3. */ int8_t trick_mode_control; //3bits int8_t trick_mode_value; //5bits // 1B // 1bit const '1' /** * This 7-bit field contains private data relating to copyright information. */ int8_t additional_copy_info; //7bits // 2B /** * The previous_PES_packet_CRC is a 16-bit field that contains the CRC value that yields * a zero output of the 16 registers in the decoder similar to the one defined in Annex A, */ int16_t previous_PES_packet_CRC; //16bits // 1B /** * A 1-bit flag which when set to '1' indicates that the PES packet header contains private data. * When set to a value of '0' it indicates that private data is not present in the PES header. */ int8_t PES_private_data_flag; //1bit /** * A 1-bit flag which when set to '1' indicates that an ISO/IEC 11172-1 pack header or a * Program Stream pack header is stored in this PES packet header. If this field is in a PES packet that is contained in a * Program Stream, then this field shall be set to '0'. In a Transport Stream, when set to the value '0' it indicates that no pack * header is present in the PES header. */ int8_t pack_header_field_flag; //1bit /** * A 1-bit flag which when set to '1' indicates that the * program_packet_sequence_counter, MPEG1_MPEG2_identifier, and original_stuff_length fields are present in this * PES packet. When set to a value of '0' it indicates that these fields are not present in the PES header. */ int8_t program_packet_sequence_counter_flag; //1bit /** * A 1-bit flag which when set to '1' indicates that the P-STD_buffer_scale and P-STD_buffer_size * are present in the PES packet header. When set to a value of '0' it indicates that these fields are not present in the * PES header. */ int8_t P_STD_buffer_flag; //1bit /** * reverved value, must be '1' */ int8_t const1_value0; //3bits /** * A 1-bit field which when set to '1' indicates the presence of the PES_extension_field_length * field and associated fields. When set to a value of '0' this indicates that the PES_extension_field_length field and any * associated fields are not present. */ int8_t PES_extension_flag_2; //1bit // 16B /** * This is a 16-byte field which contains private data. This data, combined with the fields before and * after, shall not emulate the packet_start_code_prefix (0x000001). */ std::vector<char> PES_private_data; //128bits // (1+x)B std::vector<char> pack_field; //[pack_field_length] bytes // 2B // 1bit const '1' /** * The program_packet_sequence_counter field is a 7-bit field. It is an optional * counter that increments with each successive PES packet from a Program Stream or from an ISO/IEC 11172-1 Stream or * the PES packets associated with a single program definition in a Transport Stream, providing functionality similar to a * continuity counter (refer to 2.4.3.2). This allows an application to retrieve the original PES packet sequence of a Program * Stream or the original packet sequence of the original ISO/IEC 11172-1 stream. The counter will wrap around to 0 after * its maximum value. Repetition of PES packets shall not occur. Consequently, no two consecutive PES packets in the * program multiplex shall have identical program_packet_sequence_counter values. */ int8_t program_packet_sequence_counter; //7bits // 1bit const '1' /** * A 1-bit flag which when set to '1' indicates that this PES packet carries information from * an ISO/IEC 11172-1 stream. When set to '0' it indicates that this PES packet carries information from a Program Stream. */ int8_t MPEG1_MPEG2_identifier; //1bit /** * This 6-bit field specifies the number of stuffing bytes used in the original ITU-T * Rec. H.222.0 | ISO/IEC 13818-1 PES packet header or in the original ISO/IEC 11172-1 packet header. */ int8_t original_stuff_length; //6bits // 2B // 2bits const '01' /** * The P-STD_buffer_scale is a 1-bit field, the meaning of which is only defined if this PES packet * is contained in a Program Stream. It indicates the scaling factor used to interpret the subsequent P-STD_buffer_size field. * If the preceding stream_id indicates an audio stream, P-STD_buffer_scale shall have the value '0'. If the preceding * stream_id indicates a video stream, P-STD_buffer_scale shall have the value '1'. For all other stream types, the value * may be either '1' or '0'. */ int8_t P_STD_buffer_scale; //1bit /** * The P-STD_buffer_size is a 13-bit unsigned integer, the meaning of which is only defined if this * PES packet is contained in a Program Stream. It defines the size of the input buffer, BS n , in the P-STD. If * P-STD_buffer_scale has the value '0', then the P-STD_buffer_size measures the buffer size in units of 128 bytes. If * P-STD_buffer_scale has the value '1', then the P-STD_buffer_size measures the buffer size in units of 1024 bytes. */ int16_t P_STD_buffer_size; //13bits // (1+x)B // 1bit const '1' std::vector<char> PES_extension_field; //[PES_extension_field_length] bytes // NB /** * This is a fixed 8-bit value equal to '1111 1111' that can be inserted by the encoder, for example to meet * the requirements of the channel. It is discarded by the decoder. No more than 32 stuffing bytes shall be present in one * PES packet header. */ int nb_stuffings; // NB /** * PES_packet_data_bytes shall be contiguous bytes of data from the elementary stream * indicated by the packet's stream_id or PID. When the elementary stream data conforms to ITU-T * Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 13818-3, the PES_packet_data_bytes shall be byte aligned to the bytes of this * Recommendation | International Standard. The byte-order of the elementary stream shall be preserved. The number of * PES_packet_data_bytes, N, is specified by the PES_packet_length field. N shall be equal to the value indicated in the * PES_packet_length minus the number of bytes between the last byte of the PES_packet_length field and the first * PES_packet_data_byte. * * In the case of a private_stream_1, private_stream_2, ECM_stream, or EMM_stream, the contents of the * PES_packet_data_byte field are user definable and will not be specified by ITU-T | ISO/IEC in the future. */ int nb_bytes; // NB /** * This is a fixed 8-bit value equal to '1111 1111'. It is discarded by the decoder. */ int nb_paddings; ...... }PES各个字段说明请参照:http://blog.csdn.net/cabbage2008/article/details/49848937
SrsTsPayloadPES的decode函数会解析上述字段。定义如下:
int SrsTsPayloadPES::decode(SrsBuffer* stream, SrsTsMessage** ppmsg) { int ret = ERROR_SUCCESS; // find the channel from chunk. SrsTsChannel* channel = packet->context->get(packet->pid); if (!channel) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PES no channel for pid=%#x. ret=%d", packet->pid, ret); return ret; } // init msg. SrsTsMessage* msg = channel->msg; if (!msg) { msg = new SrsTsMessage(channel, packet); channel->msg = msg; } // we must cache the fresh state of msg, // for the PES_packet_length is 0, the first payload_unit_start_indicator always 1, // so should check for the fresh and not completed it. bool is_fresh_msg = msg->fresh(); // check when fresh, the payload_unit_start_indicator // should be 1 for the fresh msg. if (is_fresh_msg && !packet->payload_unit_start_indicator) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: PES fresh packet length=%d, us=%d, cc=%d. ret=%d", msg->PES_packet_length, packet->payload_unit_start_indicator, packet->continuity_counter, ret); return ret; } // check when not fresh and PES_packet_length>0, // the payload_unit_start_indicator should never be 1 when not completed. if (!is_fresh_msg && msg->PES_packet_length > 0 && !msg->completed(packet->payload_unit_start_indicator) && packet->payload_unit_start_indicator ) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: PES packet length=%d, payload=%d, us=%d, cc=%d. ret=%d", msg->PES_packet_length, msg->payload->length(), packet->payload_unit_start_indicator, packet->continuity_counter, ret); // reparse current msg. stream->skip(stream->pos() * -1); srs_freep(msg); channel->msg = NULL; return ERROR_SUCCESS; } // check the continuity counter if (!is_fresh_msg) { // late-incoming or duplicated continuity, drop message. // @remark check overflow, the counter plus 1 should greater when invalid. if (msg->continuity_counter >= packet->continuity_counter && ((msg->continuity_counter + 1) & 0x0f) > packet->continuity_counter ) { srs_warn("ts: drop PES %dB for duplicated cc=%#x", msg->continuity_counter); stream->skip(stream->size() - stream->pos()); return ret; } // when got partially message, the continous count must be continuous, or drop it. if (((msg->continuity_counter + 1) & 0x0f) != packet->continuity_counter) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: continuity must be continous, msg=%#x, packet=%#x. ret=%d", msg->continuity_counter, packet->continuity_counter, ret); // reparse current msg. stream->skip(stream->pos() * -1); srs_freep(msg); channel->msg = NULL; return ERROR_SUCCESS; } } msg->continuity_counter = packet->continuity_counter; // for the PES_packet_length(0), reap when completed. if (!is_fresh_msg && msg->completed(packet->payload_unit_start_indicator)) { // reap previous PES packet. *ppmsg = msg; channel->msg = NULL; // reparse current msg. stream->skip(stream->pos() * -1); return ret; } // contious packet, append bytes for unit start is 0 if (!packet->payload_unit_start_indicator) { if ((ret = msg->dump(stream, &nb_bytes)) != ERROR_SUCCESS) { return ret; } } // when unit start, parse the fresh msg. if (packet->payload_unit_start_indicator) { // 6B fixed header. if (!stream->require(6)) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PSE failed. ret=%d", ret); return ret; } // 3B packet_start_code_prefix = stream->read_3bytes(); // 1B stream_id = stream->read_1bytes(); // 2B PES_packet_length = stream->read_2bytes(); // check the packet start prefix. packet_start_code_prefix &= 0xFFFFFF; if (packet_start_code_prefix != 0x01) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PES start code failed, expect=0x01, actual=%#x. ret=%d", packet_start_code_prefix, ret); return ret; } int pos_packet = stream->pos(); // @remark the sid indicates the elementary stream format. // the SrsTsPESStreamIdAudio and SrsTsPESStreamIdVideo is start by 0b110 or 0b1110 SrsTsPESStreamId sid = (SrsTsPESStreamId)stream_id; msg->sid = sid; if (sid != SrsTsPESStreamIdProgramStreamMap && sid != SrsTsPESStreamIdPaddingStream && sid != SrsTsPESStreamIdPrivateStream2 && sid != SrsTsPESStreamIdEcmStream && sid != SrsTsPESStreamIdEmmStream && sid != SrsTsPESStreamIdProgramStreamDirectory && sid != SrsTsPESStreamIdDsmccStream && sid != SrsTsPESStreamIdH2221TypeE ) { // 3B flags. if (!stream->require(3)) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PES flags failed. ret=%d", ret); return ret; } // 1B int8_t oocv = stream->read_1bytes(); // 1B int8_t pefv = stream->read_1bytes(); // 1B PES_header_data_length = stream->read_1bytes(); // position of header start. int pos_header = stream->pos(); const2bits = (oocv >> 6) & 0x03; PES_scrambling_control = (oocv >> 4) & 0x03; PES_priority = (oocv >> 3) & 0x01; data_alignment_indicator = (oocv >> 2) & 0x01; copyright = (oocv >> 1) & 0x01; original_or_copy = oocv & 0x01; PTS_DTS_flags = (pefv >> 6) & 0x03; ESCR_flag = (pefv >> 5) & 0x01; ES_rate_flag = (pefv >> 4) & 0x01; DSM_trick_mode_flag = (pefv >> 3) & 0x01; additional_copy_info_flag = (pefv >> 2) & 0x01; PES_CRC_flag = (pefv >> 1) & 0x01; PES_extension_flag = pefv & 0x01; // check required together. int nb_required = 0; nb_required += (PTS_DTS_flags == 0x2)? 5:0; nb_required += (PTS_DTS_flags == 0x3)? 10:0; nb_required += ESCR_flag? 6:0; nb_required += ES_rate_flag? 3:0; nb_required += DSM_trick_mode_flag? 1:0; nb_required += additional_copy_info_flag? 1:0; nb_required += PES_CRC_flag? 2:0; nb_required += PES_extension_flag? 1:0; if (!stream->require(nb_required)) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PES payload failed. ret=%d", ret); return ret; } // 5B if (PTS_DTS_flags == 0x2) { if ((ret = decode_33bits_dts_pts(stream, &pts)) != ERROR_SUCCESS) { return ret; } dts = pts; // update the dts and pts of message. msg->dts = dts; msg->pts = pts; } // 10B if (PTS_DTS_flags == 0x3) { if ((ret = decode_33bits_dts_pts(stream, &pts)) != ERROR_SUCCESS) { return ret; } if ((ret = decode_33bits_dts_pts(stream, &dts)) != ERROR_SUCCESS) { return ret; } // check sync, the diff of dts and pts should never greater than 1s. if (dts - pts > 90000 || pts - dts > 90000) { srs_warn("ts: sync dts=%" PRId64 ", pts=%" PRId64, dts, pts); } // update the dts and pts of message. msg->dts = dts; msg->pts = pts; } // 6B if (ESCR_flag) { ESCR_extension = 0; ESCR_base = 0; stream->skip(6); srs_warn("ts: demux PES, ignore the escr."); } // 3B if (ES_rate_flag) { ES_rate = stream->read_3bytes(); ES_rate = ES_rate >> 1; ES_rate &= 0x3FFFFF; } // 1B if (DSM_trick_mode_flag) { trick_mode_control = stream->read_1bytes(); trick_mode_value = trick_mode_control & 0x1f; trick_mode_control = (trick_mode_control >> 5) & 0x03; } // 1B if (additional_copy_info_flag) { additional_copy_info = stream->read_1bytes(); additional_copy_info &= 0x7f; } // 2B if (PES_CRC_flag) { previous_PES_packet_CRC = stream->read_2bytes(); } // 1B if (PES_extension_flag) { int8_t efv = stream->read_1bytes(); PES_private_data_flag = (efv >> 7) & 0x01; pack_header_field_flag = (efv >> 6) & 0x01; program_packet_sequence_counter_flag = (efv >> 5) & 0x01; P_STD_buffer_flag = (efv >> 4) & 0x01; const1_value0 = (efv >> 1) & 0x07; PES_extension_flag_2 = efv & 0x01; nb_required = 0; nb_required += PES_private_data_flag? 16:0; nb_required += pack_header_field_flag? 1:0; // 1+x bytes. nb_required += program_packet_sequence_counter_flag? 2:0; nb_required += P_STD_buffer_flag? 2:0; nb_required += PES_extension_flag_2? 1:0; // 1+x bytes. if (!stream->require(nb_required)) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PSE ext payload failed. ret=%d", ret); return ret; } // 16B if (PES_private_data_flag) { PES_private_data.resize(16); stream->read_bytes(&PES_private_data[0], 16); } // (1+x)B if (pack_header_field_flag) { // This is an 8-bit field which indicates the length, in bytes, of the pack_header_field() uint8_t pack_field_length = stream->read_1bytes(); if (pack_field_length > 0) { // the adjust required bytes. nb_required = nb_required - 16 - 1 + pack_field_length; if (!stream->require(nb_required)) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PSE ext pack failed. ret=%d", ret); return ret; } pack_field.resize(pack_field_length); stream->read_bytes(&pack_field[0], pack_field_length); } } // 2B if (program_packet_sequence_counter_flag) { program_packet_sequence_counter = stream->read_1bytes(); program_packet_sequence_counter &= 0x7f; original_stuff_length = stream->read_1bytes(); MPEG1_MPEG2_identifier = (original_stuff_length >> 6) & 0x01; original_stuff_length &= 0x3f; } // 2B if (P_STD_buffer_flag) { P_STD_buffer_size = stream->read_2bytes(); // '01' //int8_t const2bits = (P_STD_buffer_scale >>14) & 0x03; P_STD_buffer_scale = (P_STD_buffer_scale >>13) & 0x01; P_STD_buffer_size &= 0x1FFF; } // (1+x)B if (PES_extension_flag_2) { /** * This is a 7-bit field which specifies the length, in bytes, of the data following this field in * the PES extension field up to and including any reserved bytes. */ uint8_t PES_extension_field_length = stream->read_1bytes(); PES_extension_field_length &= 0x7F; if (PES_extension_field_length > 0) { if (!stream->require(PES_extension_field_length)) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PSE ext field failed. ret=%d", ret); return ret; } PES_extension_field.resize(PES_extension_field_length); stream->read_bytes(&PES_extension_field[0], PES_extension_field_length); } } } // stuffing_byte nb_stuffings = PES_header_data_length - (stream->pos() - pos_header); if (nb_stuffings > 0) { if (!stream->require(nb_stuffings)) { ret = ERROR_STREAM_CASTER_TS_PSE; srs_error("ts: demux PSE stuffings failed. ret=%d", ret); return ret; } stream->skip(nb_stuffings); } // PES_packet_data_byte, page58. // the packet size contains the header size. // The number of PES_packet_data_bytes, N, is specified by the // PES_packet_length field. N shall be equal to the value // indicated in the PES_packet_length minus the number of bytes // between the last byte of the PES_packet_length field and the // first PES_packet_data_byte. /** * when actual packet length > 0xffff(65535), * which exceed the max uint16_t packet length, * use 0 packet length, the next unit start indicates the end of packet. */ if (PES_packet_length > 0) { int nb_packet = PES_packet_length - (stream->pos() - pos_packet); msg->PES_packet_length = srs_max(0, nb_packet); } // xB if ((ret = msg->dump(stream, &nb_bytes)) != ERROR_SUCCESS) { return ret; } } else if (sid == SrsTsPESStreamIdProgramStreamMap || sid == SrsTsPESStreamIdPrivateStream2 || sid == SrsTsPESStreamIdEcmStream || sid == SrsTsPESStreamIdEmmStream || sid == SrsTsPESStreamIdProgramStreamDirectory || sid == SrsTsPESStreamIdDsmccStream || sid == SrsTsPESStreamIdH2221TypeE ) { // for (i = 0; i < PES_packet_length; i++) { // PES_packet_data_byte // } // xB if ((ret = msg->dump(stream, &nb_bytes)) != ERROR_SUCCESS) { return ret; } } else if (sid == SrsTsPESStreamIdPaddingStream) { // for (i = 0; i < PES_packet_length; i++) { // padding_byte // } nb_paddings = stream->size() - stream->pos(); stream->skip(nb_paddings); srs_info("ts: drop %dB padding bytes", nb_paddings); } else { int nb_drop = stream->size() - stream->pos(); stream->skip(nb_drop); srs_warn("ts: drop the pes packet %dB for stream_id=%#x", nb_drop, stream_id); } } // when fresh and the PES_packet_length is 0, // the payload_unit_start_indicator always be 1, // the message should never EOF for the first packet. if (is_fresh_msg && msg->PES_packet_length == 0) { return ret; } // check msg, reap when completed. if (msg->completed(packet->payload_unit_start_indicator)) { *ppmsg = msg; channel->msg = NULL; srs_info("ts: reap msg for completed."); } return ret; }该函数通过SrsTsContext::get函数获取记录的SrsTsChannel,解析后的数据就放在SrsTsChannel::msg中。
pts,dts也会被解析记录到msg中。
发送视频时帧会超过188字节,因此一帧数据会被拆成多个PES包发送,因此接收到PES包的数据时就需要根据continuity_counter将解析后的数据重新组装成一个视频帧。
if (adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth)
相关文章推荐
- SRS 代码分析【FLV文件解析】
- CVE-2012-0003 Microsoft Windows Media Player ‘winmm.dll’ MIDI文件解析远程代码执行漏洞 分析
- MPEG-2 TS学习(七)tsfilter源码阅读(4)解析PAT
- MPEG-2传输流的码流分析(PS与TS码流)
- C# 解析 Excel 并且生成 Csv 文件代码分析
- Android 开源框架Universal-Image-Loader完全解析(五)- 从代码分析Android-Universal-Image-Loader的图片加载、显示流程
- FFmpeg对mpeg2-TS解析的最详细分析
- demo-rgbd论文解析+代码分析(一)
- SRS 代码分析【RTMP Chunck数据读取】
- netlink监听网络变化代码(转载)+流程分析(原创+转载)+数据结构以及相关宏的解析(原创)
- wojilu系统的ORM代码解析-[源代码结构分析,用特性和反射来感知属性-特性介绍篇]
- wav文件格式分析详解和解析代码
- Ogre材质解析代码初步分析(六)
- ffmpeg对mpeg2-TS解析的最详细分析
- SRS 代码分析【HLS切片】
- ffmpeg对mpeg2-TS解析的最详细分析
- SRS 代码分析【RTMP信息play/publish】
- MPEG-2 TS码流分析
- JavaScript 解析Json字符串的性能比较分析代码
- MPEG-2 TS码流分析