TS 流、包結構以及同步
1. TS 流: 可以將TS流理解為一種單一碼流、混合碼流。
單一碼流:TS流的基本組成單位是長度為188字節的TS包。
混合碼流:TS流有多種數據組成,一個TS包中的數據可以是視頻數據、音頻數據、填充數據,PSI/SI表格數據.....(唯一的PID對應)
2. TS 包結構分析:
TS 包由包頭、有效載荷區組成。(有些包中包括自適應區)。大小: 188 字節
TS包頭:4 個字節
同步字節 0x47,用於檢測碼流是否同步。
包ID: PID, 解碼器通過該標志號確定該TS包中的數據屬於那種類型。
PCR: 自適應區, 解碼器通過該時間參數,進行解碼端的時鍾重置。
有效載荷: 最高 184 字節
視頻、音頻 or 其他數據

語法:







五、












HLS,Http Live Streaming 是由Apple公司定義的用於實時流傳輸的協議,HLS基於HTTP協議實現,傳輸內容包括兩部分,一是M3U8描述文件,二是TS媒體文件。
1、M3U8文件
用文本方式對媒體文件進行描述,由一系列標簽組成。
#EXTM3U
#EXT-X-TARGETDURATION:5
#EXTINF:5,
./0.ts
#EXTINF:5,
./1.ts
#EXTM3U:每個M3U8文件第一行必須是這個tag。
#EXT-X-TARGETDURATION:指定最大的媒體段時間長度(秒),#EXTINF中指定的時間長度必須小於或等於這個最大值。該值只能出現一次。
#EXTINF:描述單個媒體文件的長度。后面為媒體文件,如./0.ts
2、ts文件
ts文件為傳輸流文件,視頻編碼主要格式h264/mpeg4,音頻為acc/MP3。
ts文件分為三層:ts 層 Transport Stream, 就是在pes層加入數據流的識別和傳輸必須的信息;
pes 層 Packet Elemental Stream,pes層是在音視頻數據上加了時間戳等對數據幀的說明信息;
es層 Elementary Stream. es層就是音視頻數據。
(1)ts層
ts包大小固定為188字節,ts層分為三個部分:ts header、adaptation field、payload。ts header固定4個字節;adaptation field可能存在也可能不存在,主要作用是給不足188字節的數據做填充;payload是pes數據。
| sync_byte | 8 | 同步字節,固定為0x47 |
| transport_error_indicator | 1 | 傳輸錯誤指示符,表明在ts頭的adapt域后由一個無用字節,通常都為0,這個字節算在adapt域長度內 |
| payload_unit_start_indicator | 1 | 負載單元起始標示符,一個完整的數據包開始時標記為1, 表示攜帶的是PSI或PES第一個包 |
| transport_priority | 1 | 傳輸優先級,0為低優先級,1為高優先級,通常取0 |
| pid | 13 | pid值 |
| transport_scrambling_control | 2 | 傳輸加擾控制,00表示未加密,TS包中的有效數據未經加擾處理。 |
| adaptation_field_control | 2 | 是否包含自適應區,‘00’保留;‘01’為無自適應域,僅含有效負載;‘10’為僅含自適應域,無有效負載;‘11’為同時帶有自適應域和有效負載。 |
| continuity_counter | 4 | 遞增計數器,從0-f,起始值不一定取0,PID相同的包,計數器必須是連續的 |
ts層的內容是通過PID值來標識的,主要內容包括:PAT表、PMT表、音頻流、視頻流。解析ts流要先找到PAT表,只要找到PAT就可以找到PMT,然后就可以找到音視頻流了。PAT表的PID值固定為0。PAT表和PMT表需要定期插入ts流,因為用戶隨時可能加入ts流,這個間隔比較小,通常每隔幾個視頻幀就要加入PAT和PMT。PAT和PMT表是必須的,還可以加入其它表如SDT(業務描述表)等,不過hls流只要有PAT和PMT就可以播放了。

