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 |
原始流的类型
值 | 描述 |
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;};
新闻热点
疑难解答