查看: 4584|回复: 6

[杂谈] 游戏汉化中视频字幕一些处理办法小结

[复制链接]
发表于 2014-11-2 20:30:01 | 显示全部楼层 |阅读模式
通过汉化时摸索小有所获,
依旧简要总结下以备忘。
PSP汉化手记有空补


- 字幕内嵌 -
作为汉化补丁,重新压制视频会带来体积明显的增加,
100MB的补丁视频就占了90MB(笑
虽然兼容性强,也是常用方案,但是并不认为是个好办法。


- CSRI接口 -
适用于视频或图像绘制由引擎完全接管时,
如通过引擎自带解码流程(常见于跨平台作,例爱神餐馆2的SFD视频),
或等其余需要额外绘制字幕的情况。

CSRI(common subtitle renderer interface),
目前主要还是在vsfilter上的实现。

虽然libass也支持,
但编译繁琐,加载字幕时预缓冲字体的过程也过于明显,个人讲Windows下并不推荐使用。

csri.h摘要,稍有改动:
  1. enum csri_pixfmt {
  2.         CSRI_F_RGBA = 0,
  3.         CSRI_F_ARGB,
  4.         CSRI_F_BGRA,
  5.         CSRI_F_ABGR,

  6.         CSRI_F_RGB_ = 0x100,
  7.         CSRI_F__RGB,
  8.         CSRI_F_BGR_,                        /**< Windows "RGB32" */
  9.         CSRI_F__BGR,

  10.         CSRI_F_RGB  = 0x200,
  11.         CSRI_F_BGR,                        /**< Windows "RGB24" */

  12.         CSRI_F_AYUV = 0x1000,
  13.         CSRI_F_YUVA,
  14.         CSRI_F_YVUA,
  15.         
  16.         CSRI_F_YUY2 = 0x1100,

  17.         CSRI_F_YV12A = 0x2011,                /**< planar YUV 2x2 + alpha plane */
  18.         CSRI_F_YV12 = 0x2111                /**< planar YUV 2x2 */
  19. };

  20. typedef const char *csri_ext_id;
  21. union csri_vardata {
  22.         long lval;
  23.         double dval;
  24.         const char *utf8val;
  25.         void *otherval;
  26. };

  27. struct csri_openflag {
  28.         /** flag name */
  29.         csri_ext_id name;
  30.         /** flag data argument */
  31.         union csri_vardata data;
  32.         /** link to next flag */
  33.         struct csri_openflag *next;
  34. };


  35. struct csri_fmt {
  36.         /** format to be used */
  37.         enum csri_pixfmt pixfmt;
  38.         /** image width, full frame.
  39.          *
  40.          * This should specify the full size of the frame.
  41.          * Specifying the video sub-size (in case of added black
  42.          * borders) is left to an extension.
  43.          */
  44.         unsigned width;
  45.         /** image height */
  46.         unsigned height;
  47. };
  48. typedef void csri_rend;
  49. typedef void csri_inst;

  50. struct csri_frame {
  51.         /** frame format.
  52.          * It is an application bug if this differs from the one
  53.          * passed in struct #csri_fmt to csri_query_fmt()
  54.          */
  55.         enum csri_pixfmt pixfmt;
  56.         /** the frame's data.
  57.          * Packed formats only use planes[0]; planar formats
  58.          * have the data ordered as Y, U, V[, A].
  59.          *
  60.          * Also note that the topmost line always comes first.
  61.          * The Windows biHeight strange-ity is \a NOT duplicated.
  62.          */
  63.         unsigned char *planes[4];
  64.         /** strides for the individual planes.
  65.          * Stride means full byte offset, i.e. do \a not add
  66.          * frame width
  67.          */
  68.         ptrdiff_t strides[4];
  69. };

  70. typedef int (__cdecl* csri_request_fmt_t)(csri_inst *inst, const struct csri_fmt *fmt);

  71. typedef csri_inst* (__cdecl* csri_open_file_t)(csri_rend *renderer, const char *filename, struct csri_openflag *flags);
  72. typedef csri_inst* (__cdecl* csri_open_mem_t)(csri_rend *renderer, const void *data, size_t length, struct csri_openflag *flags);
  73. typedef void (__cdecl* csri_render_t)(csri_inst *inst, struct csri_frame *frame, double time);
  74. typedef void (__cdecl* csri_close_t)(csri_inst *inst);
复制代码
流程:
csri_open_xxx加载字幕文件后得到实例(?)
csri_request_fmt设定输出分辨率等
csri_render将字幕叠加到画面上(RGB32)
结束后csri_close。

例:
  1. struct csri_fmt fmt;
  2. fmt.pixfmt = CSRI_F_BGR_;
  3. fmt.width = 640;
  4. fmt.height = 480;

  5. csri_inst * subrenderinst;
  6. subrenderinst = csri_open_file(NULL, "test.ass", NULL);
  7. csri_request_fmt(subrenderinst, &fmt);

  8. unsigned char imgbuf[640 * 480 * 4];
  9. //TODO:
  10. //load picture to imgbuf

  11. struct csri_frame frame;
  12. frame.pixfmt = CSRI_F_BGR_;
  13. frame.planes[0] = imgbuf;
  14. frame.planes[1] = NULL;
  15. frame.planes[2] = NULL;
  16. frame.planes[3] = NULL;
  17. frame.strides[0] = 640 * 4;//RGB32

  18. csri_render(subrenderinst, &frame, 1.0);

  19. //...

  20. 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)加载指定字幕。
  1. // {93A22E7A-5091-45EF-BA61-6DA26156A5D0}
  2. // 7A2EA2939150EF45BA616DA26156A5D0
  3. DEFINE_GUID(CLSID_DirectVobSub,
  4. 0x93A22E7A, 0x5091, 0x45EF, 0xBA, 0x61, 0x6D, 0xA2, 0x61, 0x56, 0xA5, 0xD0);

  5. // {EBE1FB08-3957-47ca-AF13-5827E5442E56}
  6. // 08FBE1EB5739CA47AF135827E5442E56
  7. DEFINE_GUID(IID_IDirectVobSub,
  8. 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等。
  1. FormatData大致结构:

  2. DWORD Header_Length
  3. DWORD Header_Lanuage
  4. Char[?]  Header_Title_UTF8

  5. Char[?]  Subtitle_Data
复制代码
总之需要自制个简单(?)的Filter,只继承(?)一下CBaseFilter,CBaseOutputPin,CEnumMediaTypes就好了。

最后为方便DLL的控制,再导出 字幕加载和播放Callback 的两个函数。
Callback里,提供IGraphBuilder,或不便使用dshow相关时直接提供视频大小(字节),
来区分视频特征,以选择字幕。
  1. // {E487EB08-6B26-4be9-9DD3-993434D313FD}
  2. DEFINE_GUID(MEDIATYPE_Subtitle,
  3. 0xe487eb08, 0x6b26, 0x4be9, 0x9d, 0xd3, 0x99, 0x34, 0x34, 0xd3, 0x13, 0xfd);
  4. // {326444F7-686F-47ff-A4B2-C8C96307B4C2}
  5. DEFINE_GUID(MEDIASUBTYPE_ASS,
  6. 0x326444f7, 0x686f, 0x47ff, 0xa4, 0xb2, 0xc8, 0xc9, 0x63, 0x07, 0xb4, 0xc2);
  7. // {A33D2F7D-96BC-4337-B23B-A8B9FBC295E9}
  8. DEFINE_GUID(FORMAT_SubtitleInfo,
  9. 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艹。
回复

使用道具 举报

发表于 2014-11-2 23:51:49 | 显示全部楼层
好哇,无视了我以后到这儿来发帖,太嚣张了吧!
下次我一定会把乃的菊花扩张2cm
回复 支持 反对

使用道具 举报

发表于 2014-11-3 17:47:35 | 显示全部楼层
以為L大也失蹤了,但是今天看到看到L大,第一時間感覺到的是安堵。

唯笑大大先來親一個
回复 支持 反对

使用道具 举报

发表于 2014-11-3 19:31:32 | 显示全部楼层
今坂唯笑 发表于 2014-11-2 23:51
好哇,无视了我以后到这儿来发帖,太嚣张了吧!
下次我一定会把乃的菊花扩张2cm

我想说我也被无视了……

点评

咦?何解?  发表于 2014-11-3 21:15
回复 支持 反对

使用道具 举报

发表于 2014-11-10 22:44:56 | 显示全部楼层
Ru~RaRa~RunnRunn~晃晃晃(←背景

点评

该说是戳萌点还是……  发表于 2014-11-12 08:10
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

小黑屋|Archiver|星空网

Powered by Discuz! , Design by Seraphoenix && 北北″.

GMT+8, 2024-11-22 01:33 , Processed in 16 ms, 14 queries, 0.13 loads , Redis On.

Copyright © 2009-2024 Seikuu. All Rights Reserved.

快速回复 返回顶部 返回列表