1. 程式人生 > >libdvbpsi庫使用方法簡介

libdvbpsi庫使用方法簡介

只是簡單介紹了libdvbpsi庫的使用方法,希望對其他童鞋有所幫助。
對於函式和結構體的定義,請查詢線上幫助文件:http://www.videolan.org/developers/libdvbpsi/doc/doxygen/html/
本文是以1.1.0版本為參照進行介紹。

1 基本使用方法

  基本步驟如下:

(1)使用dvbpsi_new介面獲取控制代碼並註冊訊息處理函式
(2)繫結所需要的解碼器,繫結解碼器所需要的回撥函式。
(3)傳送TS資料包進行解析
(4)解除繫結的解碼器
(5)釋放庫控制代碼

  下面是以pat表的解析為例進行說明的。具體可以參照例項程式中的decode_pat.c。

(1)使用dvbpsi_new介面獲取控制代碼並註冊訊息處理函式。

dvbpsi_t *p_dvbpsi = dvbpsi_new(&message, DVBPSI_MSG_DEBUG);
if(p_dvbpsi==NULL)
    //錯誤處理
  在使用前需要獲得dvbpsi庫的控制代碼(這裡的句其實是指向一個數據結構的指標),一個控制代碼對應一個解碼環境,就相當於一個工作空間,通過獲取多個控制代碼就可以同時進行多種解碼。
  訊息處理函式是使用者編寫的,由庫呼叫,當庫有訊息(比如錯誤提示,警告)提示使用者時,庫將呼叫該函式。使用者在該函式內處理訊息(比如列印到螢幕上)。訊息處理函式的宣告可以在線上文件中找到。
  函式宣告為:
dvbpsi_t * dvbpsi_new (
    dvbpsi_message_cb  callback,
    enum dvbpsi_msg_level  level
);
  dvbpsi_t結構體型別:

typedef struct dvbpsi_s dvbpsi_t; //dvbpsi_t是dvbpsi_s的別名
struct dvbpsi_s
{
    dvbpsi_decoder_t *  p_decoder ,//標識解碼器
    dvbpsi_message_cb  pf_message ,//訊息處理函式,dvbpsi_new函式中設定
    enum dvbpsi_msg_level  i_msg_level ,//訊息等級,dvbpsi_new函式中設定
    void *  p_sys,//指向呼叫者的私有資料,不能使用庫內的資料,否則程式崩潰
};

  典型的訊息處理函式如下(取自examples資料夾中的示例程式),可以根據需要編寫:

static void message(
    dvbpsi_t *handle,
    const dvbpsi_msg_level_t level,
    const char* msg)//訊息內容
{
    switch(level)
    {
        case DVBPSI_MSG_ERROR: fprintf(stderr, "Error: "); break;
        case DVBPSI_MSG_WARN:  fprintf(stderr, "Warning: "); break;
        case DVBPSI_MSG_DEBUG: fprintf(stderr, "Debug: "); break;
        default: /* do nothing */
            return;
    }
    fprintf(stderr, "%s\n", msg);
}

  這一步需要包含標頭檔案dvbpsi.h,psi.h

(2)繫結所需要的解碼器,繫結解碼器所需要的回撥函式。

  dvbpsi_XXX_attach()將給dvbpsi_t* 附上一個XXX表解碼器(XXX:一個表對應一個解碼器,一個表也對應一個繫結函式,由於繫結函式命名非常規整,所以介面    dvbpsi_XXX_attach就是繫結某個表的解碼器),庫將使用該解碼器進行工作。也就是給(dvbpsi_t*)->p_decoder賦值。該函式將返回新的綁定了解碼器的庫控制代碼。
  介面需要指定一個回撥函式,當庫解析出了某個表時,將呼叫該回調函式,使用者在回撥函式中實現資料的處理即可。回撥函式的格式也都是統一的。見線上幫助文件。
  當解碼器不需要使用,需用相應的函式進行解綁。
  需包含相應表的標頭檔案。

例如(PAT表解碼):

//包含標頭檔案pat.h
//DumpPAT為回撥函式,處理解析出的PAT表
if(!dvbpsi_pat_attach(p_dvbpsi, DumpPAT, NULL))
    錯誤處理
 
//---------------------------------------------------------------------------------
//附函式宣告和結構體定義
//繫結函式
bool dvbpsi_pat_attach(
    dvbpsi_t *  p_dvbpsi,
    dvbpsi_pat_callback  pf_callback,
    void *  p_cb_data);//一般為NULL
typedef void(* dvbpsi_pat_callback )(void *p_cb_data, dvbpsi_pat_t *p_new_pat);
 
//記錄表資料的結構體
typedef struct dvbpsi_pat_s  dvbpsi_pat_t;
struct dvbpsi_pat_s
{
    uint16_t  i_ts_id;//ts流id
    int8_t  i_version;//版本號
    bool  b_current_next;
    dvbpsi_pat_program_t *  p_first_program;//第一個節目節點。(節目列表用連結串列記錄的,遍歷該連結串列就可以獲取所有節目,關於該結構體的詳細說明見線上幫助文件)
};

