libdvbpsi 是vlc中的一個解碼程式庫。它能解碼或解析出所有的節目專屬資訊(PSI)以及MPEG2 TS流或DVB流 中的說明符(descriptor)。
目前能解析的PSI/SI表包括( BAT,CAT,EIT,NIT,PAT,PMT,SDT,SIS,TOT,TDT).
BAT:Bouquet Association Table 業務關聯表
CAT:Conditional Access Table 條件接入表
EIT: Event Information Table 事件資訊表(EPG)
NIT: Network Information Table 網路資訊表
PAT: Program Association Table 節目關聯表
PMT: Program Map Table 節目對映表
SDT: Service Description Table 業務說明表
libdvbpsi首頁: http://www.videolan.org/developers/libdvbpsi.html
其中涉及到的DVB規格如下:
系統: ISO/IEC 13818-1
視訊編碼: ISO/IEC 13818-2
聲訊編碼。 ISO/IEC 13818-3
DVB官網:http://www.dvb.org/
此處筆者使用的是version 1.1.1.
libdvbpsi
是
VLC
裡
的一
個輕
便的用
來實現
MPEG-2 TS
流解析和
編碼
的
開
源
程式庫
,可以解析的
PSI/SI
表有:
PAT,PMT,NIT,CAT,SDT,EIT,TOT,
可以解析大部分的
說明
子。
下
載
:
http://www.videolan.org/developers/libdvbpsi.html
。
詳細
文
件
(
API,
檔案
,
架構體說
明等):
http://www.videolan.org/developers/libdvbpsi/doc/doxygen/html/index.html
。
以
libdvbpsi-0.1.7/examplesdecode_pat.c
為
例子,
說
明
libdvbpsi
的大概框架:
1
、
dvbpsi_handle h_dvbpsi = dvbpsi_AttachPAT(DumpPAT, NULL);
指定
資料匯
出的回
調
函
數
,并
建立
一
個
decoder
控點
,
同時
在
這裡
面指定了私有的
decoder
(
h_dvbpsi->p_private_decoder
),和收集
section
的回
調
函
數
(
h_dvbpsi->pf_callback = &dvbpsi_GatherPATSections;
)。
2
、
dvbpsi_PushPacket(h_dvbpsi, data);
這個
函
數
是
libdvbpsi
裡
核心的一
個
函
數
,用
來實現
段(
section
)到包(
packet
)的
對映
,得到一
個
完整的
section
后,通
過
回
調
函
數
把
這個
section
送到私有的
decoder
(
h_dvbpsi->p_private_decoder
)
掛載
上去,直到找到
TS
流中子表中所 有
section
。
它
處
理每一
個
PID
為
0x0000
(
PAT_PID
)的包,
這個
函
數
先是
對
packet
做一些判
斷
,比如
同
步
碼
,
連續計數
,
調
整
欄位
(有的
話
直接
略過
), 然后
搆
建填
補
h_dvbpsi->p_current_section,
當
h_dvbpsi->p_current_section
填
補
完成后,
進
入回
調
函
數
h_dvbpsi->pf_callback
。
h_dvbpsi->pf_callback
這個
回
調
按照我的理解,有
點
像
C++
中的
虛
函
數
,有各
種
Override
版本,以
實現
不
同
的功能。在解 析
PAT
時
在
dvbpsi_AttachPAT(DumpPAT, NULL)
中已
經
指定了
這個
回
調
函
數
:
dvbpsi_GatherPATSections
()。
這個
函
數
先判
斷
h_dvbpsi->p_current_section
是否有效,比如
table_id
欄位
是不是
PAT
表的
TID
等等,之后通
過
version number
和
section number
等
欄位
判
斷
是否
掛載
到
h_dvbpsi->p_private_decoder
上。如果
h_dvbpsi->p_private_decoder
已
經
收集到所有
section,
則呼叫
解析函
數
dvbpsi_DecodePATSections
()
從
section
中解析出
PAT
資訊
。
緊
接
著呼叫
h_dvbpsi->p_private_decoder
的回
調
函
數
(DumpPAT),
匯
出
PAT
資訊
。
3
、
dvbpsi_DetachPAT(h_dvbpsi)
釋
放
記憶體
。
這
是
libdvbpsi
解析
TS
流的一
個
大概的框架,由
於
PAT
子表一般只有一
個
,像
SDT
,
EIT
等有多
個
子表
時
,
h_dvbpsi->pf_callback
就
無法
直接
進
行收集
section
了,必
須
先
dvbpsi_Demux()
,把
section
送到 合
適
的子表
decoder
(
dvbpsi_demux_subdec_t
)中去收集。
剛開始學習MPEG-2 TS流解析時,看ISO/IEC13818-1的文件上面的PAT,PMT表:program_association_section()和 TS_program_map_section()時,很容易就以為可以直接從188位元組的TS packet中取資料填到各個欄位中,網上也可以搜到這樣類似的程式:
[c-sharp] view plaincopy
-
void ParsePat(psi_pat *p_pat, u8 *p_packet)
-
{
-
// declare variables and initial
-
...
-
// get payload_unit_start_indicator
-
b_pusi = p_packet[1] & 0x40;
-
b_adaptation = p_packet[3] & 0x20;
-
-
// calculate the skip to skip to payload field
-
if(b_adaptation)
-
{
-
skip = 5 + p_packet[4];
-
}
-
else
-
{
-
skip = 4;
-
}
-
-
// skip pointer_field
-
if(b_pusi)
-
{
-
skip = skip + p_packet[skip] + 1;
-
}
-
-
// now skip to payload field
-
p_payload = p_packet + skip;
-
-
// now parse table
-
p_pat->table_id = p_payload[0];
-
...
-
}
這樣在解析PAT和PMT表時往往也能得到正確的答案,這是因為PAT,PMT表資料量比對小的緣故。其實在TS流中還有一個重要的概念—— section。文件中program_association_section()和TS_program_map_section()指的就是PAT section和PMT section。它們中都有一個欄位:section_length,文件告訴我們這個欄位值的是此欄位之后section的位元組數,除了EIT section的最大位元組數是4096外,其他section最大位元組數是1024位元組。 而TS packet最大只有188位元組,因此這裡需要考慮section到packet的對映問題,基本上存在三種情況:
1、section對應一個packet
2、section太長,在几個連續的packet中
3、在一個packet的負載中結束上一個section之后馬上開始了一個新的section。
這三種情況可以在libdvbpsi源碼的void dvbpsi_PushPacket(dvbpsi_handle h_dvbpsi, uint8_t* p_data)函數中找到。明白了這三種情況,再對照函數的註釋,應該就不難看懂這個函數了。
[c-sharp] view plaincopy
-
/*****************************************************************************
-
* dvbpsi_PushPacket
-
*****************************************************************************
-
* Injection of a TS packet into a PSI decoder.
-
*****************************************************************************/
-
void dvbpsi_PushPacket(dvbpsi_handle h_dvbpsi, uint8_t* p_data)
-
{
-
uint8_t i_expected_counter; /* Expected continuity counter */
-
dvbpsi_psi_section_t* p_section; /* Current section */
-
uint8_t* p_payload_pos; /* Where in the TS packet */
-
uint8_t* p_new_pos = NULL; /* Beginning of the new section,
-
updated to NULL when the new
-
section is handled */
-
int i_available; /* Byte count available in the
-
packet */
-
/* 本次放進來的packet的負載位元組數 */
-
-
-
/* TS start code */
-
if(p_data[0] != 0x47)
-
{
-
DVBPSI_ERROR("PSI decoder", "not a TS packet");
-
return;
-
}
-
-
/* Continuity check */
-
i_expected_counter = (h_dvbpsi->i_continuity_counter + 1) & 0xf;
-
h_dvbpsi->i_continuity_counter = p_data[3] & 0xf;
-
-
if(i_expected_counter == ((h_dvbpsi->i_continuity_counter + 1) & 0xf)
-
&& !h_dvbpsi->b_discontinuity)
-
{
-
DVBPSI_ERROR_ARG("PSI decoder",
-
"TS duplicate (received %d, expected %d) for PID %d",
-
h_dvbpsi->i_continuity_counter, i_expected_counter,
-
((uint16_t)(p_data[1] & 0x1f) << 8) | p_data[2]);
-
return;
-
}
-
-
if(i_expected_counter != h_dvbpsi->i_continuity_counter)
-
{
-
DVBPSI_ERROR_ARG("PSI decoder",
-
"TS discontinuity (received %d, expected %d) for PID %d",
-
h_dvbpsi->i_continuity_counter, i_expected_counter,
-
((uint16_t)(p_data[1] & 0x1f) << 8) | p_data[2]);
-
h_dvbpsi->b_discontinuity = 1;
-
if(h_dvbpsi->p_current_section)
-
{
-
dvbpsi_DeletePSISections(h_dvbpsi->p_current_section);
-
h_dvbpsi->p_current_section = NULL;
-
}
-
}
-
-
/* Return if no payload in the TS packet */
-
if(!(p_data[3] & 0x10))
-
{
-
return;
-
}
-
-
/* Skip the adaptation_field if present */
-
if(p_data[3] & 0x20)
-
p_payload_pos = p_data + 5 + p_data[4];
-
else
-
p_payload_pos = p_data + 4;
-
-
/* Unit start -> skip the pointer_field and a new section begins */
-
/* payload_unit_start_indicator 為1時不僅有一個pointer_field指標,更意味著一個新的分段section開始 */
-
if(p_data[1] & 0x40)
-
{
-
p_new_pos = p_payload_pos + *p_payload_pos + 1;
-
p_payload_pos += 1;
-
}
-
-
/* 用來判斷上個分段是否結束,為NULL表示上個分段結束了并已被收集 */
-
p_section = h_dvbpsi->p_current_section;
-
-
/* If the psi decoder needs a begginning of section and a new section
-
begins in the packet then initialize the dvbpsi_psi_section_t structure */
-
/* 上個分段結束,一般就開始新的分段(payload_unit_start_indicator為1),否則錯誤直接傳回 */
-
if(p_section == NULL)
-
{
-
if(p_new_pos)
-
{
-
/* Allocation of the structure */
-
h_dvbpsi->p_current_section
-
= p_section
-
= dvbpsi_NewPSISection(h_dvbpsi->i_section_max_size);
-
/* Update the position in the packet */
-
p_payload_pos = p_new_pos;
-
/* New section is being handled */
-
p_new_pos = NULL;
-
/* Just need the header to know how long is the section */
-
/* 開始新的分段,先讀分段的header,到section_length剛好3位元組,主要是要先讀section_length用來確定此分段有多少位元組。 */
-
h_dvbpsi->i_need = 3;
-
h_dvbpsi->b_complete_header = 0;
-
}
-
else
-
{
-
/* No new section => return */
-
return;
-
}
-
}
-
-
/* Remaining bytes in the payload */
-
/* p_payload_pos = p_data + offset, 其中offset是section包頭的長度 */
-
/* 所以,i_available = 188 - offset */
-
i_available = 188 + p_data - p_payload_pos;
-
-
while(i_available > 0)
-
{
-
/* h_dvbpsi->i_need 用來記錄該分段還差多少個位元組才結束分段 */
-
/* 此包的負載足夠填補目前分段,需要考慮:
-
* 1、如果已經填補過分段的header,則將此包的負載追加到分段資料的末尾即結束此分段,并呼叫h_dvbpsi->pf_callback收集目前section以完成一個子表
-
* 這個時候還要判斷,如果此包的負載還有剩餘,并且不是填補欄位(0xff)則意味著直接開始了一個新的分段。
-
* 2、如果還沒有填補過分段的header,則解析段的header,主要是section_length欄位用來更新h_dvbpsi->i_need */
-
if(i_available >= h_dvbpsi->i_need)
-
{
-
/* There are enough bytes in this packet to complete the
-
header/section */
-
memcpy(p_section->p_payload_end, p_payload_pos, h_dvbpsi->i_need);
-
p_payload_pos += h_dvbpsi->i_need;
-
p_section->p_payload_end += h_dvbpsi->i_need;
-
i_available -= h_dvbpsi->i_need;
-
-
if(!h_dvbpsi->b_complete_header)
-
{
-
/* Header is complete */
-
h_dvbpsi->b_complete_header = 1;
-
/* Compute p_section->i_length and update h_dvbpsi->i_need */
-
/* 分段已經讀了3個位元組也即讀到section_length欄位,恰恰還需要section_length個位元組 */
-
h_dvbpsi->i_need = p_section->i_length
-
= ((uint16_t)(p_section->p_data[1] & 0xf)) << 8
-
| p_section->p_data[2];
-
/* Check that the section isn't too long */
-
if(h_dvbpsi->i_need > h_dvbpsi->i_section_max_size - 3)
-
{
-
DVBPSI_ERROR("PSI decoder", "PSI section too long");
-
dvbpsi_DeletePSISections(p_section);
-
h_dvbpsi->p_current_section = NULL;
-
/* If there is a new section not being handled then go forward
-
in the packet */
-
if(p_new_pos)
-
{
-
h_dvbpsi->p_current_section
-
= p_section
-
= dvbpsi_NewPSISection(h_dvbpsi->i_section_max_size);
-
p_payload_pos = p_new_pos;
-
p_new_pos = NULL;
-
h_dvbpsi->i_need = 3;
-
h_dvbpsi->b_complete_header = 0;
-
i_available = 188 + p_data - p_payload_pos;
-
}
-
else
-
{
-
i_available = 0;
-
}
-
}
-
}
-
else /* 分段准備結束并呼叫回調函數收集section */
-
{
-
/* PSI section is complete */
-
p_section->b_syntax_indicator = p_section->p_data[1] & 0x80;
-
p_section->b_private_indicator = p_section->p_data[1] & 0x40;
-
/* Update the end of the payload if CRC_32 is present */
-
if(p_section->b_syntax_indicator)
-
p_section->p_payload_end -= 4;
-
-
/* 主要是CRC校驗 */
-
if(p_section->p_data[0] != 0x72 && dvbpsi_ValidPSISection(p_section))
-
{
-
/* PSI section is valid */
-
p_section->i_table_id = p_section->p_data[0];
-
if(p_section->b_syntax_indicator)
-
{
-
p_section->i_extension = (p_section->p_data[3] << 8)
-
| p_section->p_data[4];
-
p_section->i_version = (p_section->p_data[5] & 0x3e) >> 1;
-
p_section->b_current_next = p_section->p_data[5] & 0x1;
-
p_section->i_number = p_section->p_data[6];
-
p_section->i_last_number = p_section->p_data[7];
-
p_section->p_payload_start = p_section->p_data + 8;
-
}
-
else
-
{
-
p_section->i_extension = 0;
-
p_section->i_version = 0;
-
p_section->b_current_next = 1;
-
p_section->i_number = 0;
-
p_section->i_last_number = 0;
-
p_section->p_payload_start = p_section->p_data + 3;
-
}
-
-
/* 收集目前section */
-
h_dvbpsi->pf_callback(h_dvbpsi, p_section);
-
/* 結束目前section */
-
h_dvbpsi->p_current_section = NULL;
-
}
-
else
-
{
-
/* PSI section isn't valid => trash it */
-
dvbpsi_DeletePSISections(p_section);
-
h_dvbpsi->p_current_section = NULL;
-
}
-
-
/* A TS packet may contain any number of sections, only the first
-
* new one is flagged by the pointer_field. If the next payload
-
* byte isn't 0xff then a new section starts. */
-
/* 負載還有剩餘,并且不是填補欄位,則新的分段開始了,這種情況就是一個packet中有2個分段。 */
-
if(p_new_pos == NULL && i_available && *p_payload_pos != 0xff)
-
p_new_pos = p_payload_pos;
-
-
/* If there is a new section not being handled then go forward
-
in the packet */
-
if(p_new_pos)
-
{
-
h_dvbpsi->p_current_section
-
= p_section
-
= dvbpsi_NewPSISection(h_dvbpsi->i_section_max_size);
-
p_payload_pos = p_new_pos;
-
p_new_pos = NULL;
-
h_dvbpsi->i_need = 3;
-
h_dvbpsi->b_complete_header = 0;
-
i_available = 188 + p_data - p_payload_pos;
-
}
-
else
-
{
-
i_available = 0;
-
}
-
}
-
}
-
/* 此包的負載不夠填補目前分段,則直接把此包的負載追加到目前分段的分段資料末尾上。
-
* 這種情況就是一個分段在多個packet中。 */
-
else
-
{
-
/* There aren't enough bytes in this packet to complete the
-
header/section */
-
memcpy(p_section->p_payload_end, p_payload_pos, i_available);
-
p_section->p_payload_end += i_available;
-
h_dvbpsi->i_need -= i_available;
-
i_available = 0;
-
}
-
}
-
}
由上一篇 libdvbpsi源码分析(二)main函数,簡單解析了demo程式中main函數的執行流程。現在將對具體的PSI表作詳細解析。主要是對main函數中的libdvbpsi_init和dvbpsi_new以及相關的dvbpsi_pat_attach作相關解析。
1.建立PSI decoders
1 |
|
將每張table表的資料各自抽象成specific decoder(ts_xxx_t),最后封裝成通用的universal decoder即:ts_stream_t
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 33 34 35 36 37 38 |
|
其中,libdvbpsi_init建立了一個整體的decoder,它由各個具體的specific decoder搆成。程式碼展開如下:
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 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
|
2.PAT表的解析
由於每個表的具體解析流程都是相似的,所以選取其中一個做典型解析。比如PAT。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
2.1.pat表的sepecfic decoder的抽象(也就是它的struct設計)
ts_pat_t:
1 2 3 4 5 6 7 8 9 |
|
dvbpsi_t:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
dvbpsi_decoder_t:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
dvbpsi_psi_section_t:
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 |
|
dvbpsi_psi_section_t架構體的設計,是根據標准ISO/IEC 13818-1 section 2.4.4.11協定如下表 private section所示:
table1: private section
由上述可知,用物件導向的思想來看,其繼承關係是:
dvbpsi_psi_section_t<--dvbpsi_pat_decoder_t<--dvbpsi_decoder_t<--dvbpsi_t<--ts_pat_t<--ts_stream_t.
由上一章 libdvbpsi源码分析(三)PSI decocder详细分析,我們知道了psi decoder的搆建過程。本章將延續上文 以PAT表詳細解析為例,由點及面的簡介libdvbpsi的實現。
下面詳細解析pat decoder解碼器的建立過程:
a、首先通過dvbpsi_new建立一個通用的decoder,但還未真正的實例化為pat decoder,即pat decoder還未起始化。
dvbpsi_t *dvbpsi_new(dvbpsi_message_cb callback, enum dvbpsi_msg_level level)
{
dvbpsi_t *p_dvbpsi = calloc(1, sizeof(dvbpsi_t));
if (p_dvbpsi == NULL)
return NULL;
p_dvbpsi->p_decoder = NULL; //賦值為NULL,pat decoder還未起始化
p_dvbpsi->pf_message = callback;
p_dvbpsi->i_msg_level = level;
return p_dvbpsi;
}
b、起始化PAT decoder 并且連結pat表的解析handle動作。其中dvbpsi_pat_attach中dvbpsi_decoder_new建立了 真正的pat decoder實例,并通過p_dvbpsi->p_decoder = DVBPSI_DECODER(p_pat_decoder)將其賦值給了抽象的decoder。
bool dvbpsi_pat_attach(dvbpsi_t *p_dvbpsi, dvbpsi_pat_callback pf_callback, void* p_cb_data)
{
assert(p_dvbpsi);
assert(p_dvbpsi->p_decoder == NULL);
/* PSI decoder configuration and initial state */
dvbpsi_pat_decoder_t *p_pat_decoder;
/*建立真正的pat decoder*/
p_pat_decoder = (dvbpsi_pat_decoder_t*) dvbpsi_decoder_new(&dvbpsi_pat_sections_gather,
1024, true, sizeof(dvbpsi_pat_decoder_t));
if (p_pat_decoder == NULL)
return false;
/* PAT decoder information */
p_pat_decoder->pf_pat_callback = pf_callback;
p_pat_decoder->p_cb_data = p_cb_data;
p_pat_decoder->p_building_pat = NULL;
/*將pat decoder賦值給p_decoder(類別的強轉dvbpsi_pat_decoder_t* 轉成dvbpsi_decoder_t*)*/
p_dvbpsi->p_decoder = DVBPSI_DECODER(p_pat_decoder);
return true;
}
【總結a,b】
dvbpsi_new是一個介面,建立抽象的decoder。但具體的要建立哪種specific decoder需要caller自己去起始化,賦予其實例化的屬性。通過以下介面去實例化decoder。每個表的解析動作放置在tables/目錄下的 各個.c檔案中。
{
dvbpsi_pat_attach,(tables/pat.c)
dvbpsi_bat_attach,(tables/bat.c)
...
}
c、PAT表中section的聚集
建立pat decoder實例時,傳入了callback函數dvbpsi_pat_sections_gather, 用於gather PAT表中的所有section。 只有等待表中所有section收集完整并進行CRC校驗后,才開始進行表的解析或重建。
void dvbpsi_pat_sections_gather(dvbpsi_t* p_dvbpsi, dvbpsi_psi_section_t* p_section)
{
dvbpsi_pat_decoder_t* p_pat_decoder;
assert(p_dvbpsi);
assert(p_dvbpsi->p_decoder);
if (!dvbpsi_CheckPSISection(p_dvbpsi, p_section, 0x00, "PAT decoder"))
{
dvbpsi_DeletePSISections(p_section);
return;
}
/* Now we have a valid PAT section */
p_pat_decoder = (dvbpsi_pat_decoder_t *)p_dvbpsi->p_decoder;
/* TS discontinuity check */
if (p_pat_decoder->b_discontinuity)
{
dvbpsi_ReInitPAT(p_pat_decoder, true);
p_pat_decoder->b_discontinuity = false;
}
else
{
if (p_pat_decoder->p_building_pat)
{
if (dvbpsi_CheckPAT(p_dvbpsi, p_section))
dvbpsi_ReInitPAT(p_pat_decoder, true);
}
else
{
if( (p_pat_decoder->b_current_valid)
&& (p_pat_decoder->current_pat.i_version == p_section->i_version)
&& (p_pat_decoder->current_pat.b_current_next ==
p_section->b_current_next))
{
/* Don't decode since this version is already decoded */
dvbpsi_debug(p_dvbpsi, "PAT decoder",
"ignoring already decoded section %d",
p_section->i_number);
dvbpsi_DeletePSISections(p_section);
return;
}
}
}
/* Add section to PAT */
if (!dvbpsi_AddSectionPAT(p_dvbpsi, p_pat_decoder, p_section))
{
dvbpsi_error(p_dvbpsi, "PAT decoder", "failed decoding section %d",
p_section->i_number);
dvbpsi_DeletePSISections(p_section);
return;
}
/* Check if we have all the sections */
if (dvbpsi_decoder_psi_sections_completed(DVBPSI_DECODER(p_pat_decoder)))
{
assert(p_pat_decoder->pf_pat_callback);
/* Save the current information */
p_pat_decoder->current_pat = *p_pat_decoder->p_building_pat;
p_pat_decoder->b_current_valid = true;
/* Decode the sections */
dvbpsi_pat_sections_decode(p_pat_decoder->p_building_pat,
p_pat_decoder->p_sections);
/* Delete the sections */
dvbpsi_DeletePSISections(p_pat_decoder->p_sections);
p_pat_decoder->p_sections = NULL;
/* signal the new PAT */
p_pat_decoder->pf_pat_callback(p_pat_decoder->p_cb_data,
p_pat_decoder->p_building_pat);
/* Reinitialize the structures */
dvbpsi_ReInitPAT(p_pat_decoder, false);
}
}
d、PAT表及PMT表的解析
緊接著對pat table進行解析或重建。 解析的動作實質就是連結到pat decoder的handle(callback)。
(兩者含義其實一樣,知道了table表的所有細節,可以說成解析了table,也可以說成重建了table)
PAT表的協定根據標准 ISO/IEC 13818-1 section 2.4.4.3如下表所示:
源碼解析如下:
/*****************************************************************************
* handle_PAT
*****************************************************************************/
static void handle_PAT(void* p_data, dvbpsi_pat_t* p_pat)
{
dvbpsi_pat_program_t* p_program = p_pat->p_first_program;
ts_stream_t* p_stream = (ts_stream_t*) p_data;
p_stream->pat.i_pat_version = p_pat->i_version;
p_stream->pat.i_ts_id = p_pat->i_ts_id;
printf("\n");
printf(" PAT: Program Association Table\n");
printf("\tTransport stream id : %d\n", p_pat->i_ts_id);
printf("\tVersion number : %d\n", p_pat->i_version);
printf("\tCurrent next : %s\n", p_pat->b_current_next ? "yes" : "no");
if (p_stream->pat.pid->i_prev_received > 0)
printf("\tLast received : %"PRId64" ms ago\n",
(mtime_t)(p_stream->pat.pid->i_received - p_stream->pat.pid->i_prev_received));
printf("\t\t| program_number @ [NIT|PMT]_PID\n");
while (p_program)
{
/* Attach new PMT decoder */
ts_pmt_t *p_pmt = calloc(1, sizeof(ts_pmt_t));
if (p_pmt)
{
/* PMT */
p_pmt->handle = dvbpsi_new(&dvbpsi_message, p_stream->level);
if (p_pmt->handle == NULL)
{
fprintf(stderr, "dvbinfo: Failed attach new PMT decoder\n");
free(p_pmt);
break;
}
p_pmt->i_number = p_program->i_number;
p_pmt->pid_pmt = &p_stream->pid[p_program->i_pid];
p_pmt->pid_pmt->i_pid = p_program->i_pid;
p_pmt->p_next = NULL;
/*建立PMT Decoder*/
if (!dvbpsi_pmt_attach(p_pmt->handle, p_program->i_number, handle_PMT, p_stream))
{
fprintf(stderr, "dvbinfo: Failed to attach new pmt decoder\n");
break;
}
/* insert at start of list */
p_pmt->p_next = p_stream->pmt;
p_stream->pmt = p_pmt;
p_stream->i_pmt++;
assert(p_stream->pmt);
}
else
fprintf(stderr, "dvbinfo: Failed create new PMT decoder\n");
printf("\t\t| %14d @ pid: 0x%x (%d)\n",
p_program->i_number, p_program->i_pid, p_program->i_pid);
p_program = p_program->p_next;
}
printf("\tActive : %s\n", p_pat->b_current_next ? "yes" : "no");
dvbpsi_pat_delete(p_pat);
}
PAT表中含有PMT表的節目號,所以需要解析PMT表。
PMT表的協定根據標准ISO/IEC 13818-1 section 2.4.4.8如下表所示:
源碼解析如下:
/*****************************************************************************
* handle_PMT
*****************************************************************************/
static void handle_PMT(void* p_data, dvbpsi_pmt_t* p_pmt)
{
dvbpsi_pmt_es_t* p_es = p_pmt->p_first_es;
ts_stream_t* p_stream = (ts_stream_t*) p_data;
/* Find signalled PMT */
ts_pmt_t *p = p_stream->pmt;
while (p)
{
if (p->i_number == p_pmt->i_program_number)
break;
p = p->p_next;
}
assert(p);
p->i_pmt_version = p_pmt->i_version;
p->pid_pcr = &p_stream->pid[p_pmt->i_pcr_pid];
p_stream->pid[p_pmt->i_pcr_pid].b_pcr = true;
printf("\n");
printf(" PMT: Program Map Table\n");
printf("\tProgram number : %d\n", p_pmt->i_program_number);
printf("\tVersion number : %d\n", p_pmt->i_version);
printf("\tPCR_PID : 0x%x (%d)\n", p_pmt->i_pcr_pid, p_pmt->i_pcr_pid);
printf("\tCurrent next : %s\n", p_pmt->b_current_next ? "yes" : "no");
DumpDescriptors("\t ]", p_pmt->p_first_descriptor);
printf("\t| type @ elementary_PID : Description\n");
while(p_es)
{
printf("\t| 0x%02x @ pid 0x%x (%d): %s\n",
p_es->i_type, p_es->i_pid, p_es->i_pid,
GetTypeName(p_es->i_type) );
DumpDescriptors("\t| ]", p_es->p_first_descriptor);
p_es = p_es->p_next;
}
dvbpsi_pmt_delete(p_pmt);
}
【小結】至此,PAT表的解析解析完成了。具體在程式碼除錯過程中,可借助pes碼流解析工具,詳細的檢視各個表的資訊,并結合dvb規格,詳細查詢每個descriptor的說明,完成解碼工作。
http://my.oschina.net/mavericsoung/blog/175574