Skip to main content

vsd_mp4/boxes/
tenc.rs

1use crate::{Mp4Parser, ParsedBox, Result, data, parser};
2
3/// Track Encryption Box (tenc) - default encryption parameters for a track.
4///
5/// This box is found in the protection scheme info (sinf/schi) and provides
6/// the default KID, IV size, and pattern encryption parameters.
7#[derive(Debug, Clone)]
8pub struct TencBox {
9    /// The default Key ID for this track.
10    pub default_kid: [u8; 16],
11    /// Whether the track is protected (encrypted).
12    pub is_protected: bool,
13    /// Per-sample IV size (0 means constant IV is used, typically for CBCS).
14    pub per_sample_iv_size: u8,
15    /// Number of 16-byte blocks to encrypt in pattern mode (CENS/CBCS).
16    /// Only present in tenc version 1.
17    pub crypt_byte_block: u8,
18    /// Number of 16-byte blocks to skip in pattern mode (CENS/CBCS).
19    /// Only present in tenc version 1.
20    pub skip_byte_block: u8,
21    /// Constant IV for CBCS mode (when per_sample_iv_size is 0).
22    pub constant_iv: Option<[u8; 16]>,
23}
24
25impl TencBox {
26    /// Parse a tenc box from init segment data.
27    ///
28    /// This method navigates through the MP4 box hierarchy to find and parse
29    /// the tenc box.
30    pub fn from_init(data: &[u8]) -> Result<Option<Self>> {
31        let tenc_box = data!();
32        let tenc_box_c = tenc_box.clone();
33
34        Mp4Parser::new()
35            .base_box("moov", parser::children)
36            .base_box("trak", parser::children)
37            .base_box("mdia", parser::children)
38            .base_box("minf", parser::children)
39            .base_box("stbl", parser::children)
40            .full_box("stsd", parser::sample_description)
41            .base_box("encv", parser::visual_sample_entry)
42            .base_box("enca", parser::audio_sample_entry)
43            .base_box("sinf", parser::children)
44            .base_box("schi", parser::children)
45            .full_box("tenc", move |mut box_| {
46                *tenc_box_c.borrow_mut() = Some(Self::new(&mut box_)?);
47                Ok(())
48            })
49            .parse(data, true, false)?;
50
51        Ok(tenc_box.take())
52    }
53
54    /// Parse a `tenc` box from a `ParsedBox`.
55    pub fn new(box_: &mut ParsedBox) -> Result<Self> {
56        let reader = &mut box_.reader;
57        let version = box_.version.unwrap_or(0);
58
59        // Skip first reserved byte
60        reader.skip(1)?;
61
62        // In version 0, skip another reserved byte
63        // In version 1, read crypt/skip pattern blocks
64        let (crypt_byte_block, skip_byte_block) = if version == 0 {
65            reader.skip(1)?;
66            (0, 0)
67        } else {
68            let pattern = reader.read_u8()?;
69            ((pattern >> 4) & 0x0F, pattern & 0x0F)
70        };
71
72        let is_protected = reader.read_u8()? != 0;
73        let per_sample_iv_size = reader.read_u8()?;
74
75        let kid_bytes = reader.read_bytes_u8(16)?;
76        let mut default_kid = [0u8; 16];
77        default_kid.copy_from_slice(&kid_bytes);
78
79        // Read constant IV if per_sample_iv_size is 0 (CBCS mode)
80        let mut constant_iv = None;
81
82        if per_sample_iv_size == 0 {
83            let constant_iv_size = reader.read_u8()?;
84            if constant_iv_size > 0 && constant_iv_size <= 16 {
85                let bytes = reader.read_bytes_u8(constant_iv_size as usize)?;
86                let mut iv = [0u8; 16];
87                iv[..bytes.len()].copy_from_slice(&bytes);
88                constant_iv = Some(iv);
89            }
90        }
91
92        Ok(Self {
93            default_kid,
94            is_protected,
95            per_sample_iv_size,
96            crypt_byte_block,
97            skip_byte_block,
98            constant_iv,
99        })
100    }
101
102    /// Get the default Key ID as a hexadecimal string.
103    pub fn default_kid_hex(&self) -> String {
104        hex::encode(self.default_kid)
105    }
106}