一個針對日本的數字電視應用(ISDBT)裏字幕處理有一些問題,規範文檔龐大又複雜,讀起來還以爲語焉不詳。接手遺留項目嘗試處理字幕顯示的問題,邊讀spec邊看代碼,先猜想、試圖理解既有邏輯,再分析問題產生的緣由,尋找解決方案。git
文檔是ARIB STD-B24 Version 5.1 Volume 1(Data Coding and Transmission Specification for Digital Broadcasting)。文檔中出現的"02/0"表示0x20,"07/15"表示0x7F。ide
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開始、包含字體大小、顏色和字符編碼的數據,若是隻有命令而沒有字符編碼數據則會被丟棄。
this
怎麼理解只有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。
code
根據0C的定義,咱們能夠這樣理解每個包含字符數據的data unit,0x0C開頭意即清除上一條字幕,開始準備顯示當前字幕。TIME命令後若是跟隨0x0C(9D 20 XX 0C),意即presentation了duration的時間後清除(TIME命令的分析見下文)。單獨的一個0x0C是否是意味着,沒有要顯示的字幕,但也要清除上一條字幕呢?目前只能這樣去實現了。orm
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等於十二、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秒。
<!-- lang: cpp --> /* 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馬上清屏。