必威体育Betway必威体育官网
当前位置:首页 > IT技术

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

时间:2019-08-29 12:12:06来源:IT技术作者:seo实验室小编阅读:70次「手机版」
 

ppmsg

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 fieldlength和PCR,这里重点讲解他们的主要用处:

adaptationfield length指的是自适应字段的长度,也就是,从discontinuity indicator 到adaptation field最后的长度,也就是从第6字节(包含第6字节)开始算到最后。

PCR

这个值是系统时间戳,在PES层时间戳是PTS与DTS,这里要注意与PCR,PTS,DTS的概念,可能会让人模糊。PCR是TS层的时间戳,PTS与DTS是PES的时间戳,PCR在PES层相当于DTS,TS不需要考虑PTS。为啥不需要,这里就要讲下,PTS的具体概念。详细的在ISO-13818-1上有,详细到可以看到你吐。其实实际中不需要考虑这么多。我简单的讲吧。在ES流中,依次组成图像帧序为I1P4B2B3P7B5B6I10B8B9的,这里,I、P、B分别指I帧,P帧,B帧。具体意义可以参考H264的相关基本概念,对于I、P帧而言,PES的图像帧序为I1P4B2B3P7B5B6I10B8B9,应该P4比B2、B3在先,但显示时P4一定 要比B2、B3在后,这就必须重新排序。在PTS/DTS时间标志指引下,将P4提前插入数据流,经过缓存器重新排序,重建视频帧序 I1B2B3P4B5B6P7B8B9I10。显然,PTS/DTS是表明确定事件或确定信息,并以专用时标形态确定事件或信息的开始时刻。说到这里,PTS,与DTS的概念应该明白了。但是为啥TS层不需要呢,因为TS层只是负责传输,你知道解码的时间在什么位置,确保传输的TS包不是延迟太久就可以了,具体的显示细节交给PES层去做。

存在payload的场合,会接着根据pid类型判断包的类型,有PAT,PMT,PES,以及resolved。

if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth)

下面是pid类型定义的枚举

enum SrsTsPid
{
    // Program Association Table(see Table 2-25).
    SrsTsPidPAT = 0x00,
    // Conditional Access Table (see Table 2-27).
    SrsTsPidCAT = 0x01,
    // Transport Stream Description Table
    SrsTsPidTSDT = 0x02,
    // Reserved
    SrsTsPidReservedStart = 0x03,
    SrsTsPidReservedEnd = 0x0f,
    // May be assigned as network_PID, Program_map_PID, elementary_PID, or for other purposes
    SrsTsPidAppStart = 0x10,
    SrsTsPidappend = 0x1ffe,
    // null packets (see Table 2-3)
    SrsTsPidNULL = 0x01FFF,
};
类型说明

PID program id

节目标示符,一个13位的无符号整数。作用如下表描述。

3.PAT包的处理,PAT包的pid为0x00

PAT表中定义的字段说明如下

unsigned table_id : 8; //固定为0x00 ,标志是该表是PAT

unsigned section_syntax_indicator : 1; //段语法标志位,固定为1

unsigned zero : 1; //0

unsigned reserved_1 : 2; // 保留位

unsigned section_length : 12; //表示这个字节后面有用的字节数,包括CRC32

unsigned transport_stream_id : 16; //该传输流的ID,区别于一个网络中其它多路复用的流

unsigned reserved_2 : 2;// 保留位

unsigned version_number : 5; //范围0-31,表示PAT的版本号

unsigned current_next_indicator : 1; //发送的PAT是当前有效还是下一个PAT有效

unsigned section_number : 8; //分段的号码。PAT可能分为多段传输,第一段为00,以后每个分段加1,最多可能有256个分段

unsigned last_section_number : 8; //最后一个分段的号码

std::vector<TS_PAT_Program> program;

unsigned reserved_3 : 3; // 保留位

