]> git.nihav.org Git - nihav.git/commitdiff
mkv: support Block element
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 31 Oct 2026 11:13:34 +0000 (12:13 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 31 Oct 2026 11:13:34 +0000 (12:13 +0100)
nihav-commonfmt/src/demuxers/mkv/mod.rs

index 1b893d421f7e946a36a3a97a706d8d0ecd496d30..f9df82ad7ddfe73fc358c7b56988264426f4c24e 100644 (file)
@@ -101,6 +101,14 @@ impl std::convert::From<CueEntry> for MSeekInfo {
     }
 }
 
+#[allow(dead_code)]
+struct BlockHeader {
+    full_ts:        u64,
+    keyframe:       bool,
+    invisible:      bool,
+    lacing:         u8,
+}
+
 struct MatroskaDemuxer<'a> {
     src:            &'a mut dyn ByteIO,
     segments:       Vec<Segment>,
@@ -197,7 +205,54 @@ impl<'a> DemuxCore<'a> for MatroskaDemuxer<'a> {
                             }
                             self.clu_ts = Some(ts);
                         },
-                        0xA0 => return Err(DemuxerError::NotImplemented), // BlockGroup
+                        0xA0 => {
+                            validate!(self.clu_ts.is_some());
+
+                            let blk_end = end;
+                            let mut has_block = false;
+                            while self.src.tell() < blk_end {
+                                let off = self.src.tell();
+                                let id          = self.src.read_ebml_id()?;
+                                let size        = self.src.read_vint()?;
+                                let end = self.src.tell() + size;
+                                if self.print_chunks {
+                                    print_entry(3, off, id, size);
+                                }
+                                validate!(end <= blk_end);
+                                if id == 0xA1 {
+                                    validate!(!has_block);
+                                    has_block = true;
+
+                                    let trk_id      = self.src.read_vint()?;
+                                    let timestamp   = self.src.read_u16be()?;
+                                    let flags       = self.src.read_byte()?;
+                                    let mut full_ts = self.clu_ts.unwrap() + u64::from(timestamp);
+                                    if (timestamp & 0x8000) != 0 {
+                                        full_ts = full_ts.wrapping_sub(0x10000);
+                                    }
+                                    validate!((flags & 0xF0) == 0);
+                                    let invisible = (flags & 0x08) != 0;
+                                    let lacing    = (flags >> 1) & 3;
+                                    if self.print_chunks {
+                                        println!("            block - track {trk_id} ts {full_ts} ({timestamp}) {}{}", if invisible { " invisible" } else {""}, if lacing != 0 { " laced" } else {""});
+                                    }
+                                    if trk_id > U32_MAX { return Err(DemuxerError::NotImplemented); }
+                                    let trk_id = trk_id as u32;
+                                    validate!(self.src.tell() < end);
+                                    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)) {
+                                        let blk_hdr = BlockHeader { full_ts, keyframe: false, lacing, invisible };
+                                        let first_pkt = Self::read_block(trk, stream, self.src, &blk_hdr, end, &mut self.queue)?;
+                                        self.queue.push_front(first_pkt);
+                                    }
+                                }
+                                validate!(self.src.tell() <= end);
+                                          self.src.seek(SeekFrom::Start(end))?;
+                            }
+                            if let Some(pkt) = self.queue.pop_front() {
+                                return Ok(pkt);
+                            }
+                        },
                         0xA3 => {
                             validate!(self.clu_ts.is_some());
                             let trk_id      = self.src.read_vint()?;
@@ -220,70 +275,8 @@ impl<'a> DemuxCore<'a> for MatroskaDemuxer<'a> {
                             validate!(self.src.tell() < end);
                             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 {
-                                    validate!(trk.lacing);
-                                    let nframes = usize::from(self.src.read_byte()?) + 1;
-                                    validate!(nframes > 1 && self.src.tell() < end);
-                                    let mut sizes = [0; 256];
-                                    let mut tot_size = 0;
-                                    match lacing {
-                                        1 => { // Xiph lacing
-                                            for dsize in sizes[..nframes - 1].iter_mut() {
-                                                let mut b = self.src.read_byte()?;
-                                                let mut size = usize::from(b);
-                                                while b == 255 {
-                                                    b = self.src.read_byte()?;
-                                                    size += usize::from(b);
-                                                }
-                                                *dsize = size;
-                                                tot_size += size;
-                                            }
-                                            validate!(self.src.tell() + (tot_size as u64) < end);
-                                            sizes[nframes - 1] = (end - self.src.tell()) as usize - tot_size;
-                                        },
-                                        3 => { // EBML lacing
-                                            let first_size = self.src.read_vint()?;
-                                            let mut prev = first_size as i64;
-                                            let fsize = first_size as usize;
-                                            sizes[0] = fsize;
-                                            tot_size += fsize;
-                                            for dsize in sizes[..nframes - 1].iter_mut().skip(1) {
-                                                let diff = self.src.read_vint_s()?;
-                                                let size = prev + diff;
-                                                validate!((1..=(U32_MAX as i64)).contains(&size));
-                                                prev = size;
-                                                let size = size as usize;
-                                                *dsize = size;
-                                                tot_size += size;
-                                            }
-                                            validate!(self.src.tell() + (tot_size as u64) < end);
-                                            sizes[nframes - 1] = (end - self.src.tell()) as usize - tot_size;
-                                        },
-                                        2 => { // fixed-size lacing
-                                            let rest_size = (end - self.src.tell()) as usize;
-                                            validate!((rest_size > 0) && (rest_size % nframes == 0));
-                                            let pkt_size = rest_size / nframes;
-                                            for el in sizes.iter_mut().take(nframes) {
-                                                *el = pkt_size;
-                                            }
-                                        },
-                                        _ => unreachable!()
-                                    }
-                                    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 = trk.read_packet(self.src, stream.clone(), ts, keyframe, pkt_size)?;
-                                        keyframe = false;
-                                        ts.pts = None;
-                                        self.queue.push_back(pkt);
-                                    }
-                                    validate!(self.src.tell() == end);
-                                    return Ok(self.queue.pop_front().unwrap());
-                                } 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 trk.read_packet(self.src, stream, ts, keyframe, pkt_size);
-                                }
+                                let blk_hdr = BlockHeader { full_ts, keyframe, lacing, invisible };
+                                return Self::read_block(trk, stream, self.src, &blk_hdr, end, &mut self.queue);
                             }
                         },
                         _ => {},
