本帖最后由 shane007 于 2023-9-2 00:06 编辑 / a! r/ [ k$ g4 c% _% c( U
/ Y' Y6 b9 O0 ]% V+ A该游戏是scummvm支持的少数几款FMV AVG。5 J; K! H b7 W
视频采用了一种叫做RL2的格式。* K3 X: ^ s3 Y3 l
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
: _6 E/ w3 K, b" s5 I" o, z然后用whisper语音识别之后就能配上字幕了。
6 ~0 Q& q4 v) g+ k' j此外,rl2格式用potplayer也能直接播放。, }% l* V$ a- y4 y$ |
g; Z) V; `3 U: z文件格式
$ A0 ]1 M9 m& n' u5 S o& w+ Jhttps://wiki.multimedia.cx/index.php/RL2
y, F, \0 P$ ` L
8 }, X: @/ Z8 R% M4 r2 i) g9 @) _7 ^- + 0 dword Header -- "FORM"# o) ~ ^3 E7 m, \ Z, m- f
- + 4 dword BackSize -- size of the background frame
9 W; D% ~' E1 \ - + 8 dword Signature -- "RLV2" or "RLV3"
' ]3 e5 L8 g, |1 {6 z - + C dword DataSize -- size of the data past this point BIG-ENDIAN% A3 _4 _, m4 c- Y- Y* ^4 ^# t
- + 10 dword NumFrames -- number of frames* q- u, M" C. [. Q$ s
- + 14 word Method -- encoding method. ignored, zero, q% L4 w" s- K7 j5 S8 m
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound
& }; }. y O8 h: n - + 18 word Rate -- sound sample rate in Hz# X/ i# U9 q/ D& C2 A
- + 1A word Channels -- number of sound channels
* l5 }# w" t0 R - + 1C word DefSoundSize -- size of the single sound chunk. see notes below
3 H3 x! `! K V& s$ }2 R: z5 ^ - + 1E word VideoBase -- initial drawing offset within 320x200 viewport5 g% r- e( Y( |$ Z+ P3 T
- + 20 dword ClrCount -- number of used colors6 l; E/ Z" f' e. D5 h- q1 Y: q5 J8 R
- + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries5 o) R+ C' r. ~) l6 d: r9 O2 e
- -- if Signature == "RLV3" AND BackSize <> 0
4 n3 f+ ]5 e7 f - +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding7 N/ ^ `! h$ g: O! K; x. w7 k
- --
7 [9 { \+ F. G; |" t3 z: q - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
3 Q4 O" X9 u- ]5 @ - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file/ X. @2 x' F' \+ f$ F5 R
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk. X' G3 Z. [) i4 ~
- -- for each frame --7 D' E$ o! A, x7 [ |: I3 f
- +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
4 S7 {6 u% `3 y* [/ Z* B - +yyy byte Video[...] -- compressed video stream
复制代码
, O1 a% Y8 J9 A: a参考代码(有问题,但可参考)- using System;
7 o0 f6 w! [+ o- o - using System.IO;
: s7 ^0 Y; D: B2 {! B - using System.Text;
% Q+ |0 J; r. G3 } - . Q( k3 V# \# [/ ]# V
- public class RL2ToWavConverter {/ J, V) R1 Y1 y. c
- {
4 N: n. X5 U$ y2 N* \. h v% d+ t - public static void ConvertToWav(string inputFile, string outputFile)
! _3 O* j9 ^& y x/ X8 k - {
+ `$ |+ c4 ^( z) k2 F - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
R: j8 ]/ a* i3 ]" I" t - using (BinaryReader reader = new BinaryReader(fs))
8 w8 @- ?" `. x2 h- i& z) c - {; n: A$ _8 {. ~* c6 ]* v
- // 读取头部& S1 f5 h5 X; Z0 x2 r: R8 n \9 h
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));/ ?0 e6 {; c7 h4 `
- if (header != "FORM")! q4 Y9 f5 o9 J/ h# k
- {
& j: c/ |' d9 U' a3 u7 ] - Console.WriteLine("无效的.rl2文件格式。");
m4 h) O5 G0 a& D - return;' m$ N; @/ S5 \3 c
- }0 V+ V) M' f) v- I" }5 b) Q4 ~
- 3 A- y5 d! N: i% ~& L
- uint backSize = reader.ReadUInt32();
8 |0 ^/ R4 D) S - string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
+ y- [% ]5 f; \- u8 l - uint dataSize = reader.ReadUInt32();7 Z& n _/ Z. i* Z4 k
- uint numFrames = reader.ReadUInt32();+ @8 v* F9 }2 z0 ]
- ushort method = reader.ReadUInt16();
& R0 |' e9 z2 v, I% ^* X/ n - ushort soundRate = reader.ReadUInt16();3 Q; M5 z2 j( i4 [+ ^
- ushort rate = reader.ReadUInt16();# P, p; \+ _2 H
- ushort channels = reader.ReadUInt16();
, F6 q, F" W- A+ x9 A - ushort defSoundSize = reader.ReadUInt16();8 g1 ?3 t2 \7 }. M& n) z
- ushort videoBase = reader.ReadUInt16();
7 d) z( ?1 N! q w& ` - uint clrCount = reader.ReadUInt32();' k1 U. j, y2 u( q. l0 M( r
- uint[] chunkSize = new uint[numFrames]; u5 P( Z5 M/ }, Q1 x
- uint[] chunkOffs = new uint[numFrames];
( _2 W* m2 D& {0 C' u" t0 i' L3 j - uint[] soundSize = new uint[numFrames];3 @" r, U" v; a. j
- : i9 o4 Z0 v, o' S
- if (signature != "RLV2" && signature != "RLV3")
2 u' D$ \. {, Z( [7 ]+ F - {
0 R. R* M/ \: _9 i. o$ I0 u - Console.WriteLine("不支持的签名。");) G8 Q% f) b/ j/ [3 ?, L/ v. _8 B5 @
- return;" C8 ~1 U0 Q9 ^. G$ }2 t0 N; S( R
- }0 e* F; [/ l6 ~- { J/ |
- ( m' ^) q( c4 ?
- // 读取块信息, ~; T9 S6 x9 e
- for (int i = 0; i < numFrames; i++)0 Z) S4 \8 Y# S6 o! b
- {
7 X" R* a: ?9 G( u, X1 N& A - chunkSize[i] = reader.ReadUInt32();; F1 U4 j; t9 j' X0 ]( V
- chunkOffs[i] = reader.ReadUInt32();
" H8 R, n, H3 X3 ~; V z - soundSize[i] = reader.ReadUInt32();& \- B! ? a. v) k
- }
2 v3 A6 X: B7 V( w7 @
6 U; T6 y5 V7 R/ Q( c4 C: O$ y- // 如果存在背景帧,请跳过它: {3 L3 c& y" l/ W* W. u
- if (signature == "RLV3" && backSize != 0)3 F2 l2 j! f `8 G8 @$ g) S
- {$ ]. e9 t* w; z" r! F
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);; ~# F0 I3 a8 n; M4 H
- }
) q# V* S0 G3 a! i8 L- O
_) R" {& H! c- // 创建一个WAV文件并写入音频数据
; [0 e) ]: k& Q+ ^) e: ? - using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
- X5 r+ A( P% }# i, L% j7 H" {: K) P - {
6 B. J* O; U& J+ o) L2 Z. a! h: y/ g - // 写入WAV头部7 T4 J0 N- E) M" Z. w' y
- wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
9 z% z8 I( C8 P" x* k) |& ^ - wavWriter.Write(36 + dataSize); // 总文件大小 - 8# a& ?6 S5 q: v' E. g4 V. l/ h
- wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));; q: R& c" d$ G: B
- wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
3 |6 }; s! y) X! r+ i0 @6 j7 }% j - wavWriter.Write(16); // fmt块大小4 W0 k5 f* z$ |% P6 |, \# V+ b
- wavWriter.Write((ushort)1); // 音频格式(PCM)
" ?: b9 p- j8 b* i9 I- u0 J n - wavWriter.Write(channels); // 声道数) p) o( o# b4 V- T" e' u8 ^
- wavWriter.Write(rate); // 采样率- m2 s. A/ e4 z8 K, e% L
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数0 g. w; [, l+ n, r z) G4 a" t3 b
- wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数! K; r' W+ z, Y: K6 j4 p8 s
- wavWriter.Write(defSoundSize); // 每个样本的位深度
, O ~/ |! q' o. q3 Z, @' q U - wavWriter.Write(Encoding.ASCII.GetBytes("data"));. l7 U' a0 L% A! L! p" S
- wavWriter.Write(dataSize); // 数据大小4 N1 |( z e. A s2 f* b4 `3 x6 y
- * _, l$ w/ e/ y; W) i: S4 _. h" j
- // 从.rl2文件中读取并写入PCM音频数据
8 y; ^ N+ |# s - for (int i = 0; i < numFrames; i++)
! M7 s A) h$ p$ w& b - {
3 M/ Y5 l: m& Y( x - byte[] audioData = reader.ReadBytes((int)soundSize[i]);% r( m7 n: ]3 ]7 I# i- v# g
- wavWriter.Write(audioData);
: |6 P; s# J4 } d0 j - }
; A3 _% c& {8 o" P, C7 R0 L$ n - }
9 q, {8 A1 b3 p$ G - }
* L$ T( A( x! J8 X! L( \: m4 [* l% M - }8 n J1 g6 p, x% Z
- }$ F; F# K+ d7 |' U+ P
1 w5 u$ @1 t- r- class Program, m, l& e& q+ w+ y! C& k2 q. l
- {" V' F- a2 h$ m
- static void Main(string[] args)* a# b8 Q5 U6 R8 ?8 U( j6 V; G* i
- {
2 S9 L) R3 J: T$ K4 }3 v0 X; M8 i - string inputFile = "N1275510.RL2";
7 Q6 ^( g( R& d$ N% R i - string outputFile = "output.wav";- m0 z- @ t! ^
- RL2ToWavConverter.ConvertToWav(inputFile, outputFile);& ^ W) A9 `, b" ~3 g
- Console.WriteLine("转换完成。");; C' g" G' w- B O* s. `( c
- }- [( e% ]- D" F7 ~' n4 }4 m
- }% w0 {5 [) J+ }6 [/ o
复制代码
" T2 m$ O$ p3 m5 K; F
6 \% g* f M9 y9 ^0 |9 z1 R5 v* J
|