| adaptation_field_length | 1B | 自適應域長度,后面的字節數 |
| flag | 1B | 取0x50表示包含PCR或0x40表示不包含PCR |
| PCR | 5B | Program Clock Reference,節目時鍾參考,用於恢復出與編碼端一致的系統時序時鍾STC(System Time Clock)。 |
| stuffing_bytes | xB | 填充字節,取值0xff |
自適應區的長度要包含傳輸錯誤指示符標識的一個字節。pcr是節目時鍾參考,pcr、dts、pts都是對同一個系統時鍾的采樣值,pcr是遞增的,因此可以將其設置為dts值,音頻數據不需要pcr。如果沒有字段,ipad是可以播放的,但vlc無法播放。打包ts流時PAT和PMT表是沒有adaptation field的,不夠的長度直接補0xff即可。視頻流和音頻流都需要加adaptation field,通常加在一個幀的第一個ts包和最后一個ts包里,中間的ts包不加。
PAT格式
| table_id | 8b | PAT表固定為0x00 |
| section_syntax_indicator | 1b | 固定為1 |
| zero | 1b | 固定為0 |
| reserved | 2b | 固定為11 |
| section_length | 12b | 后面數據的長度 |
| transport_stream_id | 16b | 傳輸流ID,固定為0x0001 |
| reserved | 2b | 固定為11 |
| version_number | 5b | 版本號,固定為00000,如果PAT有變化則版本號加1 |
| current_next_indicator | 1b | 固定為1,表示這個PAT表可以用,如果為0則要等待下一個PAT表 |
| section_number | 8b | 固定為0x00, 表明這個段是該pat表的第幾個段。 |
| last_section_number | 8b | 固定為0x00, 表明PAT表一共有多少段。 |
| 開始循環 | ||
| program_number | 16b | 節目號為0x0000時表示這是NIT,節目號為0x0001時,表示這是PMT |
| reserved | 3b | 固定為111 |
| PID | 13b | 節目號對應內容的PID值 |
| 結束循環 | ||
| CRC32 | 32b | 前面數據的CRC32校驗碼 |
PMT格式
| table_id | 8b | PMT表取值隨意,0x02 |
| section_syntax_indicator | 1b | 固定為1 |
| zero | 1b | 固定為0 |
| reserved | 2b | 固定為11 |
| section_length | 12b | 后面數據的長度 |
| program_number | 16b | 頻道號碼,表示當前的PMT關聯到的頻道,取值0x0001 |
| reserved | 2b | 固定為11 |
| version_number | 5b | 版本號,固定為00000,如果PAT有變化則版本號加1 |
| current_next_indicator | 1b | 固定為1 |
| section_number | 8b | 固定為0x00 |
| last_section_number | 8b | 固定為0x00 |
| reserved | 3b | 固定為111 |
| PCR_PID | 13b | PCR(節目參考時鍾)所在TS分組的PID,指定為視頻PID |
| reserved | 4b | 固定為1111 |
| program_info_length | 12b | 節目描述信息,指定為0x000表示沒有 |
| 開始循環 | ||
| stream_type | 8b | 流類型,標志是Video還是Audio還是其他數據,h.264編碼對應0x1b,aac編碼對應0x0f,mp3編碼對應0x03 |
| reserved | 3b | 固定為111 |
| elementary_PID | 13b | 與stream_type對應的PID |
| reserved | 4b | 固定為1111 |
| ES_info_length | 12b | 描述信息,指定為0x000表示沒有 |
| 結束循環 | ||
| CRC32 | 32b | 前面數據的CRC32校驗碼 |
(2)pes層
pes層是在每一個視頻/音頻幀上加入了時間戳等信息,pes包內容很多,我們只留下最常用的。
| pes start code | 3B | 開始碼,固定為0x000001 |
| stream id | 1B | 音頻取值(0xc0-0xdf),通常為0xc0 視頻取值(0xe0-0xef),通常為0xe0 |
| pes packet length | 2B | 后面pes數據的長度,0表示長度不限制, 只有視頻數據長度會超過0xffff |
| flag | 1B | 通常取值0x80,表示數據不加密、無優先級、備份的數據 |
| flag | 1B | 取值0x80表示只含有pts,取值0xc0表示含有pts和dts |
| pes data length | 1B | 后面數據的長度,取值5或10 |
| pts | 5B | 33bit值 |
| dts | 5B | 33bit值 |
pts是顯示時間戳、dts是解碼時間戳,視頻數據兩種時間戳都需要,音頻數據的pts和dts相同,所以只需要pts。有pts和dts兩種時間戳是B幀引起的,I幀和P幀的pts等於dts。如果一個視頻沒有B幀,則pts永遠和dts相同。從文件中順序讀取視頻幀,取出的幀順序和dts順序相同。dts算法比較簡單,初始值 + 增量即可,pts計算比較復雜,需要在dts的基礎上加偏移量。
音頻的pes中只有pts(同dts),視頻的I、P幀兩種時間戳都要有,視頻B幀只要pts(同dts)。打包pts和dts就需要知道視頻幀類型,但是通過容器格式我們是無法判斷幀類型的,必須解析h.264內容才可以獲取幀類型。
舉例說明:
I P B B B P
讀取順序: 1 2 3 4 5 6
dts順序: 1 2 3 4 5 6
pts順序: 1 5 3 2 4 6
點播視頻dts算法:
dts = 初始值 + 90000 / video_frame_rate,初始值可以隨便指定,但是最好不要取0,video_frame_rate就是幀率,比如23、30。
pts和dts是以timescale為單位的,1s = 90000 time scale , 一幀就應該是90000/video_frame_rate 個timescale。
用一幀的timescale除以采樣頻率就可以轉換為一幀的播放時長
點播音頻dts算法:
dts = 初始值 + (90000 * audio_samples_per_frame) / audio_sample_rate,
audio_samples_per_frame這個值與編解碼相關,aac取值1024,mp3取值1158,audio_sample_rate是采樣率,比如24000、41000。AAC一幀解碼出來是每聲道1024個sample,也就是說一幀的時長為1024/sample_rate秒。所以每一幀時間戳依次0,1024/sample_rate,...,1024*n/sample_rate秒。
直播視頻的dts和pts應該直接用直播數據流中的時間,不應該按公式計算。
(3)es層
es層指的就是音視頻數據,我們只介紹h.264視頻和aac音頻。
h.264視頻:
打包h.264數據我們必須給視頻數據加上一個nalu(Network Abstraction Layer unit),nalu包括nalu header和nalu type,nalu header固定為0x00000001(幀開始)或0x000001(幀中)。h.264的數據是由slice組成的,slice的內容包括:視頻、sps、pps等。nalu type決定了后面的h.264數據內容。
| F | 1b | forbidden_zero_bit,h.264規定必須取0 |
| NRI | 2b | nal_ref_idc,取值0~3,指示這個nalu的重要性,I幀、sps、pps通常取3,P幀通常取2,B幀通常取0 |
| Type | 5b | 參考下表 |
| nal_unit_type | 說明 |
| 0 | 未使用 |
| 1 | 非IDR圖像片,IDR指關鍵幀 |
| 2 | 片分區A |
| 3 | 片分區B |
| 4 | 片分區C |
| 5 | IDR圖像片,即關鍵幀 |
| 6 | 補充增強信息單元(SEI) |
| 7 | SPS序列參數集 |
| 8 | PPS圖像參數集 |
| 9 | 分解符 |
| 10 | 序列結束 |
| 11 | 碼流結束 |
| 12 | 填充 |
| 13~23 | 保留 |
| 24~31 | 未使用 |
紅色字體顯示的內容是最常用的,打包es層數據時pes頭和es數據之間要加入一個type=9的nalu,關鍵幀slice前必須要加入type=7和type=8的nalu,而且是緊鄰。
協議數據單元(protocol data unit,PDU)
TS數據流PAT和PMT分析
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;
//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;
:流類型取值說明
| 取值 |
描述 |
| 0x00 |
國際標准保留 |
| 0x01 |
視頻 |
| 0x02 |
視頻或受限參數視頻流 |
| 0x03 |
音頻 |
| 0x04 |
音頻 |
| 0x05 |
private_sections |
| 0x06 |
包含專用數據的PES分組 |
| 0x07 |
ISO/IEC 13533 MHEG |
| 0x08 |
|
| 0x09 |
ITU-T Rec.H.222.1 |
| 0x0A~0x0D |
GB/T類型 |
| 0x0E |
GB/T輔助 |
| 0x0F~0x7F |
GB/T保留 |
| 0x80~0xFF |
用戶專用 |
從TS開始
PAT 節目關聯表(PAT Program Association Table): PAT是機頂盒接收的入口點,是它獲取數據的開始
| 結構名 |
中文 |
所定義標准 |
PID |
描述 |
| PAT |
節目關聯表 |
MPEG2標准 |
0x0000 |
將節目號碼和節目映射表PID相關聯,是獲取數據的開始 |
| PMT |
節目映射表 |
MPEG2標准 |
在PAT中指出 |
指定一個或多個節目的PID |
| CAT |
條件接收表 |
MPEG2標准 |
0x0001 |
將一個或多個專用EMM流分別與唯一的PID相關聯 |
| NIT |
網絡信息表 |
SI標准 |
PAT中指出 |
描述整個網絡,如多少個TS流、頻點和調制方式等信息 |
*NOTE:
TS流和PS流的區別:TS流的包結構是長度是固定的;PS流的包結構是可變長度的。這導致了TS流的抵抗傳輸誤碼的能力強於PS流(TS碼流由於采用了固定長度的包結構,當傳輸誤碼破壞了某一TS包的同步信息時,接收機可在固定的位置檢測它后面包中的同步信息,從而恢復同步,避免了信息丟失。而PS包由於長度是變化的,一旦某一 PS包的同步信息丟失,接收機無法確定下一包的同步位置,就會造成失步,導致嚴重的信息丟失。因此,在信道環境較為惡劣,傳輸誤碼較高時,一般采用TS碼流;而在信道環境較好,傳輸誤碼較低時,一般采用PS碼流。)
由於TS碼流具有較強的抵抗傳輸誤碼的能力,因此目前在傳輸媒體中進行傳輸的MPEG-2碼流基本上都采用了TS碼流的包格。

