1use super::UtcTime;
6use super::*;
7use std::fmt;
8
9#[derive(Debug, Clone)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct MediaProperties(Vec<MediaProperty>);
13
14#[derive(Debug, Clone, PartialEq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17pub enum MediaProperty {
18 RandomAccess(Option<f64>),
20 BeginningOnly,
22 NoSeeking,
24 Immutable,
26 Dynamic,
28 TimeProgressing,
30 Unlimited,
32 TimeLimited(UtcTime),
34 TimeDuration(f64),
36 Scales(Vec<ScaleRange>),
38 Extension(String, Option<String>),
40}
41
42impl fmt::Display for MediaProperty {
43 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44 use fmt::Write;
45
46 match self {
47 MediaProperty::RandomAccess(Some(dur)) => write!(f, "Random-Access={dur}"),
48 MediaProperty::RandomAccess(None) => f.write_str("Random-Access"),
49 MediaProperty::BeginningOnly => f.write_str("Beginning-Only"),
50 MediaProperty::NoSeeking => f.write_str("No-Seeking"),
51 MediaProperty::Immutable => f.write_str("Immutable"),
52 MediaProperty::Dynamic => f.write_str("Dynamic"),
53 MediaProperty::TimeProgressing => f.write_str("Time-Progressing"),
54 MediaProperty::Unlimited => f.write_str("Unlimited"),
55 MediaProperty::TimeLimited(time) => write!(f, "Time-Limited={time}"),
56 MediaProperty::TimeDuration(dur) => write!(f, "Time-Duration={dur}"),
57 MediaProperty::Scales(scales) => {
58 let mut s = String::new();
59 for scale in scales {
60 if !s.is_empty() {
61 s.push_str(", ");
62 }
63 write!(&mut s, "{scale}").unwrap();
64 }
65 write!(f, "Scales=\"{s}\"")
66 }
67 MediaProperty::Extension(key, Some(value)) => write!(f, "{key}={value}"),
68 MediaProperty::Extension(key, None) => f.write_str(key),
69 }
70 }
71}
72
73#[derive(Debug, Clone, PartialEq)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76pub enum ScaleRange {
77 Scale(f64),
78 Range(f64, f64),
79}
80
81impl fmt::Display for ScaleRange {
82 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83 match self {
84 ScaleRange::Scale(scale) => write!(f, "{scale}"),
85 ScaleRange::Range(a, b) => write!(f, "{a}:{b}"),
86 }
87 }
88}
89
90impl std::ops::Deref for MediaProperties {
91 type Target = Vec<MediaProperty>;
92
93 fn deref(&self) -> &Self::Target {
94 &self.0
95 }
96}
97
98impl std::ops::DerefMut for MediaProperties {
99 fn deref_mut(&mut self) -> &mut Self::Target {
100 &mut self.0
101 }
102}
103
104impl AsRef<Vec<MediaProperty>> for MediaProperties {
105 fn as_ref(&self) -> &Vec<MediaProperty> {
106 &self.0
107 }
108}
109
110impl AsMut<Vec<MediaProperty>> for MediaProperties {
111 fn as_mut(&mut self) -> &mut Vec<MediaProperty> {
112 &mut self.0
113 }
114}
115
116impl From<Vec<MediaProperty>> for MediaProperties {
117 fn from(v: Vec<MediaProperty>) -> Self {
118 MediaProperties(v)
119 }
120}
121
122impl<'a> From<&'a [MediaProperty]> for MediaProperties {
123 fn from(v: &'a [MediaProperty]) -> Self {
124 MediaProperties(v.to_vec())
125 }
126}
127
128impl MediaProperties {
129 pub fn builder() -> MediaPropertiesBuilder {
131 MediaPropertiesBuilder(Vec::new())
132 }
133}
134
135#[derive(Debug, Clone)]
137pub struct MediaPropertiesBuilder(Vec<MediaProperty>);
138
139impl MediaPropertiesBuilder {
140 pub fn property(mut self, property: MediaProperty) -> Self {
142 self.0.push(property);
143 self
144 }
145
146 pub fn build(self) -> MediaProperties {
148 MediaProperties(self.0)
149 }
150}
151
152pub(super) mod parser {
153 use super::*;
154
155 use super::parser_helpers::{
156 cond_parser, quoted_string, rtsp_unreserved, split_once, token, trim,
157 };
158 use nom::branch::alt;
159 use nom::bytes::complete::tag;
160 use nom::combinator::{all_consuming, map_res};
161 use nom::multi::separated_list1;
162 use nom::sequence::tuple;
163 use nom::{Err, IResult};
164 use std::str;
165
166 fn param(input: &[u8]) -> IResult<&[u8], (&str, Option<&str>)> {
167 if input.is_empty() {
168 return Err(Err::Error(nom::error::Error::new(
169 input,
170 nom::error::ErrorKind::Eof,
171 )));
172 }
173
174 tuple((
175 trim(map_res(token, str::from_utf8)),
176 cond_parser(
177 tag(b"="),
178 trim(map_res(
179 alt((quoted_string, rtsp_unreserved)),
180 str::from_utf8,
181 )),
182 ),
183 ))(input)
184 }
185
186 fn media_property(input: &[u8]) -> IResult<&[u8], MediaProperty> {
187 map_res(param, |p| -> Result<_, HeaderParseError> {
188 dbg!(&p);
189 match p {
190 ("Random-Access", None) => Ok(MediaProperty::RandomAccess(None)),
191 ("Random-Access", Some(dur)) => {
192 let dur = dur.parse().map_err(|_| HeaderParseError)?;
193 Ok(MediaProperty::RandomAccess(Some(dur)))
194 }
195 ("Beginning-Only", None) => Ok(MediaProperty::BeginningOnly),
196 ("No-Seeking", None) => Ok(MediaProperty::NoSeeking),
197 ("Immutable", None) => Ok(MediaProperty::Immutable),
198 ("Dynamic", None) => Ok(MediaProperty::Dynamic),
199 ("Time-Progressing", None) => Ok(MediaProperty::TimeProgressing),
200 ("Unlimited", None) => Ok(MediaProperty::Unlimited),
201 ("Time-Limited", Some(time)) => {
202 let time = time.parse().map_err(|_| HeaderParseError)?;
203 Ok(MediaProperty::TimeLimited(time))
204 }
205 ("Time-Duration", Some(dur)) => {
206 let dur = dur.parse().map_err(|_| HeaderParseError)?;
207 Ok(MediaProperty::TimeDuration(dur))
208 }
209 ("Scales", Some(scales)) => {
210 if !scales.starts_with('"') || !scales.ends_with('"') {
211 return Err(HeaderParseError);
212 }
213
214 let mut s = Vec::new();
215 for scale in scales[1..(scales.len() - 1)].split(',') {
216 let scale = scale.trim();
217 if let Some((a, b)) = split_once(scale, ':') {
218 let a = a.parse().map_err(|_| HeaderParseError)?;
219 let b = b.parse().map_err(|_| HeaderParseError)?;
220 s.push(ScaleRange::Range(a, b));
221 } else {
222 let a = scale.parse().map_err(|_| HeaderParseError)?;
223 s.push(ScaleRange::Scale(a));
224 }
225 }
226
227 Ok(MediaProperty::Scales(s))
228 }
229 (key, value) => Ok(MediaProperty::Extension(
230 key.into(),
231 value.map(String::from),
232 )),
233 }
234 })(input)
235 }
236
237 pub(crate) fn media_properties(input: &[u8]) -> IResult<&[u8], Vec<MediaProperty>> {
238 all_consuming(separated_list1(tag(","), media_property))(input)
239 }
240}
241
242impl super::TypedHeader for MediaProperties {
243 fn from_headers(headers: impl AsRef<Headers>) -> Result<Option<Self>, HeaderParseError> {
244 let headers = headers.as_ref();
245
246 let header = match headers.get(&MEDIA_PROPERTIES) {
247 None => return Ok(None),
248 Some(header) => header,
249 };
250
251 let (_rem, properties) =
252 parser::media_properties(header.as_str().as_bytes()).map_err(|_| HeaderParseError)?;
253
254 Ok(Some(properties.into()))
255 }
256
257 fn insert_into(&self, mut headers: impl AsMut<Headers>) {
258 let headers = headers.as_mut();
259
260 let mut properties = String::new();
261 for property in &self.0 {
262 if !properties.is_empty() {
263 properties.push_str(", ");
264 }
265
266 properties.push_str(&property.to_string());
267 }
268
269 headers.insert(MEDIA_PROPERTIES, properties);
270 }
271}
272
273impl super::TypedAppendableHeader for MediaProperties {
274 fn append_to(&self, mut headers: impl AsMut<Headers>) {
275 let headers = headers.as_mut();
276
277 let mut properties = String::new();
278 for property in &self.0 {
279 if !properties.is_empty() {
280 properties.push_str(", ");
281 }
282
283 properties.push_str(&property.to_string());
284 }
285
286 headers.append(MEDIA_PROPERTIES, properties);
287 }
288}
289
290#[cfg(test)]
291mod tests {
292 use super::*;
293
294 #[test]
295 fn test_media_properties() {
296 let header = "Random-Access=2.5, Unlimited, Immutable, Scales=\"-20, -10, -4, 0.5:1.5, 4, 8, 10, 15, 20\"";
297 let response = crate::Response::builder(crate::Version::V2_0, crate::StatusCode::Ok)
298 .header(crate::headers::MEDIA_PROPERTIES, header)
299 .empty();
300
301 let props = response
302 .typed_header::<super::MediaProperties>()
303 .unwrap()
304 .unwrap();
305
306 assert_eq!(
307 &*props,
308 &[
309 MediaProperty::RandomAccess(Some(2.5)),
310 MediaProperty::Unlimited,
311 MediaProperty::Immutable,
312 MediaProperty::Scales(vec![
313 ScaleRange::Scale(-20.0),
314 ScaleRange::Scale(-10.0),
315 ScaleRange::Scale(-4.0),
316 ScaleRange::Range(0.5, 1.5),
317 ScaleRange::Scale(4.0),
318 ScaleRange::Scale(8.0),
319 ScaleRange::Scale(10.0),
320 ScaleRange::Scale(15.0),
321 ScaleRange::Scale(20.0),
322 ]),
323 ]
324 );
325
326 let response2 = crate::Response::builder(crate::Version::V2_0, crate::StatusCode::Ok)
327 .typed_header(&props)
328 .empty();
329 assert_eq!(response, response2);
330 }
331}