本帖最后由 shane007 于 2023-9-2 00:06 编辑
$ B' M& a2 c: M. h- V: z. @+ T/ M5 F1 m! _9 X3 w6 }. B4 G
该游戏是scummvm支持的少数几款FMV AVG。
, T* R8 O2 i s) |* D4 |% o视频采用了一种叫做RL2的格式。
4 X" J4 n4 ~, @参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,
% r ] v- D$ n! {然后用whisper语音识别之后就能配上字幕了。3 @& N$ n7 @/ n
此外,rl2格式用potplayer也能直接播放。5 B( y- I! `4 L7 _* o
2 \9 _: x N) l4 D/ y文件格式
! I! k9 R" E" d) Q5 ?https://wiki.multimedia.cx/index.php/RL2
) j; S: J7 G) X2 A$ I1 z7 {
+ r& e- x5 F$ q. B y ^, H- D, ?- + 0 dword Header -- "FORM"8 d& c5 V7 c# p
- + 4 dword BackSize -- size of the background frame* `" W4 Q5 d! n. Y! T( a& K
- + 8 dword Signature -- "RLV2" or "RLV3"
) W8 F- v. p$ q V% ^, [. H - + C dword DataSize -- size of the data past this point BIG-ENDIAN; L+ G b& a3 w/ g3 l, U$ f0 a1 `
- + 10 dword NumFrames -- number of frames
1 @; L. _- F* C2 C4 ?) R9 ]. V! S - + 14 word Method -- encoding method. ignored, zero7 l4 P, T2 i s# n
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound0 B3 p. A; p2 F5 R
- + 18 word Rate -- sound sample rate in Hz6 g& I" i& N% ~; I6 Z- I
- + 1A word Channels -- number of sound channels* m/ S2 _/ M$ j/ i9 K/ a3 v
- + 1C word DefSoundSize -- size of the single sound chunk. see notes below" z. c+ I9 P7 y- h H. Q$ c
- + 1E word VideoBase -- initial drawing offset within 320x200 viewport
, J# m# n' _7 E) z - + 20 dword ClrCount -- number of used colors
3 t1 ~8 D# s- w% u2 `" a s! V! [! k" ^ - + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
8 `4 X6 U; u+ Y Z- i8 D. j5 g# T - -- if Signature == "RLV3" AND BackSize <> 0
# {$ N+ R3 s% j8 ?6 B8 \3 h' Q - +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding9 X. [) d2 q7 z4 {* Q* C+ x# _
- --
0 ]4 v) y$ h6 W1 Y1 {3 d - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)! L. d' [% N2 n! ~: R# C _7 J; d
- +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file( R8 n( b O0 z' d3 F
- +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk
6 p* g0 r7 f8 {6 @) A6 e - -- for each frame --; I I1 U" g+ R
- +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio
% v! k3 @ m0 P# K& C! @) F! W- m/ @ - +yyy byte Video[...] -- compressed video stream
复制代码 : c+ m( }1 `5 B6 N8 M" z Z/ P
参考代码(有问题,但可参考)- using System;. k/ a, ?- F/ v1 M0 J1 v
- using System.IO;
5 N+ ]2 z# M7 G. ~ ^0 A - using System.Text;
8 Q6 ?4 T" M# C. ^9 z2 L9 {# Z
. f$ z( r, W% w- v/ X" d% w- public class RL2ToWavConverter5 S2 \/ o% o6 m8 u9 H
- {
( l) G. r. o! N/ K2 c - public static void ConvertToWav(string inputFile, string outputFile)& M8 p- o) S$ e) n- ^2 Z! ~
- {
' Y" g9 Y7 U! n' O# | - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))4 \5 Q: v& z( Z4 y
- using (BinaryReader reader = new BinaryReader(fs))
# P. ]$ s5 H! `' W( @ - {
1 T# l0 ]. G6 ]" y - // 读取头部! k! w U6 ~8 u: \& r* v6 G
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));) @. k, {- q* \( k" B
- if (header != "FORM")
! l# y K7 j0 `/ B* R - {
: k9 z; O# K$ ^6 }0 g# x# b5 P - Console.WriteLine("无效的.rl2文件格式。");4 _8 j2 P0 z! a6 T7 u. @$ L! n
- return;
' p, \+ h: c% k/ @0 N" ~1 F( {4 _ - }
. x5 L0 p0 E$ E- W; I" v9 {) g8 a
! W$ G u! K6 [8 l2 n2 L7 J) Y- uint backSize = reader.ReadUInt32();9 U6 a; S' l e' {' q" R
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));5 e/ m5 v9 k- J4 a$ i
- uint dataSize = reader.ReadUInt32();
2 E0 r3 P j2 l# \. T - uint numFrames = reader.ReadUInt32();
# `: p& ?/ H7 }( A: ^' z3 N - ushort method = reader.ReadUInt16();
- K$ g. j, H* | - ushort soundRate = reader.ReadUInt16();. C( z7 D7 K( g1 {2 q' F4 W
- ushort rate = reader.ReadUInt16();
+ f6 B/ ? G0 u5 o6 m8 X# F - ushort channels = reader.ReadUInt16();
5 A# v: f& h& z - ushort defSoundSize = reader.ReadUInt16();/ l. t+ q2 Y- I9 U: i' J& D
- ushort videoBase = reader.ReadUInt16();$ q9 J* a( e# ~2 ^% w! X, c" z
- uint clrCount = reader.ReadUInt32();
0 w2 d R7 y5 d! p; Q' l/ t - uint[] chunkSize = new uint[numFrames];
7 G; X g$ t! Y% _6 O- {1 d/ J - uint[] chunkOffs = new uint[numFrames];
% N( t! R$ V8 s$ O& J B) R - uint[] soundSize = new uint[numFrames];
' y# V6 N/ ~; _- c
( @3 q& M8 ]- R3 P, J) j' Z- if (signature != "RLV2" && signature != "RLV3")
0 m+ _- W) r5 \2 ?6 ~- Q - {$ C& X; l5 U' {5 u! H+ \+ y3 X% t* j
- Console.WriteLine("不支持的签名。");
b1 S" }2 A! U+ \4 Y - return;0 P& b5 L" M# {3 \# A0 }9 g! `
- }3 ?$ _3 _: Q7 J2 t% F
4 f0 P" @+ D3 c- M t- // 读取块信息
' c% \% F3 ] i, ]8 [$ k - for (int i = 0; i < numFrames; i++)
8 ] ^8 h* ]3 H - {
) a: `3 @" ?6 a; W$ I) ? - chunkSize[i] = reader.ReadUInt32();
4 R ^7 h) w+ Z4 R; p$ n7 z - chunkOffs[i] = reader.ReadUInt32();
8 |8 M6 l! l$ t$ g+ j# i3 ~1 K& T - soundSize[i] = reader.ReadUInt32();
; k' e" V' F/ [. X. [/ Z - }
2 `- M+ ?, i; v( n - + S1 K2 c) X% R, B. ]
- // 如果存在背景帧,请跳过它- d5 v, b3 k6 P2 h' k3 D# A8 I
- if (signature == "RLV3" && backSize != 0)3 Z% }0 J6 Y6 `3 H- Y* Y. o
- {6 N9 N+ E) c/ o, Q( i" V/ d
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);7 K6 C, X: O. \, `; r) |1 q
- }3 T4 N1 Y3 p5 F9 Q% K
- 7 S- x& I, n* `
- // 创建一个WAV文件并写入音频数据
% ]4 S: q: v$ }- x) @0 b8 J7 y; y - using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))
3 F4 v4 |% q; r" |6 U" T3 r- u4 h - {5 _% l W) M) x. L$ f8 A+ n1 v1 L( q
- // 写入WAV头部
- f4 |2 M" ]7 w( x5 H# @: c - wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
9 I' R. ~ O r5 ?/ i/ o( j - wavWriter.Write(36 + dataSize); // 总文件大小 - 8
; N0 Q9 T9 t/ p - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
0 k2 T, q) o: C- I - wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));
& z2 s- x x" l+ J6 I - wavWriter.Write(16); // fmt块大小
6 e0 C, i( m; S: O: @( R* s: r( X3 h - wavWriter.Write((ushort)1); // 音频格式(PCM)* {* D8 {* R( g, y# y
- wavWriter.Write(channels); // 声道数 E }' D1 S# a6 A8 m. u
- wavWriter.Write(rate); // 采样率! z: |$ W+ f" c* k
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数
# W6 a) m2 z G - wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数9 k3 _) E( Q8 a) Q% d) x0 c+ q5 p
- wavWriter.Write(defSoundSize); // 每个样本的位深度4 ]0 |4 g: y! p
- wavWriter.Write(Encoding.ASCII.GetBytes("data"));, ~: z2 h- M8 j, j' s Q; A4 Q. ]
- wavWriter.Write(dataSize); // 数据大小2 [/ b$ a) P# ]
# ?. u" b K5 b; X( |3 p, A7 Q- // 从.rl2文件中读取并写入PCM音频数据
& N+ x, M. K9 Q" }- S - for (int i = 0; i < numFrames; i++)$ b5 g' `+ l Z: Z
- {
2 r3 t) ~6 k- t+ T3 @ - byte[] audioData = reader.ReadBytes((int)soundSize[i]);$ y9 x, \1 M6 e' D% M
- wavWriter.Write(audioData);
4 E% X, s- Q9 C! g j2 a# g. f( m - }$ a* W9 q; `- Y2 Z3 Y* j5 w7 R
- }0 M3 i; {: z- T( @" F |
- }9 T. r( V; O/ e0 V3 g. z
- }+ r' U; M) H% y+ i$ A
- }2 R( o. j% v8 P. n
: ~2 t' @+ r& |0 c1 ]; a$ Z6 b- class Program
8 u# r8 y' Y" B, `! r) _ - {% G3 x" Z' k. }1 a+ U
- static void Main(string[] args)
- _1 I' a* j0 }/ e. T6 b/ c) H - {
0 w- l1 ~9 a) |$ J; Y - string inputFile = "N1275510.RL2";
0 @* ]) x' C4 t - string outputFile = "output.wav";
( m0 l* a0 P5 m5 A7 F) J( | - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);
* ~& N4 e* S! ]. ]* y2 ?: c% l - Console.WriteLine("转换完成。");
4 g( w& Q7 U7 U. [3 a - }) r$ [, q9 d2 Y; n0 v& U
- }6 ]) R* U1 Q# g' m ]# i' b. u: g
复制代码 9 b' O$ L8 w% U7 f/ h
" _+ d5 _$ D" }# c1 D) a9 ~
* H; b9 W: m0 u |