通过汉化时摸索小有所获,
依旧简要总结下以备忘。
PSP汉化手记有空补
- 字幕内嵌 -
作为汉化补丁,重新压制视频会带来体积明显的增加,
100MB的补丁视频就占了90MB(笑
虽然兼容性强,也是常用方案,但是并不认为是个好办法。
- CSRI接口 -
适用于视频或图像绘制由引擎完全接管时,
如通过引擎自带解码流程(常见于跨平台作,例爱神餐馆2的SFD视频),
或等其余需要额外绘制字幕的情况。
CSRI(common subtitle renderer interface),
目前主要还是在vsfilter上的实现。
虽然libass也支持,
但编译繁琐,加载字幕时预缓冲字体的过程也过于明显,个人讲Windows下并不推荐使用。
csri.h摘要,稍有改动:
- enum csri_pixfmt {
- CSRI_F_RGBA = 0,
- CSRI_F_ARGB,
- CSRI_F_BGRA,
- CSRI_F_ABGR,
- CSRI_F_RGB_ = 0x100,
- CSRI_F__RGB,
- CSRI_F_BGR_, /**< Windows "RGB32" */
- CSRI_F__BGR,
- CSRI_F_RGB = 0x200,
- CSRI_F_BGR, /**< Windows "RGB24" */
- CSRI_F_AYUV = 0x1000,
- CSRI_F_YUVA,
- CSRI_F_YVUA,
-
- CSRI_F_YUY2 = 0x1100,
- CSRI_F_YV12A = 0x2011, /**< planar YUV 2x2 + alpha plane */
- CSRI_F_YV12 = 0x2111 /**< planar YUV 2x2 */
- };
- typedef const char *csri_ext_id;
- union csri_vardata {
- long lval;
- double dval;
- const char *utf8val;
- void *otherval;
- };
- struct csri_openflag {
- /** flag name */
- csri_ext_id name;
- /** flag data argument */
- union csri_vardata data;
- /** link to next flag */
- struct csri_openflag *next;
- };
- struct csri_fmt {
- /** format to be used */
- enum csri_pixfmt pixfmt;
- /** image width, full frame.
- *
- * This should specify the full size of the frame.
- * Specifying the video sub-size (in case of added black
- * borders) is left to an extension.
- */
- unsigned width;
- /** image height */
- unsigned height;
- };
- typedef void csri_rend;
- typedef void csri_inst;
- struct csri_frame {
- /** frame format.
- * It is an application bug if this differs from the one
- * passed in struct #csri_fmt to csri_query_fmt()
- */
- enum csri_pixfmt pixfmt;
- /** the frame's data.
- * Packed formats only use planes[0]; planar formats
- * have the data ordered as Y, U, V[, A].
- *
- * Also note that the topmost line always comes first.
- * The Windows biHeight strange-ity is \a NOT duplicated.
- */
- unsigned char *planes[4];
- /** strides for the individual planes.
- * Stride means full byte offset, i.e. do \a not add
- * frame width
- */
- ptrdiff_t strides[4];
- };
- typedef int (__cdecl* csri_request_fmt_t)(csri_inst *inst, const struct csri_fmt *fmt);
- typedef csri_inst* (__cdecl* csri_open_file_t)(csri_rend *renderer, const char *filename, struct csri_openflag *flags);
- typedef csri_inst* (__cdecl* csri_open_mem_t)(csri_rend *renderer, const void *data, size_t length, struct csri_openflag *flags);
- typedef void (__cdecl* csri_render_t)(csri_inst *inst, struct csri_frame *frame, double time);
- typedef void (__cdecl* csri_close_t)(csri_inst *inst);
复制代码 流程:
csri_open_xxx加载字幕文件后得到实例(?)
csri_request_fmt设定输出分辨率等
csri_render将字幕叠加到画面上(RGB32)
结束后csri_close。
例:
- struct csri_fmt fmt;
- fmt.pixfmt = CSRI_F_BGR_;
- fmt.width = 640;
- fmt.height = 480;
- csri_inst * subrenderinst;
- subrenderinst = csri_open_file(NULL, "test.ass", NULL);
- csri_request_fmt(subrenderinst, &fmt);
- unsigned char imgbuf[640 * 480 * 4];
- //TODO:
- //load picture to imgbuf
- struct csri_frame frame;
- frame.pixfmt = CSRI_F_BGR_;
- frame.planes[0] = imgbuf;
- frame.planes[1] = NULL;
- frame.planes[2] = NULL;
- frame.planes[3] = NULL;
- frame.strides[0] = 640 * 4;//RGB32
- csri_render(subrenderinst, &frame, 1.0);
- //...
- csri_close(subrenderinst);
复制代码 Note:
csri_render的time单位是秒
图左上角为原点
官版vsfilter CSRI只支持RGB32,但也有YV12的patch
CSRI效率低,如有复杂的特效建议多预渲染几帧和开多线程
xy-vsfilter CSRI处理效率比官方版vsfilter更低
(direct264附带的vsfilter 1.5.3.3698 VS xy-vsfilter 3.0.0.211)
推荐参考:
Aegisub源码
基础常识:
DLL在代码中动态加载
- 利用vsfilter自动加载 -
适用于游戏视频为利用dshow渲染的普通格式(wmv,avi...),且未被封包。
DirectShow渲染会加载vsfilter的DirectVobSub (auto-loading version) Filter,
其会检查视频文件名在目录是否有对应的同名字幕,并加载。
虽然方便,
但vsfilter随各个版本(尤国产各种流氓xx影音,xx解码的附带版)
或设置不同可能出现各种问题。
要求自行安装官方版vsfilter也总会被玩家无视。
- 强制vsfilter加入渲染链 -
主要适用视频已被封包情况。
分析引擎流程,在引擎连接解码器与视频渲染器(Rendering Filter)前,
把DirectVobSub加入FilterGraph,并连接于解码器与渲染器(VMR7居多)之间。
再QueryInterface出IDirectVobSub接口后,
用IDirectVobSub::put_FileName(WCHAR * fn)加载指定字幕。
- // {93A22E7A-5091-45EF-BA61-6DA26156A5D0}
- // 7A2EA2939150EF45BA616DA26156A5D0
- DEFINE_GUID(CLSID_DirectVobSub,
- 0x93A22E7A, 0x5091, 0x45EF, 0xBA, 0x61, 0x6D, 0xA2, 0x61, 0x56, 0xA5, 0xD0);
- // {EBE1FB08-3957-47ca-AF13-5827E5442E56}
- // 08FBE1EB5739CA47AF135827E5442E56
- DEFINE_GUID(IID_IDirectVobSub,
- 0xebe1fb08, 0x3957, 0x47ca, 0xaf, 0x13, 0x58, 0x27, 0xe5, 0x44, 0x2e, 0x56);
复制代码 Note:
稍微熟悉dshow工作流程后,加载quartz的debugsymbol(quartz.pdb),
播放视频前bpx CoCreateInstance,
有着symbol和GUID的提示之后F8跟一遍就基本能找到关键位置。
vsfilter的put_FileName时还会做各种FindFirstFileW等操作,
如使用MoleBox等通用封包封入字幕,还需要对FindFirstFileW等进行Hook。
当然,还能试试做成URL+Socket,URL+Pipe之类。
推荐参考:
《DirectShow开发指南》陆其明,2003
Windows SDK(DirectShow Samples) / MSDN
DirectShowSpy / GraphStudioNext
基础知识:
C++编译后汇编结构
COM接口基本规范
DirectShow大致工作流程
MSDN例程Filter连接之类
https://bbs2.seikuu.com/forum.php?mod=viewthread&tid=138060
- dshow下通用方法 -
对付更复杂或如使用内嵌WMP的引擎,这时往往更需要通用的方法。
基本思路,
首先设计为DLL,方便之后引擎来加载与外部控制。
媒体播放前不论由引擎还是dshow内部,
必定会调用CFGControl::CImplMediaControl::Run(call [ecx+1c]),
所以直接修改IMediaControl的虚表实现Hook。(算是IAT Hook?)
Hook断下后,寻找渲染器,断开其上级渲染链,塞入DirectVobSub,
若已有DirectVobSub则不再次处理;
之后返回旧IMediaControl::Run。
寻找当前渲染器思路,(是否可靠?)
EnumFilters枚举所有Filters,输入Pin类型为MEDIATYPE_Video,而且没有输出Pin。
关于加载非物理文件的字幕,DirectVobSub还提供了InputPin加载字幕流(MEDIATYPE_Subtitle),
分析vsfilter和lavfilter源码,在Filter与DirectVobSub连接时,
通过Pin的MediaType里FormatData即可完整将字幕传过去,无需AsyncReader,MemAllocator等。
- FormatData大致结构:
- DWORD Header_Length
- DWORD Header_Lanuage
- Char[?] Header_Title_UTF8
- Char[?] Subtitle_Data
复制代码 总之需要自制个简单(?)的Filter,只继承(?)一下CBaseFilter,CBaseOutputPin,CEnumMediaTypes就好了。
最后为方便DLL的控制,再导出 字幕加载和播放Callback 的两个函数。
Callback里,提供IGraphBuilder,或不便使用dshow相关时直接提供视频大小(字节),
来区分视频特征,以选择字幕。
- // {E487EB08-6B26-4be9-9DD3-993434D313FD}
- DEFINE_GUID(MEDIATYPE_Subtitle,
- 0xe487eb08, 0x6b26, 0x4be9, 0x9d, 0xd3, 0x99, 0x34, 0x34, 0xd3, 0x13, 0xfd);
- // {326444F7-686F-47ff-A4B2-C8C96307B4C2}
- DEFINE_GUID(MEDIASUBTYPE_ASS,
- 0x326444f7, 0x686f, 0x47ff, 0xa4, 0xb2, 0xc8, 0xc9, 0x63, 0x07, 0xb4, 0xc2);
- // {A33D2F7D-96BC-4337-B23B-A8B9FBC295E9}
- DEFINE_GUID(FORMAT_SubtitleInfo,
- 0xa33d2f7d, 0x96bc, 0x4337, 0xb2, 0x3b, 0xa8, 0xb9, 0xfb, 0xc2, 0x95, 0xe9);
复制代码 Note:
虚表只读,要VirtualProtect
Hook时注意保存ecx
MEDIASUBTYPE_ASS,MEDIASUBTYPE_ASS2,MEDIASUBTYPE_SSA,MEDIASUBTYPE_UTF8似乎无区别
DllMain主线程里CoCreateInstance会导致死锁,CreateThread个新线程即可
通过DllGetClassObject可直接使用未注册的Filter,原理见《DirectShow开发指南》1.3,
可参考mplayer-ww源码中实现。
推荐参考:
同上
+vsfilter源码
+lavfilter源码
+mplayer-ww源码
源码:
代码风格较为混乱,待整理后公开。
最终DLL与详细用法附上,使用用途不限,
141113更新:
http://www.mediafire.com/downloa ... h/autosub_141113.7z
http://s000.tinyupload.com/index.php?file_id=08951439806635357436
加载dll时自动完成初始化,RegisterCallBack非必要
SetSubtitleData只有支持有BOM的UTF8的ASS字幕
- 结语 -
C艹。 |