@@ -439,6 +432,72 @@ impl<'a> MatroskaDemuxer<'a> {
             queue:          VecDeque::new(),
         }
     }
+    fn read_block(trk: &Track, stream: NAStreamRef, br: &mut dyn ByteIO, blk_hdr: &BlockHeader, end: u64, queue: &mut VecDeque<NAPacket>) -> DemuxerResult<NAPacket> {
+        if blk_hdr.lacing != 0 {
+            validate!(trk.lacing);
+            let nframes = usize::from(br.read_byte()?) + 1;
+            validate!(nframes > 1 && br.tell() < end);
+            let mut sizes = [0; 256];
+            let mut tot_size = 0;
+            match blk_hdr.lacing {
+                1 => { // Xiph lacing
+                    for dsize in sizes[..nframes - 1].iter_mut() {
+                        let mut b = br.read_byte()?;
+                        let mut size = usize::from(b);
+                        while b == 255 {
+                            b = br.read_byte()?;
+                            size += usize::from(b);
+                        }
+                        *dsize = size;
+                        tot_size += size;
+                    }
+                    validate!(br.tell() + (tot_size as u64) < end);
+                    sizes[nframes - 1] = (end - br.tell()) as usize - tot_size;
+                },
+                3 => { // EBML lacing
+                    let first_size = br.read_vint()?;
+                    let mut prev = first_size as i64;
+                    let fsize = first_size as usize;
+                    sizes[0] = fsize;
+                    tot_size += fsize;
+                    for dsize in sizes[..nframes - 1].iter_mut().skip(1) {
+                        let diff = br.read_vint_s()?;
+                        let size = prev + diff;
+                        validate!((1..=(U32_MAX as i64)).contains(&size));
+                        prev = size;
+                        let size = size as usize;
+                        *dsize = size;
+                        tot_size += size;
+                    }
+                    validate!(br.tell() + (tot_size as u64) < end);
+                    sizes[nframes - 1] = (end - br.tell()) as usize - tot_size;
+                },
+                2 => { // fixed-size lacing
+                    let rest_size = (end - br.tell()) as usize;
+                    validate!((rest_size > 0) && (rest_size % nframes == 0));
+                    let pkt_size = rest_size / nframes;
+                    for el in sizes.iter_mut().take(nframes) {
+                        *el = pkt_size;
+                    }
+                },
+                _ => unreachable!()
+            }
+            let mut ts = NATimeInfo::new(Some(blk_hdr.full_ts), None, None, stream.tb_num, stream.tb_den);
+            let mut keyframe = blk_hdr.keyframe;
+            for &pkt_size in sizes[..nframes].iter() {
+                let pkt = trk.read_packet(br, stream.clone(), ts, keyframe, pkt_size)?;
+                keyframe = false;
+                ts.pts = None;
+                queue.push_back(pkt);
+            }
+            validate!(br.tell() == end);
+            Ok(queue.pop_front().unwrap())
+        } else {
+            let pkt_size = (end - br.tell()) as usize;
+            let ts = NATimeInfo::new(Some(blk_hdr.full_ts), None, None, stream.tb_num, stream.tb_den);
+            trk.read_packet(br, stream, ts, blk_hdr.keyframe, pkt_size)
+        }
+    }
 }
 
 pub struct MatroskaDemuxerCreator { }