libdvbpsi库的使用

来源:互联网 发布:人工智能会议 2017 编辑:程序博客网 时间:2024/06/06 04:23

@By涂启明

@email:tuqiming_x@163.com

@

@如果您发现了错误,或者有什么建议或意见,请务必通过邮件告诉我

 @直接从word里拷贝上来的,排版很烂啊,哎,写得也很烂

 

 

同其他linux代码库一样,libdvbpsi库也没有在其网站上找到一个完整的使用说明,虽然有一个简单得不能再简单的介绍,但是要使用这个库进行正式开放还远远不够,只能通过阅读示例程序进行更多的了解。这篇文章只是在我现有认识的基础上的简单介绍,希望这能让你更快掌握libdvbpsi库的使用。

对于函数和结构体的定义,请查找在线帮助文档:

http://www.videolan.org/developers/libdvbpsi/doc/doxygen/html/

本文是以1.1.0版本为参照进行介绍。

 

 

1 使用方法

libdvbpsi库自0.2.0版本起使用了新的接口和使用方法, 0.1.7之前的版本使用的是另一种更为简单的使用方法。在线帮助文档中对老版本转新版本做了说明:

http://www.videolan.org/developers/libdvbpsi/doc/doxygen/html/howto-new-api.html

下面是以pat表的解析为例进行说明的。具体可以参照实例程序中的decode_pat.c。

 

1.1 使用dvbpsi_new获取句柄并注册消息处理函数。

在使用前需要获得dvbpsi库的句柄(这里的句其实是指向一个数据结构的指针),一个句柄对应一个解码环境,就相当于一个工作空间,通过获取多个句柄就可以同时进行多种解码。

消息处理函数是用户编写的,由库调用,当库有消息(比如错误提示,警告)提示用户时,库将调用该函数。用户在该函数内处理消息(比如打印到屏幕上)。消息处理函数的声明可以在在线文档中找到。

句柄由dvbpsi_new(&message_cb,DVBPSI_MSG_DEBUG)函数返回:

dvbpsi_t *p_dvbpsi = dvbpsi_new(&message, DVBPSI_MSG_DEBUG);

if(p_dvbpsi==NULL)

    错误处理

 

       

函数声明为:

dvbpsi_t * dvbpsi_new (

    dvbpsi_message_cb  callback,

    enum dvbpsi_msg_level  level

);

dvbpsi_t结构体类型:

typedef struct dvbpsi_s dvbpsi_t; //dvbpsi_tdvbpsi_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

 

 

1.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;//tsid

    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作为参数,从而绑定相应的子表解码器。子表解码器不需要解绑。详见“复杂表的解析”。

 

1.3 发送TS数据包

通过dvbpsi_packet_push()发送数据包给解码器,如果一个表处理完了,解码器将调用dvbpsi_XXX_attach绑定的回调函数。回调函数中会包含解码出的数据。如果数据中包含descriples(p_first_descriptor指针),那么需要额外再调用处理descriptors的函数(将p_descriptor作为参数传递给处理函数,处理函数会返回结果)。descriptors处理详见dr.h部分。

函数声明为:

//p_data是一个188字节数据包

//包含dvbpsi.h

bool dvbpsi_packet_push(dvbpsi_t *  p_dvbpsi,uint8_t *  p_data);

所有表的解析都是调用这个函数发送数据进行解析。

 

例如:dvbpsi_packet_push(p_dvbpsi, data);//p_dvbpsi是句柄,data是188字节的数据。读取数据的函数为ReadPacket。

当有表被解析处理后回调函数就会被执行。

注意当记录某个表的所有数据都被解析了(即表完全解析完),解码过程才会结束,并调用回调函数。比如记录EIT (table_id=0x50,servic_id=19)的表共用了20个section,只有20个section全部解析完成才行。

 

1.4 解除绑定的解码器

当不需要再使用该解码器时,调用dvbpsi_XXX_detach解除解码器。

例如:

dvbpsi_pat_detach(p_dvbpsi);//解除后该句柄不能再用了解码了。

包含相应表的头文件。

 

1.5 释放库句柄

应用程序结束,调用dvbpsi_delete()释放句柄。

voiddvbpsi_delete(dvbpsi_t *  p_dvbpsi);

在此之前,需解除绑定的解码器。

包含dvbpsi.h

例如:dvbpsi_delete(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)//对应的描述符id0x4d

{

    dvbpsi_short_event_dr_t *p=dvbpsi_DecodeShortEventDr(p_descriptor);

    //结构体dvbpsi_short_event_dr_t中就包含了事件名和简介

}