unsigned network_PID : 13; //网络信息表(NIT)的PID,节目号为0时对应的PID为network_PID

unsigned CRC_32 : 32; //CRC32校验码

解析PAT包对应的类为SrsTsPayloadPAT,该类继承自SrsTsPayloadPSI

class SrsTsPayloadPAT : public SrsTsPayloadPSI
{
public:
    // 2B
    /**
     * This is a 16-bit field which serves as a label to identify this Transport Stream from any other
     * multiplex within a network. Its value is defined by the user.
     */
    uint16_t transport_stream_id; //16bits
    
    // 1B
    /**
     * reverved value, must be '1'
     */
    int8_t const3_value; //2bits
    /**
     * This 5-bit field is the version number of the whole Program Association Table. The version number
     * shall be incremented by 1 modulo 32 whenever the definition of the Program Association Table changes. When the
     * current_next_indicator is set to '1', then the version_number shall be that of the currently applicable Program Association
     * Table. When the current_next_indicator is set to '0', then the version_number shall be that of the next applicable Program
     * Association Table.
     */
    int8_t version_number; //5bits
    /**
     * A 1-bit indicator, which when set to '1' indicates that the Program Association Table sent is
     * currently applicable. When the bit is set to '0', it indicates that the table sent is not yet applicable and shall be the next
     * table to become valid.
     */
    int8_t current_next_indicator; //1bit
    
    // 1B
    /**
     * This 8-bit field gives the number of this section. The section_number of the first section in the
     * Program Association Table shall be 0x00. It shall be incremented by 1 with each additional section in the Program
     * Association Table.
     */
    uint8_t section_number; //8bits
    
    // 1B
    /**
     * This 8-bit field specifies the number of the last section (that is, the section with the highest
     * section_number) of the complete Program Association Table.
     */
    uint8_t last_section_number; //8bits
    
    // multiple 4B program data.
    std::vector<SrsTsPayloadPATProgram*> programs;
	......
}

由于PAT表和PMT表的读取过程存在相同的处理,相同的部分被封装到基类SrsTsPayloadPSI的decode函数中;基类SrsTsPayloadPSI中有纯虚函数psi_decode,不同的部分在子类的psi_decode中去实现。

SrsTsPayloadPSI的头文件定义如下

class SrsTsPayloadPSI : public SrsTsPayload
{
public:
    // 1B
    /**
     * This is an 8-bit field whose value shall be the number of bytes, immediately following the pointer_field
     * until the first byte of the first section that is present in the payload of the Transport Stream packet (so a value of 0x00 in
     * the pointer_field indicates that the section starts immediately after the pointer_field). When at least one section begins in
     * a given Transport Stream packet, then the payload_unit_start_indicator (refer to 2.4.3.2) shall be set to 1 and the first
     * byte of the payload of that Transport Stream packet shall contain the pointer. When no section begins in a given
     * Transport Stream packet, then the payload_unit_start_indicator shall be set to 0 and no pointer shall be sent in the
     * payload of that packet.
     */
    int8_t pointer_field;
public:
    // 1B
    /**
     * This is an 8-bit field, which shall be set to 0x00 as shown in Table 2-26.
     */
    SrsTsPsiId table_id; //8bits
    
    // 2B
    /**
     * The section_syntax_indicator is a 1-bit field which shall be set to '1'.
     */
    int8_t section_syntax_indicator; //1bit
    /**
     * const value, must be '0'
     */
    int8_t const0_value; //1bit
    /**
     * reverved value, must be '1'
     */
    int8_t const1_value; //2bits
    /**
     * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the number
     * of bytes of the section, starting immediately following the section_length field, and including the CRC. The value in this
     * field shall not exceed 1021 (0x3FD).
     */
    uint16_t section_length; //12bits
public:
    // the specified psi info, for example, PAT fields.
public:
    // 4B
    /**
     * This is a 32-bit field that contains the CRC value that gives a zero output of the registers in the decoder
     * defined in Annex A after processing the entire section.
     * @remark crc32(bytes without pointer field, before crc32 field)
     */
    int32_t CRC_32; //32bits
	......
}

