From 2d94f690936af4c2279b1e284ea7cb93ffe71717 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Thu, 30 Sep 2021 13:49:50 +0200 Subject: [PATCH] EA muxer --- nihav-allstuff/src/lib.rs | 1 + nihav-game/Cargo.toml | 6 +- nihav-game/src/lib.rs | 3 + nihav-game/src/muxers/ea.rs | 161 +++++++++++++++++++++++++++++++++++ nihav-game/src/muxers/mod.rs | 15 ++++ 5 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 nihav-game/src/muxers/ea.rs create mode 100644 nihav-game/src/muxers/mod.rs diff --git a/nihav-allstuff/src/lib.rs b/nihav-allstuff/src/lib.rs index ec41d7e..3b6d60f 100644 --- a/nihav-allstuff/src/lib.rs +++ b/nihav-allstuff/src/lib.rs @@ -85,6 +85,7 @@ pub fn nihav_register_all_encoders(re: &mut RegisteredEncoders) { pub fn nihav_register_all_muxers(rm: &mut RegisteredMuxers) { flash_register_all_muxers(rm); generic_register_all_muxers(rm); + game_register_all_muxers(rm); llaudio_register_all_muxers(rm); rad_register_all_muxers(rm); realmedia_register_all_muxers(rm); diff --git a/nihav-game/Cargo.toml b/nihav-game/Cargo.toml index 652fd67..9111b9a 100644 --- a/nihav-game/Cargo.toml +++ b/nihav-game/Cargo.toml @@ -16,7 +16,7 @@ features = ["qmf"] nihav_commonfmt = { path = "../nihav-commonfmt", default-features=false, features = ["all_demuxers"] } [features] -default = ["all_decoders", "all_demuxers"] +default = ["all_decoders", "all_demuxers", "all_muxers"] demuxers = [] all_demuxers = ["demuxer_bmv", "demuxer_bmv3", "demuxer_cnm", "demuxer_fcmp", "demuxer_fst", "demuxer_gdv", "demuxer_hl_fmv", "demuxer_imax", "demuxer_q", "demuxer_sga", "demuxer_siff", "demuxer_smush", "demuxer_vmd", "demuxer_vx"] demuxer_bmv = ["demuxers"] @@ -60,3 +60,7 @@ all_audio_decoders = ["decoder_fstaud", "decoder_lhst500f22", "decoder_smush_aud decoder_fstaud = ["decoders"] decoder_lhst500f22 = ["decoders"] decoder_smush_audio = ["decoders"] + +all_muxers = ["muxer_ea"] +muxers = [] +muxer_ea = ["muxers"] \ No newline at end of file diff --git a/nihav-game/src/lib.rs b/nihav-game/src/lib.rs index e0187ae..2a568d9 100644 --- a/nihav-game/src/lib.rs +++ b/nihav-game/src/lib.rs @@ -20,3 +20,6 @@ pub use crate::codecs::game_register_all_decoders; #[allow(clippy::upper_case_acronyms)] mod demuxers; pub use crate::demuxers::game_register_all_demuxers; + +mod muxers; +pub use crate::muxers::game_register_all_muxers; diff --git a/nihav-game/src/muxers/ea.rs b/nihav-game/src/muxers/ea.rs new file mode 100644 index 0000000..4da1fc6 --- /dev/null +++ b/nihav-game/src/muxers/ea.rs @@ -0,0 +1,161 @@ +use nihav_core::muxers::*; + +struct EAMuxer<'a> { + bw: &'a mut ByteWriter<'a>, + has_alpha: bool, + nframes: u32, + max_size: [u32; 2], +} + +impl<'a> EAMuxer<'a> { + fn new(bw: &'a mut ByteWriter<'a>) -> Self { + Self { + bw, + has_alpha: false, + nframes: 0, + max_size: [0; 2], + } + } +} + +impl<'a> MuxCore<'a> for EAMuxer<'a> { + #[allow(clippy::unreadable_literal)] + #[allow(clippy::cast_lossless)] + fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> { + if strmgr.get_num_streams() == 0 { + return Err(MuxerError::InvalidArgument); + } + + let mut nvideo = 0; + for stream in strmgr.iter() { + if stream.get_media_type() == StreamType::Video { + if stream.get_info().get_name() != "vp6" { + return Err(MuxerError::UnsupportedFormat); + } + nvideo += 1; + } else { + return Err(MuxerError::UnsupportedFormat); + } + } + if nvideo == 0 || nvideo > 2 { + return Err(MuxerError::UnsupportedFormat); + } + self.has_alpha = nvideo == 2; + + if self.has_alpha { + self.bw.write_buf(b"AVP6\x08\x00\x00\x00")?; + } + for (str_no, stream) in strmgr.iter().enumerate() { + if let NACodecTypeInfo::Video(ref vinfo) = stream.get_info().get_properties() { + let tag = if str_no == 0 { b"MVhd" } else { b"AVhd" }; + + self.bw.write_buf(tag)?; + self.bw.write_u32le(0x20)?; + self.bw.write_buf(b"vp60")?; + self.bw.write_u16le(vinfo.width as u16)?; + self.bw.write_u16le(vinfo.height as u16)?; + self.bw.write_u32le(0)?; + self.bw.write_u32le(0)?; + self.bw.write_u32le(stream.tb_den)?; + self.bw.write_u32le(stream.tb_num)?; + } else { + unimplemented!(); + } + } + + Ok(()) + } + #[allow(clippy::collapsible_else_if)] + fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> { + let stream = pkt.get_stream(); + let str_num = stream.get_num(); + if str_num > 2 { + return Err(MuxerError::UnsupportedFormat); + } + + let chunk_len = pkt.get_buffer().len() as u32; + + let tag = if pkt.is_keyframe() { + if str_num == 0 { b"MV0K" } else { b"AV0K" } + } else { + if str_num == 0 { b"MV0F" } else { b"AV0F" } + }; + self.max_size[str_num] = self.max_size[str_num].max(pkt.get_buffer().len() as u32); + self.bw.write_buf(tag)?; + self.bw.write_u32le(chunk_len + 8)?; + self.bw.write_buf(&pkt.get_buffer())?; + + if str_num == 0 { + self.nframes += 1; + } + + Ok(()) + } + fn flush(&mut self) -> MuxerResult<()> { + Ok(()) + } + fn end(&mut self) -> MuxerResult<()> { + if !self.has_alpha { + self.bw.seek(SeekFrom::Start(0x10))?; + self.bw.write_u32le(self.nframes)?; + self.bw.write_u32le(self.max_size[0])?; + } else { + self.bw.seek(SeekFrom::Start(0x18))?; + self.bw.write_u32le(self.nframes)?; + self.bw.write_u32le(self.max_size[0])?; + self.bw.seek(SeekFrom::Start(0x38))?; + self.bw.write_u32le(self.nframes)?; + self.bw.write_u32le(self.max_size[1])?; + } + Ok(()) + } +} + +impl<'a> NAOptionHandler for EAMuxer<'a> { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + +pub struct EAMuxerCreator {} + +impl MuxerCreator for EAMuxerCreator { + fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box + 'a> { + Box::new(EAMuxer::new(bw)) + } + fn get_name(&self) -> &'static str { "ea" } + fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::OnlyVideo } +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::*; + use nihav_core::demuxers::*; + use nihav_core::muxers::*; + use nihav_codec_support::test::enc_video::*; + use crate::*; + + #[test] + fn test_ea_muxer() { + let mut dmx_reg = RegisteredDemuxers::new(); + nihav_commonfmt::generic_register_all_demuxers(&mut dmx_reg); + let dec_config = DecoderTestParams { + demuxer: "avi", + in_name: "assets/Duck/vp6_crash.avi", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + game_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "ea", + enc_name: "", + out_name: "muxed.ea", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "ea", &mux_reg, + [0xc8c6484d, 0x863de1ae, 0x97a38a31, 0x59e2a7ef]); + } +} diff --git a/nihav-game/src/muxers/mod.rs b/nihav-game/src/muxers/mod.rs new file mode 100644 index 0000000..b609072 --- /dev/null +++ b/nihav-game/src/muxers/mod.rs @@ -0,0 +1,15 @@ +use nihav_core::muxers::*; + +#[cfg(feature="muxer_ea")] +mod ea; + +const MUXERS: &[&dyn MuxerCreator] = &[ +#[cfg(feature="muxer_ea")] + &ea::EAMuxerCreator {}, +]; + +pub fn game_register_all_muxers(rm: &mut RegisteredMuxers) { + for muxer in MUXERS.iter() { + rm.add_muxer(*muxer); + } +} -- 2.39.5