close

libdvbpsi vlc中的一個解碼程式庫。它能解碼或解析出所有的節目專屬資訊(PSI)以及MPEG2 TS流或DVB 中的說明符(descriptor)
目前能解析的PSI/SI表包括( BAT,CAT,EIT,NIT,PAT,PMT,SDT,SIS,TOT,TDT).

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

 

 

 

  libdvbpsiVLC的一個輕便的用來實現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的大概框架:

 

1dvbpsi_handle h_dvbpsi = dvbpsi_AttachPAT(DumpPAT, NULL);

    指定資料匯出的回調,并建立decoder控點同時這裡面指定了私有的 decoderh_dvbpsi->p_private_decoder),和收集section的回調h_dvbpsi->pf_callback = &dvbpsi_GatherPATSections;)。

 

2dvbpsi_PushPacket(h_dvbpsi, data);

    這個libdvbpsi核心的一,用來實現段(section)到包(packet)的對映,得到一完整的section后,通調這個section送到私有的decoderh_dvbpsi->p_private_decoder掛載上去,直到找到TS流中子表中所 有section

 

    理每一PID0x0000PAT_PID)的包,這個先是packet做一些判,比如連續計數調欄位(有的直接略過), 然后建填h_dvbpsi->p_current_section, h_dvbpsi->p_current_section完成后,入回調h_dvbpsi->pf_callback

 

    h_dvbpsi->pf_callback這個調按照我的理解,有C++中的,有各Override版本,以實現的功能。在解 析PATdvbpsi_AttachPAT(DumpPAT, NULL)中已指定了這個調dvbpsi_GatherPATSections()。這個先判 h_dvbpsi->p_current_section是否有效,比如table_id欄位是不是PAT表的TID等等,之后通version numbersection number欄位是否掛載h_dvbpsi->p_private_decoder上。如果 h_dvbpsi->p_private_decoder收集到所有section,則呼叫解析函 dvbpsi_DecodePATSections()section中解析出PAT資訊著呼叫 h_dvbpsi->p_private_decoder的回調(DumpPAT),PAT資訊

   

3dvbpsi_DetachPAT(h_dvbpsi)

    記憶體

 

    libdvbpsi解析TS流的一大概的框架,由PAT子表一般只有一,像SDTEIT等有多子表 h_dvbpsi->pf_callback無法直接行收集section了,必dvbpsi_Demux(),把section送到 合的子表decoderdvbpsi_demux_subdec_t)中去收集。 

 

     剛開始學習MPEG-2 TS流解析時,看ISO/IEC13818-1的文件上面的PATPMT表:program_association_section()TS_program_map_section()時,很容易就以為可以直接從188位元組的TS packet中取資料填到各個欄位中,網上也可以搜到這樣類似的程式:

[c-sharp] view plaincopy

  1. void ParsePat(psi_pat *p_pat, u8 *p_packet)  

  2. {  

  3.  // declare variables and initial  

  4.  ...  

  5.  // get payload_unit_start_indicator   

  6.  b_pusi = p_packet[1] & 0x40;  

  7.  b_adaptation = p_packet[3] & 0x20;  

  8.   

  9.  // calculate the skip to skip to payload field  

  10.  if(b_adaptation)  

  11.  {  

  12.   skip = 5 + p_packet[4];  

  13.  }  

  14.  else  

  15.  {  

  16.   skip = 4;  

  17.  }  

  18.   

  19.  // skip pointer_field  

  20.  if(b_pusi)  

  21.  {  

  22.   skip = skip + p_packet[skip] + 1;  

  23.  }  

  24.   

  25.  // now skip to payload field  

  26.  p_payload = p_packet + skip;  

  27.   

  28.  // now parse table  

  29.  p_pat->table_id = p_payload[0];  

  30.  ...  

  31. }  

    

        這樣在解析PATPMT表時往往也能得到正確的答案,這是因為PATPMT表資料量比對小的緣故。其實在TS流中還有一個重要的概念—— section。文件中program_association_section()TS_program_map_section()指的就是PAT sectionPMT section。它們中都有一個欄位:section_length,文件告訴我們這個欄位值的是此欄位之后section的位元組數,除了EIT section的最大位元組數是4096外,其他section最大位元組數是1024位元組。 而TS packet最大只有188位元組,因此這裡需要考慮sectionpacket的對映問題,基本上存在三種情況:

