DVB字幕

来源:互联网 发布:linux课程 编辑:程序博客网 时间:2024/04/28 21:07

冯卓明 龚家华刘卫忠 郑立新       (华中科技大学电子科学与技术系,湖北 武汉 430074) 摘要:本文研究在机顶盒上实现对DVB字幕的解码和呈现,基于ST公司的机顶盒软硬件平台,设计实现图形及字符型字幕的解码以及相关呈现技术与算法,分析字幕实现流程以及关键技术。 关键词:字幕(Subtitle),解码器,机顶盒,传输流 在DVB规范中定义了一种功能强大的字幕系统,用于在DVB数字电视广播系统中提供字幕服务,该系统基于位图方式,因此它可方便的用不同的字体以及颜色来呈现信函及信息;使用字幕提供的服务系统最大好处在于特殊字符的呈现,比如可方便显示中文和日文中的特殊字符。虽然该系统的主要应用于TV制作,比如台标叠加、滚动字幕等,但是运用该方法可以将任何图文叠加在电视机屏幕上。系统使用了彩色查找表(CLUT)方式来定义颜色,基于MPEG-2系统传输数据,字幕技术在数字电视高速发展的今天显得越来越重要。 1.解码器模型     在机顶盒中,底层的驱动模块提供了数据包(如字幕流)接收和基于区域的屏幕显示功能,而DVB字幕标准本身就是基于区域的,因此,字幕解码器完全实现为一个上层软件模块,将软解码得到的字幕信息由OSD驱动模块在屏幕上呈现出来。     下面是DVB字幕标准提供了一个字幕解码器的参考模型,本设计完全遵循该模型【1】。 PID过滤器 缓冲器 预处理 编码数据缓冲 字幕处理 复合缓冲 字幕象素缓冲 TS流 字幕解码器 图1 字幕解码器模型 图2是字幕解码器的软件系统结构: 应    用    程    序 字   幕   解   码   器 数 据 接 收 模 块 OSD 驱 动 模 块 硬   件 (  机  顶  盒  ) 图2 解码器系统结构     在软件系统中,字幕解码器实现为一个中间件,它提供一些必要的接口供应用层调用,如STSUBT_Init()、STSUBT_Start()、STSUBT_SetStream()等。整个系统的结构如上页图2所示。 字幕解码器提供少量必要的接口给应用层,用来实现对解码器的控制,如用STSUBT_SetStream()设置待解码的字幕流、用STSUBT_Show使能字幕在屏幕上的显示等。应用层对字幕解码器的控制流程如图3所示: 图3 字幕解码器控制流程 2、字幕解码系统的实现 2.1、字幕相关信息的获取     为了让字幕解码器正常工作,需要提供必要的信息给解码器,这些信息主要从PMT表中获取。     在一套节目中,字幕数据是作为私有数据流发送的,在PMT表中的stream_type字段为0x06,相应的PID为字幕流的PID,紧跟着的字幕描述子给出了字幕流的相关信息,如语言编码、字幕类型、composition_page_id和ancilarry_page_id等。将这些信息保存在节目列表中相关节目的描述结构中。 2.2、字幕流的接收     同其它各种数据(如PSI/SI信息)的接收一样,也要开一个通道(slot),并设置相应的通道缓冲区(用来保存该通道过滤出的数据),利用ST系统驱动模块的相关接口,实现字幕流的接收。     当在应用层用STSUBT_Open()打开字幕解码器时,用pti_allocate_dynamic_slot和pti_malloc_buffer分配字幕流的数据通道和通道缓冲区,并将其传给解码器。然后,将字幕流的PID、composition_page_id和ancilarry_page_id放在STSUBT_SetStream()的参数中,字幕解码器用这些信息来设置通道过滤器以及从字幕流中提取各种分段(segment)。     在字幕解码器中,创建了一个数据过滤任务。当调用STSUBT_Open()和STSUBT_SetStream()后,过滤器就可以工作了,这时,调用STSUBT_Start()启动过滤器。在过滤器任务中,创建了一个无限循环,该循环利用link驱动模块提供的各种接口(如pti_copy_pes_packet_to_linear),从硬件接收字幕流PES数据包。接下来,进行字幕流PES包的解析。首先,从PES包头中提取出PTS信息并保存在一个缓冲区中(coded data buffer),它提供了字幕信息显示的时间信息。然后,利用两个page_id(composition_page_id和ancilarry_page_id)从PES包中提取各种字幕分段(如PCS、RCS等),存入编码数据缓冲区中。所有这些分段都以类似的格式保存在coded data buffer中,供解码模块使用。 2.3、字幕信息的解码     在字幕流过滤模块中,已经得到了字幕信息的各种分段,现在,需要对这些分段进行解码,提取出具体的字幕信息。创建一个单独的任务,该任务从coded data buffer中取出各种字幕分段,再根据分段的类型对不同的分段进行解码【2】。     PTS分段保存了字幕显示的时间信息,利用它来计算相关字幕信息的presentation time。该分段的解码很简单,将PTS值保存下来即可。     对于PCS分段的解析,最重要的字段就是page_state字段。page_state有三种取值:normal case(0x00)、acquisition point(0x01)、mode change(0x02),三种状态的具体意义见字幕标准。在该解码器的实现中,acquisition point和mode change当作相同的情况处理,都认为是一个epoch的开始,这时,解码器的可见区域列表是空的,需要复位解码器(一个epoch的相关信息不需要保存到下一个epoch)。接下来收到页状态为mode change的PCS分段是,才建立可见区域列表,这些区域是当前需要显示出来的。将PCS分段的相关信息(如字幕显示的time out值)保存在一个结构(PCS_t)中,并将其存入PCSbuffer中,供另外一个任务(Display Engion)使用。     在一个epoch开始时,使用RCS分段引入所有在该epoch中需要用到的区域的描述,而PCS中列出的区域仅仅是它的一个子集(当前需要显示的区域)。另外,如果一个display set的内容需要作出一定的改动,就需要引入RCS分段,同时,需要改动的区域不能出现在PCS分段所列出的区域中,这使得我们可以在区域不可见的时候修改它。解析RCS分段时,将相关信息保存在一个RCS_t结构中,并将其加入RCS列表中。在RCS解析时,分两种情况处理:解码器状态为STSUBT_Normal_Case和STSUBT_Acquisition。如果解码器工作状态为STSUBT_Acquisition,这是一个epoch的开始,需要为该RCS分配Pixel Buffer,用来存储该RCS所关联的对象的数据(如一幅位图的象素)。同时,需要找到该RCS对应的CLUT表(如果已经存在),如果不存在,还要为CLUT表分配存储空间。另外,也要为RCS分段中所列出的所有对象分配存储空间,并建立一个对象列表。当解码器的工作状态为STSUBT_Normal_Case时,根据region_id找到已经存在的RCS并重建该RCS的对象列表即可。     DVB字幕信息的颜色是基于CLUT表的。DVB字幕标准定义了默认的CLUT表和颜色映射表,但用户根据自己的需要可以建立自己的CLUT表,并用CLUT分段发送下来。解码器解析CLUT分段时,如果相应的CLUT表已经存在(根据CLUT_id字段判断),只需要修改相应的颜色即可,如果CLUT表不存在,为该CLUT表分配存储空间,并用CLUT分段中列出的颜色加以填充。     字幕信息中所有需要显示出来的数据都是用对象分段发送的。对象的解码原理上比较简单,因为DVB字幕标准对数据的编码格式都做了约定(游程编码)。我们的工作只是按照标准所定义的数据格式,把字幕象素数据提取出来,并存放到Pixel Buffer中特定的位置。在对象的解码过程中,应该特别注意的就是每一个象素在Pixel Buffer中的存储位置的确定。在DVB字幕标准中,对对象的右边界是否对齐没有要求,对象分段中没有矩形区域中所有象素的编码信息,所以,我们要处理好象素的定位,如对象右边界处的换行等。 2.4、字幕信息的显示     字幕信息的呈现主要由两个任务来实现:engine task和timer task。对DVB字幕标准来说,engine task实现字幕显示的准备工作,如把特定对象的象素数据写入显示缓冲区中,timer task控制字幕的显示,当一个display的presentation time到达时,就把相应的字幕信息在屏幕上显示出来,如果显示超时则隐藏显示的字幕。     在engine task中,从PCSbuffer中取出PCS分段解析时所存入的PCS_t结构,并根据该结构中的相关信息,将需要显示的区域的象素数据(对象解析时填入的)注入到区域位置所对应的显示缓冲区中(使用prepare_display()),然后,生成一个STSUBT_DisplayItem_t结构,该结构中的相关信息供timer task任务实现字幕的显示。该结构中,最重要的成员是display_descriptor,它由prepare_display()函数建立,其中包括了当前需要显示的区域的列表以及相关信息,详细情况见字幕解码器源代码。engine task建立的所有STSUBT_DisplayItem_t结构都保存在DisplayBuffer中,供timer task读取。     timer task实现字幕信息的显示控制。首先,它用fcBuffer_Remove_Timeout()函数从DisplayBuffer中取出一个DisplayItem,如果超时,表明缓冲区中没有合法的数据,应该隐藏屏幕上显示的字幕信息。接下来,要判断新的字幕信息的时间信息,如果新的字幕信息的显示时间在当前活动字幕显示超时值之后,要先等待当前活动字幕显示的超时,并在超时后隐藏屏幕显示,当新的字幕信息的presentation time到达时,调用show_display()函数将字幕信息显示出来,并设置新的超时值。 2.5、屏幕显示服务的提供     在上面的显示控制模块中,调用了prepare_diaplay()、show_diaplay()等函数,这些函数实现了真正的屏幕显示工作,它们时OSD服务的提供者。为了控制字幕在屏幕上的正常显示,需要利用OSD驱动模块提供的区域操作功能。在该解码器的实现中,我们将OSD显示服务实现为一个单独的模块,它提供了以下接口:        (1)、STSUBT_OSD_InitializeService():完成初始化工作。        (2)、STSUBT_OSD_TerminateService():完成清楚工作。 (3)、STSUBT_OSD_PrepareDisplay():完成屏幕显示的准备工作,如创建区域、将要在区域中显示的象素数据注入到对应的显示缓冲区中。 (4)、STSUBT_OSD_ShowDisplay():在屏幕上显示相应的区域。 (5)、STSUBT_OSD_HideDisplay():隐藏屏幕区域的显示。     我们以回调函数的形式利用这些接口。在应用层调用STSUBT_Show()时,将这些函数的地址(即函数名)保存在某个特定的地方(保存为函数指针)。需要相应的OSD服务时,就通过函数指针调用对应的回调函数。 2.6、解码器缓冲区的管理     对于任何一个程序来说,提供一个良好的、高效的缓冲区管理方案是必要的,因为缓冲区误操作是导致程序不稳定的最主要原因之一。对于字幕解码器来说,为了实现高质量的显示效果(如新旧字幕的平滑过渡等),也必须有一个好的缓冲区管理方案。     在该解码器实现中,主要用到了四个缓冲区:编码数据缓冲区CodedDataBuffer;显示单元缓冲区DisplayBuffer;CompositionBuffer;PixelBuffer。CodedDataBuffer存储DVB字幕分段,DisplayBuffer存储显示单元DisplayItem(由Engine任务创建,Timer任务使用),CompositionBuffer存储各种字幕信息(如CLUT表、区域信息等),PixelBuffer存储字幕的象素数据。     对上面提到的四种缓冲区,都使用了以下两种管理策略。第一,所有的缓冲区都在解码器初始化的时候分配,以后使用时再从其中分配(使用指针),这就避免了频繁的内存分配操作,提高了效率,也提高了内存使用的安全性。第二,用信号量实现缓冲区的有效管理,如用互斥信号量(值为1)保证在任一时刻只有一个任务对其进行操作。同时,为方便缓冲区操作,对每个缓冲区都定义了一套接口函数,避免了直接操作缓冲区指针,增加了缓冲区操作的透明度,也提高了程序代码的可读性。 3、关键技术问题分析 3.1、字幕解码器采用双缓冲策略     为了实现新旧字幕显示之间的平滑过渡,不出现明显的闪烁现象,必须采用双缓冲策略。对于在一个epoch中保存字幕信息的缓冲区,如CompositionBuffer和PixelBuffer,都建立两个大小相同的缓冲区。当收到一个页状态page_state为mode_change的PCS分段时,解码器重新启动,将CompositionBuffer和PixelBuffer切换到备份的另一个缓冲区。这样,在新的字幕显示出来以前,旧字幕的相关信息还保存在缓冲区中,当新的字幕准备好以后,可以马上将其显示出来,不会出现闪烁和延迟。 3.2、字幕显示同系统界面显示的冲突     字幕显示和机顶盒控制界面的显示都是基于区域的。对于两个不同的区域来说,不能占有相同的水平扫描线,否则创建区域会失败。在ST系统的usif模块中,为了绘制系统的界面,创建了三个屏幕区域,它们占满了整个屏幕,所以,字幕显示时创建区域会失败,从而导致字幕显示不出来。因此,在显示字幕以前,要先将这三个区域隐藏起来,当使用系统菜单时,首先隐藏字幕的显示,然后再将用于绘制界面的三个区域显示出来。     这样,我们就可以解决字幕显示和界面显示之间的冲突。当然,这样操作起来比较麻烦,任何界面操作(比如调节音量),都要先隐藏字幕显示,再将绘制界面的三个区域显示出来,界面操作完成后,又要先隐藏这三个区域,再使能字幕显示。 3.3、系统OSD模块对区域数目的限制     在OSD驱动模块初始化的时候(main()中的OSD_Initialize()函数中),对可以创建的区域的最大数目设置为5个,但如果用于字幕的显示,显然是不够用的。所以,我们要对该值进行修改,加大为16个或者更多。 4、小结     DVB字幕系统在机顶盒上的实现主要集中在以下几个方面:(1)、根据DVB字幕解码器模型在嵌入式系统中设计字幕解码器软件系统;(2)、实现图形字幕的解码;(3)字符模式字幕的解码和显示功能。(4)、缓冲算法的实现。     目前本系统已经在试验环境下应用,可实现自办节目的数字台标制作与添加,滚动字幕广告等功能,运行情况良好。 参考文献: 【1】    DVB字幕标准ETS 300 743:Digital Video Broadcasting (DVB) Subtitling systems : 【2】    Subtitling Decoder API:ST Corporation