MPEG-2 TS学习(五)tsfilter源码阅读(2)TS的数据结构

3/3/2017来源:C/C++教程人气:458

TS的数据结构

TS包

    TS包的包头提供关于传输方面的信息:同步、有无差错、有无加扰、PCR(节目参考时钟)等标志。TS包的包头长度不固定,前32比特(4个字节)固定,后面可能跟有自适应字段(适配域)。32个比特(4个字节)是最小包头。包头的结构固定如下:

同步字节

传输错误指示

开始指示

传输优先级

PID

加扰控制

适配域控制

连续性计数器

适配域

8

1

1

1

13

2

2

4

typedef struct TS_packet_header

{

unsigned sync_byte : 8;

unsigned transport_error_indicator : 1;

unsigned payload_unit_start_indicator : 1;

unsigned transport_PRiority : 1;

unsigned PID : 13;

unsigned transport_scrambling_control : 2;

unsigned adaption_field_control : 2;

unsigned continuity_counter : 4;

} TS_packet_header;

sync_byte (同步字节):固定为0100 0111 (0x47);该字节由解码器识别,使包头和有效负载可相互分离。 transport_error_indicator(传输错误指示):‘1’表示在相关的传输包中至少有一个不可纠正的错误位。当被置1后,在错误被纠正之前不能重置为0。 payload_unit_start_indicator(开始指示):为1时,在前4个字节之后会有一个调整字节,其的数值为后面调整字段的长度length。因此有效载荷开始的位置应再偏移1+[length]个字节。

transport_priority(传输优先级):‘1’表明优先级比其他具有相同PID 但此位没有被置‘1’的分组高。 PID:指示存储与分组有效负载中数据的类型。PID 值 0x0000—0x000F 保留。其中0x0000为PAT保留;0x0001为CAT保留;0x1fff为分组保留,即空包。

transport_scrambling_control(加扰控制):表示TS流分组有效负载的加密模式。空包为‘00’,如果传输包包头中包括调整字段,不应被加密。 adaptation_field_control(适配域控制):表示包头是否有调整字段或有效负载。‘00’为ISO/IEC未来使用保留;‘01’仅含有效载荷,无调整字段;‘10’ 无有效载荷,仅含调整字段;‘11’ 调整字段后为有效载荷,调整字段中的前一个字节表示调整字段的长度length,有效载荷开始的位置应再偏移[length]个字节。空包应为‘10’。 continuity_counter(连续性计数器):随着每一个具有相同PID的TS流分组而增加,当它达到最大值后又回复到0。范围为0~15。

适配域

/* TS包  */
class Packet
{
public:
    Packet() :
        good(false)
    {
    }

	// 返回同步字节
    inline uint8_t getSync() const
    {
        return sync; /* should be 0x47 */
    }

	// 返回PID
    inline uint16_t getPid() const
    {
        return (pid1 << 8) | pid2;
    }

	// 返回TS载荷的起始地址
    inline bool getPayloadStart() const
    {
        return payload_start;
    }

	// 获取TS的载荷
    inline int getPayload(const char*& payload) const
    {
        // FIXME handle adaptation field
        payload = reinterpret_cast<const char*> (this) + PACKET_HEADER_SIZE;
        return PACKET_SIZE - PACKET_HEADER_SIZE;
    }

    inline std::ostream& writePayload(std::ostream& os) const
    {
        const char* payload;
        int size = getPayload(payload);
        os.write(payload, size);
        return os;
    }

	// 读取数据
    friend std::istream& Operator>>(std::istream& is, Packet& packet)
    {
        is.read(reinterpret_cast<char*> (&packet), PACKET_SIZE);
        packet.good
                = (is.gcount() == PACKET_SIZE && packet.sync == PACKET_SYNC);
        return is;
    }

    friend std::ostream& operator<<(std::ostream& os, Packet& packet)
    {
        os.write(reinterpret_cast<char*> (&packet), PACKET_SIZE);
        return os;
    }

