本帖最后由 shane007 于 2023-9-2 00:06 编辑 1 @/ _$ E7 D2 I8 D1 v
/ T5 Q! z \ u- E' x* m. M该游戏是scummvm支持的少数几款FMV AVG。
. V/ A0 e6 r7 `0 g) }视频采用了一种叫做RL2的格式。
" i [. M3 Y+ a" L# F参考以下格式和Scummvm中的代码,可以想办法将RL2中的raw data部分转换为wav,2 n9 p( h& g% s! D# z, Z, O
然后用whisper语音识别之后就能配上字幕了。 |* Z; S1 d4 K' Z8 B
此外,rl2格式用potplayer也能直接播放。) Z: R1 c% T1 [, A
. T S: f( i+ G& l" X
文件格式+ Y$ k$ E1 N) {! ?" W6 c
https://wiki.multimedia.cx/index.php/RL2, {$ |0 `! a2 `% @9 T) y. a7 H
5 O5 H+ p0 p4 V# D4 }8 y: {. A- + 0 dword Header -- "FORM"
- M8 V0 J8 t# v4 e. W2 u - + 4 dword BackSize -- size of the background frame
. n! N( }8 q X' {& k. Q2 w# M7 Q - + 8 dword Signature -- "RLV2" or "RLV3"
6 i1 u; M; W2 U! ^ - + C dword DataSize -- size of the data past this point BIG-ENDIAN
2 l+ W/ S3 J; j3 [& n! e* n3 V - + 10 dword NumFrames -- number of frames4 ]6 r* L L& b5 c( n
- + 14 word Method -- encoding method. ignored, zero) Z# @/ K/ }7 \8 X6 W" U, {
- + 16 word SoundRate -- sound sample rate in some obscure format. zero means no sound) \0 e L3 X' G8 E" {
- + 18 word Rate -- sound sample rate in Hz' M$ J( h7 I* B6 }0 n4 S) f
- + 1A word Channels -- number of sound channels
3 K6 H3 t7 `* S$ | - + 1C word DefSoundSize -- size of the single sound chunk. see notes below$ G. [8 S' n W) U& v4 m
- + 1E word VideoBase -- initial drawing offset within 320x200 viewport
/ |9 \ M( U' @ |7 ` - + 20 dword ClrCount -- number of used colors6 L* X+ c6 d0 x6 N
- + 24 RGB Palette[256] -- 256 RGB triplets. full 8-bit entries
; e8 i" ]. A$ p - -- if Signature == "RLV3" AND BackSize <> 0
7 H2 ?2 y) ]1 d1 b - +324 byte BackFrame[BackSize] -- encoded background frame. ignore VideoBase during it's decoding
" m9 `) w7 W1 I0 e3 A - --
- ^9 x7 j, H2 h" Q - +xxx dword ChunkSize[NumFrames] -- complete size of the chunk for each frame (audio+video)
3 ?% ?. z* d+ s5 u7 ~) a/ w - +yyy dword ChunkOffs[NumFrames] -- offset of the each frame chunk from the start of file
4 x" ?/ ^0 b& W$ K4 ` - +zzz dword SoundSize[NumFrames] -- size of the audio portion in the frame chunk' |# I- u" ]) F/ H9 [2 k$ J+ }
- -- for each frame --
: k: k4 T& O1 Y# R: |0 R! b - +xxx byte Audio[SoundSize[frame_nr] & 0xFFFF] -- raw 8-bit audio/ \" L" a9 u8 n$ @7 b7 G
- +yyy byte Video[...] -- compressed video stream
复制代码 4 v, o5 z" f( u1 l7 K' j6 |
参考代码(有问题,但可参考)- using System;
) r! |/ D* y) `0 t - using System.IO;
% T( {2 `% X- m! H2 q+ f$ U1 | - using System.Text;
% r9 d# ]4 X& F8 ]# q - 6 K7 l. J' {( N7 `9 [5 m% }0 | C
- public class RL2ToWavConverter
8 M8 A/ f+ p N( R e7 @; g, z - {
x' K6 Y$ O4 a m D3 e. n - public static void ConvertToWav(string inputFile, string outputFile)
6 `, P9 ^2 U3 [# c9 X - {
. Q. R: o$ n1 ?, W - using (FileStream fs = new FileStream(inputFile, FileMode.Open, FileAccess.Read))
1 I$ L) V7 B% F1 K, U9 } - using (BinaryReader reader = new BinaryReader(fs))9 X3 X: `& e6 S7 a6 F* T, U3 T; ^
- {
1 K0 b+ @( ^0 S4 x - // 读取头部. R' Q7 A+ A2 f
- string header = Encoding.ASCII.GetString(reader.ReadBytes(4));, m4 D$ x3 J6 l7 x( I' z$ H$ o5 {
- if (header != "FORM")' E2 c; |/ g/ X/ o1 [: {; m4 I+ E
- {! y" G }0 O3 T, x7 ]! l5 l9 f
- Console.WriteLine("无效的.rl2文件格式。");
' x0 ~ x% T& c6 | - return;" e k9 u: V; X5 J; K& v( B; j! `
- }
4 I0 D3 k( Q1 v - , g) b4 X4 T7 r. O1 E
- uint backSize = reader.ReadUInt32();. v$ x/ r8 ]/ q' g0 R& W0 M5 o
- string signature = Encoding.ASCII.GetString(reader.ReadBytes(4));% [- n# E* s1 K7 E3 Q
- uint dataSize = reader.ReadUInt32();
$ c/ D5 Q. Q. j! [8 u: h - uint numFrames = reader.ReadUInt32();
# x& B7 n6 p! K9 V! _* S" B# A - ushort method = reader.ReadUInt16();
# g# p) X2 V4 q) l$ O7 T0 X* q. X$ | - ushort soundRate = reader.ReadUInt16();
( n; _3 j! B* O6 ]; i0 C" [7 O - ushort rate = reader.ReadUInt16();
, N7 n$ d' V5 T6 y+ }. ]0 n% b z - ushort channels = reader.ReadUInt16();
- r. ~7 Z R& A0 C) Z6 r - ushort defSoundSize = reader.ReadUInt16();
+ W0 _: ^8 Y* o1 N) I - ushort videoBase = reader.ReadUInt16();
$ N- J9 k, D' ]7 D - uint clrCount = reader.ReadUInt32();7 b5 I; @+ M' `4 z8 V+ G
- uint[] chunkSize = new uint[numFrames];
3 k5 p; R& @. p; Q) z - uint[] chunkOffs = new uint[numFrames];" L% g5 h; E/ ^ \8 { ?" V. Z
- uint[] soundSize = new uint[numFrames];
! l- ^% s: W5 i, O' L
4 d/ p8 H9 c6 ?- if (signature != "RLV2" && signature != "RLV3")
& A% ~$ p5 p' \: Q. | - {' Q' w$ N& I [; z6 c: r j& L
- Console.WriteLine("不支持的签名。");; f, A" U6 G/ `' [7 z
- return;8 \, V" k, Z/ p4 l' `1 Q
- } Q+ e3 m% t* q4 s
* _6 z$ @! V/ D/ g- // 读取块信息
$ a$ d$ K8 C* j# e/ C1 ~* t8 I - for (int i = 0; i < numFrames; i++)0 E+ r1 G4 [8 y; E9 S# `
- {
, q5 C7 o9 x5 S$ a+ A - chunkSize[i] = reader.ReadUInt32();
' U. E6 D3 T- W0 | - chunkOffs[i] = reader.ReadUInt32();& U6 ]% g1 z& Y* A+ v
- soundSize[i] = reader.ReadUInt32();6 b- {% _2 p+ g6 J) X
- } ~) @0 V* o% s, g! h
- : Y5 k/ | t& R( Q5 n8 o# b0 D# b
- // 如果存在背景帧,请跳过它
- w5 L; Y( ?* D L - if (signature == "RLV3" && backSize != 0)9 i O% \" d+ i
- {" W! W, W, X' ~/ i
- reader.BaseStream.Seek(backSize, SeekOrigin.Current);( Y) j, [4 k9 h! E; P& U
- }
- B' s* U2 ^# Q, Q2 a- c3 D - : A+ Z0 y/ p, P6 Q, R1 @2 I4 K
- // 创建一个WAV文件并写入音频数据( ~2 K2 F* m, `/ f9 h: H
- using (BinaryWriter wavWriter = new BinaryWriter(File.Open(outputFile, FileMode.Create)))8 H* u0 h) D6 S9 p5 R/ y2 k
- {
$ ^6 X5 |2 t. A+ Q9 n - // 写入WAV头部, i6 c1 E$ C3 ^ y: c
- wavWriter.Write(Encoding.ASCII.GetBytes("RIFF"));
5 P3 u a& ^( F2 x# ^ I* t - wavWriter.Write(36 + dataSize); // 总文件大小 - 8
$ f4 U& N$ F5 z3 ^! m - wavWriter.Write(Encoding.ASCII.GetBytes("WAVE"));
* T3 X4 {4 z3 x* v) D! n - wavWriter.Write(Encoding.ASCII.GetBytes("fmt "));7 k% n p; Z8 |; e# w4 f/ L) n8 E' ~' R9 K
- wavWriter.Write(16); // fmt块大小
& `; K" A0 ? c7 a - wavWriter.Write((ushort)1); // 音频格式(PCM)+ e) M% y0 }& U# N5 j! `
- wavWriter.Write(channels); // 声道数. O8 g8 d: d. _$ i1 W) z- H
- wavWriter.Write(rate); // 采样率$ j* q. Q H+ X) l3 p" G
- wavWriter.Write(rate * channels * defSoundSize / 8); // 每秒字节数" Z$ m4 Z; H+ S- t
- wavWriter.Write((ushort)(channels * defSoundSize / 8)); // 每个采样点字节数
5 s- i$ p& r# P( F) Y n - wavWriter.Write(defSoundSize); // 每个样本的位深度
* Q4 N& R1 p! N$ s. V - wavWriter.Write(Encoding.ASCII.GetBytes("data"));5 @1 J! t' p1 F; _" u& b3 f
- wavWriter.Write(dataSize); // 数据大小
( a5 j4 Z4 b2 U6 @$ d$ w& m0 h - # a+ X$ l# |$ E/ y' r7 O' r4 l( b& X
- // 从.rl2文件中读取并写入PCM音频数据5 S4 G2 ]* A8 w8 i" W
- for (int i = 0; i < numFrames; i++), e; `( p8 i7 L: L/ i
- {/ {2 l0 ?3 @ ~$ O, k
- byte[] audioData = reader.ReadBytes((int)soundSize[i]);
. {& p1 T% d n4 y* o3 h - wavWriter.Write(audioData);
! ?3 y" l; a0 o; k - }) V( y' Q0 z0 e# y1 J0 y- O) _' J4 o
- }
: X# Z2 K% I' ~5 x$ B2 `5 V. z: r9 w - }2 d3 A5 j* |3 @& ]1 [& w9 ?
- }2 z; I1 W0 R0 a0 m6 ~3 T
- }
5 m/ }1 T" b9 c5 \% b' Y - . D1 ^9 w, p" n# K! \: O" L4 X
- class Program" I5 G- N9 z) @4 t
- {
* z7 @$ l, b1 b6 ~# l0 i - static void Main(string[] args)* s+ L9 B! l2 ^3 W% H
- {
* P% t+ K# R9 Q j4 @* R9 z - string inputFile = "N1275510.RL2";
1 l- e" ? f9 g# l! ] - string outputFile = "output.wav";
+ P8 A! }" ^- [2 q - RL2ToWavConverter.ConvertToWav(inputFile, outputFile);( m8 m' q4 p6 m8 K8 g6 T
- Console.WriteLine("转换完成。");
- c; q4 n- ~- g& } - }9 L' Z% ]( ~7 ^6 Q, k/ A2 L
- }
2 X% q3 }2 |) S$ H
复制代码 1 N) K" z# b( S* }# l! {
$ T. V3 g4 v3 H
' F/ b/ o0 S* W
|