1. 程式人生 > >PES,TS,PS,RTP等流的打包格式解析之TS流

PES,TS,PS,RTP等流的打包格式解析之TS流

上一篇描述了PES包頭的封裝格式,本篇描述一下TS包的封包格式

1.TS包頭格式

TS流,即傳輸流,是對PES包的進一步封裝,基本單位為TS包,固定每包大小為188位元組(204位元組,在188位元組後加上16位元組的CRC校驗資料),由TS包頭和payload組成;其組成如下圖:


其中包頭由4個位元組的固定頭部和其後的adaptation field資料構成,位元組順序依次如下:

sync_byte同步碼,其大小為固定8bit,值為0x47

transport_error_indicator錯誤標誌位,佔位1bit,置為1表示此分組中至少有一個不可糾正的錯誤;

payload_unit_start_indicator

負載開始標誌位,用來表示TS包的有效淨荷帶有PES包或者PSI資料的情況,佔位1bit;另若此值為1,且負載為PSI資料時,則在TS頭後,負載起始位元組會有1個調整位元組point_field

TS包帶有PES包資料時,payload_unit_start_indicator具有以下的特點:置為1,標識TS包的有效淨荷以PES包的第一個位元組開始,即此TS包為PES包的起始包,且此TS分組中有且只有一個PES包的起始欄位;置為0,表示TS包不是PES包的起始包,是後面的資料包。

TS包帶有PSI資料時,payload_unit_start_indicator具有以下特點:置為1,表示TS

包中帶有PSI資料分段的第一個位元組,即這個TS包是PSI Section的起始包,則此TS包的負載的第一個位元組帶有pointer_field;置為0,表示TS包不帶有PSI Section的第一個位元組,即此TS包不是PSI的起始包,即在有效負載中沒有point_field,有效負載的開始就是PSI的資料內容。point_field的定義將在下面的PSI節中進行介紹;對於空包的包,payload_unit_start_indicator應該置為0

例如:若TS包載荷為PAT,則當接收到的TS包的payload_unit_start_indicator1時,表明這個TS包包含了PAT頭資訊,從這個包裡面解析出

section_lengthcontinuity_counter,然後繼續收集後面的payload_unit_start_indicator = 0TS包,並判斷continuity_counter的連續性,不斷讀出TS包中的淨載荷(也就是PAT資料),用section_length作為收集TS包結束條件。

transport_priority傳送優先順序,置1則表示此包比其他相同PID0的包有高的優先順序,佔位1bit

PID:指示有效負載中的資料型別,佔位13bit;0x0000代表PAT,0x0001代表CAT,0x0002-0x000F保留,0x1FFF表示空包;

transport_scrambling_control:有效負載加密模式標誌,佔位2bit,00表示未加密;

adaptation_field_control:調整欄位標誌,表示此TS首部是否跟隨調整欄位還是負載資料,佔位2bit,其中00位保留,01表示無調整欄位,只有有效負載資料,10表示只有調整欄位,無有效負載,11表示有調整欄位,且其後跟有有效負載;空分組此欄位應為01;

如果adaptation_field_control == 1x,表示後面跟有adaptation field欄位;

如果adaptation_field_control == x1,表示後面跟有data_bytes欄位;

continuity_counter:連續性計數,隨每一個相同PID的TS分組增加,達到最大值後又歸為0;佔位4bit,如果adaptation_field_control值為00或01,此值不應增加;若調整欄位的標誌位discontinuity_indicator值為1,則此值也不連續;

Adaptation field:調整欄位,只有當adaptation_field_control == 1x時,以下欄位才會存在,其中內容如下:

   adaptation_field_length調整欄位長度標示,標示此位元組後面調整欄位的長度,佔位8bit;值為0時,表示在TS分組中插入一個調整位元組,後面沒有調整欄位,緊跟著的是有效負載;adaptation_field_control == ‘11’時,此值在0~182之間,adaptation_field_control == ‘10’時,此值為183,若欄位沒這麼長則填充0xFF欄位;

