本帖最后由 shane007 于 2023-9-2 00:06 编辑 0 L+ E7 h) g" t. R% E+ S
" e2 F5 C8 ^ G/ u" v) X: Y A$ u9 ^. {
该游戏是scummvm支持的少数几款FMV AVG。
+ ]" }9 v$ ~8 _ Q2 @/ ^! j0 O" t, \' K视频采用了一种叫做RL2的格式。! b C/ x5 `; R5 h# M9 _2 s* Y2 E
参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
4 _+ `- ~+ X; D9 m/ N- m( a然后用whisper语音识别之后就能配上字幕了。
9 J; b3 ?# K" t% g此外,rl2格式用potplayer也能直接播放。
; {0 X7 V/ S: a4 o
; T+ P+ Y+ K( S& u8 c) @文件格式$ M4 q; ^' P6 {' S$ C% O- T
https://wiki.multimedia.cx/index.php/RL2: w4 ]6 O2 x, w
' D9 P% m9 i ^% n6 w0 |; {- + 0 dword Header -- "FORM"
+ \6 Y% O7 _: b& y! | - + 4 dword BackSize -- size of the background frame/ t. B0 L5 H" s, y ]/ T, N8 n* L
- + 8 dword Signature -- "RLV2" or "RLV3"5 X7 k. X$ v* Z2 A: r, m5 ^
- + C dword DataSize -- size of the data past this point BIG-ENDIAN& i1 c% A! h+ e7 ^3 I$ E
- + 10 dword NumFrames -- number of frames
2 ?; x% h9 p6 U) Z/ R* N - + 14 word Method -- encoding method. ignored, zero
2 f0 O8 E* Q7 }! e - + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound4 A! o G; i- Z1 M: r8 G5 [
- + 18 word Rate -- sound sample rate in Hz: `2 u& o& ?: r: y- u2 M
- + 1A word Channels -- number of sound channels
8 N l" x) F# g# L - + 1C word DefSoundSize -- size of the single sound chunk. see notes below
% Q) O: a9 R3 k7 \" }/ i( j/ } - + 1E word VideoBase -- initial drawing offset within 320x200 viewport! z+ q2 m8 n3 {# c* j5 h
- + 20 dword ClrCount -- number of used colors8 q8 u6 d1 \% [
- + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
2 s5 I3 `" ]% A - -- if Signature == "RLV3" AND BackSize <> 0
( a, b) k+ }9 r2 c& Y& i - +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding
, A( ~; A4 j' ]5 {; ~ - --1 x! X3 D6 j# J0 \; {$ z
- +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
* t# A% a& z6 u1 N - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file
( ?; f* j; Z' S0 H/ ]# N+ p - +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk! h3 Y( z- U4 g4 n' ^
- -- for each frame --. J6 u7 v' C: w5 h0 r0 d0 }; e
- +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
0 }( _4 t4 H6 u. F2 O - +yyy byte Video[...] -- compressed video stream
复制代码 9 V1 W7 _+ }3 o1 j+ d
参考代码(有问题,但可参考)- using System;% Q; W7 R$ } O: @3 ]( D
- using System.IO;
9 y, A; b+ H2 i5 g - using System.Text;
7 ?! X4 p& N0 L/ O% F
& h' @2 d: I0 v* w- public class RL2ToWavConverter
0 ]& C" _! m9 v9 H$ u - {
/ a2 Z6 C) O1 a4 x - public static void ConvertToWav(string inputFile, string outputFile)
9 ^+ R. d4 v3 `" O$ _: @+ s - {( Z; X; J2 B3 a5 g4 V
- using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
' B- @' n* Z6 i8 Y9 m$ Y. V6 B0 F( U - using (BinaryReader reader = new BinaryReader(fs))0 [9 L1 Y ]- `9 J! @) D
- {
* l! |4 ?1 s5 [4 \( y - // 读取头部. K; S) r9 M+ k9 H2 h
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));
$ U m! T3 e- R; F; N1 T* } - if (header != "FORM")% B$ X7 R* N5 S% s: [0 |# r
- {
( S& e0 p% S1 W; o" j( l3 [4 ~ - Console.WriteLine("无效的.rl2文件格式。");
! A7 z( {7 Z; M" E6 k - return;" C9 v! r, n! p, t( |% W
- }
& A' D, f# S f# u& m( B* ^ - ) O l! S7 D6 z( V$ @, }. `2 j
- uint backSize = reader.ReadUInt32();! J; x) g7 n: n! }# p' M% W
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));* \ c, b) F( |0 T* |, L- p
- uint dataSize = reader.ReadUInt32();& y! e: A) A$ @: [" S- j
- uint numFrames = reader.ReadUInt32();
% M1 J- o { P. L$ Z - ushort method = reader.ReadUInt16(); N' c' g) q- _% R# K
- ushort soundRate = reader.ReadUInt16();
9 N% b& I* w% G0 c0 ^, i3 O- S- ] - ushort rate = reader.ReadUInt16();6 n& W9 j2 P, E3 g" q' R2 s" O
- ushort channels = reader.ReadUInt16();% O. D+ e( t9 ~! f
- ushort defSoundSize = reader.ReadUInt16();
* u+ b& j: P9 y/ o) O9 V/ @; n - ushort videoBase = reader.ReadUInt16();
8 z5 ?- w" r" k# F W - uint clrCount = reader.ReadUInt32();
* V% Y; d/ ^$ w% e# `7 [1 D8 [9 y - uint[] chunkSize = new uint[numFrames];
3 v) u; s! F9 d1 T - uint[] chunkOffs = new uint[numFrames];: M3 y8 S; n- R) G' X
- uint[] soundSize = new uint[numFrames];
7 n5 g" S2 X: D- k) L! [ - ' w' g w0 E* J( A8 K% [2 L7 Z
- if (signature != "RLV2" && signature != "RLV3")
& J: G: Z; L2 R/ K+ m - {, W7 b# ?: o9 f1 K: u K1 }
- Console.WriteLine("不支持的签名。");5 B; L4 a A) J2 [; a* m' g
- return;
0 @ P- ?0 x: Z4 m& ~4 G- u - }/ J8 z* w5 o+ F! C
1 E2 G* h& @4 s+ ^- // 读取块信息4 U# P& i! @) k T- @% L) |3 `
- for (int i = 0; i < numFrames; i++)
( k* v+ q0 ]/ [( b% O+ Y - {
* Q H5 B( j8 Y - chunkSize[i] = reader.ReadUInt32();
+ `- K5 h3 p+ F" f8 z/ R. H - chunkOffs[i] = reader.ReadUInt32();/ D# h( E, ]+ z, |
- soundSize[i] = reader.ReadUInt32();, k" j/ b9 V, o; A$ o5 R
- }, B( I* k A7 H3 }/ o* ^# v- i
- & B' R3 ]- l1 Z! ^- n: M6 p4 h
- // 如果存在背景帧,请跳过它
' z+ s4 b7 }. b. t' I1 K - if (signature == "RLV3" && backSize != 0)9 E: r5 |- _2 f9 ^0 r4 d
- {
. {$ t& G; [& ?+ `# O- H/ n/ I - reader.BaseStream.Seek(backSize, SeekOrigin.Current);
. b4 x8 Q1 U+ i- d9 w; @/ f - }9 i. o) H8 R( B5 h
- # e9 F7 g' C! c6 V7 A% G0 q! O( h
- // 创建一个WAV文件并写入音频数据
7 n# i' [9 a6 u9 K2 J* `$ \9 j! P( l - using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))+ k2 T6 P. ]7 r& w) L
- {) D6 T# y% Z: N: D: ]4 i
- // 写入WAV头部! a9 H" o. _. \% U- Y
- wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
* Q$ b* R! n8 y6 \' T - wavWriter.Write(36 + dataSize); // 总文件大小 - 8
7 U) w9 `7 u% N. @. H# F; ~ - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
3 Q5 P% W0 L6 X" T - wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));& Z9 J1 @5 |! P) @2 y
- wavWriter.Write(16); // fmt块大小
9 D& Z$ h/ s6 P' [# G - wavWriter.Write((ushort)1); // 音频格式(PCM), g* d0 _" Y) z, I, q8 S; B
- wavWriter.Write(channels); // 声道数
" K6 Z) D. O! {4 F. j' W, U6 j# R; u - wavWriter.Write(rate); // 采样率
% _2 j. Y( W( d$ t - wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
+ a0 {8 Q6 x2 K, [3 R' q" ~ - wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
2 V+ Z. ^5 R% |5 Z* t - wavWriter.Write(defSoundSize); // 每个样本的位深度# }+ l6 Q p3 ~5 r7 P
- wavWriter.Write(Encoding.ASCII.GetBytes("data"));: J; u/ x- ]4 K- c% v. Q
- wavWriter.Write(dataSize); // 数据大小1 x. @4 Q$ ~3 b
- 8 V5 Q3 [8 k8 a
- // 从.rl2文件中读取并写入PCM音频数据
/ h# y1 X3 ^6 t6 Z% Z - for (int i = 0; i < numFrames; i++)
; [* D/ Z R8 A* M - {4 H d4 J( M4 r( H! R. X
- byte[] audioData = reader.ReadBytes((int)soundSize[i]);: E$ s0 a1 G7 j( g3 l: @
- wavWriter.Write(audioData);
: c8 c* [/ N1 g6 ]( N* v - }
- {# s( }- Q, e0 G* E - }, z' ]) F% E" h; g( a
- }! I1 P# ?- E) f8 C
- }% c8 P( L* Z3 o4 q$ f% G
- }
1 L4 x! ^* }% N) t% |$ w1 l
3 \1 N2 A- U- v' g& {- class Program
; j: {+ |" y3 c" Z - {
( n$ }8 i; B l7 R1 d. p - static void Main(string[] args)
" R0 j0 \4 B) X# o$ J/ | - {
0 t. x/ L- [9 z) q( h0 T$ Z4 } - string inputFile = "N1275510.RL2";3 f- X! @ e2 G' g
- string outputFile = "output.wav"; w- s; s# t! ]: `
- RL2ToWavConverter.ConvertToWav(inputFile, outputFile);
$ I; [! @7 P! F& [8 P9 M2 M - Console.WriteLine("转换完成。");! z( W4 f3 s' R1 U$ [
- }* L$ ]# o8 G& p& s% L# i& z
- }
( J' j' i' R( v7 p
复制代码
. z5 \( a' _6 W+ G+ j' Y$ z7 ~
/ \7 O T' `! A B8 l& `; t! U' w! S& u: v
|