SrsTsPayloadPAT没有重写decode方法,实际还是调用父类SrsTsPayloadPSI的decode方法,定义如下

int SrsTsPayloadPSI::decode(SrsBuffer* stream, SrsTsMessage** /*ppmsg*/)
{
    int ret = ERROR_SUCCESS;
    
    /**
     * When the payload of the Transport Stream packet contains PSI data, the payload_unit_start_indicator has the following
     * significance: if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value
     * shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field. If the
     * Transport Stream packet does not carry the first byte of a PSI section, the payload_unit_start_indicator value shall be '0',
     * indicating that there is no pointer_field in the payload. Refer to 2.4.4.1 and 2.4.4.2. This also applies to private streams of
     * stream_type 5 (refer to Table 2-29).
     */
    if (packet->payload_unit_start_indicator) {
        if (!stream->require(1)) {
            ret = ERROR_STREAM_CASTER_TS_PSI;
            srs_error("ts: demux PSI failed. ret=%d", ret);
            return ret;
        }
        pointer_field = stream->read_1bytes();
    }
    
    // to calc the crc32
    char* ppat = stream->data() + stream->pos();
    int pat_pos = stream->pos();
    
    // atleast 3B for all psi.
    if (!stream->require(3)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: demux PSI failed. ret=%d", ret);
        return ret;
    }
    // 1B
    table_id = (SrsTsPsiId)stream->read_1bytes();
    
    // 2B
    int16_t slv = stream->read_2bytes();
    
    section_syntax_indicator = (slv >> 15) & 0x01;
    const0_value = (slv >> 14) & 0x01;
    const1_value = (slv >> 12) & 0x03;
    section_length = slv & 0x0FFF;
    
    // no section, ignore.
    if (section_length == 0) {
        srs_warn("ts: demux PAT ignore empty section");
        return ret;
    }
    
    if (!stream->require(section_length)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: demux PAT section failed. ret=%d", ret);
        return ret;
    }
    
    // call the virtual method of actual PSI.
    if ((ret = psi_decode(stream)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // 4B
    if (!stream->require(4)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: demux PSI crc32 failed. ret=%d", ret);
        return ret;
    }
    CRC_32 = stream->read_4bytes();
    
    // verify crc32.
    int32_t crc32 = srs_crc32_mpegts(ppat, stream->pos() - pat_pos - 4);
    if (crc32 != CRC_32) {
        ret = ERROR_STREAM_CASTER_TS_CRC32;
        srs_error("ts: verify PSI crc32 failed. ret=%d", ret);
        return ret;
    }
    
    // consume left stuffings
    if (!stream->empty()) {
        int nb_stuffings = stream->size() - stream->pos();
        char* stuffing = stream->data() + stream->pos();
        
        // all stuffing must be 0xff.
        // TODO: FIXME: maybe need to remove the following.
        for (int i = 0; i < nb_stuffings; i++) {
            if ((uint8_t)stuffing[i] != 0xff) {
                srs_warn("ts: stuff is not 0xff, actual=%#x", stuffing[i]);
                break;
            }
        }
        
        stream->skip(nb_stuffings);
    }
    
    return ret;
}
关于pointfiled的说明:当TS包带有PSI数据并且payload_unit_start_indicator为1时,表示TS包带有PSI部分的第一个字节,即第一个字节带有指针pointer_field,为0表示TS包不带有一个PSI部分的第一个字节,即在有效净荷中没有指针pointer_field。

SrsTsPayloadPSI的decode函数会调用子类的psi_decode函数,这里调用的是SrsTsPayloadPAT的psi_decode函数,定义如下:

int SrsTsPayloadPAT::psi_decode(SrsBuffer* stream)
{
    int ret = ERROR_SUCCESS;
    
    // atleast 5B for PAT specified
    if (!stream->require(5)) {
        ret = ERROR_STREAM_CASTER_TS_PAT;
        srs_error("ts: demux PAT failed. ret=%d", ret);
        return ret;
    }
    
    int pos = stream->pos();
    
    // 2B
    transport_stream_id = stream->read_2bytes();
    
    // 1B
    int8_t cniv = stream->read_1bytes();
    
    const3_value = (cniv >> 6) & 0x03;
    version_number = (cniv >> 1) & 0x1F;
    current_next_indicator = cniv & 0x01;
    
    // TODO: FIXME: check the indicator.
    
    // 1B
    section_number = stream->read_1bytes();
    // 1B
    last_section_number = stream->read_1bytes();
    
    // multiple 4B program data.
    int program_bytes = section_length - 4 - (stream->pos() - pos);
    for (int i = 0; i < program_bytes; i += 4) {
        SrsTsPayloadPATProgram* program = new SrsTsPayloadPATProgram();
        
        if ((ret = program->decode(stream)) != ERROR_SUCCESS) {
            return ret;
        }
        
        // update the apply pid table.
        packet->context->set(program->pid, SrsTsPidApplyPMT);
        
        programs.push_back(program);
    }
    
    // update the apply pid table.
    packet->context->set(packet->pid, SrsTsPidApplyPAT);
    packet->context->on_pmt_parsed();
    
    return ret;
}
psi_decode函数将解析的节目信息存放在SrsTsPayloadPATProgram,该类定义如下:

class SrsTsPayloadPATProgram
{
public:
    // 4B
    /**
     * Program_number is a 16-bit field. It specifies the program to which the program_map_PID is
     * applicable. When set to 0x0000, then the following PID reference shall be the network PID. For all other cases the value
     * of this field is user defined. This field shall not take any single value more than once within one version of the Program
     * Association Table.
     */
    int16_t number; // 16bits
    /**
     * reverved value, must be '1'
     */
    int8_t const1_value; //3bits
    /**
     * program_map_PID/network_PID 13bits
     * network_PID - The network_PID is a 13-bit field, which is used only in conjunction with the value of the
     * program_number set to 0x0000, specifies the PID of the Transport Stream packets which shall contain the Network
     * Information Table. The value of the network_PID field is defined by the user, but shall only take values as specified in
     * Table 2-3. The presence of the network_PID is optional.
     */
    int16_t pid; //13bits
	......
}
字段说明

unsigned number:16;//节目号

unsigned program_map_PID:13; //节目映射表的PID,每个节目对应一个

SrsTsPayloadPAT::psi_decode函数中每解析一个SrsTsPayloadPATProgram都会调用SrsTsContext::set方法更新节目映射表。后面在解析PMT数据时会用到更新的pid。

packet->context->set(program->pid, SrsTsPidApplyPMT)
void SrsTsContext::set(int pid, SrsTsPidApply apply_pid, SrsTsStream stream)
{
    SrsTsChannel* channel = NULL;
    
    if (pids.find(pid) == pids.end()) {
        channel = new SrsTsChannel();
        channel->context = this;
        pids[pid] = channel;
    } else {
        channel = pids[pid];
    }
    
    channel->pid = pid;
    channel->apply = apply_pid;
    channel->stream = stream;
}

4.PMT包的处理,PMT包的pid在解析PAT表时通过SrsTsContext::set进行了记录。

SrsTsPacket::decode中调用下面的方法去判断包中数据是否为PMT

            SrsTsChannel* channel = context->get(pid);
            if (channel && channel->apply == SrsTsPidApplyPMT) {
                // 2.4.4.8 Program Map Table
                srs_freep(payload);
                payload = new SrsTsPayloadPMT(this);
            }

PMT表的描述

如果一个ts流中含有多个频道,那么就会包含多个PID不同的PMT表。

PMT表中包含的数据如下:

(1)当前频道中包含的所有Video数据的PID

(2)当前频道中包含的所有Audio数据的PID

(3)和当前频道关联在一起的其他数据的PID(如数字广播,数据通讯等使用的PID)

只要我们处理了PMT,那么我们就可以获取频道中所有的PID信息,如当前频道包含多少个Video、共多少个Audio和其他数据,还能知道每种数据对应的PID分别是什么。这样如果我们要选择其中一个Video和Audio收看,那么只需要把要收看的节目的Video PID和Audio PID保存起来,在处理Packet的时候进行过滤即可实现。

PMT表字段说明如下:

  1. unsignedtable_id:8;//固定为0x02,表示PMT表
  2. unsignedsection_syntax_indicator:1;//固定为0x01
  3. unsignedzero:1;//0x01
  4. unsignedreserved_1:2;//0x03
  5. unsignedsection_length:12;//首先两位bit置为00,它指示段的byte数,由段长度域开始,包含CRC。
  6. unsignedprogram_number:16;//指出该节目对应于可应用的ProgrammapPID
  7. unsignedreserved_2:2;//0x03
  8. unsignedversion_number:5;//指出TS流中Programmapsection的版本号
  9. unsignedcurrent_next_indicator:1;//当该位置1时,当前传送的Programmapsection可用;
  10. //当该位置0时,指示当前传送的Programmapsection不可用,下一个TS流的Programmapsection有效。
  11. unsignedsection_number:8;//固定为0x00
  12. unsignedlast_section_number:8;//固定为0x00
  13. unsignedreserved_3:3;//0x07
  14. unsignedPCR_PID:13;//指明TS包的PID值,该TS包含有PCR域,
  15. //该PCR值对应于由节目号指定的对应节目。
  16. //如果对于私有数据流的节目定义与PCR无关,这个域的值将为0x1FFF。
  17. unsignedreserved_4:4;//预留为0x0F
  18. unsignedprogram_info_length:12;//前两位bit为00。该域指出跟随其后对节目信息的描述的byte数。
  19. std::vector<TS_PMT_Stream>PMT_Stream;//每个元素包含8位,指示特定PID的节目元素包的类型。该处PID由elementaryPID指定
  20. unsignedreserved_5:3;//0x07
  21. unsignedreserved_6:4;//0x0F
  22. unsignedCRC_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. unsignedstream_type:8;//指示特定PID的节目元素包的类型。音视频编码格式。
  2. unsignedelementary_PID:13;//该域指示TS包的PID值。这些TS包含有相关的节目元素
  3. unsignedES_info_length:12;//前两位bit为00。该域指示跟随其后的描述相关节目元素的byte数
  4. unsigneddescriptor;

媒体节目信息的解析如下:

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 pided 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 pided 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)

相关阅读

什么是Csrss.exe进程?此进程有何作用?

今天一网友发信息给小编说,他打开计算机后在资源管理器里面看到一个进程:“Csrss.exe进程”,网友说其他进程都有描述信息,但是此进程

SONY索尼SRS-WS1无线可穿戴扬声器拆解图解评测

SONY拥有各种各样的&ldquo;黑科技&rdquo;,并且源源不断地将这些科技体现在产品上,索尼SRS-WS1这套颈挂式扬声器就是款很Amazing的个

SRS premium sound 音效增强工具到底有没有用

SRS premium sound是戴尔笔记本上随机附赠的一款模拟立体环绕声的音效软件。其目的是为用户提供良好的视听感受。在音响上模拟环

SRS Audio Sandbox SRS音效增强软件怎么使用?

SRS音效增强软件是利用操作系统的底层技术,以驱动程序的形式装入系统。不论采用何种声卡,任何媒体播放程序,这款软件都能够为你的PC

分享到:

栏目导航

推荐阅读

热门阅读