後面的欄位都是在adaptation_field_length>0的時候才會出現,順序如下:

   discontinuity_indicator:不連續狀態指示符,佔位1bit,置位1時表示此TS分組的不連續狀態為真;

   random_access_indicator:隨機訪問指示符,佔位1bit;

   elementary_stream_priority_indicator:原始流資料優先順序指示符,佔位1bit,置位1表示此原始流資料比相同PID的TS包中的其他原始流優先順序高;

   PCR_flag:PCR標誌位,佔位1bit,置位1表示調整欄位中包含PCR欄位,置位0則沒有PCR欄位;

   OPCR_flag:OPCR標誌位,佔位1bit,置位1表示調整欄位中包含OPCR欄位,置位0則沒有OPCR欄位;

   splicing_point_flag:splice_countdown標誌位,佔位1bit,置位1表示調整欄位中包含splice_countdown欄位,置位0則沒有splice_countdown欄位;

   transport_private_data_flag:transport_private_data標誌位,佔位1bit,置位1時表示調整欄位中含有1個或者多個私有資料位元組,置位0則無此位元組;

   adaptation_field_extension_flag:調整欄位擴充套件標誌位,佔位1bit,置位1表示含有調整欄位擴充套件欄位,置位0則無擴充套件欄位;

以上8bit是識別符號,後面是根據識別符號的值來確定的欄位,順序如下

   PCR欄位:當PCR_flag == 1時,此欄位才存在,佔位48bit,依次順序為:

program_clock_reference_base欄位:佔位33bit;

     reserved欄位:佔位6bit;

     program_clock_reference_extension欄位:佔位9bit;

   OPCR欄位:當OPCR_flag == 1時,此欄位才存在,佔位48bit,依次順序為:

original_program_clock_reference_base欄位:佔位33bit;

     reserved欄位:佔位6bit;

     original_program_clock_reference_extension欄位:佔位9bit;

   splice_countdown欄位:當splicing_point_flag == 1時此欄位存在,佔位8bit;

   transport_private_data欄位:私有資料欄位,當transport_private_data_flag == 1時此欄位存在,佔位N*8bit,位元組順序為:

transport_private_data_length:表明私有資料的位元組長度,佔位8bit;

     private_data_byte:私有資料,長度由前面的長度欄位確定;

   adaptation_field_extension欄位:調整欄位擴充套件欄位,佔用長度不確定,當adaptation_field_extension_flag == 1時此欄位存在,欄位中也有3個標誌位,來確定一些欄位存不存在,其具體位元組順序如下:

adaptation_field_extension_length:調整欄位擴充套件欄位的長度,佔位8bit;

   ltw_flag:ltw欄位標誌位,置位1時表示此欄位存在,佔位1bit;

   piecewise_rate_flag:piecewise_rate欄位標誌位,置位1時此欄位存在,佔位1bit;

   seamless_splice_flag:seamless_splice標誌位,置位1時此欄位存在,佔位1bit;

   Reserved:保留欄位,佔位5bit;

   Ltw欄位:當ltw_flag == 1時此欄位存在,佔位16bit,其由以下兩個欄位組成

     ltw_valid_flag:佔位1bit,當ltw_valid_flag == 1時,ltw_offset才有效;

     ltw_offset:佔位15bit;

   piecewise_rate欄位:當piecewise_rate_flag == 1時此欄位存在,佔位24bit,其位元組順序如下:

       reserved欄位:保留欄位,佔位2bit;

    piecewise_rate欄位:佔位22bit;此欄位只有在當ltw_flag == 1和ltw_valid_flag == 1時才有定義,有定義時此欄位是一個正整數;

   seamless_splice欄位:當seamless_splice_flag == 1時此欄位存在,佔位40bit;位元組順序依次為:

   splice_type欄位:佔位4bit;標識delay和rate值;

      DTS_next_AU[32..30]:佔位3bit;

   marker_bit欄位:佔位1bit;

   DTS_next_AU[29..15]欄位:佔位15bit;

   marker_bit:佔位1bit;

   DTS_next_AU[14..0]:佔位15bit;

   marker_bit:佔位1bit;

   stuffing_byte:填充欄位,固定為0xFF;

Payload_bytes:有效負載欄位,位元組來自PES包,PSI部分等;


總之,這些欄位就是一層套一層,一個欄位標誌位控制一個欄位,標誌位為1時,其標誌的欄位才會存在;

2.PSI程式特殊資訊表

