scuffle_mp4/boxes/types/
url.rs1use 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)]
10pub 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 } 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}