]> git.nihav.org Git - nihav.git/commitdiff
movmuxer: (hopefully) improve CBR audio track writing
authorKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 1 Apr 2026 16:52:55 +0000 (18:52 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 1 Apr 2026 17:12:32 +0000 (19:12 +0200)
nihav-commonfmt/src/muxers/mov/audiotrack.rs
nihav-commonfmt/src/muxers/mov/mod.rs
nihav-commonfmt/src/muxers/mov/rawaudiotrack.rs
nihav-commonfmt/src/muxers/mov/videotrack.rs

index 04cf078cda2c418eef05afb3c0a5f9fac5b02563..ad44e3861cbd2f7e60c7dc3b428ca88234aa6af3 100644 (file)
@@ -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")?;
index 56422b9afb3e7994d8c08f331a163fc5c9c79e36..748d36f1a1dffe8db7c655f964baeb4cf915133e 100644 (file)
@@ -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]);
     }
 }
index cc3278e73d9c2fd4123e55610f95add7f37ca359..a30c08983da84863cec7eb142216de56b7febf57 100644 (file)
@@ -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
index 9f73d59a10744cf3181097fc902ef72a2fe05c3a..02429b64465711cf0c5dcdbdb61551d70d64b1bc 100644 (file)
@@ -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