TS包頭之後,就是負載payload的內容了,裡面可以是PES分組的資料,也可以是PSI資訊,PSI資訊主要由PATPMTCAT等,在這裡主要介紹PATPMT兩種資訊表;由上所描述資訊可知,payload的型別是由PID來確定的,一般PID==0x0000payloadPATPID== 0x0001,則payloadCAT,而PMTPID則是在PAT中進行指定的;

PSI還有可能有一個特殊的欄位:

Point_field欄位:跟在包頭之後,佔位8bit屬於有效負載,表示從此欄位開始到負載中PSI Section的第一個位元組之間的位元組數;當payload_unit_start_indicator == 1時,此欄位才存在;若point_field == 0x00,則表示此位元組後跟著的就是PSI Section的起始位元組;此欄位是在有效負載中的,計入有效負載的長度;

2.1.PAT:程式關聯表

PAT主要包含了節目編號和每一個節目對應的PMT的PID號碼,提供了節目編號和包含此節目定義資訊的TS分組(PMT分組)PID之間的對應關係(一般我們的TS流中只有一個節目(頻道),所以PAT中一般只有一個PMT)

整個表由很多欄位組成;這個表可能會被分為多個分段section進行傳輸,即PAT可能會被分在多個TS包進行傳輸;PAT的資料分段section由以下欄位組成,依次順序為:


table_id欄位標示PSI分段的內容,佔位8bit,當table_id == 0x00,表示此分段是PAT分段,當table_id == 0x01,表示此分段是CAT分段,當table_id == 0x02,表示此分段是PMT分段;在PAT中,id的值為0x00

section_syntax_indicator欄位佔位1bit,固定置位’1’;

‘0’欄位:佔位1bit

Reserved欄位保留欄位,佔位2bit

section_length欄位分段長度,佔位12bit,其中前2bit固定為’00’,後10bit表明了後面section欄位的長度,包括CRC的長度;

transport_stream_id欄位TS流識別id,用於從網路中其他的多路複用中識別出此TS流,其值由使用者定義,佔位16bit

Reserved保留欄位,佔位2bit

version_number欄位整個PAT的版本號,佔位5bit;當PAT變化時,其值從031迴圈累加,當current_next_indicator == 1時,version_number為當前PAT的版本號,當current_next_indicator == 0時,其值為下一個PAT的版本號;

current_next_indicator欄位指示符,佔位1bit,置位1時表示當前PAT有效,置位0時表示當前PAT無效,下一個PAT才有效;

section_number欄位此分段Section的序號,佔位8bitPAT的第一個分段Section的序號應該為0x00,將隨著PAT中的每一個分段累加1

last_section_number欄位PAT最後一個分段Section的序號,即最高序號值,佔位8bit

Loop

program_number欄位佔位16bit,指明PMT可用的節目的編號;若program_number == 0x0000,則下一個參考PIDnetwork PID,其他情況的值由使用者定義;在PAT的一個版本version_number中,這個值不能取某單個值多於一次(即一個PAT分段Section中只能有一個節目編號和其PMTPID的對應關係)

Reserved保留欄位,佔位3bit

network_PID欄位網路PID,佔位13bit,當program_number == 0x0000時,此欄位才存在;指明含有網路資訊表NITTS包的PID值;

program_map_PID欄位佔位13bit,當program_number != 0x0000時此欄位存在,表示program_number所指明的節目可用的PMT分段的TS包的PID值;一個program_number不應有多個program_map_PID賦值,這個program_map_PID的值是由使用者定義的,不過不能取為其他目的而保留的值;

Loop end

CRC_32欄位CRC校驗值,佔位32bit


2.2.PMT:節目對映表

PMT提供了節目編號和組成他們的節目原始流之間的對映關係,如果一個TS流中有多個節目,那個就會有多個PID不同的PMT表,在每個PMT中,都包含了節目原始流中不同的流型別TS包所對應的PID;即在PMT中,標識了當前節目中的視訊流,音訊流和與此節目相關的其他資料的TS包所對應的PID值;

PAT一樣,PMT也可能被分為一個或多個Section分段進行傳輸,PMT由很多欄位組成,其欄位順序如下所示:


table_id欄位標示PSI分段的內容,佔位8bit,在PMT中,固定為0x02

section_syntax_indicator欄位佔位1bit,固定置位’1’;

‘0’欄位:佔位1bit

Reserved欄位保留欄位,佔位2bit

