From: Kostya Shishkov Date: Thu, 29 Oct 2026 04:40:43 +0000 (+0100) Subject: mkv: support header stripping track compression X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=38232dc932ecd11c0afe1bc0eba6ccdb539f49ad;p=nihav.git mkv: support header stripping track compression --- diff --git a/nihav-commonfmt/src/demuxers/mkv/mod.rs b/nihav-commonfmt/src/demuxers/mkv/mod.rs index da034f8..0a9c6e2 100644 --- a/nihav-commonfmt/src/demuxers/mkv/mod.rs +++ b/nihav-commonfmt/src/demuxers/mkv/mod.rs @@ -12,11 +12,39 @@ struct MSeekInfo { tracks: Vec<(u32, u64, u64)>, } +#[allow(dead_code)] +enum TrackCompression { + None, + Deflate, + BZ2, + LZO, + Strip(Vec), + 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 { + 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 for Segment { impl std::convert::From 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); } } }, diff --git a/nihav-commonfmt/src/demuxers/mkv/parse.rs b/nihav-commonfmt/src/demuxers/mkv/parse.rs index bf8d86d..f32f15c 100644 --- a/nihav-commonfmt/src/demuxers/mkv/parse.rs +++ b/nihav-commonfmt/src/demuxers/mkv/parse.rs @@ -354,6 +354,9 @@ pub struct TrackInfo { pub real_rate: Option, pub channels: Option, pub adepth: Option, + pub ctype: Option, + pub calgo: Option, + pub csettings: Option>, } static PARSE_TRACK: &[ParseEntry] = &[ @@ -367,6 +370,7 @@ static PARSE_TRACK: &[ParseEntry] = &[ 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] = &[ @@ -381,6 +385,16 @@ static PARSE_TRACK_AUDIO_ELEM: &[ParseEntry] = &[ ParseEntry { id: 0x6264, parse: TrackInfo::parse_adepth }, ]; +static PARSE_TRACK_CONTENT_ENCODING: &[ParseEntry] = &[ + ParseEntry { id: 0x5033, parse: TrackInfo::parse_content_enc_type }, + ParseEntry { id: 0x5034, parse: TrackInfo::parse_content_compr }, +]; + +static PARSE_TRACK_CONTENT_ENC_COMPR: &[ParseEntry] = &[ + 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(()) + } }