Skip to main content

st377_1/
content_storage.rs

1//! Content Storage — SMPTE ST 377-1:2019 Annex A.4 (`docs/st377-1.md`):
2//! the file's inventory of every Package and Essence Container Data Set.
3
4extern crate alloc;
5
6use alloc::vec::Vec;
7
8use broadcast_common::{Parse, Serialize};
9
10use crate::error::{Error, Result};
11use crate::local_set::{LocalSet, StructuralSetKind};
12use crate::sets::{
13    InterchangeObjectFields, LocalSetOwnedItem, collect_dark, finish_owned_set, get_optional_raw,
14    get_required_raw, owned_set_serialized_len, serialize_owned_set,
15};
16use crate::types::{UlBytes, parse_uid_batch, serialize_uid_batch};
17
18/// Local tag: Packages (A.4).
19pub const TAG_PACKAGES: u16 = 0x1901;
20/// Local tag: Essence Container Data (A.4).
21pub const TAG_ESSENCE_CONTAINER_DATA: u16 = 0x1902;
22
23const KNOWN_TAGS: [u16; 5] = [
24    crate::sets::TAG_INSTANCE_UID,
25    crate::sets::TAG_GENERATION_UID,
26    crate::sets::TAG_OBJECT_CLASS,
27    TAG_PACKAGES,
28    TAG_ESSENCE_CONTAINER_DATA,
29];
30
31/// The Content Storage Set — SMPTE ST 377-1:2019 Annex A.4: strong
32/// references to every Package (`Packages`) and every Essence Container
33/// Data Set (`EssenceContainerData`) in the file, referenced from the
34/// Preface's `ContentStorage` property.
35#[derive(Debug, Clone, PartialEq, Eq)]
36pub struct ContentStorage {
37    /// Interchange Object (A.1) base properties.
38    pub interchange: InterchangeObjectFields,
39    /// Packages (`0x1901`, Req) — Batch of strong references to every
40    /// Package used in this file.
41    pub packages: Vec<UlBytes>,
42    /// Essence Container Data (`0x1902`, Opt) — Batch of strong references
43    /// to every Essence Container Data Set used in this file.
44    pub essence_container_data: Option<Vec<UlBytes>>,
45    /// Every other property found on parse (private/dark extension).
46    pub dark: Vec<(u16, Vec<u8>)>,
47}
48
49impl<'a> Parse<'a> for ContentStorage {
50    type Error = Error;
51
52    fn parse(bytes: &'a [u8]) -> Result<Self> {
53        let set = LocalSet::parse(bytes)?;
54        if set.kind() != StructuralSetKind::ContentStorage {
55            return Err(Error::KeyPrefixMismatch {
56                what: "Content Storage (Table 17)",
57            });
58        }
59        let items = &set.items;
60        let interchange = InterchangeObjectFields::decode(items, "Content Storage")?;
61        let packages = parse_uid_batch(get_required_raw(
62            items,
63            TAG_PACKAGES,
64            "Packages",
65            "Content Storage",
66        )?)?;
67        let essence_container_data = get_optional_raw(items, TAG_ESSENCE_CONTAINER_DATA)
68            .map(parse_uid_batch)
69            .transpose()?;
70        let dark = collect_dark(items, &KNOWN_TAGS);
71
72        Ok(ContentStorage {
73            interchange,
74            packages,
75            essence_container_data,
76            dark,
77        })
78    }
79}
80
81impl ContentStorage {
82    fn owned_items(&self) -> Vec<LocalSetOwnedItem> {
83        let mut out = Vec::new();
84        self.interchange.encode_into(&mut out);
85        out.push(LocalSetOwnedItem::owned(
86            TAG_PACKAGES,
87            serialize_uid_batch(&self.packages),
88        ));
89        if let Some(ecd) = &self.essence_container_data {
90            out.push(LocalSetOwnedItem::owned(
91                TAG_ESSENCE_CONTAINER_DATA,
92                serialize_uid_batch(ecd),
93            ));
94        }
95        out
96    }
97}
98
99impl Serialize for ContentStorage {
100    type Error = Error;
101
102    fn serialized_len(&self) -> usize {
103        let (key, items) = finish_owned_set(
104            StructuralSetKind::ContentStorage,
105            self.owned_items(),
106            &self.dark,
107        );
108        owned_set_serialized_len(key, &items)
109    }
110
111    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
112        let (key, items) = finish_owned_set(
113            StructuralSetKind::ContentStorage,
114            self.owned_items(),
115            &self.dark,
116        );
117        serialize_owned_set(key, &items, buf)
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    fn sample() -> ContentStorage {
126        ContentStorage {
127            interchange: InterchangeObjectFields {
128                instance_uid: [0x11; 16],
129                generation_uid: None,
130                object_class: None,
131            },
132            packages: alloc::vec![[0x22; 16], [0x33; 16]],
133            essence_container_data: Some(alloc::vec![[0x44; 16]]),
134            dark: Vec::new(),
135        }
136    }
137
138    #[test]
139    fn construct_serialize_parse_round_trip() {
140        let cs = sample();
141        let bytes = cs.to_bytes();
142        let parsed = ContentStorage::parse(&bytes).unwrap();
143        assert_eq!(parsed, cs);
144        assert_eq!(parsed.to_bytes(), bytes);
145    }
146
147    #[test]
148    fn optional_essence_container_data_omitted_round_trips() {
149        let mut cs = sample();
150        cs.essence_container_data = None;
151        let bytes = cs.to_bytes();
152        let parsed = ContentStorage::parse(&bytes).unwrap();
153        assert_eq!(parsed.essence_container_data, None);
154        assert_eq!(parsed.to_bytes(), bytes);
155    }
156
157    #[test]
158    fn mutation_changes_serialized_bytes() {
159        let mut cs = sample();
160        let before = cs.to_bytes();
161        cs.packages.push([0x55; 16]);
162        let after = cs.to_bytes();
163        assert_ne!(before, after);
164    }
165}