首页 > 学院 > 开发设计 > 正文

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

2019-11-06 09:30:38
字体:
来源:转载
供稿:网友

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


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表