從上圖可以看出,視頻ES和音頻ES通過打包器和共同或獨立的系統時間基准形成一個個PES,通過TS復用器復用形成的傳輸流。注意這里的TS流是位流格式(分析Packet的時候會解釋),也即是說TS流是可以按位讀取的。
TS流是基於Packet的位流格式,每個包是188個字節(或204個字節,在188個字節后加上了16字節的CRC校驗數據,其他格式一樣)。整個TS流組成形式如下:

| Packet Header(包頭)信息說明 |
|||
| 1 |
sync_byte |
8bits |
同步字節 |
| 2 |
transport_error_indicator |
1bit |
錯誤指示信息(1:該包至少有1bits傳輸錯誤) |
| 3 |
payload_unit_start_indicator |
1bit |
負載單元開始標志(packet不滿188字節時需填充) |
| 4 |
transport_priority |
1bit |
傳輸優先級標志(1:優先級高) |
| 5 |
PID |
13bits |
Packet ID號碼,唯一的號碼對應不同的包 |
| 6 |
transport_scrambling_control |
2bits |
加密標志(00:未加密;其他表示已加密) |
| 7 |
adaptation_field_control |
2bits |
附加區域控制 |
| 8 |
continuity_counter |
4bits |
包遞增計數器 |
PID是TS流中唯一識別標志,Packet Data是什么內容就是由PID決定的。如果一個TS流中的一個Packet的Packet Header中的PID是0x0000,那么這個Packet的Packet Data就是DVB的PAT表而非其他類型數據(如Video、Audio或其他業務信息)。下表給出了一些表的PID值,這些值是固定的,不允許用於更改。
| 表 |
PID 值 |
| PAT |
0x0000 |
| CAT |
0x0001 |
| TSDT |
0x0002 |
| EIT,ST |
0x0012 |
| RST,ST |
0x0013 |
| TDT,TOT,ST |
0x0014 |
下面給出了PID字段的取值要求:
| 值 |
描述 |
| 0x0000 |
PAT |
| 0x0001 |
CAT |
| 0x0002~0x000F |
保留 |
| 0x0010~0x1FFE |
可賦給network_PID、Program_map_PID、elementary_PID或作其他用途 |
| 0x1FFF |
空的分組 |
下面以一個TS流的其中一個Packet中的Packet Header為例進行說明:
|
|
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
… |
| Packet(十六進制) |
4 |
7 |
0 |
7 |
e |
5 |
1 |
2 |
… |
||||||||||||||||||||||||
| Packet(二進制) |
0 |
1 |
0 |
0 |
0 |
1 |
1 |
1 |
0 |
0 |
0 |
0 |
0 |
1 |
1 |
1 |
1 |
1 |
1 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
0 |
1 |
0 |
… |
| Packet Header 信息 |
1 sync_byte=0x47 |
2 |
3 |
4 |
5 PID=0x07e5 |
6 |
7 |
8 |
… |
||||||||||||||||||||||||
sync_byte=01000111, 就是0x47,這是DVB TS規定的同步字節,固定是0x47.
transport_error_indicator=0, 表示當前包沒有發生傳輸錯誤.
payload_unit_start_indicator=0, 含義參考ISO13818-1標准文檔
transport_priority=0, 表示當前包是低優先級.
PID=00111 11100101即0x07e5, Video PID
transport_scrambling_control=00, 表示節目沒有加密
adaptation_field_control=01 即0x01,具體含義請參考ISO13818-1
continuity_counte=0010 即0x02,表示當前傳送的相同類型的包是第3個
PAT表定義了當前TS流中所有的節目,其PID為0x0000,它是PSI的根節點,要查尋找節目必須從PAT表開始查找。
PAT表攜帶以下信息:
| TS流ID |
transport_stream_id |
該ID標志唯一的流ID |
| 節目頻道號 |
program_number |
該號碼標志TS流中的一個頻道,該頻道可以包含很多的節目(即可以包含多個Video PID和Audio PID) |
| PMT的PID |
program_map_PID |
表示本頻道使用哪個PID做為PMT的PID,因為可以有很多的頻道,因此DVB規定PMT的PID可以由用戶自己定義 |
PAT表主要包含頻道號碼和每一個頻道對應的PMT的PID號碼,這些信息我們在處理PAT表格的時候會保存起來,以后會使用到這些數據
PAT表描述了當前流的NIT(Network Information Table,網絡信息表)中的PID、當前流中有多少不同類型的PMT表及每個PMT表對應的頻道號。
(二) PMT表(Program Map Table,節目映射表)(Service Descriptor Table)
1. PMT表的描述
如果一個TS流中含有多個頻道,那么就會包含多個PID不同的PMT表。
每一個節目的所有信息必須包含在一個PMT中,但在一個PMT中可以包含多個節目信息。PMT本身的PID由PAT表格提供。
PMT表中包含的數據如下:
(1) 當前頻道中包含的所有Video數據的PID
(2) 當前頻道中包含的所有Audio數據的PID
(3) 和當前頻道關聯在一起的其他數據的PID(如數字廣播,數據通訊等使用的PID)
只要我們處理了PMT,那么我們就可以獲取頻道中所有的PID信息,如當前頻道包含多少個Video、共多少個Audio和其他數據,還能知道每種數據對應的PID分別是什么。這樣如果我們要選擇其中一個Video和Audio收看,那么只需要把要收看的節目的Video PID和Audio PID保存起來,在處理Packet的時候進行過濾即可實現。
解復用的意義在於,由於TS流是一種復用的碼流,里面混雜了多種類型的包;解復用TS流可以將類型相同的Packet存入相同緩存,分別處理。這樣就可以將Video、Audio或者其他業務信息的數據區分開來。
(四) 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相關的表,將走得更遠。
TS流的解碼過程-ES-PES-DTS-PTS-PCR
http://blog.csdn.net/soulxu/article/details/6167060
TS 流解碼過程:
1. 獲取TS中的PAT
2. 獲取TS中的PMT
3. 根據PMT可以知道當前網絡中傳輸的視頻(音頻)類型(H264),相應的PID,PCR的PID等信息。
4. 設置demux 模塊的視頻Filter 為相應視頻的PID和stream type等。
5. 從視頻Demux Filter 后得到的TS數據包中的payload 數據就是 one piece of PES,在TS header中有一些關於此 payload屬於哪個 PES的 第多少個數據包。 因此軟件中應該將此payload中的數據copy到PES的buffer中,用於拼接一個PES包。
6. 拼接好的PES包的包頭會有 PTS,DTS信息,去掉PES的header就是 ES。
7. 直接將 被被拔掉 PES包頭的ES包送給decoder就可以進行解碼。解碼出來的數據就是一幀一幀的視頻數據,這些數據至少應當與PES中的PTS關聯一下,以便進行視 音頻同步。
8. I,B,B,P 信息是在ES中的。
ES是直接從編碼器出來的數據流,可以是編碼過的視頻數據流,音頻數據流,或其他編碼數據流的統稱。ES流經過PES打 包器之后,被轉換成PES包。PES包由包頭和payload組成.
在PES層, 主要是在PES包頭信息中加入PTS(顯 示時間標簽)和DTS(解 碼時間標簽)用於視頻、音頻同步。其實,Mpeg-2用於視音頻同步以及系統時鍾恢復的時間標簽分別在ES,PES和TS這3個層次 中。在ES層,與同步有關的主要是視頻緩沖驗證VBV(Video Buffer Verifier),用以防止解碼器的緩沖器出現上溢或下溢;在PES層,主要是在PES頭 信息里出現的顯示時間標簽PTS(Presentation Time Stamp)和解碼時間標簽DTS(Decoding Time Stamp);在TS層 中,TS頭信息包含了節目時鍾參考PCR(Program Clock Reference),用於恢復出與編碼端一致的系統時序時鍾STC(System Time Clock)。
基本流程如下:首先MPEG-2壓縮編碼得到的ES基本流,這個數據流很大,並且只是I,P,B的這些視頻幀或音頻取樣信息,然后加入一些同步信息,打包成長度可變長度的數據包PES,原來是流的格式,現在成了數據包的分割形式。同時要注意的是,ES是只包含一種內容的數據流,如只含視頻,或只含音頻等,打包之后的PES也是只含一種性質的ES,如 只含視頻ES的PES,只 含音頻ES的PES等。 可以知道,ES是編碼視頻數據流或音頻數據流,每個ES都由若干個存取單元(AU) 組成,每個視頻AU或音頻AU都是由頭部和編碼數據兩部分組成,1個AU相當於編碼的1幅視頻圖像或1個音頻 幀,也可以說,每個AU實際上是編碼數據流的顯示單元,即相當於 解碼的1幅視頻圖像或1個音頻 幀的取樣。PEG-2對視頻的壓縮產生I幀、P幀、B幀。把 幀順序I1,P4,B2,B3,P7,B5,B6幀的編碼ES,通 過打包並在每個幀中插入 PTS/DTS標志,變成PES。在插入PTS/DTS標志時,由於在B幀PTS和DTS相 等,所以無須在B幀多插入DTS。而對於I幀 和P幀,由 於經過復用后數據包的順序會發生變化,顯示前一定要存儲於視頻解碼器的從新排序緩存器中,經過從新排序后再顯示,所以一定要同時插入PTS和 DTS作 為從新排序的依據。
其中,有否PTS/DTS標志,是解決視音頻同步顯示、防止解碼器輸入緩存器上溢或下溢的關鍵所在。PTS表明顯示單元出現在系統目標解碼器(STD- System Target Decoder)的時間, DTS表明將存取單元全部字節從STD的ES解碼緩存器移走的時刻。視頻編碼圖像幀次序為 I1,P4,B2,B3,P7,B5,B6,I10,B8,B9的ES,加 入PTS/DTS后,打包成一個個視頻PES包。每個PES包都有一個包頭,用於定義PES內的數據內容,提供定時資料。每個I、P、B幀的包頭都有一個PTS和DTS,但PTS與DTS對B幀都是 一樣的,無須標出B幀的DTS。對I幀和P幀,顯示前一定要存儲於視頻解碼器的重新排序緩存器中,經過延遲(重新排序)后再顯示,一定要分別標 明PTS和DTS。 例如,解碼器輸入的圖像幀次序為I1,P4,B2,B3,P7,B5,B6,I10,B8,B9,依解碼器輸出的幀次序,應該P4比B2、B3在先,但顯示時P4一定 要比B2、B3在 后,即P4要在提前插入數據流中的時間標志指引下,經過緩存器重新排序,以重建編碼前視頻幀次序I1,B2,B3,P4,B5,B6,P7,B8,B9,I10。顯然,PTS/DTS標志表明對確定事件或確定信息解碼的專用時標的存在,依靠專用時標解碼器,可知道該確定事件或確定信息開始解碼或顯示的時刻。例如,PTS/DTS標志可用於確定編碼、多路復用、解碼、重建的時間。
PCR
PCR是TS里面的,即TS packet的header里面可能會有,他用來指定所期望的該ts packet到達decoder的時間,他 的作用於SCR類似。
DTS, PTS
對於一個ES來說,比如視頻,他又許多I,P,B幀,而P, B幀都是以I,P幀作為參考。由於B幀是前向后向參考,因此要對B幀 作decode的話,就必須先decode該B幀后面的P,或者I幀,於是,decode的時間與幀的真正的present的時間就不一致了,按照DTS一 次對各個幀進行decode,然后再按照PTS對各個幀進行展現。
有時候PES包頭里面也會有DTS,PTS,對於PTS來說,他代表了這個PES包得payload里面的第一個完整地audio access unit或者video access unit的PTS時間(並不是每個audio/video access unit都帶有PTS/DTS,因此,你可以在PES里面指定一個,作為開始)。
PES包頭的DTS也是這個原理,只 不過注意的是:對於video來說他的DTS和PTS是可以不一樣的,因為B幀的存 在使其順序可以倒置。而對於audio來說,audio沒有雙向的預測,他的DTS和PTS可以看成是一個順序的,因此可一直采用一個,即可只采用PTS。
PES,TS,PS,RTP等流的打包格式解析之PES流
http://blog.csdn.net/appledurian/article/details/70851428
一、PES流
PES流是對原始ES流進行的第一層封裝,PES流的基本單位是PES包,由包頭和payload組成,ES流即音視頻裸流,是從編碼器里面出來的原始視頻音頻流;ES流只包含一種內容,里面是視頻或者音頻;封裝時不對其進行改變,只在前面添加頭部,如私有頭,解碼時,將私有頭剝掉,將原始ES碼流送進解碼器解碼,這也是解碼通用性,若是修改了,則其他解碼器就沒法解碼了;PES和ES一樣,都是單一原始碼流,一般我遇到的是一幀數據放在一個PES包里面,但是一個PES包的最大長度為65535字節,因此一幀數據有可能被分為多個PES;其包頭格式如下:

