DirectVobSub(VsFilter)的基本原理和实现实现

来源:互联网 发布:c语言实现文件复制传输 编辑:程序博客网 时间:2024/05/21 06:58
DirectVobSub,以前习惯被称作 VSFilter,是一个Microsoft Windows的插件(DirectShow滤镜),用来在视频播放时加载外部字幕。
DirectVobSub/VSFilter曾经是一个独立的程序VobSub的一部分,这个程序还有诸如从DVD-Video中提取字幕或制作文字字幕等功能。Vobsub的最后一个版本是2.23,然后便停止了开发。之后,VSFilter成为了开源网站SourceForge上的“guliverkli”项目的一部分,但这个计划也于2005年停止开发,最后的版本是2.37。从2007年起,它又成为了“Guliverkli2”项目的一部分,自2.38版本起,VSFilter被称作DirectVobSub。

*注:以上来自维基百科。

目前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的开销。
0 0
原创粉丝点击