From: Kostya Shishkov Date: Wed, 1 Mar 2023 18:36:38 +0000 (+0100) Subject: add RealMedia and RealAudio muxers X-Git-Url: https://git.nihav.org/?p=nihav.git;a=commitdiff_plain;h=9dc1fb4be1d02a1d1c1ea85340f49151e00ecad2 add RealMedia and RealAudio muxers --- diff --git a/nihav-allstuff/src/lib.rs b/nihav-allstuff/src/lib.rs index f655b95..fc60450 100644 --- a/nihav-allstuff/src/lib.rs +++ b/nihav-allstuff/src/lib.rs @@ -78,6 +78,7 @@ pub fn nihav_register_all_muxers(rm: &mut RegisteredMuxers) { flash_register_all_muxers(rm); generic_register_all_muxers(rm); llaudio_register_all_muxers(rm); + realmedia_register_all_muxers(rm); } #[cfg(test)] diff --git a/nihav-realmedia/Cargo.toml b/nihav-realmedia/Cargo.toml index dc3c0d6..84a07e5 100644 --- a/nihav-realmedia/Cargo.toml +++ b/nihav-realmedia/Cargo.toml @@ -12,10 +12,13 @@ path = "../nihav-codec-support" features = ["h263", "mdct", "blockdsp"] [features] -default = ["all_decoders", "all_demuxers"] +default = ["all_decoders", "all_demuxers", "all_muxers"] demuxers = [] all_demuxers = ["demuxer_real"] demuxer_real = ["demuxers"] +muxers = [] +all_muxers = ["muxer_real"] +muxer_real = ["muxers"] all_decoders = ["all_video_decoders", "all_audio_decoders"] decoders = [] diff --git a/nihav-realmedia/src/lib.rs b/nihav-realmedia/src/lib.rs index bcc66fd..8012992 100644 --- a/nihav-realmedia/src/lib.rs +++ b/nihav-realmedia/src/lib.rs @@ -25,3 +25,8 @@ pub use crate::codecs::realmedia_register_all_decoders; mod demuxers; #[cfg(feature="demuxers")] pub use crate::demuxers::realmedia_register_all_demuxers; + +#[cfg(feature="muxers")] +mod muxers; +#[cfg(feature="muxers")] +pub use crate::muxers::realmedia_register_all_muxers; diff --git a/nihav-realmedia/src/muxers/mod.rs b/nihav-realmedia/src/muxers/mod.rs new file mode 100644 index 0000000..e0594c9 --- /dev/null +++ b/nihav-realmedia/src/muxers/mod.rs @@ -0,0 +1,26 @@ +use nihav_core::muxers::*; + +#[cfg(debug_assertions)] +macro_rules! validate { + ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(MuxerError::InvalidData); } }; +} +#[cfg(not(debug_assertions))] +macro_rules! validate { + ($a:expr) => { if !$a { return Err(MuxerError::InvalidData); } }; +} + +#[cfg(feature="muxer_real")] +mod rmvb; + +const MUXERS: &[&dyn MuxerCreator] = &[ +#[cfg(feature="muxer_real")] + &rmvb::RealMediaMuxerCreator {}, +#[cfg(feature="muxer_real")] + &rmvb::RealAudioMuxerCreator {}, +]; + +pub fn realmedia_register_all_muxers(rm: &mut RegisteredMuxers) { + for muxer in MUXERS.iter() { + rm.add_muxer(*muxer); + } +} diff --git a/nihav-realmedia/src/muxers/rmvb/audiostream.rs b/nihav-realmedia/src/muxers/rmvb/audiostream.rs new file mode 100644 index 0000000..fcb3d9c --- /dev/null +++ b/nihav-realmedia/src/muxers/rmvb/audiostream.rs @@ -0,0 +1,693 @@ +use nihav_core::frame::*; +use nihav_core::muxers::*; +use super::RMStreamWriter; + +// fourcc, codec name, interleaver, version +static AUDIO_CODEC_REGISTRY: &[(&[u8;4], &str, &[u8;4], u8)] = &[ + (b"lpcJ", "ra14.4", b"Int0", 3), + (b"28_8", "ra28.8", b"Int4", 4), + (b"cook", "cook", b"genr", 5), + (b"dnet", "ac3", b"Int0", 4), + (b"sipr", "sipro", b"sipr", 4), + (b"atrc", "atrac3", b"genr", 5), + (b"LSD:", "ralf", b"Int0", 6), + (b"raac", "aac", b"vbrs", 5), + (b"racp", "aac", b"vbrf", 5), +]; + +struct InterleaveParams { + block_size: usize, + factor: usize, + frames_per_blk: usize, +} + +trait Interleaver { + fn get_flavor(&self) -> usize; + fn get_block_size(&self) -> usize; + fn get_factor(&self) -> usize; + fn get_frames_per_block(&self) -> usize; + fn add_packet(&mut self, src: &[u8]) -> bool; + fn get_packet(&mut self) -> Option<(Vec, bool)>; + fn flush(&mut self); + + fn get_frame_size(&self) -> usize { self.get_block_size() / self.get_frames_per_block() } +} + +struct NoInterleaver { + frame_size: usize, + pkt: Option>, +} +impl Interleaver for NoInterleaver { + fn get_flavor(&self) -> usize { 0 } + fn get_block_size(&self) -> usize { self.frame_size } + fn get_factor(&self) -> usize { 1 } + fn get_frames_per_block(&self) -> usize { 1 } + fn add_packet(&mut self, src: &[u8]) -> bool { + if self.pkt.is_none() { + self.pkt = Some(src.to_vec()); + true + } else { + false + } + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { + let mut ret = None; + std::mem::swap(&mut self.pkt, &mut ret); + if let Some(pkt) = ret { + Some((pkt, true)) + } else { + None + } + } + fn flush(&mut self) {} +} + +struct Int4Interleaver { + flavor: usize, + factor: usize, + frame_size: usize, + block_size: usize, + fpb: usize, + cur_frame: usize, + rd_block: usize, + buf: Vec, + map: Vec, +} +impl Int4Interleaver { + fn new(frame_size: usize) -> MuxerResult { + let params = RA_28_8_INTERLEAVE_PARAMS; + for (flavor, entry) in params.iter().enumerate() { + if entry.block_size / entry.frames_per_blk == frame_size { + let full_size = entry.frames_per_blk * entry.factor; + let mut map = vec![0; full_size]; + for i in 0..full_size { + let fval = i * entry.factor; + let mapped = (fval % full_size) + fval / full_size; + map[mapped] = i; + } + + return Ok(Self { + flavor, + frame_size, + map, + factor: entry.factor, + block_size: entry.block_size, + fpb: entry.frames_per_blk, + cur_frame: 0, + rd_block: 0, + buf: vec![0; entry.block_size * entry.factor], + }); + } + } + Err(MuxerError::UnsupportedFormat) + } +} +impl Interleaver for Int4Interleaver { + fn get_flavor(&self) -> usize { self.flavor } + fn get_block_size(&self) -> usize { self.block_size } + fn get_factor(&self) -> usize { self.factor } + fn get_frames_per_block(&self) -> usize { self.fpb } + fn add_packet(&mut self, src: &[u8]) -> bool { + if self.cur_frame == self.factor * self.fpb { + return false; + } + let pos = self.map[self.cur_frame]; + self.buf[pos * self.frame_size..][..self.frame_size].copy_from_slice(&src); + self.cur_frame += 1; + true + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { + if self.cur_frame == self.factor * self.fpb { + let first = self.rd_block == 0; + let src = &self.buf[self.rd_block * self.block_size..][..self.block_size]; + self.rd_block += 1; + if self.rd_block == self.factor { + self.rd_block = 0; + self.cur_frame = 0; + } + Some((src.to_vec(), first)) + } else { + None + } + } + fn flush(&mut self) { + if self.cur_frame != 0 { + self.cur_frame = self.factor * self.fpb; + } + } +} + +struct GenericInterleaver { + flavor: usize, + factor: usize, + frame_size: usize, + block_size: usize, + fpb: usize, + cur_frame: usize, + rd_block: usize, + buf: Vec, + map: Vec, +} +impl GenericInterleaver { + fn new(frame_size: usize, fcc: [u8; 4]) -> MuxerResult { + let params = match &fcc { + b"atrc" => ATRAC_INTERLEAVE_PARAMS, + b"cook" => COOK_INTERLEAVE_PARAMS, + b"sipr" => SIPRO_INTERLEAVE_PARAMS, + _ => return Err(MuxerError::UnsupportedFormat), + }; + for (flavor, entry) in params.iter().enumerate() { + if entry.block_size / entry.frames_per_blk == frame_size { + let full_size = entry.frames_per_blk * entry.factor; + let mut map = vec![0; full_size]; + + let mut frm = 0; + let mut blk = 0; + let mut even = true; + for dst in map.iter_mut() { + let mapped = blk * entry.frames_per_blk + frm; + blk += 2; + if blk >= entry.factor { + if even { + blk = 1; + } else { + blk = 0; + frm += 1; + } + even = !even; + } + *dst = mapped; + } + + return Ok(Self { + flavor, + frame_size, + map, + factor: entry.factor, + block_size: entry.block_size, + fpb: entry.frames_per_blk, + cur_frame: 0, + rd_block: 0, + buf: vec![0; entry.block_size * entry.factor], + }); + } + } + Err(MuxerError::UnsupportedFormat) + } +} +impl Interleaver for GenericInterleaver { + fn get_flavor(&self) -> usize { self.flavor } + fn get_block_size(&self) -> usize { self.block_size } + fn get_factor(&self) -> usize { self.factor } + fn get_frames_per_block(&self) -> usize { self.fpb } + fn add_packet(&mut self, src: &[u8]) -> bool { + if self.cur_frame == self.factor * self.fpb { + return false; + } + let pos = self.map[self.cur_frame]; + self.buf[pos * self.frame_size..][..self.frame_size].copy_from_slice(&src); + self.cur_frame += 1; + true + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { + if self.cur_frame == self.factor * self.fpb { + let first = self.rd_block == 0; + let src = &self.buf[self.rd_block * self.block_size..][..self.block_size]; + self.rd_block += 1; + if self.rd_block == self.factor { + self.rd_block = 0; + self.cur_frame = 0; + } + Some((src.to_vec(), first)) + } else { + None + } + } + fn flush(&mut self) { + if self.cur_frame != 0 { + self.cur_frame = self.factor * self.fpb; + } + } +} + +struct SiproInterleaver { + block_size: usize, + factor: usize, + flavor: usize, + buf: Vec, + wr_pos: usize, + rd_pos: usize, +} +impl SiproInterleaver { + fn new(mut frame_size: usize) -> MuxerResult { + if frame_size == 0 { + return Err(MuxerError::UnsupportedFormat); + } + while frame_size < 200 { + frame_size <<= 1; + } + for (flavor, entry) in SIPRO_INTERLEAVE_PARAMS.iter().enumerate() { + if entry.block_size == frame_size { + return Ok(Self { + block_size: entry.block_size, + factor: entry.factor, + flavor, + buf: vec![0; entry.block_size * entry.factor], + wr_pos: 0, + rd_pos: 0, + }); + } + } + Err(MuxerError::UnsupportedFormat) + } +} +impl Interleaver for SiproInterleaver { + fn get_flavor(&self) -> usize { self.flavor } + fn get_block_size(&self) -> usize { self.block_size } + fn get_factor(&self) -> usize { self.factor } + fn get_frames_per_block(&self) -> usize { 1 } + fn add_packet(&mut self, src: &[u8]) -> bool { + if self.wr_pos < self.factor { + self.buf[self.wr_pos * self.block_size..][..self.block_size].copy_from_slice(src); + self.wr_pos += 1; + true + } else { + false + } + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { + if self.wr_pos == self.factor { + let first = self.rd_pos == 0; + if self.rd_pos == 0 { + sipro_scramble(&mut self.buf, self.factor, self.block_size); + } + let ret = self.buf[self.rd_pos * self.block_size..][..self.block_size].to_vec(); + self.rd_pos += 1; + if self.rd_pos == self.factor { + self.rd_pos = 0; + self.wr_pos = 0; + } + Some((ret, first)) + } else { + None + } + } + fn flush(&mut self) { + if self.wr_pos != self.factor { + self.wr_pos = self.factor; + } + } +} + +fn sipro_scramble(buf: &mut [u8], factor: usize, fsize: usize) { + let stride = factor * fsize * 2 / 96; + for &swap_pair in SIPRO_SWAPS.iter() { + let mut sidx = usize::from(swap_pair[0]) * stride; + let mut didx = usize::from(swap_pair[1]) * stride; + for _ in 0..stride { + let in0 = buf[sidx >> 1]; + let in1 = buf[didx >> 1]; + let nib0 = (in0 >> ((sidx & 1) * 4)) & 0xF; + let nib1 = (in1 >> ((didx & 1) * 4)) & 0xF; + + buf[didx >> 1] = (nib0 << (4 * (didx & 1))) | (in1 & (0xF << (4 * (!didx & 1)))); + buf[sidx >> 1] = (nib1 << (4 * (sidx & 1))) | (in0 & (0xF << (4 * (!sidx & 1)))); + + sidx += 1; + didx += 1; + } + } +} + +struct AACInterleaver { + frame_size: usize, + data: Vec, + sizes: Vec, + full: bool, +} +impl AACInterleaver { + fn new(frame_size: usize) -> Self { + Self { + frame_size, + data: Vec::with_capacity(frame_size * 3 / 2), + sizes: Vec::with_capacity((frame_size / 128).max(8)), + full: false, + } + } +} +impl Interleaver for AACInterleaver { + fn get_flavor(&self) -> usize { 0 } + fn get_block_size(&self) -> usize { self.frame_size } + fn get_factor(&self) -> usize { 1 } + fn get_frames_per_block(&self) -> usize { 1 } + fn add_packet(&mut self, src: &[u8]) -> bool { + if !self.full { + self.data.extend_from_slice(src); + self.sizes.push(src.len()); + if self.data.len() >= self.frame_size { + self.full = true; + } + true + } else { + false + } + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { + if self.full { + let mut dst = Vec::with_capacity(self.frame_size); + let mut gw = GrowableMemoryWriter::new_write(&mut dst); + let mut bw = ByteWriter::new(&mut gw); + bw.write_u16be((self.sizes.len() * 16) as u16).unwrap(); + for &pkt_size in self.sizes.iter() { + bw.write_u16be(pkt_size as u16).unwrap(); + } + bw.write_buf(&self.data).unwrap(); + + self.data.clear(); + self.sizes.clear(); + self.full = false; + + Some((dst, true)) + } else { + None + } + } + fn flush(&mut self) { + if !self.sizes.is_empty() { + self.full = true; + } + } +} + +struct InterleaveInfo { + fcc: [u8; 4], + il_fcc: [u8; 4], + block_size: usize, + frame_size: usize, + factor: usize, + flavor: usize, +} + +struct AudioStreamWriter { + fcc: [u8; 4], + il_fcc: [u8; 4], + is_raw: bool, + version: u8, + header_pos: u64, + interleave: Box, + data_size: usize, +} + +impl RMStreamWriter for AudioStreamWriter { + fn write_header(&mut self, bw: &mut ByteWriter, astream: &NAStream) -> MuxerResult<()> { + self.header_pos = bw.tell(); + if self.version < 6 { + bw.write_buf(b".ra\xFD")?; + bw.write_u16be(self.version.into())?; + } + + let il_info = InterleaveInfo { + fcc: self.fcc, + il_fcc: self.il_fcc, + block_size: self.interleave.get_block_size(), + frame_size: self.interleave.get_frame_size(), + factor: self.interleave.get_factor(), + flavor: self.interleave.get_flavor(), + }; + + match self.version { + 3 => write_aformat3(bw, astream, &il_info)?, + 4 => write_aformat4(bw, astream, &il_info)?, + 5 => write_aformat5(bw, astream, &il_info)?, + 6 => write_lsd(bw, astream)?, + _ => unreachable!(), + } + Ok(()) + } + fn queue_packet(&mut self, pkt: NAPacket) -> bool { + let src = pkt.get_buffer(); + self.data_size += src.len(); + let frame_size = self.interleave.get_frame_size(); + if !self.is_raw { + let mut ret = false; + for frame in src.chunks(frame_size) { + ret = self.interleave.add_packet(frame); + } + ret + } else { + self.interleave.add_packet(&src) + } + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { self.interleave.get_packet() } + fn flush(&mut self) { self.interleave.flush() } + fn finish(&mut self, bw: &mut ByteWriter) -> MuxerResult<()> { + let cur_pos = bw.tell(); + match self.version { + 3 => { + bw.seek(SeekFrom::Start(self.header_pos + 18))?; + bw.write_u32be(self.data_size as u32)?; + }, + 4 | 5 => { + bw.seek(SeekFrom::Start(self.header_pos + 12))?; + bw.write_u32be(self.data_size/*+header_size*/ as u32)?; + bw.seek(SeekFrom::Current(12))?; + bw.write_u32be(self.data_size as u32)?; + }, + 6 => unimplemented!(), + _ => unreachable!(), + }; + bw.seek(SeekFrom::Start(cur_pos))?; + Ok(()) + } +} + +fn write_audio_metadata(bw: &mut ByteWriter) -> MuxerResult<()> { + bw.write_byte(0)?; // title_string_length + bw.write_byte(0)?; // author_string_length + bw.write_byte(0)?; // copyright_string_length + bw.write_byte(0)?; // user_string_length + Ok(()) +} + +fn write_aformat3(bw: &mut ByteWriter, _stream: &NAStream, il_info: &InterleaveInfo) -> MuxerResult<()> { + let start = bw.tell(); + bw.write_u16be(0)?; // header_bytes + bw.write_u16be(il_info.flavor as u16)?; + bw.write_u32be(il_info.frame_size as u32)?; // granularity + bw.write_u32be(0)?; // bytes per minute + bw.write_u32be(0)?; // total bytes + write_audio_metadata(bw)?; + bw.write_byte(0)?; //can_copy + bw.write_byte(4)?; // FCC length + bw.write_buf(&il_info.fcc)?; + let end = bw.tell(); + bw.seek(SeekFrom::Start(start))?; + bw.write_u16be((end - start - 2) as u16)?; + bw.seek(SeekFrom::Start(end))?; + + Ok(()) +} + +fn write_aformat4(bw: &mut ByteWriter, stream: &NAStream, il_info: &InterleaveInfo) -> MuxerResult<()> { + let info = stream.get_info().get_properties().get_audio_info().unwrap(); + + bw.write_u16be(0)?; + let start = bw.tell(); + bw.write_buf(b".ra4")?; + bw.write_u32be(0)?; // data size + bw.write_u16be(4)?; // version + bw.write_u16be(0)?; // revision + bw.write_u16be(0)?; // header_bytes + bw.write_u16be(il_info.flavor as u16)?; + bw.write_u32be(il_info.frame_size as u32)?; // granularity + bw.write_u32be(0)?; // total bytes + bw.write_u32be(0)?; // bytes_per_minute + bw.write_u32be(0)?; // bytes_per_minute2 + bw.write_u16be(il_info.factor as u16)?; + bw.write_u16be(il_info.block_size as u16)?; + bw.write_u16be(0)?; // user data + bw.write_u32be(info.sample_rate)?; // sample rate + bw.write_u32be(info.format.bits.into())?; // sample size + bw.write_u16be(info.channels.into())?; // num channels + bw.write_byte(4)?; // ileave ID len + bw.write_buf(&il_info.il_fcc)?; + bw.write_byte(4)?; // codec ID len + bw.write_buf(&il_info.fcc)?; + bw.write_byte(if &il_info.il_fcc == b"Int0" { 0 } else { 1 })?; + bw.write_byte(7)?; // can_copy + bw.write_byte(0)?; // stream_type + write_audio_metadata(bw)?; + let end = bw.tell(); + bw.seek(SeekFrom::Start(start + 12))?; + bw.write_u16be((end - start - 4) as u16)?; + bw.seek(SeekFrom::Start(end))?; + + Ok(()) +} + +fn write_aformat5(bw: &mut ByteWriter, stream: &NAStream, il_info: &InterleaveInfo) -> MuxerResult<()> { + let info = stream.get_info().get_properties().get_audio_info().unwrap(); + + bw.write_u16be(0)?; + let start = bw.tell(); + bw.write_buf(b".ra5")?; + bw.write_u32be(0)?; // data size + bw.write_u16be(5)?; // version + bw.write_u16be(0)?; // revision + bw.write_u16be(0)?; // header_bytes + bw.write_u16be(il_info.flavor as u16)?; + bw.write_u32be(il_info.block_size as u32)?; // granularity + bw.write_u32be(0)?; // total bytes + bw.write_u32be(0)?; // bytes_per_minute + bw.write_u32be(0)?; // bytes_per_minute2 + bw.write_u16be(il_info.factor as u16)?; + bw.write_u16be(il_info.block_size as u16)?; + bw.write_u16be(il_info.frame_size as u16)?; + bw.write_u16be(0)?; // user data + bw.write_u32be(info.sample_rate)?; // sample rate + bw.write_u32be(info.sample_rate)?; // actual sample rate + bw.write_u32be(info.format.bits.into())?; // sample size + bw.write_u16be(info.channels.into())?; // num channels + bw.write_buf(&il_info.il_fcc)?; + bw.write_buf(&il_info.fcc)?; + bw.write_byte(if &il_info.il_fcc == b"Int0" { 0 } else { 1 })?; + bw.write_byte(7)?; // can_copy + bw.write_byte(0)?; // stream_type + bw.write_byte(0)?; // has_interleave_pattern + if let Some(edata) = stream.get_info().get_extradata() { + if !matches!(&il_info.fcc, b"raac" | b"racp") { + bw.write_u32be(edata.len() as u32)?; + bw.write_buf(&edata)?; + } else { + bw.write_u32be((edata.len() + 1) as u32)?; + bw.write_byte(2)?; + bw.write_buf(&edata)?; + } + } else { + bw.write_u32be(0)?; + } + let end = bw.tell(); + bw.seek(SeekFrom::Start(start + 12))?; + bw.write_u16be((end - start - 4) as u16)?; + bw.seek(SeekFrom::Start(end))?; + + Ok(()) +} + +fn write_lsd(bw: &mut ByteWriter, stream: &NAStream) -> MuxerResult<()> { + if let Some(edata) = stream.get_info().get_extradata() { + bw.write_buf(&edata)?; + } + Ok(()) +} + +fn create_interleaver(id: [u8; 4], fcc: [u8; 4], ainfo: NAAudioInfo) -> MuxerResult> { + let frame_size = ainfo.block_len; + match &id { + b"Int0" => Ok(Box::new(NoInterleaver{ frame_size, pkt: None })), + b"Int4" => Ok(Box::new(Int4Interleaver::new(frame_size)?)), + b"sipr" => Ok(Box::new(SiproInterleaver::new(frame_size)?)), + b"genr" => Ok(Box::new(GenericInterleaver::new(frame_size, fcc)?)), + b"vbrs" | b"vbrf" => Ok(Box::new(AACInterleaver::new(1024))), + _ => unimplemented!(), + } +} + +pub fn create_audio_stream(stream: &NAStream) -> MuxerResult> { + let info = stream.get_info(); + let cname = info.get_name(); + let mut fourcc = [0u8; 4]; + let mut ileave = [0u8; 4]; + let mut version = 0; + for &(fcc, name, ileaver, cversion) in AUDIO_CODEC_REGISTRY.iter() { + if name == cname { + fourcc = *fcc; + ileave = *ileaver; + version = cversion; + break; + } + } + if version > 0 { + let ainfo = info.get_properties().get_audio_info().unwrap(); + Ok(Box::new(AudioStreamWriter { + fcc: fourcc, + il_fcc: ileave, + is_raw: &ileave == b"Int0", + version, + interleave: create_interleaver(ileave, fourcc, ainfo)?, + header_pos: 0, + data_size: 0, + })) + } else { + Err(MuxerError::UnsupportedFormat) + } +} + +const SIPRO_SWAPS: [[u8; 2]; 38] = [ + [ 0, 63 ], [ 1, 22 ], [ 2, 44 ], [ 3, 90 ], + [ 5, 81 ], [ 7, 31 ], [ 8, 86 ], [ 9, 58 ], + [ 10, 36 ], [ 12, 68 ], [ 13, 39 ], [ 14, 73 ], + [ 15, 53 ], [ 16, 69 ], [ 17, 57 ], [ 19, 88 ], + [ 20, 34 ], [ 21, 71 ], [ 24, 46 ], [ 25, 94 ], + [ 26, 54 ], [ 28, 75 ], [ 29, 50 ], [ 32, 70 ], + [ 33, 92 ], [ 35, 74 ], [ 38, 85 ], [ 40, 56 ], + [ 42, 87 ], [ 43, 65 ], [ 45, 59 ], [ 48, 79 ], + [ 49, 93 ], [ 51, 89 ], [ 55, 95 ], [ 61, 76 ], + [ 67, 83 ], [ 77, 80 ] +]; + +const RA_28_8_INTERLEAVE_PARAMS: &[InterleaveParams] = &[ + InterleaveParams { block_size: 228, factor: 12, frames_per_blk: 6 }, +]; +const SIPRO_INTERLEAVE_PARAMS: &[InterleaveParams] = &[ + InterleaveParams { block_size: 232, factor: 6, frames_per_blk: 16 }, + InterleaveParams { block_size: 304, factor: 6, frames_per_blk: 16 }, + InterleaveParams { block_size: 296, factor: 6, frames_per_blk: 16 }, + InterleaveParams { block_size: 320, factor: 6, frames_per_blk: 16 } +]; +const ATRAC_INTERLEAVE_PARAMS: &[InterleaveParams] = &[ + InterleaveParams { block_size: 768, factor: 20, frames_per_blk: 4 }, + InterleaveParams { block_size: 1088, factor: 20, frames_per_blk: 4 }, + InterleaveParams { block_size: 912, factor: 30, frames_per_blk: 3 }, + InterleaveParams { block_size: 1152, factor: 30, frames_per_blk: 3 }, + InterleaveParams { block_size: 1272, factor: 30, frames_per_blk: 3 }, + InterleaveParams { block_size: 1024, factor: 30, frames_per_blk: 2 }, + InterleaveParams { block_size: 768, factor: 10, frames_per_blk: 1 }, + InterleaveParams { block_size: 1024, factor: 10, frames_per_blk: 1 } +]; +const COOK_INTERLEAVE_PARAMS: &[InterleaveParams] = &[ + InterleaveParams { block_size: 288, factor: 8, frames_per_blk: 9 }, + InterleaveParams { block_size: 352, factor: 8, frames_per_blk: 11 }, + InterleaveParams { block_size: 564, factor: 8, frames_per_blk: 12 }, + InterleaveParams { block_size: 600, factor: 9, frames_per_blk: 10 }, + InterleaveParams { block_size: 651, factor: 14, frames_per_blk: 7 }, + InterleaveParams { block_size: 640, factor: 15, frames_per_blk: 5 }, + InterleaveParams { block_size: 744, factor: 20, frames_per_blk: 4 }, + InterleaveParams { block_size: 558, factor: 16, frames_per_blk: 6 }, + InterleaveParams { block_size: 288, factor: 6, frames_per_blk: 12 }, + InterleaveParams { block_size: 580, factor: 10, frames_per_blk: 10 }, + InterleaveParams { block_size: 564, factor: 14, frames_per_blk: 6 }, + InterleaveParams { block_size: 640, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 744, factor: 20, frames_per_blk: 4 }, + InterleaveParams { block_size: 834, factor: 30, frames_per_blk: 3 }, + InterleaveParams { block_size: 644, factor: 20, frames_per_blk: 4 }, + InterleaveParams { block_size: 600, factor: 9, frames_per_blk: 10 }, + InterleaveParams { block_size: 651, factor: 14, frames_per_blk: 7 }, + InterleaveParams { block_size: 528, factor: 8, frames_per_blk: 11 }, + InterleaveParams { block_size: 600, factor: 10, frames_per_blk: 10 }, + InterleaveParams { block_size: 600, factor: 10, frames_per_blk: 10 }, + InterleaveParams { block_size: 465, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 465, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 640, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 640, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 930, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 1400, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 376, factor: 8, frames_per_blk: 11 }, + InterleaveParams { block_size: 930, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 1400, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 640, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 1395, factor: 16, frames_per_blk: 5 }, + InterleaveParams { block_size: 1143, factor: 10, frames_per_blk: 3 }, + InterleaveParams { block_size: 1064, factor: 10, frames_per_blk: 2 }, + InterleaveParams { block_size: 778, factor: 1, frames_per_blk: 1 } +]; diff --git a/nihav-realmedia/src/muxers/rmvb/mod.rs b/nihav-realmedia/src/muxers/rmvb/mod.rs new file mode 100644 index 0000000..32fbbb9 --- /dev/null +++ b/nihav-realmedia/src/muxers/rmvb/mod.rs @@ -0,0 +1,599 @@ +use nihav_core::muxers::*; +mod audiostream; +use audiostream::*; +mod videostream; +use videostream::*; + +trait RMWriterHelper { + fn write_chunk(&mut self, id: &[u8], size: u32, version: u16) -> MuxerResult<()>; + fn write_string(&mut self, data: &[u8]) -> MuxerResult<()>; + fn patch_value(&mut self, val: u32, off: u64) -> MuxerResult<()>; +} + +impl<'a> RMWriterHelper for ByteWriter<'a> { + fn write_chunk(&mut self, id: &[u8], size: u32, version: u16) -> MuxerResult<()> { + self.write_buf(id)?; + self.write_u32be(size)?; + self.write_u16be(version)?; + Ok(()) + } + fn write_string(&mut self, data: &[u8]) -> MuxerResult<()> { + validate!(data.len() < 256); + self.write_byte(data.len() as u8)?; + self.write_buf(data)?; + Ok(()) + } + fn patch_value(&mut self, val: u32, off: u64) -> MuxerResult<()> { + let cur_pos = self.tell(); + self.seek(SeekFrom::Start(off))?; + self.write_u32be(val)?; + self.seek(SeekFrom::Start(cur_pos))?; + Ok(()) + } +} + +pub trait RMStreamWriter { + fn write_header(&mut self, bw: &mut ByteWriter, astream: &NAStream) -> MuxerResult<()>; + fn queue_packet(&mut self, pkt: NAPacket) -> bool; + fn get_packet(&mut self) -> Option<(Vec, bool)>; + fn flush(&mut self); + fn finish(&mut self, bw: &mut ByteWriter) -> MuxerResult<()>; +} + +#[derive(Clone,Copy)] +struct IndexEntry { + time: u32, + pos: u64, + pkt_no: u32, +} + +struct RMStream { + packetiser: Box, + stream_id: u16, + mdpr_pos: u64, + npkts: usize, + data_size: usize, + max_pkt_size: usize, + time: u32, + cur_time: u32, + keyframe: bool, + audio: bool, + index: Vec, +} + +impl RMStream { + fn new(strno: usize, stream: &NAStream) -> MuxerResult { + let packetiser = match stream.get_media_type() { + StreamType::Video => create_video_stream(stream)?, + StreamType::Audio => create_audio_stream(stream)?, + _ => Box::new(DummyStreamWriter{}), + }; + Ok(Self{ + packetiser, + stream_id: strno as u16, + mdpr_pos: 0, + npkts: 0, + data_size: 0, + max_pkt_size: 0, + time: 0, + cur_time: 0, + keyframe: false, + audio: false, + index: Vec::new(), + }) + } + fn write_mdpr(&mut self, bw: &mut ByteWriter, strm: &NAStream) -> MuxerResult<()> { + self.mdpr_pos = bw.tell(); + + bw.write_chunk(b"MDPR", 0, 0)?; + bw.write_u16be(self.stream_id as u16)?; + bw.write_u32be(0)?; //max br + bw.write_u32be(0)?; //avg br + bw.write_u32be(0)?; //max ps + bw.write_u32be(0)?; //avg ps + bw.write_u32be(0)?; //num packets + bw.write_u32be(0)?; //duration + bw.write_u32be(0)?; //preroll + + match strm.get_media_type() { + StreamType::Video => { + bw.write_string(b"The Video Stream")?; + bw.write_string(b"video/x-pn-realvideo")?; + }, + StreamType::Audio => { + bw.write_string(b"The Audio Stream")?; + bw.write_string(b"audio/x-pn-realaudio")?; + self.audio = true; + }, + _ => { + bw.write_string(b"some other stream")?; + bw.write_string(b"data")?; + }, + }; + bw.write_u32be(0)?; //extradata size + let edata_start = bw.tell(); + self.packetiser.write_header(bw, strm)?; + let edata_end = bw.tell(); + bw.patch_value((edata_end - edata_start) as u32, edata_start - 4)?; + + patch_size(bw, self.mdpr_pos)?; + + Ok(()) + } + fn write_packet(&mut self, bw: &mut ByteWriter, pkt: NAPacket, pkt_no: &mut u32) -> MuxerResult<()> { + if let Some(pts) = pkt.get_pts() { + let (tb_num, tb_den) = pkt.get_stream().get_timebase(); + let ms = NATimeInfo::ts_to_time(pts, 1000, tb_num, tb_den) as u32; + self.time = self.time.max(ms); + self.cur_time = ms; + } + self.keyframe = pkt.keyframe; + self.packetiser.queue_packet(pkt); + self.write_packets(bw, pkt_no) + } + fn write_packets(&mut self, bw: &mut ByteWriter, pkt_no: &mut u32) -> MuxerResult<()> { + while let Some((data, first)) = self.packetiser.get_packet() { + validate!(data.len() < 65000); + if self.keyframe && first { + self.index.push(IndexEntry{ time: self.cur_time, pos: bw.tell(), pkt_no: *pkt_no }); + } + let is_keyframe = self.keyframe && (!self.audio || first); + bw.write_u16be(0)?; //version; + bw.write_u16be((data.len() + 12) as u16)?; + bw.write_u16be(self.stream_id)?; + bw.write_u32be(self.cur_time)?; + bw.write_byte(0)?; //packet group + bw.write_byte(if is_keyframe { 0x2 } else { 0x0 })?; + bw.write_buf(&data)?; + + self.npkts += 1; + self.data_size += data.len(); + + *pkt_no += 1; + } + Ok(()) + } + fn finish(&mut self, bw: &mut ByteWriter, pkt_no: &mut u32) -> MuxerResult<()> { + self.packetiser.flush(); + self.write_packets(bw, pkt_no)?; + + let pos = bw.tell(); + bw.seek(SeekFrom::Start(self.mdpr_pos + 12))?; + bw.write_u32be(if self.time > 0 { (self.data_size * 1000 / (self.time as usize)) as u32 } else { 0 })?; + bw.write_u32be(if self.time > 0 { (self.data_size * 1000 / (self.time as usize)) as u32 } else { 0 })?; + bw.write_u32be(self.max_pkt_size as u32)?; + bw.write_u32be(if self.npkts > 0 { (self.data_size / self.npkts) as u32 } else { 0 })?; + bw.seek(SeekFrom::Current(8))?; + bw.write_u32be(self.time)?; + + bw.seek(SeekFrom::Start(pos))?; + Ok(()) + } +} + +struct RMMuxer<'a> { + bw: &'a mut ByteWriter<'a>, + streams: Vec, + data_pos: u64, + num_chunks: u32, + cur_packet: u32, +} + +impl<'a> RMMuxer<'a> { + fn new(bw: &'a mut ByteWriter<'a>) -> Self { + Self { + bw, + streams: Vec::new(), + data_pos: 0, + num_chunks: 0, + cur_packet: 0, + } + } + fn write_index(&mut self) -> MuxerResult<()> { + let mut indx_pos = 0x38; + + for stream in self.streams.iter() { + let cur_pos = self.bw.tell(); + self.bw.patch_value(cur_pos as u32, indx_pos)?; + indx_pos = cur_pos + 16; + + let idx_size = 10 + 10 + stream.index.len() * 14; + self.bw.write_chunk(b"INDX", idx_size as u32, 0)?; + self.bw.write_u32be(stream.index.len() as u32)?; + self.bw.write_u16be(stream.stream_id)?; + self.bw.write_u32be(0)?; // next index position + for entry in stream.index.iter() { + self.bw.write_u16be(0)?; // version + self.bw.write_u32be(entry.time)?; + self.bw.write_u32be(entry.pos as u32)?; + self.bw.write_u32be(entry.pkt_no)?; + } + + self.num_chunks += 1; + } + + Ok(()) + } + fn update_prop(&mut self) -> MuxerResult<()> { + let mut data_size = 0; + let mut npkts = 0; + let mut max_pkt_size = 0; + let mut time = 0; + + for stream in self.streams.iter() { + data_size += stream.data_size; + time = time.max(stream.time); + npkts += stream.npkts; + max_pkt_size = max_pkt_size.max(stream.max_pkt_size); + } + + if npkts > 0 && time > 0 { + let cur_pos = self.bw.tell(); + + let bitrate = (data_size * 1000 / (time as usize)) as u32; + self.bw.seek(SeekFrom::Start(28))?; + self.bw.write_u32be(bitrate)?; + self.bw.write_u32be(bitrate)?; + self.bw.write_u32be(max_pkt_size as u32)?; + self.bw.write_u32be((data_size / npkts) as u32)?; + self.bw.write_u32be(npkts as u32)?; + self.bw.write_u32be(time)?; + + self.bw.seek(SeekFrom::Start(cur_pos))?; + } + + self.bw.patch_value(self.data_pos as u32, 0x3C)?; + + Ok(()) + } +} + +fn patch_size(bw: &mut ByteWriter, pos: u64) -> MuxerResult<()> { + let end = bw.tell(); + bw.patch_value((end - pos) as u32, pos + 4) +} + +impl<'a> MuxCore<'a> for RMMuxer<'a> { + fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> { + if strmgr.get_num_streams() == 0 { + return Err(MuxerError::InvalidArgument); + } + if strmgr.get_num_streams() > 100 { + return Err(MuxerError::UnsupportedFormat); + } + + self.bw.write_chunk(b".RMF", 18, 0)?; + self.bw.write_u32be(0)?; // container version + self.bw.write_u32be(0)?; // number of chunks + + self.num_chunks = 1; + let prop_start = self.bw.tell(); + self.bw.write_chunk(b"PROP", 0, 0)?; + self.bw.write_u32be(0)?; //max br + self.bw.write_u32be(0)?; //avg br + self.bw.write_u32be(0)?; //max ps + self.bw.write_u32be(0)?; //avg ps + self.bw.write_u32be(0)?; //num packets + self.bw.write_u32be(0)?; //duration + self.bw.write_u32be(0)?; //preroll + self.bw.write_u32be(0)?; //index offset + self.bw.write_u32be(0)?; //data offset + self.bw.write_u16be(strmgr.get_num_streams() as u16)?; + self.bw.write_u16be(0)?; // flags + patch_size(self.bw, prop_start)?; + + self.streams.clear(); + for (strno, strm) in strmgr.iter().enumerate() { + let mut swriter = RMStream::new(strno, &strm)?; + swriter.write_mdpr(self.bw, &strm)?; + self.streams.push(swriter); + self.num_chunks += 1; + } + + self.data_pos = self.bw.tell(); + self.bw.write_chunk(b"DATA", 0, 0)?; + self.bw.write_u32be(0)?; //num packets + self.bw.write_u32be(0)?; //next data chunk + self.num_chunks += 1; + + Ok(()) + } + fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> { + if self.data_pos == 0 { + return Err(MuxerError::NotCreated); + } + let stream = pkt.get_stream(); + let str_num = stream.get_num(); + if str_num > self.streams.len() { + return Err(MuxerError::UnsupportedFormat); + } + self.streams[str_num].write_packet(self.bw, pkt, &mut self.cur_packet)?; + + Ok(()) + } + fn flush(&mut self) -> MuxerResult<()> { + Ok(()) + } + fn end(&mut self) -> MuxerResult<()> { + if self.data_pos == 0 { + return Err(MuxerError::NotCreated); + } + let mut tot_npkts = 0; + for stream in self.streams.iter_mut() { + stream.finish(self.bw, &mut self.cur_packet)?; + tot_npkts += stream.npkts; + } + + let data_size = self.bw.tell() - self.data_pos; + self.bw.patch_value(data_size as u32, self.data_pos + 4)?; + self.bw.patch_value(tot_npkts as u32, self.data_pos + 10)?; + + self.write_index()?; + self.update_prop()?; + + self.bw.patch_value(self.num_chunks, 14)?; + Ok(()) + } +} + +impl<'a> NAOptionHandler for RMMuxer<'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 RealMediaMuxerCreator {} + +impl MuxerCreator for RealMediaMuxerCreator { + fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box + 'a> { + Box::new(RMMuxer::new(bw)) + } + fn get_name(&self) -> &'static str { "realmedia" } + fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::Universal } +} + +struct RAMuxer<'a> { + bw: &'a mut ByteWriter<'a>, + sw: Option>, +} + +impl<'a> RAMuxer<'a> { + fn new(bw: &'a mut ByteWriter<'a>) -> Self { + Self { + bw, + sw: None, + } + } +} + +impl<'a> MuxCore<'a> for RAMuxer<'a> { + fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> { + if strmgr.get_num_streams() != 1 { + return Err(MuxerError::InvalidArgument); + } + let astream = strmgr.get_stream(0).unwrap(); + if astream.get_media_type() != StreamType::Audio { + return Err(MuxerError::InvalidArgument); + } + self.sw = Some(create_audio_stream(&astream)?); + if let Some(ref mut sw) = self.sw { + sw.write_header(self.bw, &astream)?; + } + Ok(()) + } + fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> { + if let Some(ref mut sw) = self.sw { + sw.queue_packet(pkt); + while let Some((data, _)) = sw.get_packet() { + self.bw.write_buf(&data)?; + } + Ok(()) + } else { + Err(MuxerError::NotCreated) + } + } + fn flush(&mut self) -> MuxerResult<()> { + Ok(()) + } + fn end(&mut self) -> MuxerResult<()> { + if let Some(ref mut sw) = self.sw { + sw.finish(&mut self.bw)?; + } + Ok(()) + } +} + +impl<'a> NAOptionHandler for RAMuxer<'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 RealAudioMuxerCreator {} + +impl MuxerCreator for RealAudioMuxerCreator { + fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box + 'a> { + Box::new(RAMuxer::new(bw)) + } + fn get_name(&self) -> &'static str { "realaudio" } + fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::SingleAudio("any") } +} + +#[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_rm_muxer() { + let mut dmx_reg = RegisteredDemuxers::new(); + realmedia_register_all_demuxers(&mut dmx_reg); + // sample from a private collection + let dec_config = DecoderTestParams { + demuxer: "realmedia", + in_name: "assets/RV/rv30_weighted_mc.rm", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + realmedia_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "realmedia", + enc_name: "", + out_name: "muxed.rm", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "realmedia", &mux_reg, + [0x26422839, 0xa2d7bdd1, 0xd6ea2a78, 0x1b58033a]); + } + + #[test] + fn test_ra_muxer_v3() { + let mut dmx_reg = RegisteredDemuxers::new(); + realmedia_register_all_demuxers(&mut dmx_reg); + //test sample: https://samples.mplayerhq.hu/real/RA/14_4/drummers.14.ra + let dec_config = DecoderTestParams { + demuxer: "realaudio", + in_name: "assets/RV/drummers.14.ra", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + realmedia_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "realaudio", + enc_name: "", + out_name: "v3.ra", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "realaudio", &mux_reg, + [0x8101a484, 0xf5d80805, 0x24577596, 0x9b27262f]); + } + #[test] + fn test_ra_muxer_v4() { + let mut dmx_reg = RegisteredDemuxers::new(); + realmedia_register_all_demuxers(&mut dmx_reg); + //test sample: https://samples.mplayerhq.hu/real//RA/ra_with_comment_field/diemusik.ra + let dec_config = DecoderTestParams { + demuxer: "realaudio", + in_name: "assets/RV/diemusik.ra", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + realmedia_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "realaudio", + enc_name: "", + out_name: "v4.ra", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "realaudio", &mux_reg, + [0x33665ec3, 0x69b68ea2, 0x08d4b138, 0x318e305f]); + } + #[test] + fn test_ra_muxer_sipro() { + let mut dmx_reg = RegisteredDemuxers::new(); + realmedia_register_all_demuxers(&mut dmx_reg); + //test sample: https://samples.mplayerhq.hu/real/AC-sipr/autahi-vox.rm + let dec_config = DecoderTestParams { + demuxer: "realmedia", + in_name: "assets/RV/autahi-vox.rm", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + realmedia_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "realaudio", + enc_name: "", + out_name: "v4-sipro.ra", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "realaudio", &mux_reg, + [0x08bd496d, 0x5f35d7ae, 0xe9c93c50, 0x9e803f76]); + } + #[test] + fn test_ra_muxer_v5() { + let mut dmx_reg = RegisteredDemuxers::new(); + realmedia_register_all_demuxers(&mut dmx_reg); + //test sample: https://samples.mplayerhq.hu/real/AC-cook/cook_5.1/multichannel.rma + let dec_config = DecoderTestParams { + demuxer: "realmedia", + in_name: "assets/RV/multichannel.rma", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + realmedia_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "realaudio", + enc_name: "", + out_name: "v5.ra", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "realaudio", &mux_reg, + [0x52f42c49, 0x90ac79a7, 0x275a465f, 0x7a6f3659]); + } + #[test] + fn test_rm_muxer_aac() { + let mut dmx_reg = RegisteredDemuxers::new(); + realmedia_register_all_demuxers(&mut dmx_reg); + //sample from a private collection + let dec_config = DecoderTestParams { + demuxer: "realmedia", + in_name: "assets/RV/rv40_weighted_mc_2.rmvb", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + realmedia_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "realmedia", + enc_name: "", + out_name: "aac.ram", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "realmedia", &mux_reg, + [0xe38b36c0, 0x1aedef10, 0x4e418ac4, 0x4ff57f6c]); + } + #[test] + fn test_rm_muxer_ralf() { + let mut dmx_reg = RegisteredDemuxers::new(); + realmedia_register_all_demuxers(&mut dmx_reg); + //sample from a private collection + let dec_config = DecoderTestParams { + demuxer: "realmedia", + in_name: "assets/RV/rv40_ralf.rmvb", + limit: None, + stream_type: StreamType::None, + dmx_reg, dec_reg: RegisteredDecoders::new(), + }; + let mut mux_reg = RegisteredMuxers::new(); + realmedia_register_all_muxers(&mut mux_reg); + /*let enc_config = EncoderTestParams { + muxer: "realmedia", + enc_name: "", + out_name: "ralf.ram", + mux_reg, enc_reg: RegisteredEncoders::new(), + }; + test_remuxing(&dec_config, &enc_config);*/ + test_remuxing_md5(&dec_config, "realmedia", &mux_reg, + [0xa0c336d1, 0x76221455, 0x75252067, 0x6189d4af]); + } +} diff --git a/nihav-realmedia/src/muxers/rmvb/videostream.rs b/nihav-realmedia/src/muxers/rmvb/videostream.rs new file mode 100644 index 0000000..708874d --- /dev/null +++ b/nihav-realmedia/src/muxers/rmvb/videostream.rs @@ -0,0 +1,168 @@ +use nihav_core::frame::*; +use nihav_core::muxers::*; +use super::RMStreamWriter; + +static VIDEO_CODEC_REGISTRY: &[(&[u8;4], &str)] = &[ + (b"RV10", "realvideo1"), + (b"RV20", "realvideo2"), + (b"RVTR", "realvideo2"), + (b"RV30", "realvideo3"), + (b"RV40", "realvideo4"), + (b"RV60", "realvideo6"), + (b"CLV1", "clearvideo_rm"), +]; + +pub struct DummyStreamWriter {} +impl RMStreamWriter for DummyStreamWriter { + fn write_header(&mut self, _bw: &mut ByteWriter, _stream: &NAStream) -> MuxerResult<()> { + Ok(()) + } + fn queue_packet(&mut self, _pkt: NAPacket) -> bool { + true + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { + None + } + fn flush(&mut self) { } + fn finish(&mut self, _bw: &mut ByteWriter) -> MuxerResult<()> { + Ok(()) + } +} + +struct VideoStreamWriter { + fcc: [u8; 4], + buf: Vec, + nslices: usize, + cur_slice: usize, + seq_no: u8, +} + +impl RMStreamWriter for VideoStreamWriter { + fn write_header(&mut self, bw: &mut ByteWriter, vstream: &NAStream) -> MuxerResult<()> { + let info = vstream.get_info().get_properties().get_video_info().unwrap(); + let start = bw.tell(); + + bw.write_u32be(0)?; // header size + bw.write_buf(b"VIDO")?; + bw.write_buf(&self.fcc)?; + bw.write_u16be(info.width as u16)?; + bw.write_u16be(info.height as u16)?; + bw.write_u16be(12)?; // bpp + bw.write_u16be(0)?; // aligned width + bw.write_u16be(0)?; // aligned height + let (tb_num, tb_den) = vstream.get_timebase(); + if tb_num != 0 && tb_den != 0 { + bw.write_u16be((tb_den / tb_num) as u16)?; + let mut fps_frac = tb_den % tb_num; + let mut div = tb_num; + while div >= 0x10000 { + fps_frac >>= 1; + div >>= 1; + } + fps_frac = (fps_frac << 16) / div; + bw.write_u16le(fps_frac as u16)?; + } else { + bw.write_u16be(0)?; + bw.write_u16be(0)?; + } + + if let Some(edata) = vstream.get_info().get_extradata() { + bw.write_buf(&edata)?; + } + let end = bw.tell(); + bw.seek(SeekFrom::Start(start))?; + bw.write_u32be((end - start) as u32)?; + bw.seek(SeekFrom::Start(end))?; + Ok(()) + } + fn queue_packet(&mut self, pkt: NAPacket) -> bool { + if self.nslices == 0 { + let src = pkt.get_buffer(); + let nslices = usize::from(src[0]) + 1; + if src.len() > nslices * 8 + 1 { + self.nslices = nslices; + self.cur_slice = 0; + self.buf.resize(src.len(), 0); + self.buf.copy_from_slice(&src); + } + true + } else { + false + } + } + fn get_packet(&mut self) -> Option<(Vec, bool)> { + if self.cur_slice < self.nslices { + let first = self.cur_slice == 0; + let hdr_size = self.nslices * 8 + 1; + let cur_off = (read_u32be(&self.buf[self.cur_slice * 8 + 5..]).unwrap_or(0) as usize) + hdr_size; + let next_off = if self.cur_slice + 1 < self.nslices { + (read_u32be(&self.buf[self.cur_slice * 8 + 13..]).unwrap_or(0) as usize) + hdr_size + } else { + self.buf.len() + }; + let next_off = next_off.max(cur_off); + let src = &self.buf[cur_off..next_off]; + let ret = if self.nslices == 1 { + let mut dst = vec![0; src.len() + 2]; + dst[0] = 0x40; + dst[1] = self.seq_no; + dst[2..].copy_from_slice(src); + dst + } else { + let mut dst = Vec::with_capacity(src.len() + 11); + let mut gw = GrowableMemoryWriter::new_write(&mut dst); + let mut bw = ByteWriter::new(&mut gw); + + let hdr = ((self.nslices as u16) << 7) | ((self.cur_slice + 1) as u16); + bw.write_u16be(hdr).unwrap(); + + let full_size = self.buf.len() - hdr_size; + if full_size < (1 << 14) { + bw.write_u16be(0xC000 | (full_size as u16)).unwrap(); + } else { + bw.write_u32be(0x80000000 | (full_size as u32)).unwrap(); + } + let coff = cur_off - hdr_size; + if coff < (1 << 14) { + bw.write_u16be(0x4000 | (coff as u16)).unwrap(); + } else { + bw.write_u32be(coff as u32).unwrap(); + } + bw.write_byte(self.seq_no).unwrap(); + bw.write_buf(src).unwrap(); + dst + }; + self.cur_slice += 1; + if self.cur_slice == self.nslices { + self.nslices = 0; + self.cur_slice = 0; + self.seq_no = self.seq_no.wrapping_add(1); + } + Some((ret, first)) + } else { + None + } + } + fn flush(&mut self) { } + fn finish(&mut self, _bw: &mut ByteWriter) -> MuxerResult<()> { + Ok(()) + } +} + +pub fn create_video_stream(stream: &NAStream) -> MuxerResult> { + let info = stream.get_info(); + let cname = info.get_name(); + + for &(fcc, name) in VIDEO_CODEC_REGISTRY.iter() { + if name == cname { + return Ok(Box::new(VideoStreamWriter { + fcc: *fcc, + buf: Vec::new(), + nslices: 0, + cur_slice: 0, + seq_no: 0, + })); + } + } + Err(MuxerError::UnsupportedFormat) +}