可以看出,PES包是由固定包頭,可選包頭和負載三部分組成,其中固定包頭固定6個字節;PES包長度字段占位16bit,最大值為65536,故一幀可能會分為多個PES包;下面依次介紹其每個字段的含義:
Packet start code prefix: 包頭起始碼,固定為0x000001,占位24bit;
Stream id: (UI)PES包中的負載流類型,一般視頻為0xe0,音頻為0xc0,占位8bit;
PES packet length: (UI)PES包長度,包括此字節后的可選包頭和負載的長度,占位16bit;
Optional PES Header,順序依次為:
’10’字段: 占位2bit;
PES scrambling control: 加密模式,占2bit;00未加密,01或10或11由用戶定義;
PES priority: 有效負載的優先級,占位1bit;值為1比值為0的負載優先級高;
Data alignment indicator: 數據定位指示器,占位1bit;
Copyright: 版權信息,1為有版權,0無版權,占位1bit;
Original or copy: 原始或備份,1為原始,0為備份,占位1bit;
// 后面是7個flags(一般我們關注的就是PTS DTS的標志位):
PTS_DTS_flags :PTS和DTS標志位,占位2bit;10表示首部有PTS字段,11表示有PTS和DTS字段,00表示都沒有,01被禁止,不會出現此種情況;
ESCR_flag :ESCR標志,占位1bit;1表示首部有ESCR字段,0則無此字段
ES_rate_flag :ES_rate字段,占位1bit;1表示首部有此字段,0無此字段;
DSM_trick_mode_flag :占位1bit;1表示有8位的DSM_trick_mode_flag字段,0無此字段;
Additional_copy_info_flag :占位1bit;1表示首部有此字段,0表示無此字段;
PES_CRC_flag :占位1bit;置1表示PES分組有CRC字段,0無此字段;
PES_extension_flag :占位1bit;擴展標志位,置1表示有擴展字段,0無此字段;
PES header data length :(UI)PES首部中可選字段和填充字段的長度;占位8bit;可選字段的內容由上面7個flags來進行控制;
Optional fields:可選字段的描述信息區域,其內容由上面的7個flag來控制;
PTS/DTS字段:顯示時間戳/解碼時間戳,占位40bit,當PTS_DTS_flags == 1x時此字段存在;時間占用33個bit,PTS和DTS的內容是在這40bit中取33位,方式相同;
PTS(presentation time stamp)顯示時間戳和DTS(Decoding Time Stamp)解碼時間戳,是用來音視頻同步的,是打在PES包的包頭里面的,PTS/DTS是相對SCR(系統參考)的時間戳,是以90000為單位的,PTS/DTS到ms的轉換公式是PTS/90,系統時鍾頻率(H264采樣頻率?)為90Khz,所以轉換到秒為PTS/90000,所以如果是以ms為單位的播放器,PTS/DTS是要使用公式ms=pts/90來轉換才行的,而如果是以時鍾頻率為單位的話,則直接將PTS/DTS送進去解碼即可;如果沒有B幀,PTS和DTS的順序應該是一致的,如果有B幀,則需要先解碼P幀,才能解出來B幀,所以需要PTS和DTS來控制解碼時間和顯示時間;

