TS流基本知识【HI3798 AVPLAY播放TS流】
目錄
一、TS流相關知識
1.1 TS流、PS流、PES流和ES流都是什么?
1.2 TS流是如何產生的?
1.3TS流的格式
二、從TS流到PAT、PMT
2.1 PAT表(Program Association Table,節目關聯表)
2.1.1PAT表的描述(表格+分析)
2.1.2 PAT表的定義(代碼+分析)
2.1.3 PAT表的結構(代碼+分析)?
2.1.4 PAT表的解析(代碼+分析)
2.1.5?通過一段TS流中一個Packet分析PAT表(表格+分析)
2.1.6 過濾PAT表信息的偽代碼
三、 PMT表(Program Map Table,節目映射表)(Service Descriptor Table)
3.1 PMT表的描述
3.2 PMT表的定義(代碼)
3.3 PMT表的結構體定義(代碼)
3.4 PMT表的解析(代碼)?
3.5 通過一端TS流中的一個packet分析PMT表(表格+分析)
四、解復用模型
五、DVB搜臺原理以及SDT表(Service Descriptor Table,業務描述表)
六、從PAT開始,走向更遠
參考
?
一、TS流相關知識
? 數字電視機頂盒接收到的是一段段的碼流,我們稱之為TS(Transport Stream,傳輸流),每個TS流都攜帶一些信息,如Video、Audio以及我們需要學習的PAT、PMT等信息。因此,我們首先需要了解TS流是什么,以及TS流是怎樣形成、有著怎樣的結構。
1.1 TS流、PS流、PES流和ES流都是什么?
ES流(Elementary Stream):基本碼流,不分段的音頻、視頻或其他信息的連續碼流。
PES流:把基本流ES分割成段,并加上相應頭文件打包成形的打包基本碼流。
PS流(Program Stream):節目流,將具有共同時間基準的一個或多個PES組合(復合)而成的單一數據流(用于播放或編輯系統,如m2p)。
???????TS流(Transport Stream):傳輸流,將具有共同時間基準或獨立時間基準的一個或多個PES組合(復合)而成的單一數據流(用于數據傳輸)。
??????*NOTE:TS流和PS流的區別:TS流的包結構是長度是固定的;PS流的包結構是可變長度的。這導致了TS流的抵抗傳輸誤碼的能力強于PS流(TS碼流由于采用了固定長度的包結構,當傳輸誤碼破壞了某一TS包的同步信息時,接收機可在固定的位置檢測它后面包中的同步信息,從而恢復同步,避免了信息丟失。而PS包由于長度是變化的,一旦某一?PS包的同步信息丟失,接收機無法確定下一包的同步位置,就會造成失步,導致嚴重的信息丟失。因此,在信道環境較為惡劣,傳輸誤碼較高時,一般采用TS碼流;而在信道環境較好,傳輸誤碼較低時,一般采用PS碼流。)
???????由于TS碼流具有較強的抵抗傳輸誤碼的能力,因此目前在傳輸媒體中進行傳輸的MPEG-2碼流基本上都采用了TS碼流的包格。
1.2 TS流是如何產生的?
從上圖可以看出,視頻ES和音頻ES通過打包器和共同或獨立的系統時間基準形成一個個PES,通過TS復用器復用形成的傳輸流。注意這里的TS流是位流格式(分析Packet的時候會解釋),也即是說TS流是可以按位讀取的。
1.3TS流的格式
PID是TS流中唯一識別標志,Packet Data是什么內容就是由PID決定的。如果一個TS流中的一個Packet的Packet Header中的PID是0x0000,那么這個Packet的Packet Data就是DVB的PAT表而非其他類型數據(如Video、Audio或其他業務信息)。下表給出了一些表的PID值,這些值是固定的,不允許用于更改。
回顧一下,TS流是一種位流(當然就是數字的),它是由ES流分割成PES后復用而成的;它經過網絡傳輸被機頂盒接收到;數字電視機頂盒接收到TS流后將解析TS流。
下面以一個TS流的其中一個Packet中的 packet header 為例進行說明。
??????? TS流是由一個個Packet(包)構成的,每個包都是由Packet Header(包頭)和Packet Data(包數據)組成的。其中Packet Header指示了該Packet是什么屬性的,并給出了該Packet Data的數據的唯一網絡標識符PID。
??????? 到這里,我們對TS流已經有了一定的了解,下面將從TS流轉向PAT表和PMT表的學習。
二、從TS流到PAT、PMT
說完了TS流的基本概念,就該開始對TS流進行更深入的研究了。首先需要想一想:TS流的本質是什么?它的確是一段碼流,并且是一段由數據包(Packet)組成的碼流。那么這些數據包究竟是怎樣的呢?它和我們收看的電視節目之間又有什么區別?這些都是這部分需要了解的內容。
??????? 在上一節中,我們可以看到PID這個被標紅的字段頻繁地出現。PID是當前TS流的Packet區別于其他Packet類型的唯一識別符,通過讀取每個包的Packet Header,我們可以知道這個Packet的數據屬于何種類型。上一節列出了幾項固定的PID值,它們用于識別存儲了特殊信息的Packet。下面要談的PAT表的PID值就是固定的0x0000。?
2.1 PAT表(Program Association Table,節目關聯表)
2.1.1PAT表的描述(表格+分析)
PAT表定義了當前TS流中所有的節目,其PID為0x0000,它是PSI(節目特定信息,Program Specific Information)的根節點,要查尋找節目必須從PAT表開始查找。
PAT表攜帶以下信息:
標準ISO13818-1中,有PAT的個字段詳細描述:
2.1.2 PAT表的定義(代碼+分析)
PAT表主要包含頻道號碼和每一個頻道對應的PMT的PID號碼,這些信息我們在處理PAT表格的時候會保存起來,以后會使用到這些數據。下面將PAT表的定義給出:
typedef struct TS_PAT_Program { unsigned program_number : 16; //節目號 unsigned program_map_PID : 13; // 節目映射表的PID,節目號大于0時對應的PID,每個節目對應一個 }TS_PAT_Program2.1.3 PAT表的結構(代碼+分析)?
再將PAT表的結構體給出:?
typedef struct TS_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校驗碼 } TS_PAT;2.1.4 PAT表的解析(代碼+分析)
接下來給出的是PAT表的解析代碼:
HRESULT CTS_Stream_Parse::adjust_PAT_table( TS_PAT * packet, unsigned char * buffer) { packet->table_id = buffer[0]; packet->section_syntax_indicator = buffer[1] >> 7; packet->zero = buffer[1] >> 6 & 0x1; packet->reserved_1 = buffer[1] >> 4 & 0x3; packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2]; packet->transport_stream_id = buffer[3] << 8 | buffer[4]; packet->reserved_2 = buffer[5] >> 6; packet->version_number = buffer[5] >> 1 & 0x1F; packet->current_next_indicator = (buffer[5] << 7) >> 7; packet->section_number = buffer[6]; packet->last_section_number = buffer[7]; int len = 0; len = 3 + packet->section_length; packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24 | (buffer[len-3] & 0x000000FF) << 16 | (buffer[len-2] & 0x000000FF) << 8 | (buffer[len-1] & 0x000000FF); int n = 0; for ( n = 0; n < packet->section_length - 12; n += 4 ) { unsigned program_num = buffer[8 + n ] << 8 | buffer[9 + n ]; packet->reserved_3 = buffer[10 + n ] >> 5; packet->network_PID = 0x00; if ( program_num == 0x00) { packet->network_PID = (buffer[10 + n ] & 0x1F) << 8 | buffer[11 + n ]; TS_network_Pid = packet->network_PID; //記錄該TS流的網絡PID TRACE(" packet->network_PID %0x /n/n", packet->network_PID ); } else { TS_PAT_Program PAT_program; PAT_program.program_map_PID = (buffer[10 + n] & 0x1F) << 8 | buffer[11 + n]; PAT_program.program_number = program_num; packet->program.push_back( PAT_program ); TS_program.push_back( PAT_program );//向全局PAT節目數組中添加PAT節目信息 } } return 0; }從for()開始,就是描述了當前流中的頻道數目(N),每一個頻道對應的PMT PID是什么。解復用程序需要接收所有的頻道號碼和對應的PMT 的PID,并把這些信息在緩沖區中保存起來。在后部的處理中需要使用到PMT的 PID。
2.1.5?通過一段TS流中一個Packet分析PAT表(表格+分析)
如上圖所示,PAT表描述了當前流的NIT(Network Information Table,網絡信息表)中的PID、當前流中有多少不同類型的PMT表及每個PMT表對應的頻道號。上圖當中PAT包含一個節目,其對應的PMT為0x1000.
2.1.6 過濾PAT表信息的偽代碼
int Video_PID=0x07e5,Audio_PID=0x07e6; void Process_Packet(unsigned char*buff) { int I; int PID=GETPID(buff); if(PID==0x0000) { Process_PAT(buff+4); } // 如果PID為0x0000,則該Packet Data為PAT信息,因此調用處理PAT表的函數 else{ // 這里buff+4 意味著從Packet Header之后進行解析(包頭占4個字節) …… } }三、 PMT表(Program Map Table,節目映射表)(Service Descriptor Table)
3.1 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的時候進行過濾即可實現。
3.2 PMT表的定義(代碼)
TS_PMT_Stream {unsigned stream_type : 8; //指示特定PID的節目元素包的類型。該處PID由elementary PID指定unsigned elementary_PID : 13; //該域指示TS包的PID值。這些TS包含有相關的節目元素unsigned ES_info_length : 12; //前兩位bit為00。該域指示跟隨其后的描述相關節目元素的byte數unsigned descriptor; }TS_PMT_Stream;3.3 PMT表的結構體定義(代碼)
//PMT 表結構體 typedef struct TS_PMT {unsigned table_id : 8; //固定為0x02, 表示PMT表 unsigned section_syntax_indicator : 1; //固定為0x01 unsigned zero : 1; //0x01 unsigned reserved_1 : 2; //0x03 unsigned section_length : 12;//首先兩位bit置為00,它指示段的byte數,由段長度域開始,包含CRC。 unsigned program_number : 16;// 指出該節目對應于可應用的Program map PID unsigned reserved_2 : 2; //0x03 unsigned version_number : 5; //指出TS流中Program map section的版本號 unsigned current_next_indicator : 1; //當該位置1時,當前傳送的Program map section可用; //當該位置0時,指示當前傳送的Program map section不可用,下一個TS流的Program map section有效。 unsigned section_number : 8; //固定為0x00 unsigned last_section_number : 8; //固定為0x00 unsigned reserved_3 : 3; //0x07 unsigned PCR_PID : 13; //指明TS包的PID值,該TS包含有PCR域, //該PCR值對應于由節目號指定的對應節目。 //如果對于私有數據流的節目定義與PCR無關,這個域的值將為0x1FFF。 unsigned reserved_4 : 4; //預留為0x0F unsigned program_info_length : 12; //前兩位bit為00。該域指出跟隨其后對節目信息的描述的byte數。 std::vector<TS_PMT_Stream> PMT_Stream; //每個元素包含8位, 指示特定PID的節目元素包的類型。該處PID由elementary PID指定 unsigned reserved_5 : 3; //0x07 unsigned reserved_6 : 4; //0x0F unsigned CRC_32 : 32; } TS_PMT;3.4 PMT表的解析(代碼)?
HRESULT CTS_Stream_Parse::adjust_PMT_table ( TS_PMT * packet, unsigned char * buffer ) { packet->table_id = buffer[0]; packet->section_syntax_indicator = buffer[1] >> 7; packet->zero = buffer[1] >> 6 & 0x01; packet->reserved_1 = buffer[1] >> 4 & 0x03; packet->section_length = (buffer[1] & 0x0F) << 8 | buffer[2]; packet->program_number = buffer[3] << 8 | buffer[4]; packet->reserved_2 = buffer[5] >> 6; packet->version_number = buffer[5] >> 1 & 0x1F; packet->current_next_indicator = (buffer[5] << 7) >> 7; packet->section_number = buffer[6]; packet->last_section_number = buffer[7]; packet->reserved_3 = buffer[8] >> 5; packet->PCR_PID = ((buffer[8] << 8) | buffer[9]) & 0x1FFF;PCRID = packet->PCR_PID; packet->reserved_4 = buffer[10] >> 4; packet->program_info_length = (buffer[10] & 0x0F) << 8 | buffer[11]; // Get CRC_32int len = 0; len = packet->section_length + 3; packet->CRC_32 = (buffer[len-4] & 0x000000FF) << 24 | (buffer[len-3] & 0x000000FF) << 16 | (buffer[len-2] & 0x000000FF) << 8 | (buffer[len-1] & 0x000000FF); int pos = 12; // program info descriptor if ( packet->program_info_length != 0 ) pos += packet->program_info_length; // Get stream type and PID for ( ; pos <= (packet->section_length + 2 ) - 4; ) {TS_PMT_Stream pmt_stream; pmt_stream.stream_type = buffer[pos]; packet->reserved_5 = buffer[pos+1] >> 5; pmt_stream.elementary_PID = ((buffer[pos+1] << 8) | buffer[pos+2]) & 0x1FFF; packet->reserved_6 = buffer[pos+3] >> 4; pmt_stream.ES_info_length = (buffer[pos+3] & 0x0F) << 8 | buffer[pos+4]; pmt_stream.descriptor = 0x00; if (pmt_stream.ES_info_length != 0) { pmt_stream.descriptor = buffer[pos + 5]; for( int len = 2; len <= pmt_stream.ES_info_length; len ++ ) { pmt_stream.descriptor = pmt_stream.descriptor<< 8 | buffer[pos + 4 + len]; }pos += pmt_stream.ES_info_length;}pos += 5; packet->PMT_Stream.push_back( pmt_stream ); TS_Stream_type.push_back( pmt_stream ); } return 0; }3.5 通過一端TS流中的一個packet分析PMT表(表格+分析)
四、解復用模型
int Video_PID=0x07e5,Audio_PID=0x07e6; void Process_Packet(unsigned char*buff) {int i; int PID=GETPID(buff); if(PID==0x0000) //PAT表的PID為0x0000{ Process_PAT(buff+4); } else if(PID==Video_PID) //PID指示該數據包為視頻包 { SaveToVideoBuffer(buff+4); } else if(PID==Audio_PID) //PID指示該數據包為音頻包{ SaveToAudioBuffer(buff+4); } else // buff+4 意味著要除去buff前4個字節(即包頭){ for( i=0;i<64;i++){ if(PID==pmt[i].pmt_pid) {Process_PMT(buff+4); Break; }} } }五、DVB搜臺原理以及SDT表(Service Descriptor Table,業務描述表)
機頂盒先調整高頻頭到一個固定的頻率(如498MHZ),如果此頻率有數字信號,則COFDM芯片(如MT352)會自動把TS流數據傳送給MPEG- 2 decoder。 MPEG-2 decoder先進行數據的同步,也就是等待完整的Packet的到來.然后循環查找是否出現PID== 0x0000的Packet,如果出現了,則馬上進入分析PAT的處理,獲取了所有的PMT的PID。接著循環查找是否出現PMT,如果發現了,則自動進入PMT分析,獲取該頻段所有的頻道數據并保存。如果沒有發現PAT或者沒有發現PMT,說明該頻段沒有信號,進入下一個頻率掃描。
?
在解析TS流的時候,首先尋找PAT表,根據PAT獲取所有PMT表的PID;再尋找PMT表,獲取該頻段所有節目數據并保存。這樣,只需要知道節目的PID就可以根據PacketHeade給出的PID過濾出不同的Packet,從而觀看不同的節目。這些就是PAT表和PMT表之間的關系。而由于PID是一串枯燥的數字,用戶不方便記憶、且容易輸錯,所以需要有一張表將節目名稱和該節目的PID對應起來,DVB設計了SDT表來解決這個問題。 該表格標志一個節目的名稱,并且能和PMT中的PID聯系起來,這樣用戶就可以通過直接選擇節目名稱來選擇節目了。
SDT可以提供的信息包括:
(1)?該節目是否在播放中
(2)?該節目是否被加密
(3)?該節目的名稱
六、從PAT開始,走向更遠
在本章的學習中,我們發現了一個特點:所有的TS流的解析都是從尋找PAT表開始的,只有找到了PAT表,我們才能繼續下一步的解析。因此,在進行了TS流、PAT表和PMT表的初步知識儲備后,在接下來的學習中將從PAT表開始,學習更多的PSI/SI相關的表,將走得更遠。
七、海思Hi3798 AVPlAY模塊
AVPLAY(Audio Video Player)模塊整合SDK 內部音視頻播放相關模塊,提供給用戶基本播放業務相關的接口。AVPLAY 在典型媒體播放終端方案中的位置如圖所示。AVPLAY 主要依賴ADEC/VDEC/DEMUX 等模塊,其向應用或中間件播放器提供基本的播放業務相關接口。
首先在官方給的sample_ipplayer例程中。首先需要進行一系列的初始化工作,在這里就不一一講述。主要是講述如何使用Demux模塊來進行節目搜索。
可以按照以上步驟去分析,發現海思官方的編程也是按照先搜尋PAT,搜尋節目,查找PMT,然后再講解碼器與對應的視頻相綁定,從而進行解碼。
注意在解析PMT的時候,會設置3層filter,第一層是PMT TABLE數據(table id =0x02);第二層和第三層為program id(節目號);
?
參考
【1】https://blog.csdn.net/qq_31213433/article/details/50571499
總結
以上是生活随笔為你收集整理的TS流基本知识【HI3798 AVPLAY播放TS流】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Jmeter面试题
- 下一篇: 斐讯K2路由器,版本号V22.6.507