X-Git-Url: https://git.nihav.org/?p=nihav.git;a=blobdiff_plain;f=nihav-realmedia%2Fsrc%2Fmuxers%2Frmvb%2Fmod.rs;fp=nihav-realmedia%2Fsrc%2Fmuxers%2Frmvb%2Fmod.rs;h=32fbbb9940f3b7c6dbedab6641f1791dcdf553da;hp=0000000000000000000000000000000000000000;hb=9dc1fb4be1d02a1d1c1ea85340f49151e00ecad2;hpb=e614e5e611a4ae6769a0da7cbccec5842b3dbd0f 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]); + } +}