字節順序依次:
start_code :起始碼,占位4bit;若PTS_DTS_flags == ‘10’,則說明只有PTS,起始碼為0010;
若PTS_DTS_flags == ‘11’,則PTS和DTS都存在,PTS的起始碼為0011,DTS的起始碼為0001;(PTS的起始碼后2個bit與flag相同)
PTS[32..30] :占位3bit;
marker_bit :占位1bit;
PTS[29..15] :占位15bit;
marker_bit :占位1bit;
PTS[14..0] :占位15bit;
marker_bit :占位1bit;
PTS/DTS = (PTS1 & 0x0e) << 29 + (PTS2 & 0xfffe) << 14 + (PTS3 & 0xfffe ) >> 1;
ESCR字段 :此字段占位48bit,由33bit的ESCR_base字段和9bit的ESCR_extension字段組成,ESCR_flag == 1時此字段存在;數據依次順序:
Reserved :保留字段,占位2bit;
ESCR_base[32..30]:占位3bit;
marker_bit :占位1bit;
ESCR_base[29..15]:占位15bit;
marker_bit :占位1bit;
ESCR_base[14..0] :占位15bit;
marker_bit :占位1bit;
ESCR_extension :(UI)占位9bit;周期數,取值范圍0~299;循環一次,base+1;
marker_bit :占位1bit;
ES rate字段 :目標解碼器接收PES分組字節速率,禁止為0,占位24bit,ES_rate_flag == 1時此字段存在;數據順序為:
marker_bit :占位1bit;
ES_rate:占位22bit;
marker_bit:占位1bit;
Trick mode control字段:表示哪種trick mode被應用於相應的視頻流,占位8個bit,
DSM_trick_mode_flag == 1時此字段存在;其中trick_mode_control占前3個bit,根據其值后面有5個bit的不同內容;
如果trick_mode_control == ‘000’,依次字節順序為:
field_id:占位2bit;
intra_slice_refresh :占位1bit;
frequency_truncation:占位2bit;
如果trick_mode_control == ‘001’,依次字節順序為:
rep_cntrl:占位5bit;
如果trick_mode_control == ‘010’,依次字節順序為:
field_id:占位2bit;
Reserved:占位3bit;
如果trick_mode_control == ‘011’,依次字節順序為:
field_id:占位2bit;
intra_slice_refresh:占位1bit;
frequency_truncation:占位2bit;
如果trick_mode_control== ‘100’,依次字節順序為:
rep_cntrl:占位5bit;
其他情況,字節順序為:
reserved :占位5bit;
Additional copy info字段:占8個bit,Additional_copy_info_flag == 1時此字段存在;數據順序為:
marker_bit:占位1bit;
copy info字段:占位7bit;表示和版權相關的私有數據;
Previous PES CRC字段:占位16bit字段,包含CRC值,PES_CRC_flag == 1時此字段存在;
PES extension字段:PES擴展字段,PES_extension_flag == 1時此字段存在;內容如下,字節順序依次為:
PES_private_data_flag:占位1bit,置1表示有私有數據,0則無;
Pack_header_field_flag:占位1bit,置1表示有Pack_header_field字段,0則無;
Program_packet_sequence_counter_flag:占位1bit,置1表示有此字段,0則無;
P-STD_buffer_flag:占位1bit,置1表示有P-STD_buffer字段,0則無此字段;
Reserved字段:3個bit;
PES_extension_flag_2:占位1bit,置1表示有擴展字段,0則無此字段;
Optional field :PES擴展字段的可選字段內容順序為:
PES_private_data字段:私有數據內容,占位128bit,PES_private_data_flag == 1時此字段存在;
Pack_header_field字段:Pack_header_field_flag == 1時此字段存在;字段組成順序如下:
Pack_field_length字段:(UI)指定后面的field的長度,占位8bit;
pack_header_field():長度為Pack_field_length指定;
Program_packet_sequence_counter字段:計數器字段,16個bit;當flag字段Program_packet_sequence_counter_flag == 1時此字段存在;字節順序依次為:
marker_bit:占位1bit;
packet_sequence_counter字段:(UI)占位7bit;
marker_bit:占位1bit;
MPEG1_MPEG2_identifier:占位1bit;置位1表示此PES包的負載來自MPEG1流,置位0表示此PES包的負載來自PS流;
original_stuff_length:(UI)占位6bit;表示PES頭部填充字節長度;
P-STD_buffer字段:表示P-STD_buffer內容,占位16bit;P-STD_buffer_flag == '1'時此字段存在;字節順序依次為:
’01’字段:占位2bit;
P-STD_buffer_scale:占位1bit;表示用來解釋后面P-STD_buffer_size字段的比例因子;如果之前的stream_id表示音頻流,則此值應為0,若之前的stream_id表示視頻流,則此值應為1,對於其他stream類型,此值可以0或1;
P-STD_buffer_size:占位13bit;無符號整數;大於或等於所有P-STD輸入緩沖區大小BSn的最大值;若P-STD_buffer_scale == 0,則P-STD_buffer_size以128字節為單位;若P-STD_buffer_scale == 1,則P-STD_buffer_size以1024字節為單位;
PES_extension2字段:擴展字段的擴展字段;占用N*8個bit,PES_extension_flag_2 == '1'時此字段存在;字節順序依次為:
marker_bit:占位1bit;
PES_extension_field_length:占位7bit,表示擴展區域的長度;
Reserved字段:占位8*PES_extension_field_length個bit;
Stuffing bytes:填充字段,固定為0xFF;不能超過32個字節;
PES_packet_data_byte:PES包負載中的數據,即ES原始流數據;
PES包是TS和PS包封裝的基礎,TS和PS其實就是對PES包的再一次封裝,下篇將講解一下TS流

