SRS 代码分析【mpeg-ts解析】
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,PID,adaptation_field_control,continuity_counter
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;
}
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)