From: Kostya Shishkov Date: Wed, 1 Apr 2026 16:52:55 +0000 (+0200) Subject: movmuxer: (hopefully) improve CBR audio track writing X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=d40eb12140fc9cdb07e8bb03c7c9a9ce4aaa867c;p=nihav.git movmuxer: (hopefully) improve CBR audio track writing --- diff --git a/nihav-commonfmt/src/muxers/mov/audiotrack.rs b/nihav-commonfmt/src/muxers/mov/audiotrack.rs index 04cf078..ad44e38 100644 --- a/nihav-commonfmt/src/muxers/mov/audiotrack.rs +++ b/nihav-commonfmt/src/muxers/mov/audiotrack.rs @@ -62,6 +62,13 @@ impl TrackHandler for AudioTrackHandler { fn flush_chunks(&mut self, _bw: &mut dyn ByteIO, _ca: &mut ChunkAccount) -> MuxerResult<()> { Ok(()) } fn get_time_info(&self) -> (u64, u32) { (self.pts, self.ainfo.sample_rate) } fn get_dimensions(&self) -> (usize, usize) { (0, 0) } + fn get_pkt_duration(&self) -> u64 { + if self.frm_sz != 0 && self.frameno > 2 { + self.last_ts / u64::from(self.frameno - 2) + } else { + 1 + } + } fn write_media_info(&mut self, bw: &mut dyn ByteIO) -> MuxerResult<()> { let smhd = AtomMarker::start(bw, b"smhd")?; bw.write_byte(0)?; // version @@ -80,13 +87,23 @@ impl TrackHandler for AudioTrackHandler { bw.write_u32be(0)?; // vendor bw.write_u16be(u16::from(self.ainfo.channels))?; bw.write_u16be(u16::from(self.ainfo.format.bits))?; - bw.write_u16be(0xFFFE)?; // compression ID + bw.write_u16be(if self.frm_sz != 0 { 0 } else { 0xFFFE })?; // compression ID bw.write_u16be(0)?; // packet size bw.write_u32be(self.ainfo.sample_rate << 16)?; - bw.write_u32be(0)?; // samples per packet - bw.write_u32be(self.frm_sz as u32)?; // bytes per packet - bw.write_u32be(0)?; // bytes per frame - bw.write_u32be(0)?; // bytes per sample + if self.frm_sz != 0 { // CBR audio + let samples_per_pkt = if self.frameno > 2 { + self.last_ts / u64::from(self.frameno - 2) + } else { 0 }; + bw.write_u32be(samples_per_pkt as u32)?; // samples per packet + bw.write_u32be(self.frm_sz as u32 / u32::from(self.ainfo.channels))?; // bytes per packet + bw.write_u32be(self.frm_sz as u32)?; // bytes per frame + bw.write_u32be(2)?; // bytes per sample + } else { + bw.write_u32be(0)?; // samples per packet + bw.write_u32be(0)?; // bytes per packet + bw.write_u32be(0)?; // bytes per frame + bw.write_u32be(0)?; // bytes per sample + } if let Some(edata) = &self.edata { bw.write_u32be(edata.len() as u32 + 20)?; bw.write_buf(b"wave")?; diff --git a/nihav-commonfmt/src/muxers/mov/mod.rs b/nihav-commonfmt/src/muxers/mov/mod.rs index 56422b9..748d36f 100644 --- a/nihav-commonfmt/src/muxers/mov/mod.rs +++ b/nihav-commonfmt/src/muxers/mov/mod.rs @@ -111,7 +111,7 @@ impl ChunkAccount { self.cmap.push(count as u32); } - fn write_stts(&mut self, bw: &mut dyn ByteIO, raw_audio: bool) -> MuxerResult<()> { + fn write_stts(&mut self, bw: &mut dyn ByteIO, raw_audio: bool, pktd: u64) -> MuxerResult<()> { if !raw_audio { let mut nentries = 1; let mut prev_delta = self.pts[1] - self.pts[0]; @@ -128,16 +128,16 @@ impl ChunkAccount { for w in self.pts.windows(2).skip(1) { let delta = w[1] - w[0]; if delta != prev_delta { - bw.write_u32be(run)?; - bw.write_u32be(prev_delta as u32)?; + bw.write_u32be(run * (pktd as u32))?; + bw.write_u32be((prev_delta / pktd) as u32)?; run = 1; prev_delta = delta; } else { run += 1; } } - bw.write_u32be(run + 1)?; - bw.write_u32be(prev_delta as u32)?; + bw.write_u32be((run + 1) * (pktd as u32))?; + bw.write_u32be((prev_delta / pktd) as u32)?; } else { bw.write_u32be(1)?; bw.write_u32be((*self.pts.last().unwrap_or(&0)) as u32)?; @@ -205,6 +205,7 @@ trait TrackHandler { fn flush_chunks(&mut self, bw: &mut dyn ByteIO, ca: &mut ChunkAccount) -> MuxerResult<()>; fn get_time_info(&self) -> (u64, u32); fn get_dimensions(&self) -> (usize, usize); + fn get_pkt_duration(&self) -> u64; fn write_media_info(&mut self, bw: &mut dyn ByteIO) -> MuxerResult<()>; fn write_descriptor(&mut self, bw: &mut dyn ByteIO) -> MuxerResult<()>; } @@ -310,10 +311,11 @@ impl Track { self.handler.write_descriptor(bw)?; stsd.end(bw)?; + let pktd = self.handler.get_pkt_duration(); let stts = AtomMarker::start(bw, b"stts")?; bw.write_byte(0)?; // version bw.write_u24be(0)?; // flags - self.ca.write_stts(bw, self.raw_audio)?; + self.ca.write_stts(bw, self.raw_audio, pktd)?; stts.end(bw)?; let stsc = AtomMarker::start(bw, b"stsc")?; @@ -576,6 +578,6 @@ mod test { let mut mux_reg = RegisteredMuxers::new(); generic_register_all_muxers(&mut mux_reg); test_remuxing_md5(&dec_config, "mov", &mux_reg, - [0xeb99a547, 0xd6c1a4ce, 0xc12d3451, 0x096639c5]); + [0x3d030f10, 0x842de3e2, 0xb6a5ae89, 0x30d9b05f]); } } diff --git a/nihav-commonfmt/src/muxers/mov/rawaudiotrack.rs b/nihav-commonfmt/src/muxers/mov/rawaudiotrack.rs index cc3278e..a30c089 100644 --- a/nihav-commonfmt/src/muxers/mov/rawaudiotrack.rs +++ b/nihav-commonfmt/src/muxers/mov/rawaudiotrack.rs @@ -102,6 +102,7 @@ impl TrackHandler for RawAudioTrackHandler { } fn get_time_info(&self) -> (u64, u32) { (self.sample, self.rate) } fn get_dimensions(&self) -> (usize, usize) { (0, 0) } + fn get_pkt_duration(&self) -> u64 { 1 } fn write_media_info(&mut self, bw: &mut dyn ByteIO) -> MuxerResult<()> { let smhd = AtomMarker::start(bw, b"smhd")?; bw.write_byte(0)?; // version diff --git a/nihav-commonfmt/src/muxers/mov/videotrack.rs b/nihav-commonfmt/src/muxers/mov/videotrack.rs index 9f73d59..02429b6 100644 --- a/nihav-commonfmt/src/muxers/mov/videotrack.rs +++ b/nihav-commonfmt/src/muxers/mov/videotrack.rs @@ -60,6 +60,7 @@ impl TrackHandler for VideoTrackHandler { fn get_dimensions(&self) -> (usize, usize) { (self.vinfo.width, self.vinfo.height) } + fn get_pkt_duration(&self) -> u64 { 1 } fn write_media_info(&mut self, bw: &mut dyn ByteIO) -> MuxerResult<()> { let vmhd = AtomMarker::start(bw, b"vmhd")?; bw.write_byte(0)?; // version