上圖給出了在一個TS流中PSI各表之間的聯系。首先從PAT表出發,獲取當前有哪些節目號(program_map_PID),再根據這些節目號找出program_number相同的PMT;獲取到PMT后,即可根據elementary_PID和stream_type來確定要過濾哪些含有基本流的TS包,以及這些包里面是什么類型的數據。這樣,機頂盒就可以過濾出相應的節目(視頻和音頻數據)來收看了。
TS總結:
TS解包流程就是現在TS包的包頭解出來PAT的PID,然后根據PID找到PAT,並從PAT中解出來每個節目所對應的PMT的PID,再根據PID找到所有節目的PMT,然后從每個節目的PMT中解出來當前節目所對應的不同流類型的TS包的PID,根據這些PID來找到對應的TS包,取出原始視頻流,音頻流和其他數據等;打包過程則是相反的;
TS頭里面的PCR字段是基准時間戳,在音視頻解碼顯示的時候,是根據PES頭里面的PTS和DTS字段與其對比,相同就說明該進行解碼和顯示了;PCR字段是在TS的PMT中指定的PID,只有指定的PID的TS包里面的PCR字段才有用,我們打包的時候使用的是視頻的PID中的PCR,只有每幀的第一包TS頭里面才會有PCR,而PES頭里面的PTS和DTS就是視頻和音頻的相對時間戳;測試遇到了音視頻不同步的問題,原因就是TS打包時,PES頭里面的音視頻PTS都用了視頻的時間戳,而我們在TS解析時是對音頻有相對延后的操作,其采用的視頻時間戳相對原來是有可能延后了多個視頻幀的,所以導致音頻有延后;
視頻編解碼概念:時間戳DTS和PTS的相關分析
http://blog.csdn.net/soaringlee_fighting/article/details/70941896
基本概念:
I frame :幀內編碼幀 又稱intra picture,I 幀通常是每個 GOP(MPEG 所使用的一種視頻壓縮技術)的第一個幀,經過適度地壓縮,做為隨機訪問的參考點,可以當成圖象。I幀可以看成是一個圖像經過壓縮后的產物。
P frame: 前向預測編碼幀 又稱predictive-frame,通過充分將低於圖像序列中前面已編碼幀的時間冗余信息來壓縮傳輸數據量的編碼圖像,也叫預測幀;
B frame: 雙向預測內插編碼幀 又稱bi-directional interpolated prediction frame,既考慮與源圖像序列前面已編碼幀,也顧及源圖像序列后面已編碼幀之間的時間冗余信息來壓縮傳輸數據量的編碼圖像,也叫雙向預測幀;
PTS:Presentation Time Stamp。PTS主要用於度量解碼后的視頻幀什么時候被顯示出來
DTS:Decode Time Stamp。DTS主要是標識讀入內存中的bit流在什么時候開始送入解碼器中進行解碼。
在沒有B幀存在的情況下DTS的順序和PTS的順序應該是一樣的。
IPB幀的不同:
I frame:自身可以通過視頻解壓算法解壓成一張單獨的完整的圖片。
P frame:需要參考其前面的一個I frame 或者B frame來生成一張完整的圖片。
B frame:則要參考其前一個I或者P幀及其后面的一個P幀來生成一張完整的圖片。
兩個I frame之間形成一個GOP,在x264中同時可以通過參數來設定bf的大小,即:I 和p或者兩個P之間B的數量。
通過上述基本可以說明如果有B frame 存在的情況下一個GOP的最后一個frame一定是P.
DTS和PTS的不同:
DTS主要用於視頻的解碼,在解碼階段使用.PTS主要用於視頻的同步和輸出.在display的時候使用.在沒有B frame的情況下.DTS和PTS的輸出順序是一樣的.
例子:
下面給出一個GOP為15的例子,其解碼的參照frame及其解碼的順序都在里面:

