一个针对日本的数字电视应用(ISDBT)里字幕处理有一些问题,规范文档庞大又复杂,读起来还觉得语焉不详。接手遗留项目尝试处理字幕显示的问题,边读spec边看代码,先猜测、试图理解既有逻辑,再分析问题产生的原因,寻找解决方案。

文档是ARIB STD-B24 Version 5.1 Volume 1(Data Coding and Transmission Specification for Digital Broadcasting)。文档中出现的"02/0"表示0x20,“07/15"表示0x7F。

1. 一个data group里的多个data unit
按照规范文档定义(见文档的Table 9-1
Data group),一个字幕数据(one caption data)最多由256个data groups组成。data
group结构里有6位的group id,2位的group version,8位的group link number(从0开始),8位的last
group link number(标明最后一个group)。但实际上绝少见到一个data
group放不下的字幕数据。目前的实现只考虑了一个data group。

根据group id,group可以分为caption management group和caption statement group(见文档的Table 9-2 Correspondence to caption data and data group identification),这两类data group结构都在最后包含了若干个data unit(见文档的Table 9-3 Structure management data和Table 9-10 Caption statement data)。

观察码流发现,大部分情况一个data group里只有一个data unit,但还是不止一次地看到同一个data group里有两个data unit。出现这种情况时,第一个data unit都是0x0C开始、包含字体大小、颜色和字符编码的数据(data unit的不同类型参见文档的Table 9-12 Types of data unit,这里只讨论0x20,即statement body),第二个data unit都是一个TIME命令(9D 20 XX 0C,这里是十六进制表示,下同)。之前的实现假设一个data group里如果有多个data unit,每个data unit也都是0x0C开始、包含字体大小、颜色和字符编码的数据,如果只有命令而没有字符编码数据则会被丢弃。

怎么理解只有TIME命令的data unit?目前只能认为是补充前一条data unit(0x0C开始、包含字体大小、颜色和字符编码的数据)。

观察码流还发现另一种情形,data group里只有一个data unit,这个data unit的数据也只有一个0x0C。前面一直没有提到0C命令,这里讨论一下。在文档的Table 7-14 Control function character set code table中可以看到,0C是清屏命令(CS),Table 7-15 C0 control set中定义为“Display area of the display screen is erased”。在之前的实现里,没有字符数据的data unit都被丢弃了。可是为什么会送出一个单独的0x0C呢?在某一个码流里还看到连续两个这样的data group,只有一个data unit,里面只有一个0x0C。

根据0C的定义,我们可以这样理解每一个包含字符数据的data unit,0x0C开头意即清除上一条字幕,开始准备显示当前字幕。TIME命令后如果跟随0x0C(9D 20 XX 0C),意即presentation了duration的时间后清除(TIME命令的分析见下文)。单独的一个0x0C是不是意味着,没有要显示的字 幕,但也要清除上一条字幕呢?目前只能这样去实现了。

2. caption management group中是否有DC
通过解析
DMF(display mode)来判断DMF之后是否有一个单字节的DC(display condition
designation)(见文档的Table 9-3 Structure management data),文档的Table 9-5
Display mode和Table 9-6 Designation of display
condition解释了DMF和DC,可还是没看懂DC。观察码流,DMF的值总是10(十进制),按照规范文档定义,DMF等于12、13或14时,
后一个字节是DC。

3. TIME命令
按照文档“ARIB STD-B24 Version 5.1 Volume
1”的解释,9D是TIME命令(见文档的Table 7-14 Control function character set code
table),在Table 7-16 C1 control set中定义了三种情况,观察测试用的一些码流,情况(1)最常见,即"TIME
02/0 P2”,情况(5)“TIME 02/8 P2"和情况(6)“TIME
02/9"不多见,这里只分析情况(1)。三种情况的解释请见下面的注释代码段。

“TIME 02/0 P2"的命令模式意味着遇到9D 20 XX这个模式,取出XX的低6位作为P2,P2设置字幕显示的duration时间,单位是0.1秒,取值范围是从0x40到0x7F。

观察实际的码流,最常见到的命令模式是9D 20 XX 0C。也比较好理解,即当前字幕显示P2/10.0秒之后清屏。之前的实现把0C作为该模式之必要。

可是,在某一个码流里发现这样的情况:9D 20 7F 9D 20 7F 9D 20 7F 9D 20 44
0C。按照之前的处理逻辑,第一组9D
20的P2后不是0C,于是放弃把9D当作TIME命令来处理,跳过9D,继续解析20和7F(20会被作为空格处理,7F本是删除,这里忽略不作处
理),直到最后一个9D,能找到那个0C,才会把44取出来计算P2,而计算出来的duration时间是0.4秒。

