本帖最后由 shane007 于 2023-9-2 00:06 编辑
4 N! P5 E/ N; J1 C0 D& t5 K; B' C+ ?/ i# R. P8 |+ Z0 G4 A
该游戏是scummvm支持的少数几款FMV AVG。
/ m, Y% n* ]7 A) x: h视频采用了一种叫做RL2的格式。
. b; t7 C, _9 F0 ^参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,' M) ?# b2 F1 ~) H+ j
然后用whisper语音识别之后就能配上字幕了。
* c0 [& ]) t" r- w% J t. O此外,rl2格式用potplayer也能直接播放。& S, s5 k: E- w2 i1 J# j7 o- q
5 A3 O: A. m( o9 R$ D
文件格式0 C, J7 B8 |1 H; z; s) e
https://wiki.multimedia.cx/index.php/RL2
5 Z0 W7 O" j/ d# r# z" m- % E& _; i# g, ~. z$ |
- + 0 dword Header -- "FORM"
. K- }& i' i4 K$ I8 w/ v, T - + 4 dword BackSize -- size of the background frame) q6 _! ]3 y$ Y+ V' J6 Z
- + 8 dword Signature -- "RLV2" or "RLV3"
4 H/ k$ l9 _' v3 h- N# T3 I/ { - + C dword DataSize -- size of the data past this point BIG-ENDIAN
X1 u& ^$ i! L8 {7 m0 X - + 10 dword NumFrames -- number of frames" P& H1 n" [& Y/ T4 p- Q0 ~
- + 14 word Method -- encoding method. ignored, zero+ @: A% c- b m$ P1 z4 k) B5 r
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound% U8 y' d7 C& k3 c( w, O1 Y- ~
- + 18 word Rate -- sound sample rate in Hz
; e/ i3 l0 \0 l' W# ?+ M - + 1A word Channels -- number of sound channels0 `2 `& u+ D) q. n, x
- + 1C word DefSoundSize -- size of the single sound chunk. see notes below
( Q% v5 X, H" h# z$ Q7 {) E# a# z - + 1E word VideoBase -- initial drawing offset within 320x200 viewport; n6 }7 b6 U: Q4 J
- + 20 dword ClrCount -- number of used colors
* ?& k, f; S8 B7 p }) e - + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries9 A% B+ l" ~" |6 w8 Q! T L+ {! A
- -- if Signature == "RLV3" AND BackSize <> 0/ S1 g6 p% o1 S; k# n6 a# P
- +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding
+ p7 |+ X2 J5 u4 h# \8 F( T - --
4 A- H) t. U R/ T* }) P - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)' T, k- M, v% H; o0 }
- +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file, V, z1 C$ O% B9 I2 ~& P: b
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk4 ~( W" y9 P0 R0 P
- -- for each frame --
r* x! o; r6 s0 ~6 F* j4 S# f$ Y - +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
* L, p3 }8 }2 Y: S! V- s - +yyy byte Video[...] -- compressed video stream
复制代码
V! m# |1 a% z) s8 N1 ]5 q/ ?% H( i( c参考代码(有问题,但可参考)- using System;, v4 D$ ]3 B9 q+ ~
- using System.IO;& Q6 r, t6 d3 _6 |- v$ h' v
- using System.Text;$ }+ |1 W" l; ~7 K
- 9 x7 x4 O4 d+ I9 C; ?( }5 P+ E
- public class RL2ToWavConverter
2 n) \, C6 [6 p. I( y+ M; t) }& ]6 c - {
# \+ }1 a- o9 [3 S - public static void ConvertToWav(string inputFile, string outputFile)
/ g2 e0 x( v# g8 K/ Y - {
N9 W4 Q( o# H. b& T6 e - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))! q6 i- h4 }0 k/ D; ?- c2 L
- using (BinaryReader reader = new BinaryReader(fs))
* E( m! u. l6 j - {4 `6 n, K$ H! t6 j7 z7 M7 v
- // 读取头部3 F7 u# @$ e" \. a' s/ ]; I9 o
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
, Z+ u9 _: m! G, V - if (header != "FORM")" l+ r) X* w4 c; w' Y
- {- ?- n3 D1 I) _* `+ t6 P! X
- Console.WriteLine("无效的.rl2文件格式。");( y) N; i, z. ^4 p# E
- return;" ^# @& e2 U; |, d+ N
- }! Q5 r0 n5 L U* N; a; N- K
$ Z( W% [5 g: ~5 B- uint backSize = reader.ReadUInt32();
- d" f3 V1 n8 Z7 x( q" p - string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));4 Q# A6 y) a) L! l) K2 B) A
- uint dataSize = reader.ReadUInt32();( B4 h' l1 \5 O2 r; F! Z
- uint numFrames = reader.ReadUInt32();
+ T2 G8 P" W! q - ushort method = reader.ReadUInt16();$ q/ F8 A! D: F2 @1 X: [
- ushort soundRate = reader.ReadUInt16();
8 ]5 h( \- q2 h8 y5 S: J/ }) m - ushort rate = reader.ReadUInt16();
, {4 m: b. X) h! ^! g1 l% z' i# v - ushort channels = reader.ReadUInt16();
" q( ^: M4 F- F% Z3 B9 S3 R - ushort defSoundSize = reader.ReadUInt16();
, d: @# ^- o9 _* M) `; d - ushort videoBase = reader.ReadUInt16();+ y/ k( z: |3 d0 u. c
- uint clrCount = reader.ReadUInt32();$ ~+ P1 C) F& N' B: @' _* l
- uint[] chunkSize = new uint[numFrames];
; o5 S5 g& }( v @6 F4 I2 e - uint[] chunkOffs = new uint[numFrames];
( C( c# e8 O0 u; h4 O! l1 g - uint[] soundSize = new uint[numFrames];) T6 U3 ~- Z' ]$ I. F/ x3 r4 @2 d
- $ U( y* X8 n$ b' E% u, P0 Y
- if (signature != "RLV2" && signature != "RLV3")
, `* G5 K$ Y1 r# w! A - {
" p$ Q2 Z9 [: I# @ a7 W0 S - Console.WriteLine("不支持的签名。");
" o2 P- _3 I8 `" W - return;
' j9 c j% U2 Q0 P* G; H* j! f) D - }
y1 f+ W5 }2 q. s3 t
1 ^; [8 Q* ]2 p! }$ i* ~( @- // 读取块信息
, ]( L! |4 r1 j1 q3 ^3 o7 t# R - for (int i = 0; i < numFrames; i++)
3 [* v# I [, d/ M - {
$ B0 q6 K; ~( z+ z - chunkSize[i] = reader.ReadUInt32();" O5 D2 W$ M' X# N& K
- chunkOffs[i] = reader.ReadUInt32();
/ ?; R: p2 r$ L* S6 k* @" r - soundSize[i] = reader.ReadUInt32();
2 O+ \6 f5 J! I9 y6 X3 k* c - }
[# M! [" {, R' u3 F9 P2 z - ( p/ }( v4 F/ s a z6 S3 _' u% I
- // 如果存在背景帧,请跳过它( D5 S* b) C6 I) K5 A+ k9 b
- if (signature == "RLV3" && backSize != 0)4 }4 x% ?, L. A+ z6 o! S2 ?' {" |
- {9 E- Z1 u7 n, b1 D. G
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);& H: a5 M7 a5 e1 m
- }
2 [1 v* }: M. g; h8 B
) o$ W( E- o& X- // 创建一个WAV文件并写入音频数据
* s) k$ f% M& t2 m/ | - using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))7 ?2 }8 o- H4 j+ J3 ?9 {; W
- {
/ A1 f' i3 E) u# _: A - // 写入WAV头部
; V" U7 J9 D; J - wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));- ^1 r, B1 Z4 B" {
- wavWriter.Write(36 + dataSize); // 总文件大小 - 8
! Z7 L( P4 y f* H# C" c- } - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));' \* P! M+ I @- `9 b" E
- wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
, G9 o, H* U8 | - wavWriter.Write(16); // fmt块大小
2 z4 h! f- ]; E1 f - wavWriter.Write((ushort)1); // 音频格式(PCM)1 c; c% f& h0 A( A8 T; D
- wavWriter.Write(channels); // 声道数
' d2 ^1 Z( r" D1 F% H# j# o- D, P - wavWriter.Write(rate); // 采样率* a) V' _8 {( y9 I( c# x
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
" a4 H" Z1 m4 s, d9 P; _& z; O - wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数+ Y, T9 @1 {& I3 r4 h( Z
- wavWriter.Write(defSoundSize); // 每个样本的位深度. h2 o$ M, N7 }0 t4 q/ _! F6 a
- wavWriter.Write(Encoding.ASCII.GetBytes("data"));
! ~1 e3 I4 E/ i6 D, ?: G - wavWriter.Write(dataSize); // 数据大小
/ }1 |9 O1 J. C4 j - 9 t; x- {" K+ A0 u4 w% S' U1 l# I
- // 从.rl2文件中读取并写入PCM音频数据
+ a( t* q9 B$ M* i4 w - for (int i = 0; i < numFrames; i++)
5 _) r* L2 {* q( h# L/ f - {
. q; Z/ X/ N1 N2 X/ L% z" ?; `* m - byte[] audioData = reader.ReadBytes((int)soundSize[i]);( F9 {4 g5 a. v6 T, \2 x( V/ H p
- wavWriter.Write(audioData);1 P5 U, y3 K6 Z- e1 t: c3 V7 |
- }# P* X8 M1 R) |. W! D/ [
- }8 q0 p3 M2 ^9 I* m; Y& J: I
- }% V# M- u% A( X) z" m, M
- }1 |' k9 Q) @& x/ ]) K
- }
# z% ], z- O8 [ - 9 j. W& h7 v4 |7 [
- class Program
8 v8 V6 [) u& \" z7 d; Z- K - {
6 L @1 h; B7 x$ ~1 i2 E - static void Main(string[] args)5 C; R9 C4 W; _2 K3 K1 T8 G
- {8 x' ^) i0 g3 Z, j- i3 S
- string inputFile = "N1275510.RL2";
" `8 o; A) x) Z- _% E" K9 P8 j! C - string outputFile = "output.wav";
' G5 a; ~* X8 S' T - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);
% v5 n5 f) f: P; w! W( z5 c! g) [ - Console.WriteLine("转换完成。");0 t4 W+ _# Q* d/ Q
- }8 q! w* V% ?" W/ F- z
- }
$ f. j2 c% i/ e* F3 p% b! E8 X! H
复制代码
" a; |% S7 }4 d1 ?' F; T! G' P! J
2 a( `% }. F$ L6 j/ a& ]) {
|