如上圖:I frame 的解碼不依賴於任何的其它的幀.而p frame的解碼則依賴於其前面的I frame或者P frame.B frame的解碼則依賴於其前的最近的一個I frame或者P frame 及其后的最近的一個P frame.
FFmpeg里有兩種時間戳:DTS(Decoding Time Stamp)和PTS(Presentation Time Stamp)。 顧名思義,前者是解碼的時間,后者是顯示的時間。要仔細理解這兩個概念,需要先了解FFmpeg中的packet和frame的概念。
FFmpeg中用AVPacket結構體來描述解碼前或編碼后的壓縮包,用AVFrame結構體來描述解碼后或編碼前的信號幀。 對於視頻來說,AVFrame就是視頻的一幀圖像。這幀圖像什么時候顯示給用戶,就取決於它的PTS。DTS是AVPacket里的一個成員,表示這個壓縮包應該什么時候被解碼。 如果視頻里各幀的編碼是按輸入順序(也就是顯示順序)依次進行的,那么解碼和顯示時間應該是一致的。可事實上,在大多數編解碼標准(如H.264或HEVC)中,編碼順序和輸入順序並不一致。 於是才會需要PTS和DTS這兩種不同的時間戳。
PTS - Presentation Timestamp,播放的時間戳。
可以簡單地這樣理解:
若視頻沒有B幀,則I和P都是解碼后即刻顯示。
若視頻含有B幀,則I是解碼后即刻顯示,P是先解碼后顯示,B是后解碼先顯示。(B 和P的先、后是相對的)。
TS碼流結構分析
http://blog.163.com/elvis1943@126/blog/static/638916982015615101821857/


