本帖最后由 shane007 于 2023-9-2 00:06 编辑
7 O4 R! ]) @. w" L ]; F7 j: q' Z! u7 x$ ~; M! M7 C) x1 q+ L
该游戏是scummvm支持的少数几款FMV AVG。
7 M% d8 C6 L9 N3 P; W0 `- P% Z视频采用了一种叫做RL2的格式。6 A/ U- l% n* }2 Y3 h7 @5 \! \
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav," A/ K2 @% ~! F9 a6 t, f% Q6 J
然后用whisper语音识别之后就能配上字幕了。
. R8 l- o9 y0 D此外,rl2格式用potplayer也能直接播放。
2 r- o' p- k6 ] _! [
' ?) S( u% L' c4 |% e6 k! B2 ^# g1 {文件格式6 C3 A8 w3 E0 P( _
https://wiki.multimedia.cx/index.php/RL29 c# |+ F! @2 [/ t9 A6 ] f, M" q
- 1 L% K7 k' p z4 j8 U2 j
- + 0 dword Header -- "FORM"
7 ^; ]$ g$ |0 m3 K$ f- i - + 4 dword BackSize -- size of the background frame
% t# V9 W# C* Q' n8 _# f - + 8 dword Signature -- "RLV2" or "RLV3"
0 s- G0 f, `2 m$ s3 Y9 z2 Z - + C dword DataSize -- size of the data past this point BIG-ENDIAN5 h9 g: ?/ O- h' ]6 I% _
- + 10 dword NumFrames -- number of frames$ C2 j- |' ?1 ^! Q8 j
- + 14 word Method -- encoding method. ignored, zero! I8 E" q$ s* E6 U6 L( D" P
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound6 S- n3 l2 U v m! }
- + 18 word Rate -- sound sample rate in Hz
' ~3 q& _# j# ^4 i9 T9 k - + 1A word Channels -- number of sound channels
( K& |9 N M3 \1 I% ], Y5 H - + 1C word DefSoundSize -- size of the single sound chunk. see notes below
2 F, R8 i" u6 Z: {7 G% y8 s - + 1E word VideoBase -- initial drawing offset within 320x200 viewport: v$ q! f( s3 k# B7 d9 J, q+ r
- + 20 dword ClrCount -- number of used colors
# b e' k; f7 E' p& H1 n$ ^# V - + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
3 P/ x) f( A4 P+ A: F - -- if Signature == "RLV3" AND BackSize <> 0; S, B, T) W7 ` x2 F% ^
- +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding
9 f) S* U. G0 ^1 q - --
. d' V, s. p: j - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
" M4 G1 S U6 m k! Q - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file5 N1 J6 q) _+ j" ^5 Q- B
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk
+ n0 y, d; {; k- t! O/ ?) Y2 C - -- for each frame --
8 [1 N; W w' i1 L: A! { - +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio7 a1 E; y$ }) L
- +yyy byte Video[...] -- compressed video stream
复制代码 * i# C, D2 {+ `8 U. g0 n' A
参考代码(有问题,但可参考)- using System;, H6 J; |' @- k2 t3 p
- using System.IO;" R: b/ U2 b* s( w0 H; s8 ?! b
- using System.Text;5 r7 s+ a- R$ D! C0 D
A' K: |7 f6 a3 h! \- public class RL2ToWavConverter
( M" n+ X K# _' K: k - {
* v; l/ ?( U8 p) s6 G - public static void ConvertToWav(string inputFile, string outputFile)
7 ]# ^+ y1 X0 D3 J0 s: U - {
9 |% V6 V* Y4 e, C' I: k, b. ^& C3 H - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
" R7 ^3 R& [' h( k. `, d# z. D - using (BinaryReader reader = new BinaryReader(fs))' |8 R5 R; a- F% R" i [
- {: _4 P; j8 ]; C/ t" e. y
- // 读取头部& v; X, t! F, p& h- P# |0 u+ ~
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));, L# R" K" J9 S) }
- if (header != "FORM")
& G) {/ I% ?: c3 F$ c; ^ - {4 H9 J" z5 R9 I5 z. H
- Console.WriteLine("无效的.rl2文件格式。");
: {3 j, j7 y; f - return;
5 V) w! ^5 t3 b% ^- U - }) o$ V6 I9 h% H* ~+ A/ `
: n. D& V' y# \, A" B W- uint backSize = reader.ReadUInt32();0 y2 y3 y( t; M" O
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));! I+ b8 `* W% C8 j
- uint dataSize = reader.ReadUInt32();
. ?! \" w* C, S& O0 H* | Q: _) u - uint numFrames = reader.ReadUInt32();; h, `/ L. n' x$ ]6 y
- ushort method = reader.ReadUInt16();0 S* `) ` v- U% Q7 z( j- \+ z
- ushort soundRate = reader.ReadUInt16();
# t+ B3 T2 F/ }3 W o0 Y5 c - ushort rate = reader.ReadUInt16(); u Q" v& U& }' E3 r% c7 d
- ushort channels = reader.ReadUInt16();
" h! Z9 Y3 ?" T; S" a - ushort defSoundSize = reader.ReadUInt16();( ? V k8 v7 G% v6 @
- ushort videoBase = reader.ReadUInt16();( @+ i/ x" ?5 o7 s- Z
- uint clrCount = reader.ReadUInt32();1 H0 n( ~0 B" {5 \
- uint[] chunkSize = new uint[numFrames];7 n4 P# N7 j1 {2 W1 {7 B
- uint[] chunkOffs = new uint[numFrames];
E$ g/ E1 n1 U6 B$ n( C3 G - uint[] soundSize = new uint[numFrames];
$ ~( h' e- M+ z. e: d - 6 i; K4 y) o; v! ?( {/ }$ z
- if (signature != "RLV2" && signature != "RLV3"), i: A6 d6 `% \ P
- {. |0 `9 S) ?5 H# |' i5 Y9 x" @( {
- Console.WriteLine("不支持的签名。");
0 M! ~8 z2 b! k' ~( z+ B& V" D - return;
h# N, n% t5 w6 \ - }
6 W, a7 t4 f5 j6 W - & a6 O7 u" @5 @
- // 读取块信息
* { h0 v0 \! l$ a+ t - for (int i = 0; i < numFrames; i++)% O+ H4 x. }1 J, _
- {8 \9 q: _$ S4 m0 l
- chunkSize[i] = reader.ReadUInt32();0 u' I* C) _4 ?! W: r0 o `
- chunkOffs[i] = reader.ReadUInt32();
' }2 K) T, H- F; l0 z/ k& Z - soundSize[i] = reader.ReadUInt32();
9 j m0 M6 J% t# q% h C0 X& Y; t - }" A' S0 V# D- a8 T* }5 Q3 o
9 B$ D% Q K7 B; E' Q- // 如果存在背景帧,请跳过它
2 f1 n. Q7 \2 D3 v: d - if (signature == "RLV3" && backSize != 0)# {8 S! ~" Y ~7 A3 b u
- {+ s* Q1 W2 S- c* g: D# w
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);9 q& p2 O( P5 T
- }: q+ N0 x4 v' `+ C2 H* E
" j- y9 [9 W4 z5 i( n# {" R; [- // 创建一个WAV文件并写入音频数据
2 n/ [7 G+ h; K: J9 c# `! f" a- U% _ - using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
+ J8 y9 }1 [% f9 P; g - {
7 a5 V' x/ N$ N. M" v - // 写入WAV头部
; @8 }" ~4 M5 k8 _ - wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));3 O) D* J6 q0 F, V/ ?
- wavWriter.Write(36 + dataSize); // 总文件大小 - 8
5 d" \: H- }/ _; _& n - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));2 P; _" r* W: M/ _* }! l4 L
- wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
6 a1 R) `4 @- h* k# r1 {) z - wavWriter.Write(16); // fmt块大小
1 t4 v4 q$ e# _ - wavWriter.Write((ushort)1); // 音频格式(PCM)5 s4 B: G) r) I& N8 D
- wavWriter.Write(channels); // 声道数
0 m( `, ^( t' E5 x7 U - wavWriter.Write(rate); // 采样率
7 F" D! o' t9 D, M - wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
1 c( |; U$ g8 C+ ], B - wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
: l: \- H6 {3 r% _2 m - wavWriter.Write(defSoundSize); // 每个样本的位深度
# A% m$ X1 F# O; _" C3 j# | - wavWriter.Write(Encoding.ASCII.GetBytes("data"));
7 l* D7 g/ C3 g3 E: U2 L- U* o - wavWriter.Write(dataSize); // 数据大小
( [4 b, c' ~6 b- d
5 a& R2 n& l3 E8 t- // 从.rl2文件中读取并写入PCM音频数据' ^1 N. [" p5 H* O
- for (int i = 0; i < numFrames; i++)
- b+ A' H& O! m' y - {
8 @8 w3 R" V J. A" ~. @; h - byte[] audioData = reader.ReadBytes((int)soundSize[i]);- O# Q" O+ W) x9 O( z7 f
- wavWriter.Write(audioData);6 C* R6 C$ z. C6 i% p
- }) a$ ~! z7 s) C9 h* j' f5 M% l
- }
, n) w- n5 x* k" `! @0 @3 e: |2 Y - }5 ~- B$ l9 o1 j) x4 P
- }. v: R: {: G. @0 {2 v& w! w
- }
& r+ J1 J5 w4 h
( J+ T% @0 \: |- g- class Program1 {8 H, @ u8 M4 }& Y' A
- {9 Y0 N6 U, J* S( ^1 G, z
- static void Main(string[] args)* R( Q1 D: {! b: r% _
- {: \' b# e- a0 y: a- ^! A8 @' ^9 x8 n, p
- string inputFile = "N1275510.RL2";3 Y, B& L! e1 X/ J! Y
- string outputFile = "output.wav";
8 m, m. f$ R6 p/ `3 F - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);( r8 v- ^& Q+ ]) }( |2 X; `5 E
- Console.WriteLine("转换完成。");+ a+ W5 }3 f4 K8 P
- }
) B" Q! J8 |; T8 Q - }, G, Q( v2 O: I: w# O0 U3 i; Z
复制代码 1 R9 A3 c: Z# j
O7 m9 m. C. k* v: h. h
2 I; f, y, Y! v( O# S0 M5 A |