From 1c40c482891f8709e36d0dc7c1d4370992548984 Mon Sep 17 00:00:00 2001 From: Earl Co Date: Fri, 23 Oct 2026 16:53:19 +0200 Subject: [PATCH] security(mxf): share resolved descriptor metadata (#2949) * security(mxf): share resolved descriptor metadata * mxf: keep every source-package track in the no-material-package fallback Two packages may reuse a track id and number; comparing only those dropped a real track. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Brandon Thomas Co-authored-by: Claude Opus 5.5 --- crates/mxf/src/file.rs | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/mxf/src/file.rs b/crates/mxf/src/file.rs index 75dd27991..3394b657c 100644 --- a/crates/mxf/src/file.rs +++ b/crates/mxf/src/file.rs @@ -1,6 +1,7 @@ //! Opening an MXF file: KLV walk, partitions, metadata resolution, sample tables. use std::collections::HashMap; +use std::sync::Arc; use crate::essence::{Codec, PictureInfo, SoundFormat, SoundInfo, decode_pcm, identify_picture, sniff_picture}; use crate::index::{IndexEntry, IndexSegment}; @@ -544,8 +545,8 @@ struct TrackRef { origin: i64, start_position: i64, duration: Option, - descriptor: Option, - sub_descriptors: Vec, + descriptor: Option>, + sub_descriptors: Vec>, body_sid: u32, index_sid: u32, } @@ -567,9 +568,13 @@ fn resolve_tracks(md: &Metadata, warnings: &mut Vec) -> (Vec, } let material = md.of_kind(st::MATERIAL_PACKAGE).next(); let mut out = Vec::new(); + let mut descriptor_cache: HashMap<[u8; 16], Arc> = HashMap::new(); + let intern_descriptor = |set: &Set, cache: &mut HashMap<[u8; 16], Arc>| { + if set.instance_uid == [0; 16] { Arc::new(set.clone()) } else { cache.entry(set.instance_uid).or_insert_with(|| Arc::new(set.clone())).clone() } + }; let mut timecode = material.and_then(|m| package_timecode(md, m)); let name = material.and_then(|m| m.string(tag::PACKAGE_NAME)); - let file_track = |pkg: &Set, kind: Option, id: Option, start: i64, duration: Option, name: Option| -> Option { + let mut file_track = |pkg: &Set, kind: Option, id: Option, start: i64, duration: Option, name: Option| -> Option { let umid = umid_of(pkg, tag::PACKAGE_UID).unwrap_or([0; 32]); for r in pkg.references(tag::PACKAGE_TRACKS) { let Some(t) = md.get(&r) else { continue }; @@ -588,10 +593,16 @@ fn resolve_tracks(md: &Metadata, warnings: &mut Vec) -> (Vec, continue; } let desc = descriptor_for(md, pkg, tid, tk); - let subs: Vec = desc + let descriptor = desc.map(|d| intern_descriptor(d, &mut descriptor_cache)); + let subs: Vec> = desc .and_then(|d| d.dynamic_item(&SUB_DESCRIPTORS_ITEM)) .map(|v| { - crate::klv::batch_of(v).into_iter().filter_map(|b| b.get(..16)).filter_map(|b| md.get(&b.try_into().unwrap_or([0; 16]))).cloned().collect() + crate::klv::batch_of(v) + .into_iter() + .filter_map(|b| b.get(..16)) + .filter_map(|b| md.get(&b.try_into().unwrap_or([0; 16]))) + .map(|s| intern_descriptor(s, &mut descriptor_cache)) + .collect() }) .unwrap_or_default(); let (body_sid, index_sid) = sids.get(&umid).copied().unwrap_or((0, 0)); @@ -604,7 +615,7 @@ fn resolve_tracks(md: &Metadata, warnings: &mut Vec) -> (Vec, origin: t.i(tag::ORIGIN).unwrap_or(0), start_position: start, duration, - descriptor: desc.cloned(), + descriptor, sub_descriptors: subs, body_sid, index_sid, @@ -807,7 +818,7 @@ fn build_track( }; match r.kind { TrackKind::Picture => { - let pi = desc.map(picture_info).unwrap_or_default(); + let pi = desc.map(|d| picture_info(d)).unwrap_or_default(); let mpeg_desc = desc.is_some_and(|d| d.kind() == Some(st::MPEG2_VIDEO_DESCRIPTOR)); let mut codec = identify_picture(pi.picture_coding, container, mpeg_desc); if r.sub_descriptors.iter().any(|s| s.kind() == Some(st::AVC_SUBDESCRIPTOR)) && !matches!(codec, Codec::Avc { .. }) { @@ -828,7 +839,7 @@ fn build_track( build_pictures(src, &mut t, elements, index, warnings)?; } TrackKind::Sound => { - let si = desc.map(sound_info).unwrap_or(SoundInfo { channels: 1, bits: 16, ..Default::default() }); + let si = desc.map(|d| sound_info(d)).unwrap_or(SoundInfo { channels: 1, bits: 16, ..Default::default() }); let d10 = container.is_some_and(|c| c.item_starts_with(&[0x0D, 0x01, 0x03, 0x01, 0x02, 0x01])); let aes_element = d10 || key4[0] == 0x06 && key4[2] == 0x10; let pcm_desc = desc.is_some_and(|d| matches!(d.kind(), Some(st::AES3_DESCRIPTOR) | Some(st::WAVE_DESCRIPTOR)));