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 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <brandon@storyteller.ai>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Earl Co
2026-10-23 10:53:19 -04:00
committed by GitHub
co-authored by Claude Opus 5.5 Brandon Thomas
parent 0b99bafcc4
commit 1c40c48289
+19 -8
View File
@@ -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<i64>,
descriptor: Option<Set>,
sub_descriptors: Vec<Set>,
descriptor: Option<Arc<Set>>,
sub_descriptors: Vec<Arc<Set>>,
body_sid: u32,
index_sid: u32,
}
@@ -567,9 +568,13 @@ fn resolve_tracks(md: &Metadata, warnings: &mut Vec<String>) -> (Vec<TrackRef>,
}
let material = md.of_kind(st::MATERIAL_PACKAGE).next();
let mut out = Vec::new();
let mut descriptor_cache: HashMap<[u8; 16], Arc<Set>> = HashMap::new();
let intern_descriptor = |set: &Set, cache: &mut HashMap<[u8; 16], Arc<Set>>| {
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<TrackKind>, id: Option<u32>, start: i64, duration: Option<i64>, name: Option<String>| -> Option<TrackRef> {
let mut file_track = |pkg: &Set, kind: Option<TrackKind>, id: Option<u32>, start: i64, duration: Option<i64>, name: Option<String>| -> Option<TrackRef> {
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<String>) -> (Vec<TrackRef>,
continue;
}
let desc = descriptor_for(md, pkg, tid, tk);
let subs: Vec<Set> = desc
let descriptor = desc.map(|d| intern_descriptor(d, &mut descriptor_cache));
let subs: Vec<Arc<Set>> = 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<String>) -> (Vec<TrackRef>,
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)));