具体实施方式
本发明方法采用接收数据和解析数据两个步骤来完成电子节目指南信息的获得,可以避免接收数据时的频繁等待,加快整个接收处理过程。将接收过程分成两个步骤,其基本出发点是,回调程序类似于中断处理,时间要求严格,应尽量作最少的工作,以防止阻塞数据的连续接收。在回调程序里,在所接收的表的所有段完全接收完毕之后,停止接收,将数据拷贝到专门开辟的存储空间;然后再执行表的数据的解析过程,从而得到所需的信息。
本发明所述的方法,包括如下步骤:
步骤一、接收数据流中的电子节目指南信息的数据,如果在指定的时间内,电子节目指南信息全部被接收,则执行步骤二;否则,超出指定的时间,则强制停止接收过程,并释放占用的资源;
步骤二、解析步骤一得到的电子节目指南信息数据,得到电子节目指南信息,释放占用的资源;
步骤三、将步骤二得到信息填入到固定的数据结构中,提供给EPG应用。
电子节目指南信息数据的接收(步骤一)和解析(步骤二)是本发明的重点,其流程图如图1所示,将解析后的信息填入到固定的数据结构中,提供给EPG应用可以采用与现有技术完全相同的方式。
关于对于电子节目指南信息数据的接收,由于这些数据是复用在整个信息流中,经过前端的解复用器后,可以单独分离出来;对这些数据的处理与其他数据如视频数据,音频数据的处理方法类似,要单独分配资源,如通道、缓存等;只是电子节目指南信息数据流量并不大,所以占用的资源也比较少。
数据的接收过程具体包括:
步骤1.1、为接收电子节目指南信息数据分配系统资源,分配过滤器;
步骤1.2、检验系统资源是否可用,如果可用,则开始接收过程;
步骤1.3、接收电子节目指南信息数据,判断是否所有电子节目指南信息表都已经接收,如果是,则执行步骤二;否则,更换新的过滤器,继续接收过程;如果超时,则强制停止接收过程,并释放占用的资源。
数据的接收过程中,首先要为接收电子节目指南信息数据分配资源,并保证这些资源在整个接收过程中可用;由于接收不同类型的电子节目指南信息数据(不同类型的信息表)要采用不同的过滤器来获取,过滤器接收完其所接收的数据(全部子表和子表中全部的Section)后,再更换过滤器,重新启动新的过滤器进行接收,直到所有的过滤器已经全部接收完数据,结束接收过程。
也可能出现由于网络状态不好,出现接收的时间过长,考虑到这种情况,同样为了节省系统资源,在接收超过预先设定的时间后,强制停止接收过程,并释放占用的资源。
图2是本发明方法步骤1.1的流程图,包括如下步骤:
步骤1.11、从解复用器中分配未使用的通道;如果分配失败,则执行步骤1.16;
步骤1.12、为步骤1.11所分配的通道注册相应的回调程序(此程序在硬件监测到数据到达时被调用),如果注册失败,则执行步骤1.16;
步骤1.13、为步骤1.11所分配的通道分配缓存资源;如果分配失败,则执行步骤1.16;
步骤1.14、为步骤1.11所分配的通道设置PID值,此PID值为过滤器接收传输流的PID值;如果设置失败,则执行步骤1.16;
步骤1.15、为步骤1.11所分配的通道分配过滤器,结束步骤1.1;如果分配失败,则执行步骤1.16;
步骤1.16、释放资源,根据失败的类型,释放过滤器、通道及缓存,退出节目指南信息的接收。
过滤器的设置方法可以依照业务信息规范,对各个表的table_id值的预先分配值进行设置。网络信息表的通道PID设为0x10,过滤器table_id设为0x40/0x41,服务描述表的通道PID设为0x11,过滤器table_id设为0x42/0x46,业务群关联信息表的通道PID设为0x11,过滤器table_id设为0x4a,节目信息表的通道PID设为0x12,过滤器table_id设为0x4e/0x4f,或0x50-0x5f/0x60-0x6f,时间日期表的通道PID设为0x14,过滤器table_id设为0x70,时间偏移表的通道PID设为0x14,过滤器table_id设为0x73。
接收步骤中,根据所要接收的数据流首先为通道分配对应的PID,然后在设置对应的过滤器来接收对应通道的节目指南数据,实现电子节目指南信息的接收。
由于以上的过程中也可能会有错误发生,为了使使用者或管理者了解错误地方,以便作出相应的调整,本发明对于每个可能出现错误的地方设置了错误采集的机制,为不同的类型错误设置了不同的错误编号,使本发明使用更加灵活、方便和人性化。
图3给出了本发明所述方法中步骤1.2的流程图,包括如下步骤:
步骤1.21、判断通道是否有效,如果有效,执行步骤1.22;否则执行步骤1.26;
步骤1.22、判断过滤器是否有效,如果有效,执行步骤1.23;否则执行步骤1.26;
步骤1.23、设置过滤器参数,包括数据位和掩码(各种信息表的过滤器的设置方法依照SI标准指定的TABLE_ID等参数执行),如果成功,执行步骤1.24;否则执行步骤1.26;
步骤1.24、启动过滤器,如果成功,执行步骤1.25;否则执行步骤1.26;
步骤1.25、启动通道,如果成功,则开始接收节目指南信息数据,结束步骤1.2;否则执行步骤1.26;
步骤1.26、根据错误的类型,设置错误号,释放过滤器、通道及缓存,结束节目指南信息的接收过程。
在步骤1.1中通道、过滤器和缓存分配成功之后,要对资源的可用性进行检验,然后根据要接收的信息表种类来设置过滤器的数据位和掩码,并启动过滤器和通道,开始接收信息表数据。
在开始接收之后,执行步骤1.3来接收数据,直到所有信息表的数据被接收或者超时,具体包括:
步骤1.31、硬件检测到数据到达,调用Section回调程序,接收当前过滤器传来的Section数据,一个Section数据的数据完全被接收,回调程序将其拷贝到缓存中;
步骤1.32、重新调用Section回调程序,接收下一个Section的数据;
步骤1.33、如果一个信息表的全部数据(全部的子表和子表的全部Section数据)被接收,则更换新的过滤器,重新接收未接收信息表的Section数据;如果所有信息表的全部数据都已经被接收,则执行步骤二;如果超时,则强制停止接收过程,并释放占用的资源。
所需的数据到达时,回调程序就被调用;并且每接收一个Section的数据,回调程序就被调用一次。本发明在回调程序里接收数据,并检查Section数据的正确性,并执行Section数据的拷贝任务,判断整个信息表/子表是否完全被接收。
在回调程序里,所需的数据被拷贝到内存的缓冲区中。为了更有效的利用内存,本发明采用了缓冲池的方式来更合理的利用内存,防止随机进行内存分配而出现的大量的小块的内存无法利用的情况,提高内存的利用效率。
内存缓冲池采用预分配方式,用pool_start、pool_free、pool_length三个变量保存缓冲池的始末地址和长度;用一个section数组保存已存入的section在缓冲池中的起始地址和长度;用section_sum保存已存入的seciton总个数。当缓冲区的大小不够使用时(即pool_free和pool_start+pool_length的差值小于某个临界值),将缓冲区的大小增大一倍,同时保持已有数据不变。内存空间连续分配,防止了内存碎片的产生。图5所示为一个存有两个Section的缓冲池,section0和section1表示存储的数据,section0_start,和section0_length表示缓冲池的数据结构。
EPG数据存储区对所有不同的表采用一致的数据结构(即内存池),包含起始地址、长度、末尾地址,总的section个数等内容。每个内存池最多存放256个section,在多子表的情况下,一个完整的表包含多个内存池,每个内存池存放一个子表。内存池的初始大小根据信息表的种类各不相同,NIT表、BAT表、SDT表可以为20KB;OSDT表可以为40KB;PF-EIT表可以为80KB;SCH-EIT表可以为64KB;TDT/TOT表可以为4KB。
本发明采用如下方法判断所有所需的数据是否到达:
缓冲池内的Section总个数等于此接收子表的最后段编号lastSectionNumber,表示该子表已经被完全接收。
接收程序也不是无限制的等待数据完全被接收,在所设定的时间内没有收完全所有的Section,即缓冲池内所接收到的Section总数小于此接收子表的最后段编号lastSectionNumber,则强行停止接收,接收程序返回超时。
通过以上的描述可以看出,回调程序中完成主要的接收功能;回调程序由硬件启动,当通道打开而硬件检测到有数据到达,就自动调用回调程序。图4是上述的回调程序的工作流程图,包括:
步骤1.300、判断接收是否已经结束,如果已经结束,则退出回调程序;否则,执行步骤1.301;
步骤1.301、判断Section的数据版本是否正确,如果正确,则执行步骤1.302;否则,执行步骤1.305;
步骤1.302、判断数据是否正确,如果正确,则执行步骤1.303;否则,重新启动通道,接收该Section的数据,结束回调程序;
步骤1.303、判断是否缓冲区中已经存在该Section的完整数据,如果是,则抛弃接收的数据;否则执行步骤1.304;
步骤1.304、将数据添加到缓冲区中,如果成功,则重新设置过滤器;重新启动通道,结束回调程序;否则,执行步骤1.305;
步骤1.305、停止通道和过滤器,根据错误类型设置错误号,结束回调程序,退出电子节目指南信息的接收。
所述的步骤1.305,停止通道和过滤器,进一步包括如下步骤:
步骤1.3051、判断通道是否有效,如果有效,则停止通道;否则执行步骤1.3053;
步骤1.3052、判断过滤器是否有效,如果有效,则停止过滤器,结束回调程序;否则执行步骤1.3053;
步骤1.3053、根据错误类型,设置错误号,结束回调程序。
缓冲池内的数据根据section_num从小到大依次排列。对于某种表有多个子表的要考虑到不同频点之间的数据可能发生重复,对于多频点EPG前端尤其如此。比如某个频点的SDT表中不但保存着本频点的电台信息,而且可能包含其它频点的电台信息,在SI标准里是当作Other SDT表来传送。但是,在其它某个频点,这些其他SDT(Other SDT)的信息可能就是它的SDT表。避免数据信息在内存中的重复就是必须要考虑的事情。在回调程序中,对此进行了考虑,比较了接收的Section数据与已有的Section数据。
由于网络信息和电台信息对于每个频点来说,实际的数据相同,只是放到不同的表中传送,而且这些信息不同于节目信息和时间信息,不是经常变化,所以这些数据只在开机后接收一次,解析后并存放到非易失存储介质中,便于以后应用。
在将所需要的所有数据拷贝到缓冲池之后,进行数据的解析。所有的表的字段和描述子的解析均严格依照业务信息规范进行。但其中不是所有的信息都进行提取,对于某些没有立即解析的数据提供单独的方法,用来在用户需要时提取,比如节目的详细描述信息,节目的Item列表等。
图6是本发明所述的步骤二解析数据的流程图,包括如下步骤:
步骤2.1、获取信息表的第一层循环的长度,如果大于零,则执行步骤3.2;否则执行步骤2.4;
步骤2.2、获取描述子内容,第一层循环长度减去此描述子长度,若内容正确,执行步骤2.3,否则回到步骤2.1;
步骤2.3、添加描述子内容到当前结果,回到步骤2.1;
步骤2.4、获取信息表第二层循环的长度,若大于零,则执行步骤2.5;否则结束步骤3;
步骤2.5、获取图表(map)内容,第二层循环长度减去此map长度,得到map内的第一个描述子的descriptor_id,根据SI标准解析此描述子,并将结构添加到当前结果;反复步骤2.5,直到解析完此map内所有的描述子;
步骤2.6、将步骤2.5中所获得的map内容中解析所得的内容添加到结果列表;返回步骤2.4。
对接收的电子节目指南信息表的解析,与现有技术的解析方式相同,并且,实际上并也不复杂,在SI标准中对于接收到的电子节目指南信息的数据结构有详细的描述,而解析的方法也容易实现。
解析之后,得到了完整的电子节目指南信息EPG,将其通过固定的数据结构存储在存储空间中,内存和通道等资源应该释放,归还系统。
图7是本发明所述的释放资源的流程图,包括如下步骤:
步骤4.1、判断过滤器是否有效,如果有效,释放过滤器及缓存;否则,执行步骤4.3;
步骤4.2、判断通道是否有效,如果有效,释放通道,结束释放资源;否则执行步骤4.3;
步骤4.3、根据错误类型,设置错误号,结束释放资源。
本发明所述的步骤三,进一步具体包括如下过程:
经过解析之后得到的数据按照固定的数据结构提供给EPG应用,即将数据赋值给数据结构中的对应的变量,便于EPG应用使用这些变量,完成相应的功能。对于多section的信息表(包括网络信息表NIT、服务描述表SDT、业务群关联信息表BAT、节目信息表EIT等),解析后存储的信息可以采用一致的单链表存储结构,见图8。采用一致的单链表存储结构可以很好的节约存储空间,并且更新电子节目指南信息也很方便。
下面给出各个信息表的数据结构的实例,用C语言描述如下:
网络信息表(NIT):
typedef struct {
unsigned short version; /* 版本号 */
unsigned short original_network_id;/* 网络识别号 */
unsigned short stream_id; /* 复用流识别号 */
unsigned long frequency; /* 传输频率 */
unsigned long symbol_rate; /* 传输符号率 */
int delevery_type /* 传输网络类型 */
dtvia_cable_delivery_ptr p_cable_delivery_desc /*有线传输
参数*/
dtvia_satellite_delivery_ptrp_satellite_delivery_desc /* 卫星传
输参数*/
int name_length /* 网络名称长度 */
unsigned char * network_name /* 网络名称 */
int multilingual_sum; /*多语言网络名称个数*/
dtvia_multilingual_name_ptr p_mul_name /*多语言网络名称链表*/
int additional_frequency_sum; /* 附加频率个数 */
dtvia_frequency_list_ptr p_a_frequency; /* 附加频率链表 */
int service_list_sum; /* 服务列表个数 */
dtvia_service_list_ptr p_service /* 服务链表 */
EPG_NIT_INFO_PTR p_next;
} EPG_NIT_INFO,*EPG_NIT_INFO_PTR;
NIT链表:
typedef struct{
int nit_sum; /* 网络个数 */
EPG_NIT_INFO_PTR p_nit_list; /* 网络信息 */
int linkage_sum; /* 链接个数 */
dtvia_linkage_ptr p_linkage_list; /* linkage链表 */
}dtiva_nit_head,*dtiva_nit_ptr;
业务群关联表(BAT):
typedef struct {
unsigned short version; /* 版本号 */
unsigned short bouquet_id; /* 业务群识别号 */
int name_length; /* 名称长度 */
unsigned char * bouquet_name /* 业务群名称*/
int multoilingual_sum; /*多语言名称个数*/
dtvia_multilingual_name_ptr p_mul_name /*多语言名称链表*/
int ca_identify_sum; /* CA Identify 个数*/
dtvia_ca_identify_ptrp_ca_identify; /* CA Identify链表*/
int available_country_sum; /* 允许国家个数 */
dtvia_country_available_ptr p_available_country;/* 允许国家链表
*/
int unavailable_country_sum; /* 不允许国家个数
*/
dtvia_country_available_ptr p_un_available_country;/*不允许国家
链表*/
int service_list_sum; /* 服务列表个数 */
dtvia_service_list_ptr p_service /* 服务链表 */
int linkage_sum; /* 链接个数 */
dtvia_linkage_ptr p_linkage_list; /* linkage链表 */
EPG_BAT_INFO_PTR p_next;
} EPG_BAT_INFO,*EPG_BAT_INFO_PTR;
BAT链表
typedef struct{
int bat_sum; /* BAT表个数 */
EPG_BAT_INFO_PTR p_bat_list; /* BAT表链表 */
}dtiva_bat_head,*dtiva_bat_ptr;
服务描述表SDT:
typedef struct {
unsigned short version; /*版本号 */
unsigned short original_network_id;/* 网络识别号 */
unsigned short stream_id; /* 复用流识别号 */
unsigned short service_id; /* 电视台识别号*/
unsigned short service_type; /* 分类*/
int name_length; /* 电视台名长度 */
unsigned char *service_name; /* 电视台名称 */
int provider_length; /* 提供商名长度*/
unsigned char *service_provider; /* 提供商名*/
unsigned short free_ca_mode; /* 加扰模式*/
unsigned short schedule_flag; /* schedule 存在标志*/
unsigned short pf_flag; /* present following 存在标志*/
int multilingual_sum; /* 多语言个数 */
dtvia_multilingual_service_desc_ptr p_mul_service /*多语言名称链表*/
int ca_identify_sum; /* CA Identify 个数*/
dtvia_ca_identify_ptr p_ca_identify; /* CA Identify 链表*/
int available_country_sum; /* 允许国家个数 */
dtvia_country_available_ptr p_available_country;/* 允许国家链表
*/
int unavailable_country_sum; /* 不允许国家个数 */
dtvia_country_available_ptr p_un_available_country;/* 不允许国
家链表*/
int bouquet_name_length; /* 业务群名称长度 */
unsigned char *bouquet_name /* 业务群名称*/
int linkage_sum; /* 链接个数 */
dtvia_linkage_ptr p_linkage_list; /* linkage链表
*/
int data_broadcast_sum; /* Data broadcast个数 */
dtvia_data_broadcast_ptr p_db_desc;/* Data broadcast链表 */
int announcement_sum /* 通告个数 0或者 1*/
dtvia_announcement_ptr p_announcement; /*通告链表 */
EPG_SDT_INFO_PTR p_next;
}EPG_SDT_INFO,*EPG_SDT_INFO_PTR;
//SDT链表:
typedef struct {
int sdt_sum; /* SDT表个数*/
EPG_SDT_INFO_PTR p_sdt_list; /* SDT表链表*/
}dtiva_sdt_head,dtiva_sdt_ptr;
节目信息表EIT:
typedef struct {
unsigned short version; /* 版本号 */
unsigned short original_network_id; /* 网络识别号 */
unsigned short service_id; /* 服务号 */
unsigned short stream_id; /* 传输流号 */
unsigned short event_id; /* 节目号 */
unsigned short year; /* 年 */
unsigned short month; /* 月 */
unsigned short day; /* 日 */
unsigned short start_hour; /* 节目起始时间(小时)
*/
unsigned short start_minute; /* 节目起始时间(分钟) */
unsigned short duration_hour; /* 节目持续小时 */
unsigned short duration_minute; /* 节目持续分钟 */
int name_length; /* 节目名称长度 */
unsigned char *event_name; /* 节目名称 */
int short_desc_length; /* 节目简短描述长度 */
unsigned char *short_desc; /* 节目简短描述 */
unsigned short event_state; /* 状态 */
unsigned short free_ca_mode; /* 加扰模式 */
unsigned short nibble1; /* 分类 1*/
unsigned short nibble2; /* 分类 2*/
int component_sum /* 组件个数 */
dtvia_component_ptr p_component /* 组件链表 */
int ca_identify_sum; /* CA Identify 个数*/
dtvia_ca_identify_ptr p_ca_identify; /* CA Identify 链表
*/
int parent_rate_sum; /* 家长级别个数 */
dtvia_parent_rate_ptrp_parent_rate; /* 家长级别链表 */
int detail_length /* 详细描述长度 */
unsigned char * event_detail /* 详细描述 */
int item_sum /* item个数 */
dtvia_item_desc_ptr p_item /* item链表 */
int linkage_sum; /* 链接个数 */
dtvia_linkage_ptr p_linkage_list; /* linkage链表 */
int data_broadcast_sum; /* Data broadcast个数 */
dtvia_data_broadcast_ptr p_db_desc; /* Data broadcast链表 */
EPG_EIT_INFO_PTR p_next
}EPG_EIT_INFO,*EPG_EIT_INFO_PTR;
EIT链表
typedef struct {
int eit_sum;
EPG_EIT_INFO_PTR p_eit_list;
}dtiva_eit_head,dtiva_eit_ptr;
对于时间信息,TDT表中接收的是UTC标准时间,如果要接收本地时间,则需要接收TOT表。时间信息的存储结构如下(C语言描述):typedef struct {
int m_year; /* 年 */
int m_month; /* 月 */
int m_day; /* 日 */
int m_week_day; /* 星期 */
int m_hour; /* 小时 */
int m_minute; /* 分钟 */
int m_second; /* 秒 */} dtvia_time,*dtvia_time_ptr;typedef struct local_time{
dtvia_time local_time; /* 本地时间 */
short current_offset_hour;/* -12-+13 */ /* 时区偏移小时 */
short current_offset_minute; /* 时区偏移分钟 */
short time_change_sum; /* 时区变更时间总数 */
TIME_CHANGE_PTR p_change_list; /* 时区变更时间列表 */}LOCAL_TIME,*LOCAL_TIME_PTR;
接收节目信息指南,一般在开机的时候进行;但是,由于某些表如TDT/TOT表的信息的发送频率较低,如果在每次开机时接收可能导致开机时间延长。为了解决开机时间延长的问题,可以采用本方法在后台设立一个EPG接收引擎,在系统空闲的时候去接收TDT/TOT以及其它一些SI信息。
TDT/TOT表里提供系统时钟的可能。在每次开机时接收TOT表,设置本地时钟,提供为电子节目指南里的时间显示和预定节目的参考时间。SI标准里规定,在节目信息表里的时间都是UTC时间,而EIT预告节目的子表table_id及segment的划分也是以UTC时间来计算的,在接收EIT表设置过滤器时就要注意到跨天的情况。
举个例子,比如一个用户在北京时间的星期二的上午九点想查看北京时间星期二零点到二十四点之间的节目信息,转成标准时间就是星期一的下午四点到星期二的下午四点。如果按照北京时间零点到二十四点来分table_id的话,它应该是table_id=0x50的segment0-segment7(每个segment包含8个section,对应于3小时),而按照SI标准,则必须是依照UTC的时间来划分,则在北京时间星期二的上午九点是UTC时间的星期二的一点,而这时table_id已经刚好更新一小时,所以实际上用户只能得到北京时间星期二八点到二十四点的节目信息,且这段时间对应于table_id=0x50的segment0-segment5。
本发明给出了一种接收一周(7天)预告节目的table_id及segment的换算方法,以便用户得到正确的时间信息,流程如下:
从TOT表中得到本地时间的偏移utc_offset;
设置预告天数偏移offset_day(取值范围0~6)
如果偏移utc_offset<0,则utc_offset=utc_offset+24;
如果当前小时hour>=utc_offset && hour<24则:
如果offset_day=4(这一天跨table_id)则接收
table_id=0x50(如果是o-sch则为0x60),
起始start_segment=31-utc_offse/3
结束end_segment=31
并接收
table_id=0x51(如果是o-sch则为0x61),
起始start_segment=0
结束end_segment=(23-utc_offset)/3
如果offset_day=0(当天)则
table_id=0x50(如果是o-sch则为0x60),
起始start_segment=0
结束end_segment=(23-utc_offset)/3
如果offset_day为其它值,则
table_id=(offset_day-1)/4+0x50(如果是o-sch则
为(offset_day-1)/4+0x60),
起始start_segment=8*((offset_day-1)%4)
+(24-utc_offset)/3
结束end_segment=start_segment+7
如果当前小时hour<utc_offset(locally second day)则
如果offset_day=3(这一天跨table_id)则接收
table_id=0x50(如果是o-sch则为0x60),
起始start_segment=31-utc_offse/3
结束end_segment=31
并接收
table_id=0x51(如果是o-sch则为0x61),
起始start_segment=0
结束end_segment=(23-utc_offset)/3
如果off_set_day为其它值,则
table_id=offset_day/4+0x50(如果是o-sch则为
offset_day/4+0x60),
起始start_segment=8*(offset_day%4)
+(24-utc_offset)/3
结束end_segment=start_segment+7
最后所应说明的是:以上实施例仅用以说明而非限制本发明的技术方案,尽管参照上述实施例对本发明进行了详细说明,本领域的普通技术人员应当理解:依然可以对本发明进行修改或者等同替换,而不脱离本发明的精神和范围的任何修改或局部替换,其均应涵盖在本发明的权利要求范围当中。