    const static int PACKET_SIZE = 188; // TS包长度

    const static int PACKET_HEADER_SIZE = 4; // TS包头部长度

    const static uint8_t PACKET_SYNC = 0x47; // TS包头部的同步字节

private:
    uint8_t sync; /* 同步字节 sync byte (0x47) */
#ifdef XXX_TBD_BIT_ORDER
	uint8_t tei : 1; /* 传输错误指示 */
	uint8_t payload_start : 1; /* 载荷开始指示 */
	uint8_t priority : 1; /* 传输优先级 */
#endif /* XXX_TBD_BIT_ORDER */
	uint8_t pid1 : 5; /* PID */
#ifndef XXX_TBD_BIT_ORDER
    uint8_t priority :1;
    uint8_t payload_start :1;
    uint8_t tei :1;
#endif /* XXX_TBD_BIT_ORDER */
    uint8_t pid2;
#ifdef XXX_TBD_BIT_ORDER
	uint8_t scrambling_control : 2; /* 加扰控制 */
	uint8_t adaptation_field_present : 2; /* 适配域控制 */
#endif /* XXX_TBD_BIT_ORDER */
	uint8_t continuity_counter : 4; /* 连续性计数器 */
#ifndef XXX_TBD_BIT_ORDER
    uint8_t adaptation_field_present :2;
    uint8_t scrambling_control :2;
#endif /* XXX_TBD_BIT_ORDER */
	uint8_t data[184]; /* 载荷 */

public:
    bool good;
};

PAT

    TS流中包含一个或者多个PAT表。PAT表由PID为0x0000的TS包传送,其作用是为复用的每一路传送流提供出所包含的节目和节目编号,以及对应节目的PMT的位置即PMT的TS包的PID值,同时还提供NIT的位置,即NIT的TS包的PID的值。

Table ID

Section syntax indicator

0

Section length

Transport stream ID

Version

number

Current next indicator

Section

Number

Last section number

N loop

8

1

1

2

12

16

2

5

1

8

8

N loop中为:

Program number0

Network PID

……

Program number1

Program map PID-1

……

CRC 32

16

3

13

16

3

13

32

table_id:固定为0x00,标志该表是PAT表。

section_syntax_indicator:段语法标志位,固定为1。

section_length:表示这个字节后面有用的字节数,包括CRC32。节目套数:(section length-9)/4

transport_stream_id:16位字段,表示该TS流的ID,区别于同一个网络中其它多路复用流。

version_number:表示PAT的版本号。

current_next_indicator:表示发送的PAT表是当前有效还是下一个PAT有效。

section_number:表示分段的号码。PAT可能分为多段传输,第一段为0,以后每个分段加1,最多可能有256个分段。

last_section_number:表示PAT最后一个分段的号码。

Program number:节目号

network_PID:网络信息表(NIT)的PID,节目号为0时对应ID为network_PID。

Program map PID:节目映射表(PMT)的PID号,节目号为大于等于1时,对应的ID为program_map_PID。一个PAT中可以有多个program_map_PID。

CRC_32:CRC32校验码Cyclic RedundancyCheck。

class PAT
{
public:
    // FIXME handle pointer field
    PAT() :
        good(false)
    {
    }

	// 节目
    class Program
    {
    public:
        inline uint16_t getPid()
        {
            return (pid1 << 8) | pid2;
        }
        inline uint16_t getProgramNumber()
        {
            return htons(program_number);
        }
    private:
        uint16_t program_number; // 节目号码
        uint8_t pid1 :5;  // PID
        uint8_t unused :3;
        uint8_t pid2;
    };

	// 从TS包中读取PAT
    friend PAT& operator<<(PAT& pat, Packet& packet)
    {
        const char* payload;
        packet.getPayload(payload);
        pat = *reinterpret_cast<const PAT*> (payload);

        if (pat.table_id == TABLE_ID_PAT)
        {
            pat.good = true;
        }

        return pat;
    }

