本帖最后由 shane007 于 2023-9-2 00:06 编辑 9 m% Q) I6 @! m( y, w
2 E7 y2 F) O1 P* k# R该游戏是scummvm支持的少数几款FMV AVG。
$ A' o: H6 K) I4 {- a$ O视频采用了一种叫做RL2的格式。
; W5 O( Q3 m8 t3 u1 u: o& a参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,4 I* A; T* g& K3 j* f/ t
然后用whisper语音识别之后就能配上字幕了。* d& O- c: V* S& _8 \: {6 k8 H
此外,rl2格式用potplayer也能直接播放。; q" L. V7 J5 X; T& J
# O# P* K7 ]$ _! \文件格式
8 `9 l& \( r9 f4 A( f' r4 }# b8 Fhttps://wiki.multimedia.cx/index.php/RL2& Q* N, \. D5 N7 }; h
3 B9 u. y+ O- u( ^' H- + 0 dword Header -- "FORM"! Z x4 h: w8 m6 Y D
- + 4 dword BackSize -- size of the background frame) b& d$ i7 w; g9 c$ C6 S
- + 8 dword Signature -- "RLV2" or "RLV3"
, H4 Q- Y8 N) m: \! B' q' l& k" I2 g - + C dword DataSize -- size of the data past this point BIG-ENDIAN: m: k9 f; H& g% j4 G, V) ^
- + 10 dword NumFrames -- number of frames. p) U$ O1 N: l+ d5 ~8 c
- + 14 word Method -- encoding method. ignored, zero
- M9 q9 d; J$ [. ~' O* c - + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound
# X6 R! w" Z. p, J - + 18 word Rate -- sound sample rate in Hz# t- p6 u5 M1 y4 s5 h# j
- + 1A word Channels -- number of sound channels1 e: o9 j' Q/ v# {& A( h
- + 1C word DefSoundSize -- size of the single sound chunk. see notes below4 }9 H2 C2 \4 c, |# ^# h
- + 1E word VideoBase -- initial drawing offset within 320x200 viewport6 _5 T: Y4 S7 d) Z. J
- + 20 dword ClrCount -- number of used colors+ `! b* e g1 t& z1 s, }
- + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries2 g1 R# w- |0 _* B+ E k' N
- -- if Signature == "RLV3" AND BackSize <> 0$ u7 `' R' U+ p" m
- +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding
6 n9 @! e: i6 Q* K6 ^" p& ]* t - --+ T4 x. m5 M* Q+ a3 P/ \
- +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
% V) e3 y+ r# q2 ]0 `" C; ? - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file' ?, X& s7 S+ |/ D9 g8 ~( `
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk: q Z$ E: q( w3 {% }2 z
- -- for each frame --& S) S% t% E/ n9 B. f% [% F
- +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
4 H& g" b" K f# {$ i8 ? - +yyy byte Video[...] -- compressed video stream
复制代码
3 S) c# G2 U7 t: ~' w; p5 F参考代码(有问题,但可参考)- using System;3 G2 n" Y; p1 U' {+ J. L) P
- using System.IO;. P2 c9 g# G3 Z" f! @ ?# j/ W
- using System.Text;# ]; e6 L+ V# D: L& c9 q9 r
- + k2 c# c/ _- I2 J
- public class RL2ToWavConverter
* a1 m; @8 V: j( m, t' H - {& l& ]: b- r# F |, p
- public static void ConvertToWav(string inputFile, string outputFile)8 X+ v! I# A9 [. ]
- {3 y) E; ^: U) l0 U) w! v7 d
- using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
, z2 @: [' c! ?- p - using (BinaryReader reader = new BinaryReader(fs))
# Z4 r( O o5 w% C6 t - {# {& S: f. O& i. ]
- // 读取头部
4 ^) W/ Q, q& W2 }- O - string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
' J/ L: |! N( |! M$ M - if (header != "FORM")1 J9 T; [" F) P
- {
& M- |: S+ I1 A, y - Console.WriteLine("无效的.rl2文件格式。");
0 H5 g: h' ^, i9 P - return;
5 K0 c# {% A! w$ \5 | - }6 j" I" i/ }6 j; u7 l3 ~
, U5 c, I' j! g! s. c9 M- uint backSize = reader.ReadUInt32();9 g; Z6 @/ k( n/ @
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));- W7 u! g. z$ x3 s9 ?
- uint dataSize = reader.ReadUInt32();
4 V9 x0 J; h. c V" T) |& ` - uint numFrames = reader.ReadUInt32();9 `2 }6 a4 b$ X
- ushort method = reader.ReadUInt16();
3 Z' T! ~0 o8 T/ x! H - ushort soundRate = reader.ReadUInt16();* l, @8 |7 [' w$ @4 Y7 x/ O
- ushort rate = reader.ReadUInt16();+ M2 C) J( _- l
- ushort channels = reader.ReadUInt16();
9 v9 Y5 Q0 j- y1 q) X6 m3 O - ushort defSoundSize = reader.ReadUInt16();& |0 g& n- q6 z |5 p( @9 O7 [
- ushort videoBase = reader.ReadUInt16();
) q: K; g0 Y) F# K b( M - uint clrCount = reader.ReadUInt32();: X4 ~$ T ^; Q
- uint[] chunkSize = new uint[numFrames];
- v& C1 L4 t0 X2 I0 f% G - uint[] chunkOffs = new uint[numFrames];
) z2 B- K* j2 Y$ @- r3 T - uint[] soundSize = new uint[numFrames];3 I6 C7 o# Y: d# a" K6 Q
- % O: q1 _' D- w' v& k t
- if (signature != "RLV2" && signature != "RLV3")
- h' Q: W6 h# F# m# ` - {
5 w; T4 K" e" U& Z - Console.WriteLine("不支持的签名。");8 `2 O* k, v e
- return;; h, \ X4 E) {. }3 o" g
- }
7 K+ {; h: u7 L/ J
8 V( C& ~: z# e8 A6 X& _- // 读取块信息$ F+ e. T5 l( w- F7 h0 }! d/ ]
- for (int i = 0; i < numFrames; i++)
2 a3 j1 c7 t9 B0 x0 A7 S - {
: @/ u6 K$ \4 `( I - chunkSize[i] = reader.ReadUInt32();9 f4 L) j, E' x# V- ?
- chunkOffs[i] = reader.ReadUInt32();; h3 U, v* ^& w. s
- soundSize[i] = reader.ReadUInt32();
% X+ Q$ Z) I8 ^. c: t, c - }
5 a0 W9 H/ ]1 m) x2 y& [8 }- s( H- b - + Y) Y/ ]- H+ K0 R, O& f: O
- // 如果存在背景帧,请跳过它
9 D2 @5 A8 ^2 ?2 L } M) M: Y - if (signature == "RLV3" && backSize != 0)0 B0 O e9 r& _/ W. {
- {% O; p, G, b9 C9 y/ E+ P
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);9 s; Y A: y( F4 B3 o, A( ?. e
- }
+ S, ?2 Q, `3 o' n0 i4 z
- D# M3 g6 X" I8 X- // 创建一个WAV文件并写入音频数据- l% H, o! H5 z/ E9 o4 K) O
- using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
: i' h! u5 U3 A# A R& I( ~ - {
! k3 P s/ i% }3 i. ~; h, z% D - // 写入WAV头部) |4 R0 l- X4 Z' k1 [. H; o
- wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
% z4 J8 x, j, y. x) S/ L2 i1 B - wavWriter.Write(36 + dataSize); // 总文件大小 - 80 P& g; X0 H- T+ ~; S
- wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
8 |0 q, l* I2 v5 k/ s0 D6 Z( c" k& z - wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
1 P0 L! G: u- v* W* |3 B - wavWriter.Write(16); // fmt块大小
# ^$ E: w" a4 a) M9 r0 B1 _ - wavWriter.Write((ushort)1); // 音频格式(PCM)
8 O3 A. H( x& m+ |7 b! ~& ^- B! B - wavWriter.Write(channels); // 声道数
8 ~! A# q+ v6 g) E$ P" x: ? - wavWriter.Write(rate); // 采样率6 c! l1 t% z% k7 x" j
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
3 H! n' P' d3 ~! ~" R# y! u - wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数( i; H2 \/ f7 U
- wavWriter.Write(defSoundSize); // 每个样本的位深度
) X' ]! ^: d+ S4 M9 b - wavWriter.Write(Encoding.ASCII.GetBytes("data"));7 q5 S8 |& |7 Y+ L, Q- o; x: c! d4 |
- wavWriter.Write(dataSize); // 数据大小
0 L: `5 z5 T6 {9 y1 Y) d0 f
: c$ }8 h1 u4 ]- [" `9 N" g- // 从.rl2文件中读取并写入PCM音频数据( T. b: R6 p% D1 c9 O: [
- for (int i = 0; i < numFrames; i++)4 k( W/ Q7 y/ W- c# i8 h1 Z
- {1 S- `- }% s8 n4 b8 s3 T# v
- byte[] audioData = reader.ReadBytes((int)soundSize[i]);- d S. A" ]8 ~' U) k! B
- wavWriter.Write(audioData);% U$ B3 x8 }& l9 P
- }
8 f5 B! Y" F( O2 ?' P5 P. m7 u - }
7 k- Z& C1 [6 t - }. V" d, _# ?) k0 Z. T/ t
- }3 y8 j9 P; u. f. f% C6 M
- }* K. a2 G9 e$ V# {5 R
- + h0 b# o7 P+ m
- class Program3 O8 |9 l$ y. h8 {/ a& d
- {
) T) T) J# X2 }1 l6 Y8 N - static void Main(string[] args)
. Q+ ]' C0 T5 n o8 b - {& V" F2 U5 Q$ y
- string inputFile = "N1275510.RL2";
, G; L# T( _# q0 l6 M5 r- D - string outputFile = "output.wav";
; l! U" ]; }/ s - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);' V4 j. O5 E; h
- Console.WriteLine("转换完成。");
' {- ~8 F K7 s0 U - }
5 ~) \2 S9 ~2 N - }
+ I- n) Q4 {' N/ o- ^- e! ?% \
复制代码 7 [8 b5 e* l( X
: X" n& o9 ~9 _7 p
: J( `8 }, |- K |