1section對應一個packet

2section太長,在几個連續的packet

3、在一個packet的負載中結束上一個section之后馬上開始了一個新的section

這三種情況可以在libdvbpsi源碼的void dvbpsi_PushPacket(dvbpsi_handle h_dvbpsi, uint8_t* p_data)函數中找到。明白了這三種情況,再對照函數的註釋,應該就不難看懂這個函數了。

 

[c-sharp] view plaincopy

  1. /***************************************************************************** 

  2.  * dvbpsi_PushPacket 

  3.  ***************************************************************************** 

  4.  * Injection of a TS packet into a PSI decoder. 

  5.  *****************************************************************************/  

  6. void dvbpsi_PushPacket(dvbpsi_handle h_dvbpsi, uint8_t* p_data)  

  7. {  

  8.   uint8_t i_expected_counter;           /* Expected continuity counter */  

  9.   dvbpsi_psi_section_t* p_section;      /* Current section */  

  10.   uint8_t* p_payload_pos;               /* Where in the TS packet */  

  11.   uint8_t* p_new_pos = NULL;            /* Beginning of the new section, 

  12.                                            updated to NULL when the new 

  13.                                            section is handled */  

  14.   int i_available;                      /* Byte count available in the 

  15.                                            packet */  

  16.                                         /* 本次放進來的packet的負載位元組數 */  

  17.   

  18.   

  19.   /* TS start code */  

  20.   if(p_data[0] != 0x47)  

  21.   {  

  22.     DVBPSI_ERROR("PSI decoder", "not a TS packet");  

  23.     return;  

  24.   }  

  25.   

  26.   /* Continuity check */  

  27.   i_expected_counter = (h_dvbpsi->i_continuity_counter + 1) & 0xf;  

  28.   h_dvbpsi->i_continuity_counter = p_data[3] & 0xf;  

  29.   

  30.   if(i_expected_counter == ((h_dvbpsi->i_continuity_counter + 1) & 0xf)  

  31.       && !h_dvbpsi->b_discontinuity)  

  32.   {  

  33.     DVBPSI_ERROR_ARG("PSI decoder",  

  34.                      "TS duplicate (received %d, expected %d) for PID %d",  

  35.                      h_dvbpsi->i_continuity_counter, i_expected_counter,  

  36.                      ((uint16_t)(p_data[1] & 0x1f) << 8) | p_data[2]);  

  37.     return;  

  38.   }  

  39.   

  40.   if(i_expected_counter != h_dvbpsi->i_continuity_counter)  

  41.   {  

  42.     DVBPSI_ERROR_ARG("PSI decoder",  

  43.                      "TS discontinuity (received %d, expected %d) for PID %d",  

  44.                      h_dvbpsi->i_continuity_counter, i_expected_counter,  

  45.                      ((uint16_t)(p_data[1] & 0x1f) << 8) | p_data[2]);  

  46.     h_dvbpsi->b_discontinuity = 1;  

  47.     if(h_dvbpsi->p_current_section)  

  48.     {  

  49.       dvbpsi_DeletePSISections(h_dvbpsi->p_current_section);  

  50.       h_dvbpsi->p_current_section = NULL;  

  51.     }  

  52.   }  

  53.   

  54.   /* Return if no payload in the TS packet */  

  55.   if(!(p_data[3] & 0x10))  

  56.   {  

  57.     return;  

  58.   }  

  59.   

  60.   /* Skip the adaptation_field if present */  

  61.   if(p_data[3] & 0x20)  

  62.     p_payload_pos = p_data + 5 + p_data[4];  

  63.   else  

  64.     p_payload_pos = p_data + 4;  

  65.   

  66.   /* Unit start -> skip the pointer_field and a new section begins */  

  67.   /* payload_unit_start_indicator 1時不僅有一個pointer_field指標,更意味著一個新的分段section開始 */  

  68.   if(p_data[1] & 0x40)  

  69.   {  

  70.     p_new_pos = p_payload_pos + *p_payload_pos + 1;  

  71.     p_payload_pos += 1;  

  72.   }  

  73.   

  74.   /* 用來判斷上個分段是否結束,為NULL表示上個分段結束了并已被收集 */  

  75.   p_section = h_dvbpsi->p_current_section;  

  76.   

  77.   /* If the psi decoder needs a begginning of section and a new section 

  78.      begins in the packet then initialize the dvbpsi_psi_section_t structure */  

  79.   /* 上個分段結束,一般就開始新的分段(payload_unit_start_indicator1),否則錯誤直接傳回 */  

  80.   if(p_section == NULL)  

  81.   {  

  82.     if(p_new_pos)  

  83.     {  

  84.       /* Allocation of the structure */  

  85.       h_dvbpsi->p_current_section  

  86.                         = p_section  

  87.                         = dvbpsi_NewPSISection(h_dvbpsi->i_section_max_size);  

  88.       /* Update the position in the packet */  

  89.       p_payload_pos = p_new_pos;  

  90.       /* New section is being handled */  

  91.       p_new_pos = NULL;  

  92.       /* Just need the header to know how long is the section */  

  93.       /* 開始新的分段,先讀分段的header,到section_length剛好3位元組,主要是要先讀section_length用來確定此分段有多少位元組。 */  

  94.       h_dvbpsi->i_need = 3;  

  95.       h_dvbpsi->b_complete_header = 0;  

  96.     }  

  97.     else  

  98.     {  

  99.       /* No new section => return */  

  100.       return;  

  101.     }  

  102.   }  

  103.   

  104.   /* Remaining bytes in the payload */  

  105.   /* p_payload_pos = p_data + offset, 其中offsetsection包頭的長度 */  

  106.   /* 所以,i_available = 188 - offset */  

  107.   i_available = 188 + p_data - p_payload_pos;  

  108.   

  109.   while(i_available > 0)  

  110.   {  

  111.     /* h_dvbpsi->i_need 用來記錄該分段還差多少個位元組才結束分段 */  

  112.     /* 此包的負載足夠填補目前分段,需要考慮: 

  113.       * 1、如果已經填補過分段的header,則將此包的負載追加到分段資料的末尾即結束此分段,并呼叫h_dvbpsi->pf_callback收集目前section以完成一個子表  

  114.       *     這個時候還要判斷,如果此包的負載還有剩餘,并且不是填補欄位(0xff)則意味著直接開始了一個新的分段。 

  115.       * 2、如果還沒有填補過分段的header,則解析段的header,主要是section_length欄位用來更新h_dvbpsi->i_need */  

  116.     if(i_available >= h_dvbpsi->i_need)  

  117.     {  

  118.       /* There are enough bytes in this packet to complete the 

  119.          header/section */  

  120.       memcpy(p_section->p_payload_end, p_payload_pos, h_dvbpsi->i_need);  

  121.       p_payload_pos += h_dvbpsi->i_need;  

  122.       p_section->p_payload_end += h_dvbpsi->i_need;  

  123.       i_available -= h_dvbpsi->i_need;  

  124.   

  125.       if(!h_dvbpsi->b_complete_header)  

  126.       {  

  127.         /* Header is complete */  

  128.         h_dvbpsi->b_complete_header = 1;  

  129.         /* Compute p_section->i_length and update h_dvbpsi->i_need */  

  130.         /* 分段已經讀了3個位元組也即讀到section_length欄位,恰恰還需要section_length個位元組 */  

  131.         h_dvbpsi->i_need = p_section->i_length  

  132.                          =   ((uint16_t)(p_section->p_data[1] & 0xf)) << 8  

  133.                            | p_section->p_data[2];  

  134.         /* Check that the section isn't too long */  

  135.         if(h_dvbpsi->i_need > h_dvbpsi->i_section_max_size - 3)  

  136.         {  

  137.           DVBPSI_ERROR("PSI decoder", "PSI section too long");  

  138.           dvbpsi_DeletePSISections(p_section);  

  139.           h_dvbpsi->p_current_section = NULL;  

  140.           /* If there is a new section not being handled then go forward 

  141.              in the packet */  

  142.           if(p_new_pos)  

  143.           {  

  144.             h_dvbpsi->p_current_section  

  145.                         = p_section  

  146.                         = dvbpsi_NewPSISection(h_dvbpsi->i_section_max_size);  

  147.             p_payload_pos = p_new_pos;  

  148.             p_new_pos = NULL;  

  149.             h_dvbpsi->i_need = 3;  

  150.             h_dvbpsi->b_complete_header = 0;  

  151.             i_available = 188 + p_data - p_payload_pos;  

  152.           }  

  153.           else  

  154.           {  

  155.             i_available = 0;  

  156.           }  

  157.         }  

  158.       }  

  159.       else /* 分段准備結束并呼叫回調函數收集section */  

  160.       {  

  161.         /* PSI section is complete */  

  162.         p_section->b_syntax_indicator = p_section->p_data[1] & 0x80;  

  163.         p_section->b_private_indicator = p_section->p_data[1] & 0x40;  

  164.         /* Update the end of the payload if CRC_32 is present */  

  165.         if(p_section->b_syntax_indicator)  

  166.           p_section->p_payload_end -= 4;  

  167.   

  168.         /* 主要是CRC校驗 */  

  169.         if(p_section->p_data[0] != 0x72 && dvbpsi_ValidPSISection(p_section))  

  170.         {  

  171.           /* PSI section is valid */  

  172.           p_section->i_table_id = p_section->p_data[0];  

  173.           if(p_section->b_syntax_indicator)  

  174.           {  

  175.             p_section->i_extension =   (p_section->p_data[3] << 8)  

  176.                                      | p_section->p_data[4];  

  177.             p_section->i_version = (p_section->p_data[5] & 0x3e) >> 1;  

  178.             p_section->b_current_next = p_section->p_data[5] & 0x1;  

  179.             p_section->i_number = p_section->p_data[6];  

  180.             p_section->i_last_number = p_section->p_data[7];  

  181.             p_section->p_payload_start = p_section->p_data + 8;  

  182.           }  

  183.           else  

  184.           {  

  185.             p_section->i_extension = 0;  

  186.             p_section->i_version = 0;  

  187.             p_section->b_current_next = 1;  

  188.             p_section->i_number = 0;  

  189.             p_section->i_last_number = 0;  

  190.             p_section->p_payload_start = p_section->p_data + 3;  

  191.           }  

  192.   

  193.           /* 收集目前section */  

  194.           h_dvbpsi->pf_callback(h_dvbpsi, p_section);  

  195.           /* 結束目前section */  

  196.           h_dvbpsi->p_current_section = NULL;  

  197.         }  

  198.         else  

  199.         {  

  200.           /* PSI section isn't valid => trash it */  

  201.           dvbpsi_DeletePSISections(p_section);  

  202.           h_dvbpsi->p_current_section = NULL;  

  203.         }  

  204.   

  205.         /* A TS packet may contain any number of sections, only the first 

  206.          * new one is flagged by the pointer_field. If the next payload 

  207.          * byte isn't 0xff then a new section starts. */  

  208.         /* 負載還有剩餘,并且不是填補欄位,則新的分段開始了,這種情況就是一個packet中有2個分段。 */   

  209.         if(p_new_pos == NULL && i_available && *p_payload_pos != 0xff)  

  210.           p_new_pos = p_payload_pos;  

  211.   

  212.         /* If there is a new section not being handled then go forward 

  213.            in the packet */  

  214.         if(p_new_pos)  

  215.         {  

  216.           h_dvbpsi->p_current_section  

  217.                         = p_section  

  218.                         = dvbpsi_NewPSISection(h_dvbpsi->i_section_max_size);  

  219.           p_payload_pos = p_new_pos;  

  220.           p_new_pos = NULL;  

  221.           h_dvbpsi->i_need = 3;  

  222.           h_dvbpsi->b_complete_header = 0;  

  223.           i_available = 188 + p_data - p_payload_pos;  

  224.         }  

  225.         else  

  226.         {  

  227.           i_available = 0;  

  228.         }  

  229.       }  

  230.     }  

  231.     /* 此包的負載不夠填補目前分段,則直接把此包的負載追加到目前分段的分段資料末尾上。    

  232.      這種情況就是一個分段在多個packet中。 */  

  233.     else  

  234.     {  

  235.       /* There aren't enough bytes in this packet to complete the 

  236.          header/section */  

  237.       memcpy(p_section->p_payload_end, p_payload_pos, i_available);  

  238.       p_section->p_payload_end += i_available;  

  239.       h_dvbpsi->i_need -= i_available;  

  240.       i_available = 0;  

  241.     }  

  242.   }  

  243. }  

    由上一篇 libdvbpsi源码分析()main函数,簡單解析了demo程式中main函數的執行流程。現在將對具體的PSI表作詳細解析。主要是對main函數中的libdvbpsi_initdvbpsi_new以及相關的dvbpsi_pat_attach作相關解析。
          1.
