diff --git a/crates/aaf/src/aaf/read.rs b/crates/aaf/src/aaf/read.rs index 206c2ead3..753675ce1 100644 --- a/crates/aaf/src/aaf/read.rs +++ b/crates/aaf/src/aaf/read.rs @@ -172,6 +172,7 @@ impl R<'_> { let mut sample_rate = tag("FilmCraft Audio Sample Rate").and_then(|s| s.parse().ok()).unwrap_or(0u32); let mut comp = Composition { name, + aaf_mob_id: c.mob_id(pid::MOB_ID).map(|id| hex(&id)), rate, sample_rate: 48_000, width: 1920, diff --git a/crates/aaf/src/aaf/tests.rs b/crates/aaf/src/aaf/tests.rs index 2773fda6d..ca2196d3f 100644 --- a/crates/aaf/src/aaf/tests.rs +++ b/crates/aaf/src/aaf/tests.rs @@ -125,7 +125,14 @@ fn edits_tagged_with_either_usage_code_both_import() { old.set(pid::USAGE_CODE, Value::Data(def::USAGE_TOP_LEVEL_OLD.to_vec())); mobs.push(old); let mixed = store::write(&root, craft_cfb::Version::V4).unwrap(); - let mut names: Vec = read_clean(&mixed).compositions.into_iter().map(|c| c.name).collect(); + let document = read_clean(&mixed); + let mut ids: Vec<_> = document.compositions.iter().map(|c| c.aaf_mob_id.as_deref().unwrap()).collect(); + assert_eq!(ids.len(), 2); + assert!(ids.iter().all(|id| id.len() == 64 && id.bytes().all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase()))); + ids.sort_unstable(); + ids.dedup(); + assert_eq!(ids.len(), 2, "each composition keeps its own MobID"); + let mut names: Vec = document.compositions.into_iter().map(|c| c.name).collect(); names.sort(); assert_eq!(names, ["Old", "S"]); } diff --git a/crates/aaf/src/model.rs b/crates/aaf/src/model.rs index 3dff507be..e6bc79140 100644 --- a/crates/aaf/src/model.rs +++ b/crates/aaf/src/model.rs @@ -32,6 +32,10 @@ pub struct Document { #[derive(Clone, Debug, PartialEq)] pub struct Composition { pub name: String, + /// AAF CompositionMob identifier as 64 lowercase hexadecimal digits. OMF has no equivalent. + /// It is a read identity for applications that let a user choose among compositions; writers + /// generate fresh format identifiers and do not preserve it. + pub aaf_mob_id: Option, pub rate: FrameRate, pub sample_rate: u32, pub width: u32, diff --git a/crates/aaf/src/omf/read.rs b/crates/aaf/src/omf/read.rs index 50e4b270c..d45b6ff63 100644 --- a/crates/aaf/src/omf/read.rs +++ b/crates/aaf/src/omf/read.rs @@ -153,6 +153,7 @@ impl R<'_, '_> { let name = self.string(m, "OMFI:MOBJ:Name").unwrap_or_else(|| "Sequence".into()); let mut comp = Composition { name, + aaf_mob_id: None, rate: FrameRate::FPS_25, sample_rate: 48_000, width: 1920, diff --git a/crates/aaf/src/testdoc.rs b/crates/aaf/src/testdoc.rs index 81db0a158..c2740e1e7 100644 --- a/crates/aaf/src/testdoc.rs +++ b/crates/aaf/src/testdoc.rs @@ -52,7 +52,18 @@ pub(crate) fn two_clips(rate: FrameRate, source: usize, cut: i64, dur: i64, (in_ } pub(crate) fn composition(name: &str, rate: FrameRate, tracks: Vec) -> Composition { - Composition { name: name.into(), rate, sample_rate: 48_000, width: 1920, height: 1080, start_tc: 0, drop: false, tracks, markers: Vec::new() } + Composition { + name: name.into(), + aaf_mob_id: None, + rate, + sample_rate: 48_000, + width: 1920, + height: 1080, + start_tc: 0, + drop: false, + tracks, + markers: Vec::new(), + } } pub(crate) fn track(kind: CKind, name: &str, number: u32, items: Vec) -> CTrack {