DirectVobSub(VsFilter)的基本原理和實現實現

VsFilter是一個字幕疊加的庫,目前由MPC-HC維護,其最新版本爲2.41,本文即基於此版本。
從實現層面上來說,其內部是一個DirectShow的Transform Filter,負責將字幕文件中的字幕轉換成bitmap併疊加在從輸入Pin進入的圖像,並交給輸出Pin。
線程結構:

VsFilter接口是線程安全的,主要包含兩個線程:
1.文件監控線程:CDirectVobSubFilter::ThreadProc,監控文件的修改,如果文件狀態變化,需要重新加載;
2.字幕圖片生成線程:CSubPicQueue::ThreadProc,CSubPicQueue內部維護一個隊列,字幕文件加載後,立刻轉換10條字幕成圖片,放入該隊列,每消耗一個圖片,則立即補充一個,直到字幕播放結束。

主要模塊:
1.CDirectVobSubFilter:Filter的實現,提供Filter接口實現、Pin的基本操作、Transform函數實現(也就是疊加)、文件監控線程;2.SubPic:字幕圖片緩存維護,CSubPicProviderImpl數據提供者接口;3.Subtitles:字幕到圖片轉換的實現算法,主要包含:1)STS:CSimpleTextSubtitle,加載字幕文件獲得字幕信息條目STSEntry;struct STSEntry { CStringW str; bool fUnicode; CString style, actor, effect; CRect marginRect;  int layer; REFERENCE_TIME start, end; int readorder;};2)STS:CRenderedTextSubtitle,派生自STS:CSimpleTextSubtitle,從字幕信息條目STSEntry生成並維護字幕對象CSubtitle,CSubtitle(派生自CAtlList<CLine*>)->CAtlList<CWord*>->CPoint,也就是維護了字幕形狀的基本信息;
3)Rasterizer:光柵化(bitmap生成),CWord從其派生,將字的形狀信息轉化成像素,從而可以疊加到圖片上。
主要流程:
1.加載VsFilter.dll,創建VsFilter的com實例,並獲取IID_IDirectVobSub接口,此時將創建文件監控線程;
2.連接Source Filter、VsFilter、Render Filter,此時將創建字幕圖片生成線程;
3.調用IID_IDirectVobSub接口的put_FileName方法,設置字幕文件,VsFilter將加載該字幕文件,同時字幕圖片生成線程將建立字幕圖片緩存;
4.Filter Graph開始工作後,CDirectVobSubFilter::Transform函數獲得輸入Sample以及時間戳,通過輸入sample的時間戳查找SubPic緩存隊列中的圖片,如果查不到,則從Entry中查找並生成bitmap,然後將字幕bitmap與輸入sample的surface進行疊加(alphablt),疊加完成進行適當的轉換(轉成YUY2),然後拷貝到輸出Sample。SubPic緩存隊列維持長度爲10,每消耗一條,則補充一條。
關鍵的數據流程:
1.CSimpleTextSubtitle::Open,獲得CAtlArray<STSEntry>,加載字幕文件;
2.CRenderedTextSubtitle::GetSubtitle,獲得CAtlMap<int, CSubtitle*> m_subtitleCache,將字幕信息轉化成字幕圖形信息CSubtitle;
3.CSubPicQueue::ThreadProc->CSubPicQueueImpl::RenderTo->CRenderedTextSubtitle::Render->CLine::PaintOutline->CWord::Paint->CText::CreatePath()
        BeginPath(g_hDC);
        TextOutW(g_hDC, 0, 0, m_str, m_str.GetLength());
        EndPath(g_hDC);
->Rasterizer::ScanConvert->Rasterizer::Rasterize
通過BeginPath、TextOutW、EndPath這3個GDI函數獲得文字的路徑點集合,並以這個路徑爲基礎構建輪廓、陰影等像素,從而實現光柵化。
4.CSubPicQueue::EnqueueSubPic,緩存生成的bitmap圖片;
5.CDirectVobSubFilter::Transform->CSubPicQueue::LookupSubPic->CMemSubPic::AlphaBlt,Source Filter推送過來一個Sample,通過輸入sample的時間戳從bitmap緩存中查找對應的bitmap,然後進行透明度混合。
如過是YV12輸入,有以下步驟:
Y分量疊加:
if (s2[3] < 0xff) {
d2[0] = (((d2[0] - 0x10) * s2[3]) >> 8) + s2[1];
 }
U、V分量疊加:
unsigned int ia = (s2[3] + s2[3 + src.pitch] + is2[3] + is2[3 + src.pitch]) >> 2;
if (ia < 0xff) {
*d2 = BYTE((((*d2 - 0x80) * ia) >> 8) + ((s2[0] + s2[src.pitch]) >> 1));
}
6.CBaseVideoFilter::CopyBuffer(如果需要則先進行必要的轉換,如BitBltFromI420ToYUY2)->CTransformOutputPin::Deliver,拷貝疊加後的圖片到輸出sample。
總結:
VsFilter通過獲取輸出的文字路徑點集合,轉化成形狀,並光柵化成bitmap像素,然後與輸入的圖像進行alpha混合,達到字幕疊加的目的,實際上是圖像疊加。所有的操作都是使用CPU進行計算,所以會明顯增加CPU的開銷。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章