建立PSI decoders

?

1

ts_stream_t *libdvbpsi_init(int debug, ts_stream_log_cb pf_log, void *cb_data)

將每張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

struct ts_stream_t

{

    /* Program Association Table */

    ts_pat_t    pat;

 

    /* Program Map Table */

    int         i_pmt;

    ts_pmt_t    *pmt;

 

    /* Conditional Access Table */

    ts_cat_t    cat;

 

#ifdef TS_USE_SCTE_SIS

    /* Splice Information Section */

    ts_sis_t    sis;

#endif

 

    ts_rst_t    rst;

 

    /* Subbtables */

    ts_sdt_t    sdt;

    ts_eit_t    eit;

    ts_tdt_t    tdt;

 

    /* pid */

    ts_pid_t    pid[8192];

 

    enum dvbpsi_msg_level level;

 

    /* statistics */

    uint64_t    i_packets;

    uint64_t    i_null_packets;

    uint64_t    i_lost_bytes;

 

    /* logging */

    ts_stream_log_cb pf_log;

    void *cb_data;

};

其中,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

ts_stream_t *libdvbpsi_init(int debug, ts_stream_log_cb pf_log, void *cb_data)

{

    ts_stream_t *stream = (ts_stream_t *)calloc(1, sizeof(ts_stream_t));

    if (stream == NULL)

        return NULL;

 

    if (pf_log)

    {

        stream->pf_log = pf_log;

        stream->cb_data = cb_data;

    }

 

    /* print PSI tables debug anyway, unless no debug is wanted at all */

    switch (debug)

    {

        case 0: stream->level = DVBPSI_MSG_NONE; break;

        case 1: stream->level = DVBPSI_MSG_ERROR; break;

        case 2: stream->level = DVBPSI_MSG_WARN; break;

        case 3: stream->level = DVBPSI_MSG_DEBUG; break;

    }

 

    /* PAT */

    /*建立dvbpsi handle structure*/

    stream->pat.handle = dvbpsi_new(&dvbpsi_message, stream->level);

    if (stream->pat.handle == NULL)

        goto error;

    /*始化PAT decoder 并且handle_PAT(callback) 連結pat decoder*/

    if (!dvbpsi_pat_attach(stream->pat.handle, handle_PAT, stream))

    {

        dvbpsi_delete(stream->pat.handle);

        stream->pat.handle = NULL;

        goto error;

    }

    /* CAT */

    stream->cat.handle = dvbpsi_new(&dvbpsi_message, stream->level);

    if (stream->cat.handle == NULL)

        goto error;

    if (!dvbpsi_cat_attach(stream->cat.handle, handle_CAT, stream))

    {

        dvbpsi_delete(stream->cat.handle);

        stream->cat.handle = NULL;

        goto error;

    }

    /* SDT demuxer */

    stream->sdt.handle = dvbpsi_new(&dvbpsi_message, stream->level);

    if (stream->sdt.handle == NULL)

        goto error;

    if (!dvbpsi_AttachDemux(stream->sdt.handle, handle_subtable, stream))

    {

        dvbpsi_delete(stream->sdt.handle);

        stream->sdt.handle = NULL;

        goto error;

    }

    /* RST */

    stream->rst.handle = dvbpsi_new(&dvbpsi_message, stream->level);

    if (stream->rst.handle == NULL)

        goto error;

    if (!dvbpsi_rst_attach(stream->rst.handle, handle_RST, stream))

    {

        dvbpsi_delete(stream->rst.handle);

        stream->rst.handle = NULL;

        goto error;

    }

    /* EIT demuxer */

    stream->eit.handle = dvbpsi_new(&dvbpsi_message, stream->level);

    if (stream->eit.handle == NULL)

        goto error;

    if (!dvbpsi_AttachDemux(stream->eit.handle, handle_subtable, stream))

    {

        dvbpsi_delete(stream->eit.handle);

        stream->eit.handle = NULL;

        goto error;

    }

    /* TDT demuxer */

    stream->tdt.handle = dvbpsi_new(&dvbpsi_message, stream->level);

    if (stream->tdt.handle == NULL)

        goto error;

    if (!dvbpsi_AttachDemux(stream->tdt.handle, handle_subtable, stream))

    {

        dvbpsi_delete(stream->tdt.handle);

        stream->tdt.handle = NULL;

        goto error;

    }

 

    stream->pat.pid = &stream->pid[0x00];

    stream->cat.pid = &stream->pid[0x01];

 

    stream->sdt.pid = &stream->pid[0x11];

    stream->eit.pid = &stream->pid[0x12];

    stream->rst.pid = &stream->pid[0x13];

    stream->tdt.pid = &stream->pid[0x14];

    return stream;

 

error:

    if (dvbpsi_decoder_present(stream->pat.handle))

        dvbpsi_pat_detach(stream->pat.handle);

    if (dvbpsi_decoder_present(stream->cat.handle))

        dvbpsi_cat_detach(stream->cat.handle);

    if (dvbpsi_decoder_present(stream->sdt.handle))

        dvbpsi_DetachDemux(stream->sdt.handle);

    if (dvbpsi_decoder_present(stream->eit.handle))

        dvbpsi_DetachDemux(stream->eit.handle);

    if (dvbpsi_decoder_present(stream->rst.handle))

        dvbpsi_rst_detach(stream->rst.handle);

    if (dvbpsi_decoder_present(stream->tdt.handle))

        dvbpsi_DetachDemux(stream->tdt.handle);

 

    if (stream->pat.handle)

        dvbpsi_delete(stream->pat.handle);

    if (stream->cat.handle)

        dvbpsi_delete(stream->cat.handle);

    if (stream->sdt.handle)

        dvbpsi_delete(stream->sdt.handle);

    if (stream->rst.handle)

        dvbpsi_delete(stream->rst.handle);

    if (stream->eit.handle)

        dvbpsi_delete(stream->eit.handle);

    if (stream->tdt.handle)

        dvbpsi_delete(stream->tdt.handle);

 

    free(stream);

    return NULL;

}

