作者| 阿裡文娛無線開發專家 黃陂
優酷外挂字幕的目标是實作對全平台和全機型的覆寫,同時能支援 ass 之外的多種字幕格 式、支援字幕互動等多種創新玩法。本文将分享我們對外挂字幕的探索與實踐。
一、字幕的簡單科普
在開始之前,先科普一些字幕知識。一般而言,字幕分為硬字幕、軟字幕和外挂字幕。
硬字幕,或者叫内嵌字幕是視訊生産時就直接疊加到視訊畫面上的,字幕不可取消、不可編輯更改,不支援多語言及國際化。軟字幕是将字幕與視訊打包在一起,形成獨立于音頻和視 頻 track 流的單獨一路 subtitle track 流,整體打包在視訊檔案中。
我們今天介紹的外挂字幕,是将字幕做成一個獨立檔案,這些字幕檔案可以有多種格式, 獨立下發及展示。優點是不破壞視訊畫面,可随時根據需要更換字幕語言,并且可随時編輯字 幕内容并重新生産釋出;缺點是由于字幕檔案單獨存在,可能會有字幕缺失、字幕跟音視訊不 同步等問題;另外,為了確定端上播放必須有字幕,整個鍊路需要有複雜的硬字幕兜底處理邏輯。
二、優酷視訊的字幕現狀
1. 優酷字幕鍊路
目前優酷使用的外挂字幕采用的是 ass 格式,優酷字幕生産鍊路大緻如下:
1)單獨生産一路字幕 ass 流,并随同對應視訊 vid 一起入庫;
2)将字幕壓入視訊流,主要出于兩種考慮:主客端側有各種降級邏輯,為了保證使用者在端 側播放上字幕必須出現,是以必須有一路包含硬字幕的視訊流;OTT 等端還不能支援外挂字幕。
2. 優酷主客端的實作
如上圖所示,優酷的播放器核心支援多個播放執行個體,每個執行個體在播放鍊路上由三個主要部分構成:
1)資料源處理子產品(Sourcer Module):負責下載下傳和解複用(demux);
2)解碼子產品(Decoder Module):對音頻、視訊等流的資料包進行解碼,得到圖像和聲音等 raw 資料;
3)消費者子產品(Consumer Module):同步處理,并做渲染。 目前優酷的外挂字幕是在優酷私有播放器播放核心裡實作的。為了對接優酷私有播放器架構,在核心裡加了單獨一路 subtitle 的 pipeline。主要也分為以下三個子產品:
1)subtitle sourcer:負責字幕檔案的下載下傳;
2)subtitle decoder:負責字幕的解析;
3)subtitle consumer:根據上層傳下來的字幕配置資訊,首先渲染成圖檔,然後與目前時鐘做同步處理,并輸出給 openrender 去做合成和渲染。
優點:
1)能有效複用核心中公共子產品,比如時鐘擷取、利用 ffmpeg 實作對字幕的下載下傳和解析等功能;
2)處理字幕特效比較靈活、高效。
缺點:
1)與優酷私有播放器播放核心緊密綁定,系統播放器無法支援外挂字幕;
2)與 openrender 耦合較緊,OTT 端無法支援外挂字幕;
3)目前隻支援 ass 格式,可擴充性不好;
4)鍊路長,并且與播放核心 AliPlayer 耦合太緊,線上問題不好排查,維護成本高。
三、新架構實作
1.外挂字幕獨立化重構目标:
不區分播放器類型(系統播放器、私有播放器)、實作對全平台(Android 主客、iOS 主客、 OTT 裝置、mac 端等)和全機型的覆寫,同時能支援 ass 之外的多種字幕格式、支援字幕互動等創新玩法。
2.子產品圖
新架構主要分為以下三個子產品:
1)Player SDK 除了之前與播放的處理邏輯外,加了字幕的三個子產品,包括:
a)subtitle cmd:與字幕引擎的互動子產品,主要是給字幕引擎下發指令。
b)subtitle msg handler:消息處理器,主要用來接收字幕引擎上抛的消息,包括每一幀對應 的字幕資訊,以及出錯或其他消息上報,使用者上層做處理和埋點統計。
c)subtitle render:渲染子產品,主要用來渲染字幕資訊。
2)Subtitle Engine
整個外挂字幕處理引擎,負責 subtitle 的核心處理邏輯,覆寫除字幕渲染之外的所有功能。 後面會詳細介紹 Subtitle Engine。
3)Player Kernel
渲染分兩種實作方式:
a)上層渲染:把字幕資訊回調給上層去做渲染,整個外挂字幕鍊路跟 Player Kernel 沒有任 何關系。這種場景下 Player Kernel 可以不用考慮在内;
b)openrender 渲染:需要先在 Subtitle Engine 側渲染成圖檔,然後從與 openrender 對接的 middle ware 層擷取目前 pts,做同步之後輸出給 openrender 去做合成和渲染。這種場景下,字 幕的渲染在 openrender 裡面。
3. Subtitle Engine 詳圖
SubtitleEngine:整個外挂字幕處理引擎,負責 subtitle 的核心處理邏輯,覆寫除字幕渲染之 外的所有功能。整個 Subtitle Engine 分為以下 4 大塊:
1)Msg Router 消息路由,主要負責:
a)從 Player SDK 到 Engine 的指令消息下發,包括所有的字幕消息控制邏輯,比如設定subtitle url、開始播放、暫停、恢複、停止、設定壓流廣告等等。
b)從 Engine 到 Player SDK 的資料消息上報,包括字幕資訊的上報,錯誤消息、埋點資訊 等的上報。
封裝這樣一個 Msg Router,我們就可以隻實作幾個簡單的 API 就能實作所有功能及其擴充。 這樣能有效避免之前播放核心擴充的痛點,加一個 api 就得整個播放鍊路一路加下去,要修改 的地方非常多,不利于擴充。
2)Common:主要是一些公共功能的封裝,包括開關、配置,埋點資訊,錯誤資訊上報, tlog 等。
3)Sourcer:主要功能是點播、直播字幕檔案的下載下傳、緩存、解析。從上到下主要分為以下幾個子產品:
a)downloader:用于下載下傳線上字幕檔案;
b)cache:用于緩存處理邏輯。某一部劇的 ass 檔案一般不會經常更新,我們在下載下傳到本地 之後可以先緩存下來,下次使用者播同樣的劇可以直接使用本地的 ass 檔案。當然,我們會有一 個時效及更新機制,會定期清掉本地的緩存,讓使用者去重新下載下傳。本地的緩存檔案也需要考慮 完整性檢測機制,來保證檔案的完好無損;
c)sniffer:考慮到我們要支援多種外挂字幕格式,需要一個嗅探器去探測字幕格式;
d)parser:字幕解析子產品。不同格式需要有不同的 parser 實作;
e)track:對解析後字幕資訊的封裝,包括字幕文本,開始時間 pts,持續時長,字型配置資訊等。得到這樣一個 track 資訊,consumer 就能擷取後去消費了。
4)Consumer:主要功能是實作與目前播放進度的同步輸出,各種與業務相關的邏輯都在 這裡實作。比如同步、seek、跳片頭、壓流廣告、倍速等等。
a)driver:Consumer 内部通過 driver 來驅動整個流程,driver 提供 PTS 與更新頻率來驅動Consumer 與 Sourcer 互動,擷取目前 PTS 下的字幕資料。pts 的擷取根據渲染方式的不同有兩種方式:從上層擷取(見箭頭(5));從 openrender 擷取(見箭頭(6))。
b)provider:根據從 driver 裡傳過來的目前 pts,去 sourcer 解析生成的 track 中擷取對應的 字幕行資訊。
c)line data manager:基于行的字幕資料管理子產品。因單條字幕擷取頻率在 10~100 次/s,line data manager 内部通過緩存、預解析政策,降低與整體字幕資料的互動頻率,優化了單行字幕 資訊擷取開銷。
d)subtitle line data:從 line data manager 我們擷取到對應的字幕行資訊,包括字幕文本, 開始時間,持續時長,字型設定等資訊。
e)兩種渲染方式:
方式一:openrender。我們需要 render to image,這一步隻是通過 openrender 渲染時才需要。 我們先把 subtitle line data 渲染成一張圖檔,然後輸出給 openrender 去做渲染(見箭頭(9));
方式二:上層渲染,我們在 consumer 流程中 driver->provider->line data manager->subtitle line data 擷取到字幕行資訊,然後轉發給 msg router,再上抛給 Player SDK 去做渲染處理。
綜上,整個外挂字幕的處理流程就完成了。除了 option 之前通過 openrender 渲染是與私有 播放器核心挂鈎之外,其他子產品、包括通過上層渲染的所有鍊路都與播放器類型無關了。
四、展望
在新架構下外挂字幕功能的展望:外挂字幕能在新架構下快速鋪量,并且能有更多創新玩 法提供給我們的使用者。
1)快速鋪量:我們設計新架構不依賴于播放器類型、并且功能獨立,這有利于外挂字幕功 能在不同裝置上的鋪量,也有利于後續的功能獨立開發和更新;
2)字型、顔色、大小調整;
3)非視訊畫面顯示:不依賴于視訊播放,聽劇模式下在鎖屏界面顯示字幕;
4)随處顯示能力:可顯示在視訊畫面裡面,也可以顯示在視訊畫面外;
5)互動能力:字幕區域可拖動,可單擊,可滑動取詞,可互動;
6)各種字幕特效,可以做成類似于表情包,并且可以做成會員權益,給會員客戶帶來更大的價值。