	// 返回PAT中的节目
    std::vector<PAT::Program> getPrograms() const
    {
        std::vector<PAT::Program> programs;

        for (const uint8_t* off = data; off < data + getSectionLength()
                - sizeof(CRC32) - (data - §ion_length2); off
                += sizeof(Program))
        {
            Program program = *reinterpret_cast<const Program*> (off);
            programs.push_back(program);
        }
        return programs;
    }

private:

    inline int16_t getSectionLength() const
    {
        return (section_length1 << 8) | section_length2;
    }

    const static int PAT_HEADER_SIZE = 9;	// PAT头部长度

    uint8_t pointer_field;					// 指针域
    uint8_t table_id;						// PAT的标识,固定是0x00
    uint8_t section_length1 :4;				// 表示这个字节后面有用的字节数,包括CRC32。节目套数:(section length-9)/4
    uint8_t reserved1 :3;
    uint8_t section_syntax_indicator :1;	// 段语法标志位,固定为1
    uint8_t section_length2;
    uint16_t stream_id;						//16位字段,表示该TS流的ID,区别于同一个网络中其它多路复用流
    uint8_t current_next_indicator :1;		// 表示发送的PAT表是当前有效还是下一个PAT有效
    uint8_t version_number :5;				// PAT的版本号
    uint8_t reserved2 :2;
    uint8_t section_number;					// 表示分段的号码。PAT可能分为多段传输,第一段为0,以后每个分段加1,最多可能有256个分段
    uint8_t last_section_number;			// 表示PAT最后一个分段的号码
    
	uint8_t data[Packet::PACKET_SIZE - Packet::PACKET_HEADER_SIZE
            - PAT_HEADER_SIZE - sizeof(CRC32)]; // PAT的数据部分

    CRC32 crc;								// CRC32校验码

public:
    bool good;
};

PMT

    PMT在传送流中用于指示组成某一套节目的视频、音频和数据在传送流中的位置,即对应的TS包的PID值,以及每路节目的节目时钟参考(PCR)字段的位置。

Table id

Section syntax indicator

0

Section

length

Transport stream id

Version

number

Current next indicator

Section number

Last section number

PCR PID

Program info length

N loop descriptors

N loop

CRC 32

8

1

1

2

12

16

2

5

1

8

8

3

13

4

N loop中为:

Stream type

Elementary PID

ES info length

N loop descriptors

8

3

13

4

12

Table id :固定为0x02,标志该表是PMT表。 Section syntax indicator:对于PMT表,设置为1。 Section length:表示这个字节后面有用的字节数,包括CRC32。 Program number:它指出该节目对应于可应用的Program map PID。 Version number:指出PMT的版本号。 Current next indicator:当该位置’1’时,当前传送的Program map section可用;当该位置’0’时,指示当前传送的Program map section不可用,下一个TS流的Programmap section有效。 Section number:总是置为0x00(因为PMT表里表示一个service的信息,一个section的长度足够)。 Last section number:该域的值总是0x00。 PCR PID:节目中包含有效PCR字段的传送流中PID。 Program info length:12bit域,前两位为00。该域指出跟随其后对节目信息的描述的byte数。 Stream type:8bit域,指示特定PID的节目元素包的类型。该处PID由elementary PID指定。下表所示为对应原始流的类型。

原始流的类型

描述

0x00

ITU-T|ISO/IEC保留

0x01

ISO/IEC 11172视频

0x02

ITU-T Rec.H.262|ISO/IEC 13818-2视频

0x03

ISO/IEC 11172音频

0x04

ISO/IEC 13818-3音频

0x05

ITU-T Rec.H.222.0|ISO/IEC 13818-1私用分段

0x06

含有私用数据的ITU-T Rec.H.222.0|ISO/IEC 13818-1分组

0x07

ISO/IEC 13522 MHEG

0x08

ITU-T Rec.H.222.0|ISO/IEC 13818-1 DSM CC

0x09

ITU-T Rec.H.222.0|ISO/IEC 13818-1/11172-1

