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...)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章