2.PAT表的解析
由於每個表的具體解析流程都是相似的,所以選取其中一個做典型解析。比如PAT

?

1

2

3

4

5

6

7

8

9

10

11

12

13

/* PAT */

 /*建立dvbpsi handle structure*/

stream->pat.handle = dvbpsi_new(&dvbpsi_message, stream->level);

if (stream->pat.handle == NULL)

    goto error;

 

/*始化PAT decoder 并且handle_PAT(callback) 連結pat decoder*/

if (!dvbpsi_pat_attach(stream->pat.handle, handle_PAT, stream))

{

    dvbpsi_delete(stream->pat.handle);

    stream->pat.handle = NULL;

    goto error;

}

2.1.pat表的sepecfic decoder的抽象(也就是它的struct設計)
ts_pat_t:

?

1

2

3

4

5

6

7

8

9

typedef struct

{

    dvbpsi_t    *handle;       

 

    int         i_pat_version;

    int         i_ts_id;

 

    ts_pid_t    *pid;

} ts_pat_t;

dvbpsi_t:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

struct dvbpsi_s

{

    dvbpsi_decoder_t             *p_decoder;          /*!< private pointer to

                                                          specific decoder */

    /* Messages callback */

    dvbpsi_message_cb             pf_message;           /*!< Log message callback */