这很奇怪,0.4秒的presentation时间让人几乎无法看清,这字幕就没有意义了。规范文档里没有提到连续多次送来TIME
02/0命令该如何解析。连续几天没有想出所以然,突然有一天在反复阅读规范文档时想到,会不会是累加。因为P2的取值范围是0x40到
0x7F,0x7F表示6.3秒,如果需要表达大于6.3秒的duration时间,岂不是需要累加表示。如果是这样,连续的9D
20命令模式中除最后一个外,前面的P2都应该是7F,这需要观察更多码流文件来验证,现在不具备这个条件。如果这样理解,我们发现的这个情况
中,duration时间应该是19.3秒。

              /* TIME, Table 7-15 C0 control set
(1) Wait for process: TIME 02/0 P2
Processing of code as of this code is stopped for set duration by parameter
P2. Parameter P2 is in the range of 04/0 to 07/15 and set by binary of 6 bit
from b6 to b1. (b7 and b8 are not used.) Designating time should be 0.1 sec.
(5) Time control mode(TMD): TIME 02/8 P2
TIME 02/8 04/0: Free
TIME 02/8 04/1: Real
TIME 02/8 04/2: Offset
TIME 02/8 04/3: Unique
(6) Presentation start time(STM), Playback time(DTM), Offset time(OTM),
Performance time(PTM), Display end time(ETM):
TIME P P11-P1i I1 P21-P2j I2 P31-P3k I3 P41-P4m I F
P = 02/9 I = 02/0 I1-I3 = 03/11
P11-P1i = 03/0-03/9 (decimal)time
P21-P2j = 03/0-03/9 (decimal)minute
P31-P3k = 03/0-03/9 (decimal)second
P41-P4m = 03/0-03/9 (decimal)millisecond
F = 04/0 STM, DTM
04/1 OTM
04/2 PTM
04/3 ETM
At performance time, I3, P41 --- P4m is not sent out.*/

4. 多行显示字幕
字符编码数据中如果遇到0x0D,认为是要换行,多数码流里用0D命令来显示人物对话,譬如电影电视剧中AB两人的对话,一般0D前后的字幕还会用不同的颜色显示。

还有一种情况需要换行,即根据data
unit中的字体大小设定、或者应用本身的设置,视频画面的宽度不够在一行里显示所有字符,需要做截断换行处理。之前的实现对字幕显示位置做了限制,只能
显示两行字幕,于是当第一行字幕(0xOD前)需要截断换行时,本该显示的第二行(0x0D)字幕就无法显示了。目前调整为放宽限制至三行。如果0x0D
前后的字符数据都有点长、需要截断换行,0x0D后的字幕将会被截断,只能部分显示。

5. 清除字幕的判断条件
字幕显示的时间可以用音视频同步的参考时钟来和视频同步,字幕数据也来自
PES包,有自己的pts(presentation time
stamp)。但是由于未知原因,我们获取到字幕数据时,其pts已经落后于参考时钟。也就是说,总是晚一步贴字幕。这个问题尚未解决。

目前的处理逻辑是,获取到字幕数据,转成图片,确定往视频帧上贴图的位置,放到队列里,渲染视频帧时如果字幕队列里有内容(read
index),取出来比较其pts和参考时钟,该显示了就贴图,然后看该字幕是否有上文提到的duration信息(9D 20
XX),如果有,将其pts加上duration后和参考时钟比较,如果过期就清除。

仅仅这样还不够,如果当前字幕没有duration信息怎么办?观察码流发现,很多场景字幕不带有duration信息,我们猜测认为,这种情况下
是要依赖下一条将要显示的字幕到了该显示的时间来取代当前字幕,相当于为其清屏(每条字幕都是0C开头)。因此,不带有duration信息的字幕将一直
显示,直到其下一条字幕需要显示了。

这种场景的典型例子就是一人或多人不停地说话,后一条字幕替换前一条。如此猜测,也包含这样一个假定,如果这样集中的说话或对话结束了,接下来无人
说话或者干脆插播广告,最后一条字幕是一定会带有duration信息的。如果没有,这条字幕将一直显示着。之前我们的实现经常错误地丢弃了
duration信息,就出现这样的问题,补丁方案是对没有duration信息的字幕,赋默认值3秒。现在去掉了这个补丁,因为它会影响下一条字幕的显
示。

这又带来一个问题,如果当前字幕的duration时间比较长,譬如有10秒,而期间下一条字幕的pts已经到了该显示的时间。谁的优先级高?目前的实现是当前字幕的duration优先级高。

前面提到过,有单独的data unit,并且只有一个0x0C,而这个0x0C也有pts,这又意味着什么?目前的实现是清屏(即贴这个空字幕)时间要看0x0C的pts,而不是接受到0x0C立刻清屏。

