]> git.nihav.org Git - nihav.git/commitdiff
mkv: support header stripping track compression master
authorKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 29 Oct 2026 04:40:43 +0000 (05:40 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 29 Oct 2026 04:40:43 +0000 (05:40 +0100)
nihav-commonfmt/src/demuxers/mkv/mod.rs
nihav-commonfmt/src/demuxers/mkv/parse.rs

index da034f8e9e8625f1658b1a51e167824152a22734..0a9c6e25c453db862be792dc0a77efa10dd85636 100644 (file)
@@ -12,11 +12,39 @@ struct MSeekInfo {
     tracks:     Vec<(u32, u64, u64)>,
 }
 
+#[allow(dead_code)]
+enum TrackCompression {
+    None,
+    Deflate,
+    BZ2,
+    LZO,
+    Strip(Vec<u8>),
+    Unknown(u8),
+}
+
 #[allow(dead_code)]
 struct Track {
     id:             u32,
     codec:          String,
     lacing:         bool,
+    compr:          TrackCompression,
+}
+
+impl Track {
+    fn read_packet(&self, br: &mut dyn ByteIO, stream: NAStreamRef, ts: NATimeInfo, keyframe: bool, pkt_size: usize) -> DemuxerResult<NAPacket> {
+        let buf = match self.compr {
+                TrackCompression::None => return br.read_packet(stream, ts, keyframe, pkt_size),
+                TrackCompression::Strip(ref hdr) => {
+                    let mut buf = vec![0; hdr.len() + pkt_size];
+                    let (header, body) = buf.split_at_mut(hdr.len());
+                    header.copy_from_slice(hdr);
+                    br.read_buf(body)?;
+                    buf
+                },
+                _ => return Err(DemuxerError::NotImplemented),
+            };
+        Ok(NAPacket::new(stream, ts, keyframe, buf))
+    }
 }
 
 struct Segment {
@@ -47,10 +75,19 @@ impl std::convert::From<SegmentInfo> for Segment {
 
 impl std::convert::From<TrackInfo> for Track {
     fn from(trk: TrackInfo) -> Self {
+        let compr = match trk.calgo {
+                None => TrackCompression::None,
+                Some(0) => TrackCompression::Deflate,
+                Some(1) => TrackCompression::BZ2,
+                Some(2) => TrackCompression::LZO,
+                Some(3) => TrackCompression::Strip(trk.csettings.unwrap()),
+                Some(id) => TrackCompression::Unknown(id),
+            };
         Self {
             id:         trk.number.unwrap() as u32,
             lacing:     trk.lacing.unwrap_or(true),
             codec:      trk.codec_id.unwrap(),
+            compr,
         }
     }
 }
@@ -178,11 +215,10 @@ impl<'a> DemuxCore<'a> for MatroskaDemuxer<'a> {
                             if trk_id > U32_MAX { return Err(DemuxerError::NotImplemented); }
                             let trk_id = trk_id as u32;
                             validate!(self.src.tell() < end);
-                            if let Some(stream) = strmgr.get_stream_by_id(trk_id) {
+                            let tfind = self.segments[self.cur_seg].tracks.iter().find(|x| x.id == trk_id);
+                            if let (Some(trk), Some(stream)) = (tfind, strmgr.get_stream_by_id(trk_id)) {
                                 if lacing != 0 {
-                                    if let Some(trk) = self.segments[self.cur_seg].tracks.iter().find(|x| x.id == trk_id) {
-                                        validate!(trk.lacing);
-                                    }
+                                    validate!(trk.lacing);
                                     let nframes = usize::from(self.src.read_byte()?) + 1;
                                     validate!(nframes > 1 && self.src.tell() < end);
                                     let mut sizes = [0; 256];
@@ -233,7 +269,7 @@ impl<'a> DemuxCore<'a> for MatroskaDemuxer<'a> {
                                     let mut ts = NATimeInfo::new(Some(full_ts), None, None, stream.tb_num, stream.tb_den);
                                     let mut keyframe = keyframe;
                                     for &pkt_size in sizes[..nframes].iter() {
-                                        let pkt = self.src.read_packet(stream.clone(), ts, keyframe, pkt_size)?;
+                                        let pkt = trk.read_packet(self.src, stream.clone(), ts, keyframe, pkt_size)?;
                                         keyframe = false;
                                         ts.pts = None;
                                         self.queue.push_back(pkt);
@@ -243,7 +279,7 @@ impl<'a> DemuxCore<'a> for MatroskaDemuxer<'a> {
                                 } else {
                                     let pkt_size = (end - self.src.tell()) as usize;
                                     let ts = NATimeInfo::new(Some(full_ts), None, None, stream.tb_num, stream.tb_den);
-                                    return self.src.read_packet(stream, ts, keyframe, pkt_size);
+                                    return trk.read_packet(self.src, stream, ts, keyframe, pkt_size);
                                 }
                             }
                         },
index bf8d86d1bda0feffb9f132dc6f4a96115d0e6d46..f32f15c9cb18a3a1e04ee19936e14b7cb80781b0 100644 (file)
@@ -354,6 +354,9 @@ pub struct TrackInfo {
     pub real_rate:  Option<f64>,
     pub channels:   Option<u8>,
     pub adepth:     Option<u8>,
+    pub ctype:      Option<u8>,
+    pub calgo:      Option<u8>,
+    pub csettings:  Option<Vec<u8>>,
 }
 
 static PARSE_TRACK: &[ParseEntry<TrackInfo>] = &[
@@ -367,6 +370,7 @@ static PARSE_TRACK: &[ParseEntry<TrackInfo>] = &[
     ParseEntry { id: 0x63A2, parse: TrackInfo::parse_edata },
     ParseEntry { id: 0xE0, parse: TrackInfo::parse_video_elem },
     ParseEntry { id: 0xE1, parse: TrackInfo::parse_audio_elem },
+    ParseEntry { id: 0x6D80, parse: TrackInfo::parse_content_encodings },
 ];
 
 static PARSE_TRACK_VIDEO_ELEM: &[ParseEntry<TrackInfo>] = &[
@@ -381,6 +385,16 @@ static PARSE_TRACK_AUDIO_ELEM: &[ParseEntry<TrackInfo>] = &[
     ParseEntry { id: 0x6264, parse: TrackInfo::parse_adepth },
 ];
 
+static PARSE_TRACK_CONTENT_ENCODING: &[ParseEntry<TrackInfo>] = &[
+    ParseEntry { id: 0x5033, parse: TrackInfo::parse_content_enc_type },
+    ParseEntry { id: 0x5034, parse: TrackInfo::parse_content_compr },
+];
+
+static PARSE_TRACK_CONTENT_ENC_COMPR: &[ParseEntry<TrackInfo>] = &[
+    ParseEntry { id: 0x4254, parse: TrackInfo::parse_comp_algo },
+    ParseEntry { id: 0x4255, parse: TrackInfo::parse_comp_settings },
+];
+
 impl TrackInfo {
     pub fn validate(&self) -> DemuxerResult<()> {
         validate!(self.number.is_some());
@@ -418,6 +432,12 @@ impl TrackInfo {
             },
             _ => {},
         }
+        if let Some(calgo) = self.calgo {
+            validate!(self.ctype.unwrap_or(0) == 0);
+            if calgo == 3 {
+                validate!(self.csettings.is_some());
+            }
+        }
         Ok(())
     }
 
@@ -471,6 +491,42 @@ impl TrackInfo {
         parse_entries(self, if self.debug { Some(3) } else { None }, br, PARSE_TRACK_AUDIO_ELEM, cur_end, ParseMode::Ignore)?;
         Ok(())
     }
+    fn parse_content_encodings(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+        let end = br.tell() + size;
+        while br.tell() + 2 <= end {
+            let off = br.tell();
+            let id          = br.read_ebml_id()?;
+            let size        = br.read_vint()?;
+            let cur_end = br.tell() + size;
+            if self.debug {
+                print_entry(3, off, id, size);
+            }
+            validate!(cur_end <= end);
+            if id == 0x6240 {
+                if self.debug {
+                    println!("            content encoding");
+                }
+                parse_entries(self, if self.debug { Some(4) } else { None }, br, PARSE_TRACK_CONTENT_ENCODING, cur_end, ParseMode::Ignore)?;
+            }
+            validate!(br.tell() <= cur_end);
+                              br.seek(SeekFrom::Start(cur_end))?;
+        }
+        Ok(())
+    }
+    read_field!(byte;  parse_content_enc_type, ctype);
+    fn parse_content_compr(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+        let cur_end = br.tell() + size;
+        parse_entries(self, if self.debug { Some(5) } else { None }, br, PARSE_TRACK_CONTENT_ENC_COMPR, cur_end, ParseMode::Ignore)?;
+        Ok(())
+    }
+    read_field!(byte;  parse_comp_algo, calgo);
+    fn parse_comp_settings(&mut self, br: &mut dyn ByteIO, size: u64) -> DemuxerResult<()> {
+        validate!(self.csettings.is_none() && (1..=U32_MAX).contains(&size));
+        let mut buf = vec![0; size as usize];
+        br.read_buf(&mut buf)?;
+        self.csettings = Some(buf);
+        Ok(())
+    }
 }