對於所在資料包中只包含某個表的表,直接呼叫dvbpsi_xxx_attach,然後在回撥函式中解碼即可。比如PAT表。
對於所在資料包包含多個表的表,需先呼叫dvbpsi_AttachDemux(解複用程式)解碼出子表,然後在回撥函式中進行dvbpsi_xxx_attach,其後的操作與上面的相同。比如SDT表。這時的dvbpsi_xxx_attach函式還需要將子表id作為引數,從而繫結相應的子表解碼器。子表解碼器不需要解綁。詳見“複雜表的解析”。

(3)傳送TS資料包

  通過dvbpsi_packet_push()傳送資料包給解碼器,如果一個表處理完了,解碼器將呼叫dvbpsi_XXX_attach繫結的回撥函式。回撥函式中會包含解碼出的資料。如果資料中包含descriples(p_first_descriptor指標),那麼需要額外再呼叫處理descriptors的函式(將p_descriptor作為引數傳遞給處理函式,處理函式會返回結果)。descriptors處理詳見dr.h部分。
  所有表的解析都是呼叫這個函式傳送資料進行解析。
  例如:

dvbpsi_packet_push(p_dvbpsi, data);//p_dvbpsi是控制代碼,data是188位元組的資料。讀取資料的函式為ReadPacket。
  當有表被解析處理後回撥函式就會被執行。
  注意當記錄某個表的所有資料都被解析了(即表完全解析完),解碼過程才會結束,並呼叫回撥函式。比如記錄EIT (table_id=0x50,servic_id=19)的表共用了20個section,只有20個section全部解析完成才行。

  函式宣告為:

//p_data是一個188位元組資料包
//包含dvbpsi.h
bool dvbpsi_packet_push(dvbpsi_t *  p_dvbpsi,uint8_t *  p_data);

(4)解除繫結的解碼器

  當不需要再使用該解碼器時,呼叫dvbpsi_XXX_detach解除解碼器。
  例如:

dvbpsi_pat_detach(p_dvbpsi);//解除後該控制代碼不能再用瞭解碼了。

  包含相應表的標頭檔案。

(5) 釋放庫控制代碼

  應用程式結束,呼叫dvbpsi_delete()釋放控制代碼,例如:

dvbpsi_delete(p_dvbpsi);

在此之前,需解除繫結的解碼器,例如:

dvbpsi_pat_detach(p_dvbpsi);//解除後該控制代碼不能再用瞭解碼了。

2 簡單表的解析

使用的基本解析流程,見“使用方法”。比如:PAT表,PMT表。PMT在attach的時候給定需要給定program_number和i_program_number,這樣庫才知道需要解析那個節目的PMT表。

3 複雜表的解析

  需先通過解複用將子表解析出來,然後為各個子表綁解碼器,解碼子表的過程同“簡單表的解析”。
  解複用需呼叫dvbpsi_AttachDemux繫結解複用過程,解複用過程解析出子表後將呼叫dvbpsi_AttachDemux函式中指定的回撥函式,然後再該回調函式中對為子表繫結解碼器。
  SDT表、EIT表等都需要使用這種方式進行解析。可以參考decode_sdt.c示例程式。
   例EIT表:
    dvbpsi_eit_attach將table_id、servic_id、callback_func關聯起來。不能立即解除繫結,因為一組(table_id,servic_id)指定一個解碼過程,該過程會記錄已解碼的表,當再次出現相同的表時不在解碼。如果解除繫結,解碼過程也釋放了,再次出現相同的表時會再次解碼。

4 描述符解析

  dir.h檔案將dr_XX.h包含至一個頭檔案。這些dr_xx.h檔案都是用來解析descriptors(描述符)的。xx對應的是描述符的識別符號。
  解碼工作完成後庫呼叫的回撥函式中,處理後的資料以指標的方式作為引數傳遞出來。在資料中,descriptors通過一個p_first_descriptor指標訪問。
  所有的descriptors都用同一個結構體描述,即:dvbpsi_descriptor_t。因此所有的解析函式都使用dvbpsi_descriptor_t*作為引數,而且這些函式的引數幾乎都一樣,只是函式名和返回值不同。p_descriptor->i_tag指明描述符型別,以便呼叫相應的解析函式。
  解析descriptors時,將p_descriptor作為引數呼叫相應的解析函式,處理結果在返回值中。
  例如解析EIT表的事件名和事件簡介: 
if(p_descriptor->i_tag==0x4d)//對應的描述符id為0x4d
{
    dvbpsi_short_event_dr_t *p=dvbpsi_DecodeShortEventDr(p_descriptor);
    //結構體dvbpsi_short_event_dr_t中就包含了事件名和簡介
}