本帖最后由 shane007 于 2023-9-2 00:06 编辑 + D3 X0 u; p* }" e) Q' u
/ Y. ]/ j; _$ z( w该游戏是scummvm支持的少数几款FMV AVG。
- @9 H% E- @5 C- J: \视频采用了一种叫做RL2的格式。' @( R ~' _4 a* b. j/ n( {
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
8 X& |9 u% ^$ K3 k3 w0 S Z# o然后用whisper语音识别之后就能配上字幕了。
0 w3 k/ k! R, ?" U此外,rl2格式用potplayer也能直接播放。
# }5 Y6 B1 p! p2 x8 J; O* b8 x: G& C* I: E0 A9 g8 H
文件格式
: u2 C9 q! R. D2 c8 i1 `6 bhttps://wiki.multimedia.cx/index.php/RL23 a6 |( `" I5 V
4 U1 c! }8 A/ E' M% G- + 0 dword Header -- "FORM"
7 v) U% y' v( Y9 C4 C6 J - + 4 dword BackSize -- size of the background frame
& I# C6 @9 C4 }9 N: L3 R- M5 R - + 8 dword Signature -- "RLV2" or "RLV3"
5 n9 ^1 t" E$ I% \" V$ f - + C dword DataSize -- size of the data past this point BIG-ENDIAN
9 ]4 `" l. i% D1 [2 o' U) r8 t - + 10 dword NumFrames -- number of frames7 Y: m F, Z- T. e8 b
- + 14 word Method -- encoding method. ignored, zero
* Q. ]+ U4 [' A& v - + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound
" h$ C8 ?' K" H& N& Q( ~7 G - + 18 word Rate -- sound sample rate in Hz% e2 E5 k/ H' Y# h {2 c
- + 1A word Channels -- number of sound channels/ B! e% v9 T& R! a4 A
- + 1C word DefSoundSize -- size of the single sound chunk. see notes below
+ R$ s/ l F3 q! d8 H& M& ^0 y - + 1E word VideoBase -- initial drawing offset within 320x200 viewport: d( y2 n( v5 }
- + 20 dword ClrCount -- number of used colors1 i/ q* O" i' C8 n9 J' }
- + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
+ j$ ?+ Y9 k- k - -- if Signature == "RLV3" AND BackSize <> 0, t6 N/ O$ C, U7 y
- +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding& m$ M$ B1 x/ d& ?+ m4 L6 x: |7 |
- --" u+ S, _: o7 d, E7 }$ d
- +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
7 g* N5 a9 m% p' r' q! e: Y+ s - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file/ }7 f( H, j; J' G, s* P2 E( \) h$ W
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk
& q9 O- ^& d0 [ - -- for each frame --3 }4 J7 @5 K; j% m3 M! F4 l
- +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio# w4 k+ i7 e9 L O: @* \# j% s( S
- +yyy byte Video[...] -- compressed video stream
复制代码
- j4 _( P+ M0 x v g参考代码(有问题,但可参考)- using System;
' g) V# W# ~" R" B" u4 a$ s9 H" J - using System.IO;1 }9 W0 I$ r7 P5 J5 k( ^1 x
- using System.Text;
3 s; P. P$ b, [' E3 j6 _ - 1 I$ `! S: u+ F2 h7 e2 C3 j' J% f7 |
- public class RL2ToWavConverter/ b; t+ [ Z' \
- {, m+ m l# V3 O) u
- public static void ConvertToWav(string inputFile, string outputFile)
( l4 R2 g2 [- O - {
7 u3 x: d2 B3 `# }" G - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
1 o" a$ [0 d1 Y, q. Y) @* {' y - using (BinaryReader reader = new BinaryReader(fs))! b0 `/ \4 x8 H
- {
, T/ g; Y4 {* I* E0 s/ A& }- @0 | - // 读取头部; d9 C9 ?+ o0 D$ ~0 j
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
0 f# [1 o+ v6 [8 N$ l- {; |: w - if (header != "FORM")
6 B1 D! t" a- } - {
5 D3 \ @, | g# B - Console.WriteLine("无效的.rl2文件格式。");1 m: H( s' V& Z
- return;
9 C7 Y, c$ c, f1 W; t0 d6 ` - }
" B4 ~9 k; Q4 E+ j
+ @; o. v8 ^. q0 y- uint backSize = reader.ReadUInt32();
2 ?, j; p; T. Y' j. W - string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
3 L3 u$ B' a/ ?) {! R5 r& e, ?9 y - uint dataSize = reader.ReadUInt32();
0 e4 O0 b' n' Y$ F0 b( C) M - uint numFrames = reader.ReadUInt32();/ {' c4 y' i2 A7 y
- ushort method = reader.ReadUInt16();1 S: o( D" d: b5 y9 t4 k; g+ N
- ushort soundRate = reader.ReadUInt16();
! J: u& H8 v& ~ - ushort rate = reader.ReadUInt16();
- ]/ {2 V: q$ M( F* @. u - ushort channels = reader.ReadUInt16();
+ z' b$ Z9 F# k( |% Q - ushort defSoundSize = reader.ReadUInt16();7 f% Z: |$ {, S7 ^ N1 e
- ushort videoBase = reader.ReadUInt16();; b6 D0 }5 L+ _1 f
- uint clrCount = reader.ReadUInt32();- d r. k% ~( w5 R. g% X" h( `* r7 n
- uint[] chunkSize = new uint[numFrames];5 r5 e c9 ^7 \) g6 ]; L2 J
- uint[] chunkOffs = new uint[numFrames];
" g2 h7 f7 Y: H5 J. { - uint[] soundSize = new uint[numFrames];1 j. i, }! r2 Y9 W+ b& s0 @" g) [
, h+ i) b9 ]3 ]8 I0 A- if (signature != "RLV2" && signature != "RLV3")5 a' u+ O2 O7 A2 U' N; `* w
- {8 r' a* A2 N* A3 f. ` v M' Q
- Console.WriteLine("不支持的签名。");
; R/ {2 ?) U, p& p3 A! i. F - return;4 p' N3 e A) {% I8 d" Z
- }% m% O+ V1 Q6 g- _/ `$ d
- 6 W, W @6 A3 y
- // 读取块信息3 @& s J# `( A0 o& w8 S! C
- for (int i = 0; i < numFrames; i++). F% {4 [" J0 b" A
- {& ~0 t3 W5 P0 j/ ]' X
- chunkSize[i] = reader.ReadUInt32();$ M: d/ a" a9 |+ S/ n" R
- chunkOffs[i] = reader.ReadUInt32();
0 u# _2 o2 k! t: ]4 H - soundSize[i] = reader.ReadUInt32();
; N, P" r; u2 H$ l - }
9 o) F% I# ~/ G1 h* j" N) n4 M1 R
8 |5 }8 i- I6 \9 b: `9 ?. f- // 如果存在背景帧,请跳过它$ }4 {5 x- h3 _+ I# `
- if (signature == "RLV3" && backSize != 0)6 ?4 |/ E- p' y9 ]
- {
6 a8 i7 P& F* J! \ - reader.BaseStream.Seek(backSize, SeekOrigin.Current);# t- j+ @9 ] v7 a
- }/ l3 T! A. t! V0 H
- `, k4 `! E+ {/ ?4 _7 s% }
- // 创建一个WAV文件并写入音频数据2 g- V; _) E: s8 b7 \' q
- using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
) W8 `: o9 p% J5 f9 f# }/ J R e9 j - {+ F, a9 ^/ F% k# K1 c2 e0 C
- // 写入WAV头部
. @0 K8 ^0 \- B5 O - wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
" O$ y6 u M: z - wavWriter.Write(36 + dataSize); // 总文件大小 - 83 I! [* ] J$ F' |5 A D
- wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
# S1 h- l9 Y w( D& h - wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));$ p% {5 L1 o2 I# D
- wavWriter.Write(16); // fmt块大小" `) ]$ M. z' J' ~
- wavWriter.Write((ushort)1); // 音频格式(PCM)* Z0 J' q" `1 R- D1 h) j' H
- wavWriter.Write(channels); // 声道数" ?! ^6 P2 d3 f# N( _. A
- wavWriter.Write(rate); // 采样率6 k# ] w- M3 |( Z- ^$ b( ?
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
' _3 T# v5 _% }6 |3 w, O8 { - wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数. u7 X7 L' j" ^% V' U
- wavWriter.Write(defSoundSize); // 每个样本的位深度9 ?" T$ o& q+ D8 T; {$ K
- wavWriter.Write(Encoding.ASCII.GetBytes("data"));; L' h- r! n( k+ I7 d
- wavWriter.Write(dataSize); // 数据大小1 ^* h- T" ]( }* b# k% W3 z7 k
/ P" t$ `9 L8 z; l- // 从.rl2文件中读取并写入PCM音频数据1 S' c3 I$ E, \4 ~
- for (int i = 0; i < numFrames; i++)
1 Q2 l; \, j' u# R - {6 y s3 Z6 e! c3 t, v
- byte[] audioData = reader.ReadBytes((int)soundSize[i]);1 g- ]8 | l0 J8 H
- wavWriter.Write(audioData);3 G! p( e E9 B# Y$ {7 N) z
- }0 R7 _/ B+ P. t. g
- }
/ S1 w N1 T2 F+ I2 y# o4 r4 ` - }
( J+ r, e5 ?2 A% X; T - }
- D% P/ Z( ~! u7 S9 P6 y+ u - }% j2 b* q# A1 I' {5 q
- $ H0 I% ?7 z0 l0 |
- class Program% ~: z! x2 F5 U
- {
5 I$ H. O9 w; d% T3 u+ M - static void Main(string[] args)1 E3 N I3 p2 {' ` R: U& L
- {6 X, o- s# n4 v1 t! S4 s
- string inputFile = "N1275510.RL2";6 c, Z0 O9 J. E L
- string outputFile = "output.wav";9 t+ y. [+ d: s
- RL2ToWavConverter.ConvertToWav(inputFile, outputFile);3 u z l9 v O! ?
- Console.WriteLine("转换完成。");
; ]" `( N+ m/ H5 I - }% ~( G" c1 k. G" f
- }
9 C. w J: D( b. P+ C- Y
复制代码
" }/ m3 g6 |; |0 u" R( B5 ?9 m) m9 O' C
& j* n0 K0 ?& _. @" X |