section_length欄位分段長度,佔位12bit,其中前2bit固定為’00’,後10bit表明了後面section欄位的長度,包括CRC的長度;

program_number欄位節目編號,佔位16bit,規定了此PMT所對應的節目編號,一個TSPMT分段Section中,只能帶有一個節目定義;

Reserved欄位保留欄位,佔位2bit

version_number欄位PMT分段的版本號,佔位5bit,隨著此分段資訊的改變而累加1,直到31後再回到0迴圈;版本號對應於單個節目的定義,也就是對應於單個分段;當current_next_indicator == 1時,number值就是當前PMT分段的版本號,當current_next_indicator == 0時,number值位下一個可用PMT分段的版本號;

current_next_indicator欄位指示符,佔位1bit,置位1時表示當前PMT分段有效,置位0時表示當前PMT分段無效,下一個PMT分段才有效;

section_number欄位佔位8bit,固定為0x00

last_section_number欄位佔位8bit,固定為0x00

Reserved欄位保留欄位,佔位3bit

PCR_PID欄位PCR所在TS包的PID值,佔位13bit;表示由program_number所指明的節目中包含PCR欄位的TS包的PID值;如果一個私有流的節目定義無PCR與之相關,則這個欄位應置位0x1FFF

Reserved欄位保留欄位,佔位4bit

program_info_length欄位長度欄位,佔位12bit;前2bit固定為00,後10bit指明瞭此欄位之後的descriptor的位元組數;

Descriptor欄位:節目描述資訊,長度由上一個欄位確定;

LOOP

   stream_type欄位流型別欄位,佔位8bit;規定了由elementary_PID所指明的TS包的負載中的節目流的型別,即是視訊流還是音訊流或者其他資料;stream_type == 0x00是保留值;stream_type == 0x01stream_type == 0x02是視訊;stream_type == 0x03stream_type == 0x04是音訊;stream_type == 0x06是包含私有資料的PES包;

   Reserved欄位:保留欄位,佔位3bit

   elementary_PID欄位PID欄位,佔位13bit;指出攜帶相關原始流ESTS包的PID值,即視訊包和音訊包等TS包的PID值;

   Reserved欄位保留欄位,佔位4bit

   ES_info_length欄位長度欄位,佔位12bit;前2bit固定為00,後10bit表示此欄位之後相關節目原始流ES的描述欄位長度;

   Descriptor欄位:ES流描述資訊,長度由上一個欄位確定;

LOOP End

   CRC_32欄位CRC校驗值,佔位32bit


TS總結:TS解包流程就是現在TS包的包頭解出來PATPID,然後根據PID找到PAT,並從PAT中解出來每個節目所對應的PMTPID,再根據PID找到所有節目的PMT,然後從每個節目的PMT中解出來當前節目所對應的不同流型別的TS包的PID,根據這些PID來找到對應的TS包,取出原始視訊流,音訊流和其他資料等;打包過程則是相反的;

TS頭裡面的PCR欄位是基準時間戳,在音視訊解碼顯示的時候,是根據PES頭裡面的PTSDTS欄位與其對比,相同就說明該進行解碼和顯示了;PCR欄位是在TSPMT中指定的PID,只有指定的PIDTS包裡面的PCR欄位才有用,我們打包的時候使用的是視訊的PID中的PCR,只有每幀的第一包TS頭裡面才會有PCR,而PES頭裡面的PTSDTS就是視訊和音訊的相對時間戳;測試遇到了音視訊不同步的問題,原因就是TS打包時,PES頭裡面的音視訊PTS都用了視訊的時間戳,而我們在TS解析時是對音訊有相對延後的操作,其採用的視訊時間戳相對原來是有可能延後了多個視訊幀的,所以導致音訊有延後;

專案上TS流在IOS系統上面播放遇到坑:

1.今天除錯IOS系統播放TS流,發現了一個坑,IOS系統播放必須將視訊PES包頭中的Packet_Length欄位設定為0,音訊PES包的這個值必須不為0,否則IOS系統將無法播放TS流!!!

2.如果TS流裡面是隻有視訊沒有音訊的,那麼在封裝PMT的時候,一定不要放進去音訊的PID,否則IOS系統也播放不了- -  (安卓各種順暢,IOS是各種坑啊,限制太多了ORZ...)