ISDBT中CC的处理疑问的更多相关文章

  1. cocos2d-html5学习笔记(六)--alpha2中cc.Sequence.create中的bug

    cocos2d-html5学习笔记(六)--alpha2中cc.Sequence.create中的bug http://blog.csdn.net/allenice1/article/details/ ...

  2. 关于SQL注入中编码问题的疑问

    提到SQL注入的绕过,编码是其中最普通的一种方法,最常用的URL编码.之前一直有个疑问,编码与未编码到底有哪些地方存在区别? 以下是本人自己对URL编码的一些见解,可能有错误的地方欢迎大佬们指正. 什 ...

  3. 关于HashMap中的扰动函数的疑问

    最近再看jdk8的hashmap源码,当看到这一步的时候有点疑问,去网上搜了一下,看到的所有文章基本上都是一篇抄一篇的(反正目前各大社区就是这么个状况),那个意思就是让高16位也参与运算,增加结果的随 ...

  4. KMP算法中的几个疑问

    KMP算法next数组求解实现 首先我们通过应用场景将KMP算法中用到的名词做一个说明: 在一个字符串(string1)中查询是否存在另一个字符串(string2). 在字符串匹配算法中,我们通常将字 ...

  5. cocos2dx+lua中cc.EventListenerMouse:create()的bug

    今天在调试项目的时候用到了鼠标事件的监听 在创建事件监听器的时候出了问题 cc.EventListenerMouse:create() 这句返回值为nil 原来这是cocos2dx引擎的一个bug,t ...

  6. 关于C++中操作符重载的疑问 :四个运算符=, ->, [], ()不可以重载为全局函数(友员函数)

    转载自:http://blog.csdn.net/u014610226/article/details/47679323     以下是对C++中不能重载为友元函数的四个运算符进行了详细的分析介绍,需 ...

  7. C++解析(25):关于动态内存分配、虚函数和继承中强制类型转换的疑问

    0.目录 1.动态内存分配 1.1 new和malloc的区别 1.2 delete和free的区别 2.虚函数 2.1 构造函数与析构函数是否可以成为虚函数? 2.2 构造函数与析构函数是否可以发生 ...

  8. 关于gc日志中Desired Survivor的疑问和对象晋升老年代的小结

    问题背景 (下面的所有内容都是根据书上的Serial/Serial Old收集器下的情况) 在<深入理解JVM>一书中的——3.6.3长期存活的对象将进入老年代的介绍中, 一个例子的jvm ...

  9. HashMap中的hash算法中的几个疑问

    HashMap中哈希算法的关键代码 //重新计算哈希值 static final int hash(Object key) { int h; return (key == null) ? 0 : (h ...

随机推荐

  1. Flask-Restful详解

    Restful API规范 restful api是用于在前端与后台进行通信的一套规范.使用这个规范可以让前后端开发变得更加轻松.以下将讨论这套规范的一些设计细节. 协议: 采用http或者https ...

  2. ThreadLocal管理Connection

    ThreadLocal管理Connection 每一个用户都对应有一个单独线程,每一个线程都有一个数据库连接对象Connection对象接待它. 一个用户对应一个线程,这个线程中的Connection ...

  3. configure: error: You need a C++ compiler for C++ support.[系统缺少c++环境]

    一.错误configure: error: You need a C++ compiler for C++ support.二.安装c++ compiler情况1.当您的服务器能链接网络时候[联网安装 ...

  4. Linux宝塔软件安装

    yum install -y wget && wget -O install.sh http://download.bt.cn/install/install.sh && ...

  5. mysql添加用户、修改权限,修改登录权限ip

    1.添加用户 1.1 登录MYSQL: @>mysql -u root -p @>密码 1.2 创建用户: 格式:grant select on 数据库.* to 用户名@登录主机 ide ...

  6. 复刻smartbits的国产网络测试工具minismb-如何添加数据流

    复刻smartbits的国产网络性能测试工具minismb,是一款专门用于测试智能路由器,网络交换机的性能和稳定性的软硬件相结合的工具.可以通过此工具测试任何ip网络设备的端口吞吐率,带宽,并发连接数 ...

  7. shell脚本案例分享 - 业务系统日志自定义保留或删除需求

    需求说明:  线上某些业务系统的日志不定期产生, 有的每天产生, 有的好几天才产生, 因为系统只有在用的时候才产生日志,日志文件均存放在以当天日期命名的目录下. 当日志目录越来越多时就需要处理, 由此 ...

  8. Docker Swarm 日常运维命令笔记

    之前介绍了Docker管理工具-Swarm部署记录,这里简单总结下Docker Swarm的日常维护命令,以作为平时运维笔记. Swarm作为一个管理Docker集群的工具,首先需要将其部署起来,可以 ...

  9. java web 机试

    经过近一个月的学习,我们的java web已经学习完了. 这是我们这次的机试题. 一:题目 请利用MVC设计模式,并使用JSP.Servlet.JSTL和JQuery等技术实现动态条件的分页显示查询. ...

  10. android开发学习笔记系列(6)--代码规范

    在开发android的时候,我对自己写的代码很是不满,原因在于自己看到别人的代码,很是头痛,原因很简单,别人写的代码,我就要去猜他的意思,极其烦恼,嗯,就是他没有遵循代码规范,因此我在博客园上寻找一篇 ...