    enum dvbpsi_msg_level         i_msg_level;          /*!< Log level */

 

    /* private data pointer for use by caller, not by libdvbpsi itself ! */

    void                         *p_sys;                /*!< pointer to private data

                                                          from caller. Do not use

                                                          from inside libdvbpsi. It

                                                          will crash any application. */

};

 dvbpsi_decoder_t:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

struct dvbpsi_decoder_s

{

    DVBPSI_DECODER_COMMON

};

 

/*實質巨集dvbpsi_decoder_t如下所示*/

 dvbpsi_decoder_t

 {

    uint8_t  i_magic[3];           /*!< Reserved magic value */                  

    bool     b_complete_header;    /*!< Flag for header completion */            

    bool     b_discontinuity;      /*!< Discontinuity flag */                    

    bool     b_current_valid;      /*!< Current valid indicator */               

    uint8_t  i_continuity_counter; /*!< Continuity counter */                    

    uint8_t  i_last_section_number;/*!< Last received section number */          

    dvbpsi_psi_section_t *p_current_section; /*!< Current section */             

    dvbpsi_psi_section_t *p_sections; /*!< List of received PSI sections */      

    dvbpsi_callback_gather_t  pf_gather;/*!< PSI decoder's callback */           

    int      i_section_max_size;   /*!< Max size of a section for this decoder */

    int      i_need;               /*!< Bytes needed */                          

 }

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

