Skip to main content

mp4_atom/meta/ilst/
cprt.rs

1use crate::*;
2
3/// An iTunes-style copyright notice, written by FFmpeg and iTunes for the
4/// `copyright` metadata key.
5///
6/// This lives inside `moov/udta/meta/ilst` and shares its fourcc with the
7/// unrelated ISO CopyrightBox ([`cprt`]) found directly under `udta` — the
8/// two have completely different layouts. It is deliberately NOT part of the
9/// global [`Any`] dispatch; [`Ilst`] decodes it by header.
10#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct Copyright {
13    pub country_indicator: u16,
14    pub language_indicator: u16,
15    pub text: String,
16}
17
18impl Atom for Copyright {
19    const KIND: FourCC = FourCC::new(b"cprt");
20
21    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
22        let data = super::data::decode_text(buf)?;
23        Ok(Copyright {
24            country_indicator: data.country_indicator,
25            language_indicator: data.language_indicator,
26            text: data.text,
27        })
28    }
29
30    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
31        super::data::encode_text(
32            self.country_indicator,
33            self.language_indicator,
34            &self.text,
35            buf,
36        )
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    // Long QuickTime/GPAC style: the value is wrapped in a nested `data` atom.
45    // This is what FFmpeg and iTunes write for the `copyright` key.
46    const ENCODED_CPRT_LONG: &[u8] = &[
47        0x00, 0x00, 0x00, 0x22, // cprt size = 34
48        b'c', b'p', b'r', b't', //
49        0x00, 0x00, 0x00, 0x1A, // data size = 26
50        b'd', b'a', b't', b'a', //
51        0x00, 0x00, 0x00, 0x01, // type indicator: UTF-8
52        0x00, 0x00, 0x00, 0x00, // country + language
53        b'(', b'c', b')', b' ', b'2', b'0', b'2', b'6', b' ', b'x',
54    ];
55
56    #[test]
57    fn test_copyright_long_style() {
58        let decoded = Copyright::decode(&mut &ENCODED_CPRT_LONG[..]).unwrap();
59        assert_eq!(decoded.country_indicator, 0);
60        assert_eq!(decoded.language_indicator, 0);
61        assert_eq!(decoded.text, "(c) 2026 x");
62
63        // The encoder always emits the long `data`-wrapped layout.
64        let mut out = Vec::new();
65        decoded.encode(&mut out).unwrap();
66        assert_eq!(out, ENCODED_CPRT_LONG);
67    }
68
69    // Short FFmpeg style: the UTF-8 value follows the item header directly (no
70    // nested `data` atom); the leading `1` is the type indicator, too small to
71    // be a valid `data` atom length.
72    #[test]
73    fn test_copyright_short_style() {
74        let buf = [
75            0x00, 0x00, 0x00, 0x14, // cprt size = 20
76            b'c', b'p', b'r', b't', //
77            0x00, 0x00, 0x00, 0x01, // type indicator (short style): UTF-8
78            0x00, 0x00, 0x00, 0x00, // country + language
79            b'2', b'0', b'2', b'6',
80        ];
81        let decoded = Copyright::decode(&mut &buf[..]).unwrap();
82        assert_eq!(decoded.country_indicator, 0);
83        assert_eq!(decoded.language_indicator, 0);
84        assert_eq!(decoded.text, "2026");
85    }
86}