0x10 ~ 0x7F

ITU-T Rec.H.222.0|ISO/IEC 13818-1保留

0x80 ~ 0xFF

用户私用

/* PMT */
class PMT
{
public:
    PMT() :
        good(false)
    {
    }

    class Stream
    {
    public:
        friend class PMT;
        enum StreamType
        {
            MPEG_1_VIDEO = 0x01,
            MPEG_2_VIDEO = 0x02,
            MPEG_1_AUDIO = 0x03,
            MPEG_2_AUDIO = 0x04,
        };

        inline StreamType getStreamType() const
        {
            return static_cast<StreamType> (stream_type);
        }

        inline uint16_t getElementaryPid() const
        {
            return (elementary_pid1 << 8) | elementary_pid2;
        }

    private:
        uint8_t stream_type; // 指示特定PID的节目元素包的类型。该处PID由elementary PID指定
        uint8_t elementary_pid1 :5;
        uint8_t reserved1 :3;
        uint8_t elementary_pid2;
        uint8_t es_info_length1 :2;
        uint8_t reserved2 :6;
        uint8_t es_info_length2;
        // FIXME no ES descriptor
    };

    // FIXME handle pointer field

	// 从TS包中读取PMT
    friend PMT& operator<<(PMT& pmt, Packet& packet)
    {
        const char* payload;
        packet.getPayload(payload);
        pmt = *reinterpret_cast<const PMT*> (payload);

        if (pmt.table_id == TABLE_ID_PMT)
        {
            pmt.good = true;
        }

        return pmt;
    }

	// 返回节目标号
    inline uint16_t getProgramNumber() const
    {
        return htons(program_number);
    }

	// 返回section的长度
    inline uint16_t getSectionLength() const
    {
        return (section_length1 << 8) | section_length2;
    }

	// 返回stream
    std::vector<PMT::Stream> getStreams() const
    {
        std::vector<PMT::Stream> streams;

        for (const uint8_t* off = data; off < data + getSectionLength()
                - sizeof(CRC32) - (data - §ion_length2); off
                += sizeof(Stream))
        {
            Stream stream = *reinterpret_cast<const Stream*> (off);
            off += (stream.es_info_length1 << 8) | stream.es_info_length2;
            streams.push_back(stream);
        }
        return streams;
    }

    const static int PMT_HEADER_SIZE = 13; // PMT的头部长度

    uint8_t pointer_field;					// 指针域
    uint8_t table_id;						// 固定为0x02,标志该表是PMT表
    uint8_t section_length1 :2;				// 表示这个字节后面有用的字节数,包括CRC32
    uint8_t reserved1 :3;
    uint8_t section_syntax_indicator :1;	// 对于PMT表,设置为1
    uint8_t section_length2;
    uint16_t program_number;				// 它指出该节目对应于可应用的Program map PID
    uint8_t current_next_indicator :1;		// 当该位置’1’时,当前传送的Program map section可用;当该位置’0’时,指示当前传送的Program map section不可用,下一个TS流的Programmap section有效
    uint8_t version_number :5;				// 指出PMT的版本号
    uint8_t reserved2 :2;
    uint8_t section_number;					// 总是置为0x00(因为PMT表里表示一个service的信息,一个section的长度足够)。
    uint8_t last_section_number;			// 该域的值总是0x00
    uint8_t pcr_pid1 :5;					// 节目中包含有效PCR字段的传送流中PID
    uint8_t reserved3 :3;
    uint8_t pcr_pid2;
    uint8_t program_info_length1 :4;		// 前两位为00。该域指出跟随其后对节目信息的描述的byte数
    uint8_t reserved4 :4;
    uint8_t program_info_length2;
    uint8_t data[Packet::PACKET_SIZE - Packet::PACKET_HEADER_SIZE
            - PMT_HEADER_SIZE - sizeof(CRC32)]; // 数据,stream
    CRC32 crc;								// 检验码

public:
    bool good;
};