struct dvbpsi_psi_section_s

{

  /* non-specific section data */

  uint8_t       i_table_id;             /*!< table_id */

  bool          b_syntax_indicator;     /*!< section_syntax_indicator */

  bool          b_private_indicator;    /*!< private_indicator */

  uint16_t      i_length;               /*!< section_length */

 

  /* used if b_syntax_indicator is true */

  uint16_t      i_extension;            /*!< table_id_extension */

                                        /*!< transport_stream_id for a

                                             PAT section */

  uint8_t       i_version;              /*!< version_number */

  bool          b_current_next;         /*!< current_next_indicator */

  uint8_t       i_number;               /*!< section_number */

  uint8_t       i_last_number;          /*!< last_section_number */

 

  /* non-specific section data */

  /* the content is table-specific */

  uint8_t *     p_data;                 /*!< complete section */

  uint8_t *     p_payload_start;        /*!< payload start */

  uint8_t *     p_payload_end;          /*!< payload end */

 

  /* used if b_syntax_indicator is true */

  uint32_t      i_crc;                  /*!< CRC_32 */

 

  /* list handling */

  struct dvbpsi_psi_section_s *         p_next;         /*!< next element of

                                                             the list */

};

dvbpsi_psi_section_t架構體設計,是根據標ISO/IEC 13818-1 section 2.4.4.11協定如下表 private section所示:

                                            table1private 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;        //NULLpat decoder始化
    p_dvbpsi->pf_message = callback;
    p_dvbpsi->i_msg_level = level;
    return p_dvbpsi;
}

b始化PAT decoder 并且連結pat表的解析handle作。其中dvbpsi_pat_attachdvbpsi_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 decoderp_decoder(類別強轉dvbpsi_pat_decoder_t* dvbpsi_decoder_t*)*/
    p_dvbpsi->p_decoder = DVBPSI_DECODER(p_pat_decoder);        
    return true;
}

【總結ab
dvbpsi_new
是一個介面,建立抽象的decoder。但具體的要建立哪種specific decoder需要caller自己去起始化,賦予其實例化的屬性。通過以下介面去實例化decoder。每個表的解析動作放置在tables/目錄下的 各個.c檔案中。

{
   dvbpsi_pat_attach(tables/pat.c)
   dvbpsi_bat_attach(tables/bat.c)
   ...
}

cPAT表中section的聚集
建立pat decoder入了callbackdvbpsi_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);
    }
}

dPAT表及PMT表的解析
緊接著對pat table進行解析或重建。 解析的動作實質就是連結到pat decoderhandle(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

arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()