scuffle_mp4/boxes/types/
url.rs

1use std::io;
2
3use byteorder::{ReadBytesExt, WriteBytesExt};
4use bytes::Bytes;
5
6use crate::boxes::header::{BoxHeader, FullBoxHeader};
7use crate::boxes::traits::BoxType;
8
9#[derive(Debug, Clone, PartialEq)]
10/// Url Box
11/// ISO/IEC 14496-12:2022(E) - 8.7.2.2
12pub struct Url {
13    pub header: FullBoxHeader,
14    pub location: Option<String>,
15}
16
17impl Default for Url {
18    fn default() -> Self {
19        Self::new()
20    }
21}
22
23impl Url {
24    pub fn new() -> Self {
25        Self {
26            header: FullBoxHeader::new(Self::NAME, 0, 1),
27            location: None,
28        }
29    }
30}
31
32impl BoxType for Url {
33    const NAME: [u8; 4] = *b"url ";
34
35    fn demux(header: BoxHeader, data: Bytes) -> io::Result<Self> {
36        let mut reader = io::Cursor::new(data);
37
38        let header = FullBoxHeader::demux(header, &mut reader)?;
39
40        let location = if header.flags == 0 {
41            let mut location = String::new();
42            loop {
43                let byte = reader.read_u8()?;
44                if byte == 0 {
45                    break;
46                }
47                location.push(byte as char);
48            }
49
50            Some(location)
51        } else {
52            None
53        };
54
55        Ok(Self { header, location })
56    }
57
58    fn primitive_size(&self) -> u64 {
59        self.header.size()
60            + if let Some(location) = &self.location {
61                location.len() as u64 + 1 // null terminator
62            } else {
63                0
64            }
65    }
66
67    fn primitive_mux<T: io::Write>(&self, writer: &mut T) -> io::Result<()> {
68        self.header.mux(writer)?;
69
70        if let Some(location) = &self.location {
71            writer.write_all(location.as_bytes())?;
72            writer.write_u8(0)?;
73        }
74
75        Ok(())
76    }
77
78    fn validate(&self) -> io::Result<()> {
79        if self.header.version != 0 {
80            return Err(io::Error::new(io::ErrorKind::InvalidData, "url version must be 0"));
81        }
82
83        if self.header.flags != 0 && self.location.is_some() {
84            return Err(io::Error::new(
85                io::ErrorKind::InvalidData,
86                "url location must be empty if flags is 1",
87            ));
88        } else if self.header.flags == 0 && self.location.is_none() {
89            return Err(io::Error::new(
90                io::ErrorKind::InvalidData,
91                "url location must be present if flags is 0",
92            ));
93        }
94
95        Ok(())
96    }
97}