SRS 代码分析【mpeg-ts解析】

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;
}


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分别是什么。这样如果我们要选择其中一个VideoAudio收看,那么只需要把要收看的节目的Video PIDAudio PID保存起来,在处理Packet的时候进行过滤即可实现。

PMT表字段说明如下:

  1.     unsigned table_id                        : 8; //固定为0x02, 表示PMT表  
  2.     unsigned section_syntax_indicator        : 1; //固定为0x01  
  3.     unsigned zero                            : 1; //0x01  
  4.     unsigned reserved_1                      : 2; //0x03  
  5.     unsigned section_length                  : 12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。  
  6.      unsigned program_number                    : 16;// 指出该节目对应于可应用的Program map PID  
  7.     unsigned reserved_2                        : 2; //0x03  
  8.     unsigned version_number                    : 5; //指出TS流中Program map section的版本号  
  9.      unsigned current_next_indicator            : 1; //当该位置1时,当前传送的Program map section可用;  
  10.                                                      //当该位置0时,指示当前传送的Program map section不可用,下一个TS流的Program map section有效。  
  11.      unsigned section_number                    : 8; //固定为0x00  
  12.     unsigned last_section_number            : 8; //固定为0x00  
  13.     unsigned reserved_3                        : 3; //0x07  
  14.     unsigned PCR_PID                        : 13; //指明TS包的PID值,该TS包含有PCR域,  
  15.             //该PCR值对应于由节目号指定的对应节目。  
  16.             //如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。  
  17.     unsigned reserved_4                        : 4; //预留为0x0F  
  18.     unsigned program_info_length            : 12; //前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。  
  19.       
  20.     std::vector<TS_PMT_Stream> PMT_Stream;  //每个元素包含8位, 指示特定PID的节目元素包的类型。该处PID由elementary PID指定  
  21.     unsigned reserved_5                        : 3; //0x07  
  22.     unsigned reserved_6                        : 4; //0x0F  
  23.     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.
	......
}

字段说明


  1. unsigned stream_type                       : 8; //指示特定PID的节目元素包的类型。音视频编码格式。
  2.  unsigned elementary_PID                    : 13; //该域指示TS包的PID值。这些TS包含有相关的节目元素    
  3.  unsigned ES_info_length                    : 12; //前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数    
  4.  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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值