本帖最后由 shane007 于 2023-9-2 00:06 编辑
; Q8 ]6 J K, Y/ `3 g& d3 y
7 w ~/ u; w- c3 f该游戏是scummvm支持的少数几款FMV AVG。
8 S& d$ F7 v& }: Z视频采用了一种叫做RL2的格式。
! X, G8 x* @, g0 @7 ~/ ]% K参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav," ?' g2 x$ T7 C/ D3 t
然后用whisper语音识别之后就能配上字幕了。7 q9 H X1 b: H# y0 ]3 m
此外,rl2格式用potplayer也能直接播放。! d( q7 Q1 r6 V" t
5 ]6 n$ f" P) }) i8 d D文件格式
" I, G6 s$ n+ U/ n3 ]https://wiki.multimedia.cx/index.php/RL2% g; l. G+ b# M' R u+ O
' U. A8 E% ^, C x( w9 D* o- + 0 dword Header -- "FORM"4 Q3 H$ `: e0 _( F
- + 4 dword BackSize -- size of the background frame7 _" Z, }2 ` m4 p$ Q
- + 8 dword Signature -- "RLV2" or "RLV3"' I) T. l& m) Y0 v& \
- + C dword DataSize -- size of the data past this point BIG-ENDIAN
: O) C: ^ r, d3 h9 Y9 w, Q6 W - + 10 dword NumFrames -- number of frames
! S/ Y8 F$ f4 }" u: P& }: y - + 14 word Method -- encoding method. ignored, zero) K: `) U& f' E0 T
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound" Q% _; ?- K- P9 s5 L8 |( o
- + 18 word Rate -- sound sample rate in Hz# x6 q& B% h8 G4 C) u
- + 1A word Channels -- number of sound channels
$ R6 L E7 a6 G6 a y9 d" R - + 1C word DefSoundSize -- size of the single sound chunk. see notes below
! v2 \- I% F2 l - + 1E word VideoBase -- initial drawing offset within 320x200 viewport
2 }- G& l$ e/ m' o' y$ q0 U - + 20 dword ClrCount -- number of used colors _0 p- r" V( i% r$ p6 L. \- B
- + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
4 g3 u# o1 ~% i, I4 c* Y7 X - -- if Signature == "RLV3" AND BackSize <> 0
+ s. u& m- d% g. o2 W - +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding( D l0 e; U, s
- --
- W+ ^ g) O( a - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video). }0 g0 U1 q* {3 P ~
- +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file; K t3 {' \# M9 h) ]
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk% U4 S- y7 `$ |! U0 D/ v
- -- for each frame --
# \; K4 T# H! z8 G$ o - +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
( _- d8 F, `0 |7 ` - +yyy byte Video[...] -- compressed video stream
复制代码 8 R: P V2 v o5 Y% v" i
参考代码(有问题,但可参考)- using System;; N1 K6 B) F6 j. s
- using System.IO;
4 T v3 Y8 {9 V4 j- c, w2 t( s/ I- | - using System.Text;
9 e7 A1 g* ^4 b - & d8 z% F/ S l4 h3 Q- ]( R
- public class RL2ToWavConverter$ _1 H2 v, P; q
- {6 \, b/ u* M1 P
- public static void ConvertToWav(string inputFile, string outputFile)
' x, P& \- |1 A- R( _$ i! I - {1 n1 w6 @! Z* y8 K
- using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))% M* b* k3 q4 W- {1 t ?
- using (BinaryReader reader = new BinaryReader(fs))" V1 n( x/ H$ ?4 ?+ l* M" v$ Z7 n
- {8 u8 m- U S% ^4 b$ V+ j8 c! u( l
- // 读取头部
' g$ K' a7 [0 u0 H - string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
. D$ q2 p) k7 G* N" b - if (header != "FORM")
( i5 m7 ~6 L: o- K9 ~ - {
: e0 u5 [' J4 \2 M3 w$ |# b: ~ - Console.WriteLine("无效的.rl2文件格式。");
: ]' T$ E- E) w2 W - return;5 A# @% B$ k1 v; S" ]
- }
) [. |8 Q9 j8 Q; x" [! o' |- P - 8 r t+ W( _" u
- uint backSize = reader.ReadUInt32();
, e3 v8 I' d: }# a5 F$ } - string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));
+ F$ \) q* R8 ]; V; t$ C - uint dataSize = reader.ReadUInt32();
% Y9 d$ m3 l: t4 Y$ i0 b' J ] - uint numFrames = reader.ReadUInt32();7 K7 q1 @3 v, D9 u9 R+ J1 Z7 b
- ushort method = reader.ReadUInt16();
2 g+ ^* s) M- P+ R; [0 c3 \ X7 A - ushort soundRate = reader.ReadUInt16();8 w+ E/ g/ {4 f# C2 z
- ushort rate = reader.ReadUInt16();
- b r9 w/ @/ g$ S9 j - ushort channels = reader.ReadUInt16();
$ ^) y2 n4 {8 y) s1 U! Q' a# Q2 k - ushort defSoundSize = reader.ReadUInt16();
2 I0 w/ I- g- M4 P9 F; r! B - ushort videoBase = reader.ReadUInt16();
2 E2 y& V! t: Q! b* U9 u9 ` - uint clrCount = reader.ReadUInt32();0 V4 S: [- G! K7 |3 u, V
- uint[] chunkSize = new uint[numFrames];; w: p, {( Q. g, d
- uint[] chunkOffs = new uint[numFrames];' m/ n/ a+ C! S! I, T- |9 y" |
- uint[] soundSize = new uint[numFrames];4 d, O, ?% B+ a+ }
- 7 V. m# f# D: Z; \5 z5 h
- if (signature != "RLV2" && signature != "RLV3")' h% C8 I4 x8 d9 o& y( J! @8 K8 N
- {
% q o8 L! l: C( i) f( I - Console.WriteLine("不支持的签名。");
5 [2 x7 u2 Z: S' P& Q1 S8 Z0 c - return;7 ~( R+ w* T3 r/ {4 N8 o
- }
8 v* b+ W6 O# M' @0 ?& {4 H' P - 0 h/ b2 ?3 K) h8 ^4 T3 j" I' N
- // 读取块信息/ t; {, ?* Q. b' h) g
- for (int i = 0; i < numFrames; i++)
; R3 h' g/ J3 K3 P$ l! Y+ b [ - {$ }& r( |& I5 w+ c) }6 z
- chunkSize[i] = reader.ReadUInt32();
/ j; ^; R' E) i0 k! K - chunkOffs[i] = reader.ReadUInt32();( s P* \0 C! j: c3 E( T
- soundSize[i] = reader.ReadUInt32();
2 b N# P5 ~4 e( o% R - }
7 \. j% D" h3 ~1 Q' A - : ~1 D9 p2 Y# q V7 H, _" E
- // 如果存在背景帧,请跳过它
9 ^+ Y1 n/ c4 U: q8 V3 a, d5 ^ - if (signature == "RLV3" && backSize != 0)
/ B- C. r" Z: S+ |& W - {' O- c) c6 @5 ^
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);* \" T# P# g2 h9 H
- }
8 d! V% g; u0 F7 n R& `- \
( @& \! R5 J0 o- // 创建一个WAV文件并写入音频数据" M: |5 }8 z5 t4 \/ i; w6 Y W
- using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))# l4 T% `% O+ S8 D. Z% I+ ^
- {" K+ C! ?. ? Z8 d
- // 写入WAV头部
+ \6 W, x/ \3 z3 c+ w" x - wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
/ l+ r v% b4 u9 M7 b - wavWriter.Write(36 + dataSize); // 总文件大小 - 8) M2 C' P7 N+ e2 t7 u: Q
- wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
! D5 ]3 m( q( W - wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
; O7 G" |9 J: b# v - wavWriter.Write(16); // fmt块大小% n9 Z% Q$ h& x, K( L c6 ~
- wavWriter.Write((ushort)1); // 音频格式(PCM)
5 O8 M! f( x. R* N4 K - wavWriter.Write(channels); // 声道数1 o* L! e( C- E- `" m+ v! V
- wavWriter.Write(rate); // 采样率
# y5 [1 }; q- Y! [ - wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数, [9 ^; y3 t6 d2 g/ {! |
- wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数$ D7 ~ G$ l- g
- wavWriter.Write(defSoundSize); // 每个样本的位深度2 c) S5 {; H! s4 |- s9 [
- wavWriter.Write(Encoding.ASCII.GetBytes("data"));
. y+ j/ b3 _6 |& K# P0 ?2 _ - wavWriter.Write(dataSize); // 数据大小3 V/ u# e! V+ D& ~# H
- # n3 N$ f! A" h% N
- // 从.rl2文件中读取并写入PCM音频数据
4 d# f$ \, n7 h1 h4 Q' ~ - for (int i = 0; i < numFrames; i++)
" T( T& D& \0 x' n( f' i - {- R& i; \9 O5 G. T
- byte[] audioData = reader.ReadBytes((int)soundSize[i]);
7 L- K" b$ y3 K! |' U - wavWriter.Write(audioData);5 y& V# K9 B7 g) ]
- }
/ N/ J& ?1 p' D1 e2 l" T1 o - }( F, D" H; W' X, @
- }
; H# l* i$ V$ T4 k/ R; z5 V1 h - }) c7 I7 v# Q2 }; y/ ~3 g5 Q' M
- }! {* I3 F5 m) F4 q
- # D* D& R7 t( |+ _) D& Z7 h4 j
- class Program c. v% n7 G+ q9 c# \7 S
- {
' k T7 h7 b# H F& R w - static void Main(string[] args)
% m1 p. B: `. _2 r6 L5 ^ - {
8 g y% L( t# G5 G- B+ Q - string inputFile = "N1275510.RL2";
# C, P) O. ~' {% r* q$ ]* P - string outputFile = "output.wav";
/ F& O" T$ x" r; P0 x - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);
2 _( V5 N' g6 J& q8 t - Console.WriteLine("转换完成。"); [9 R; S3 g5 q$ s+ c
- } o' _. w: h8 h5 M8 {
- }
1 m1 n: m$ W4 C
复制代码
2 {4 o" l' [! _4 S2 E5 Q/ q/ q) R* P& l; W5 w7 i/ A
$ [7 B, h7 X! p5 @7 I: J' w0 M
|