ES流(Elementary Stream)由三部分組成:
※經MPEG-2視頻編碼器編碼后的圖像數據流;
※經MPEG-2音頻編碼器編碼后的聲音數據流;
※其他編碼數據流;

PES流(Packetized Elementary Stream):PES流是ES流經過PES打包器處理后形成的數據流,在這個過程中完成了將ES流分組、打包、加入包頭信息等操作(對ES流的第一次打包)。PES流的基本單位是PES包。
※PS流和TS流是MPEG-2系統規范的兩種標准碼流。
※PS流用於相對無錯環境下的傳輸與存儲(如DVD中),其基本單位是PS包,長度可變。
※TS流用於相對有錯環境下的傳輸與存儲(如DVB中),其基本單位是TS包,長度固定188字節。

※ PS流由PS包組成,而一個PS包又由若干個PES包組成(到這里,ES經過了兩層的封裝)。
※ PS包的包頭中包含了同步信息與時鍾恢復信息。
※一個PS包最多可包含具有同一時鍾基准的16個視頻PES包和32個音頻PES包。

※ TS流由定長的TS包組成(188字節),而TS包是對PES包的一個重新封裝(到這里,ES經過了兩層的封裝) 。
※ PES包的包頭信息依然存在於TS包中。
鏈接: https://wenku.baidu.com/view/cd092f03a8956bec0875e337.html
https://my.oschina.net/u/727148/blog/666824
http://blog.sina.com.cn/s/blog_6b94d5680101r5l6.html
http://blog.csdn.net/zxh821112/article/details/17587215
http://www.cnblogs.com/hjj801006/p/3837435.html
http://blog.csdn.net/zxh821112/article/details/17587325

