據傳輸媒體的質量不同,MPEG-2中定義了兩種復合信息流:傳送流(TS:TransportStream)和節目流(PS:ProgramStream)

PS文件分為3層:ps層(Program Stream)、pes層(Packet Elemental Stream)、es層(Elementary Stream)。es層就是音視頻數據,pes層是在音視頻數據上加了時間戳等對數據幀的說明信息,ps層是在pes層上加入了數據流識別和傳輸的必要信息。
1.Ps和Ts的區別
2.Ps文件結構
一個完整的MPEG-2文件就是一個PS流文件。使用Elecard StreamAnalyzer打開一個MPEG-2文件,得到下面信息。

2.1.Ps層

Ps層主要由pack header和數據組成,pack header中各個bit的意義如下

我們可以通過分析一個示例文件來了解它

scr += packet_size * 90000LL / (mux_rate * 50LL);
struct pack_header { unsigned char pack_start_code[4]; unsigned char system_clock_reference_base21 : 2; unsigned char marker_bit : 1; unsigned char system_clock_reference_base1 : 3; unsigned char fix_bit : 2; unsigned char system_clock_reference_base22; unsigned char system_clock_reference_base31 : 2; unsigned char marker_bit1 : 1; unsigned char system_clock_reference_base23 : 5; unsigned char system_clock_reference_base32; unsigned char system_clock_reference_extension1 : 2; unsigned char marker_bit2 : 1; unsigned char system_clock_reference_base33 : 5; unsigned char marker_bit3 : 1; unsigned char system_clock_reference_extension2 : 7; unsigned char program_mux_rate1; unsigned char program_mux_rate2; unsigned char marker_bit5 : 1; unsigned char marker_bit4 : 1; unsigned char program_mux_rate3 : 6; unsigned char pack_stuffing_length : 3; unsigned char reserved : 5; pack_header() { pack_start_code[0] = 0x00; pack_start_code[1] = 0x00; pack_start_code[2] = 0x01; pack_start_code[3] = 0xBA; fix_bit = 0x01; marker_bit = 0x01; marker_bit1 = 0x01; marker_bit2 = 0x01; marker_bit3 = 0x01; marker_bit4 = 0x01; marker_bit5 = 0x01; reserved = 0x1F; pack_stuffing_length = 0x00; system_clock_reference_extension1 = 0; system_clock_reference_extension2 = 0; } void getSystem_clock_reference_base(UINT64 &_ui64SCR) { _ui64SCR = (system_clock_reference_base1 << 30) | (system_clock_reference_base21 << 28) | (system_clock_reference_base22 << 20) | (system_clock_reference_base23 << 15) | (system_clock_reference_base31 << 13) | (system_clock_reference_base32 << 5) | (system_clock_reference_base33); } void setSystem_clock_reference_base(UINT64 _ui64SCR) { system_clock_reference_base1 = (_ui64SCR >> 30) & 0x07; system_clock_reference_base21 = (_ui64SCR >> 28) & 0x03; system_clock_reference_base22 = (_ui64SCR >> 20) & 0xFF; system_clock_reference_base23 = (_ui64SCR >> 15) & 0x1F; system_clock_reference_base31 = (_ui64SCR >> 13) & 0x03; system_clock_reference_base32 = (_ui64SCR >> 5) & 0xFF; system_clock_reference_base33 = _ui64SCR & 0x1F; } void getProgram_mux_rate(unsigned int &_uiMux_rate) { _uiMux_rate = (program_mux_rate1 << 14) | (program_mux_rate2 << 6) | program_mux_rate3; } void setProgram_mux_rate(unsigned int _uiMux_rate) { program_mux_rate1 = (_uiMux_rate >> 14) & 0xFF; program_mux_rate2 = (_uiMux_rate >> 6) & 0xFF; program_mux_rate3 = _uiMux_rate & 0x3F; } };
這樣的好處是可以直接通過
pack_header header; header.setProgram_mux_rate(25200); header.setSystem_clock_reference_base(0); os.write((char *)&header, sizeof(header));
來寫入文件,但是不方便抽象成類,所以就參考ffmpeg使用了put_bits的方式
class PackHeader : public HeaderBase { public: UINT64 SCRBase; UINT8 SCRExt; UINT32 programMuxRate; UINT8 stuffingLength; PackHeader(); virtual ~PackHeader(); int Serialize(); };
然后在類中加一個序列化函數,來將整個類序列化
int PackHeader::Serialize() { int calcBinaryBitLen = 32 //pack_start_code + 2 // '01' + 3 //system_clock_reference_base [32..30] + 1 //marker_bit + 15 //system_clock_reference_base [29..15] + 1 //marker_bit + 15 //system_clock_reference_base [14..0] + 1 //marker_bit + 9 //system_clock_reference_extension + 1 //marker_bit + 22 // program_mux_rate + 1 //marker_bit + 1 //marker_bit + 5 //reserved + 3; //pack_stuffing_length if (stuffingLength > 0) { for (int i = 0; i < stuffingLength; i++) { calcBinaryBitLen += 8; } } if ((calcBinaryBitLen / 8) > binaryLen) { if (binary) delete[] binary; binary = new BYTE[calcBinaryBitLen / 8]; } binaryLen = calcBinaryBitLen / 8; BYTE* p = binary; bits_buffer_t bw; bits_initwrite(&bw, binaryLen, p); bits_write(&bw, 32, PACK_HEADER_START_CODE); //pack_start_code bits_write(&bw, 2, 0x1); // '01' bits_write(&bw, 3, (SCRBase >> 30) & 0x07); //system_clock_reference_base [32..30] bits_write(&bw, 1, 1); //marker_bit bits_write(&bw, 15, (SCRBase >> 15) & 0x7FFF); //system_clock_reference_base [29..15] bits_write(&bw, 1, 1); //marker_bit bits_write(&bw, 15, SCRBase & 0x7FFF); //system_clock_reference_base [14..0] bits_write(&bw, 1, 1); //marker_bit bits_write(&bw, 9, SCRExt); //system_clock_reference_extension bits_write(&bw, 1, 1); //marker_bit bits_write(&bw, 22, programMuxRate & 0x3FFFFF); // program_mux_rate bits_write(&bw, 1, 1); //marker_bit bits_write(&bw, 1, 1); //marker_bit bits_write(&bw, 5, 0x1F); //reserved bits_write(&bw, 3, stuffingLength & 0x07); //pack_stuffing_length if (stuffingLength > 0) { for (int i = 0; i < stuffingLength; i++) { bits_write(&bw, 8, 0xFF); //stuffing } } return 1; }
對於DVD而言,一般開始的pack里面還有一個System header

我們也可以通過分析一個示例文件來了解它

2.2.Pes層
Pes層由編碼的音頻或視頻數據(es)加上Pes頭組成的,Pes頭主要是通過PTS和DTS來提供音視頻同步的信息,Pes頭的各個bit的意義如下所示



Pes頭之后緊跟着的就是編碼的音頻或視頻數據(es)了,對於DVD而言,一個program pack的大小問0x800,所以一幀MPEG-2視頻被分在多個Pes包里,不夠一個包的就寫在下一幀的第一個pack里,或在Pes Header后面填充FF(PES_header_data_length要加上填充的字節數)。
