flash_register_all_muxers(rm);
generic_register_all_muxers(rm);
llaudio_register_all_muxers(rm);
+ realmedia_register_all_muxers(rm);
}
#[cfg(test)]
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 = []
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;
--- /dev/null
+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);
+ }
+}
--- /dev/null
+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<u8>, 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<Vec<u8>>,
+}
+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<u8>, 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<u8>,
+ map: Vec<usize>,
+}
+impl Int4Interleaver {
+ fn new(frame_size: usize) -> MuxerResult<Self> {
+ 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<u8>, 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<u8>,
+ map: Vec<usize>,
+}
+impl GenericInterleaver {
+ fn new(frame_size: usize, fcc: [u8; 4]) -> MuxerResult<Self> {
+ 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<u8>, 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<u8>,
+ wr_pos: usize,
+ rd_pos: usize,
+}
+impl SiproInterleaver {
+ fn new(mut frame_size: usize) -> MuxerResult<Self> {
+ 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<u8>, 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<u8>,
+ sizes: Vec<usize>,
+ 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<u8>, 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<dyn Interleaver>,
+ 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<u8>, 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<Box<dyn Interleaver>> {
+ 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<Box<dyn RMStreamWriter>> {
+ 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 }
+];
--- /dev/null
+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<u8>, 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<dyn RMStreamWriter>,
+ 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<IndexEntry>,
+}
+
+impl RMStream {
+ fn new(strno: usize, stream: &NAStream) -> MuxerResult<Self> {
+ 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<RMStream>,
+ 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<NAValue> { None }
+}
+
+pub struct RealMediaMuxerCreator {}
+
+impl MuxerCreator for RealMediaMuxerCreator {
+ fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + '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<Box<dyn RMStreamWriter>>,
+}
+
+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<NAValue> { None }
+}
+
+pub struct RealAudioMuxerCreator {}
+
+impl MuxerCreator for RealAudioMuxerCreator {
+ fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + '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]);
+ }
+}
--- /dev/null
+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<u8>, bool)> {
+ None
+ }
+ fn flush(&mut self) { }
+ fn finish(&mut self, _bw: &mut ByteWriter) -> MuxerResult<()> {
+ Ok(())
+ }
+}
+
+struct VideoStreamWriter {
+ fcc: [u8; 4],
+ buf: Vec<u8>,
+ 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<u8>, 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<Box<dyn RMStreamWriter>> {
+ 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)
+}