只是简单介绍了libdvbpsi库的使用方法,希望对其他童鞋有所帮助。
对于函数和结构体的定义,请查找在线帮助文档:http://www.videolan.org/developers/libdvbpsi/doc/doxygen/html/
本文是以1.1.0版本为参照进行介绍。
基本步骤如下:
下面是以pat表的解析为例进行说明的。具体可以参照实例程序中的decode_pat.c。(1)使用dvbpsi_new接口获取句柄并注册消息处理函数
(2)绑定所需要的解码器,绑定解码器所需要的回调函数。
(3)发送TS数据包进行解析
(4)解除绑定的解码器
(5)释放库句柄
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
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作为参数,从而绑定相应的子表解码器。子表解码器不需要解绑。详见“复杂表的解析”。
通过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。
当有表被解析处理后回调函数就会被执行。
函数声明为:
//p_data是一个188字节数据包
//包含dvbpsi.h
bool dvbpsi_packet_push(dvbpsi_t * p_dvbpsi,uint8_t * p_data);
当不需要再使用该解码器时,调用dvbpsi_XXX_detach解除解码器。
例如:
dvbpsi_pat_detach(p_dvbpsi);//解除后该句柄不能再用了解码了。
包含相应表的头文件。
应用程序结束,调用dvbpsi_delete()释放句柄,例如:
dvbpsi_delete(p_dvbpsi);
在此之前,需解除绑定的解码器,例如:
dvbpsi_pat_detach(p_dvbpsi);//解除后该句柄不能再用了解码了。
if(p_descriptor->i_tag==0x4d)//对应的描述符id为0x4d
{
dvbpsi_short_event_dr_t *p=dvbpsi_DecodeShortEventDr(p_descriptor);
//结构体dvbpsi_short_event_dr_t中就包含了事件名和简介
}