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中就包含了事件名和简介
}

猜你喜欢

转载自blog.csdn.net/qiminixi/article/details/12100151