rtsp_types/headers/
range.rs

1// Copyright (C) 2021 Sebastian Dröge <sebastian@centricular.com>
2//
3// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>
4
5use super::*;
6
7use super::parser_helpers::split_once;
8use std::fmt;
9
10/// `Range` header ([RFC 7826 section 18.40](https://tools.ietf.org/html/rfc7826#section-18.40)).
11#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub enum Range {
14    /// Normal Play Time Range ([RFC 7826 section 4.4.2](https://tools.ietf.org/html/rfc7826#section-4.4.2)).
15    Npt(NptRange),
16    /// SMPTE-Relative Timecode Range ([RFC 7826 section 4.4.1](https://tools.ietf.org/html/rfc7826#section-4.4.1)).
17    Smpte(SmpteRange),
18    /// Absolute Time (UTC) Time Range ([RFC 7826 section 4.4.3](https://tools.ietf.org/html/rfc7826#section-4.4.3)).
19    Utc(UtcRange),
20    /// Other time range.
21    Other(String),
22}
23
24impl fmt::Display for Range {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        match self {
27            Range::Npt(r) => <NptRange as fmt::Display>::fmt(r, f),
28            Range::Smpte(r) => <SmpteRange as fmt::Display>::fmt(r, f),
29            Range::Utc(r) => <UtcRange as fmt::Display>::fmt(r, f),
30            Range::Other(r) => <String as fmt::Display>::fmt(r, f),
31        }
32    }
33}
34
35impl std::str::FromStr for Range {
36    type Err = HeaderParseError;
37
38    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
39        if s.starts_with("npt") {
40            Ok(Range::Npt(s.parse()?))
41        } else if s.starts_with("clock") {
42            Ok(Range::Utc(s.parse()?))
43        } else if s.starts_with("smpte") {
44            Ok(Range::Smpte(s.parse()?))
45        } else {
46            Ok(Range::Other(s.into()))
47        }
48    }
49}
50
51/// Normal Play Time Range ([RFC 7826 section 4.4.2](https://tools.ietf.org/html/rfc7826#section-4.4.2)).
52#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
54pub enum NptRange {
55    /// Empty range.
56    Empty,
57    /// Range starting at a specific time.
58    From(NptTime),
59    /// Range from a specific time to another.
60    FromTo(NptTime, NptTime),
61    /// Range ending at a specific time.
62    To(NptTime),
63}
64
65impl fmt::Display for NptRange {
66    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            NptRange::Empty => fmt.write_str("npt"),
69            NptRange::From(f) => write!(fmt, "npt={f}-"),
70            NptRange::FromTo(f, t) => write!(fmt, "npt={f}-{t}"),
71            NptRange::To(t) => write!(fmt, "npt=-{t}"),
72        }
73    }
74}
75
76impl std::str::FromStr for NptRange {
77    type Err = HeaderParseError;
78
79    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
80        let s = s.strip_prefix("npt").ok_or(HeaderParseError)?;
81
82        if s.is_empty() {
83            return Ok(NptRange::Empty);
84        }
85
86        let s = s.strip_prefix('=').ok_or(HeaderParseError)?;
87
88        let (from, to) = split_once(s, '-').ok_or(HeaderParseError)?;
89        let from = if from.is_empty() { None } else { Some(from) };
90        let to = if to.is_empty() { None } else { Some(to) };
91
92        let from = from
93            .map(|s| s.parse::<NptTime>().map_err(|_| HeaderParseError))
94            .transpose()?;
95        let to = to
96            .map(|s| s.parse::<NptTime>().map_err(|_| HeaderParseError))
97            .transpose()?;
98
99        match (from, to) {
100            (Some(from), Some(to)) => Ok(NptRange::FromTo(from, to)),
101            (None, Some(to)) => Ok(NptRange::To(to)),
102            (Some(from), None) => Ok(NptRange::From(from)),
103            (None, None) => Err(HeaderParseError),
104        }
105    }
106}
107
108/// Normal Play Time ([RFC 7826 section 4.4.2](https://tools.ietf.org/html/rfc7826#section-4.4.2)).
109#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
110#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
111pub enum NptTime {
112    /// Now.
113    Now,
114    /// Seconds and nanoseconds.
115    Seconds(u64, Option<u32>),
116    /// Hours, minutes, seconds and nanoseconds.
117    Hms(u64, u8, u8, Option<u32>),
118}
119
120impl fmt::Display for NptTime {
121    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
122        match self {
123            NptTime::Now => fmt.write_str("now"),
124            NptTime::Seconds(seconds, None) => write!(fmt, "{seconds}"),
125            NptTime::Seconds(seconds, Some(nanoseconds)) => {
126                write!(fmt, "{seconds}.{nanoseconds:09}")
127            }
128            NptTime::Hms(hours, minutes, seconds, None) => {
129                write!(fmt, "{hours:02}:{minutes:02}:{seconds:02}")
130            }
131            NptTime::Hms(hours, minutes, seconds, Some(nanoseconds)) => {
132                write!(fmt, "{hours:02}:{minutes:02}:{seconds:02}.{nanoseconds:09}")
133            }
134        }
135    }
136}
137
138impl std::str::FromStr for NptTime {
139    type Err = HeaderParseError;
140
141    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
142        if s == "now" {
143            return Ok(NptTime::Now);
144        }
145
146        match split_once(s, ':') {
147            None => match split_once(s, '.') {
148                None => {
149                    let seconds = s.parse::<u64>().map_err(|_| HeaderParseError)?;
150                    Ok(NptTime::Seconds(seconds, None))
151                }
152                Some((seconds, subseconds)) => {
153                    let seconds = seconds.parse::<u64>().map_err(|_| HeaderParseError)?;
154                    let digits = subseconds.len();
155                    if digits > 9 || digits == 0 {
156                        return Err(HeaderParseError);
157                    }
158                    let subseconds = subseconds.parse::<u32>().map_err(|_| HeaderParseError)?;
159
160                    let nanoseconds = subseconds * u32::pow(10, 9 - digits as u32);
161
162                    Ok(NptTime::Seconds(seconds, Some(nanoseconds)))
163                }
164            },
165            Some((hours, s)) => {
166                let hours = hours.parse::<u64>().map_err(|_| HeaderParseError)?;
167                let mut it = s.split(':');
168                let minutes = it
169                    .next()
170                    .and_then(|s| s.parse::<u8>().ok())
171                    .ok_or(HeaderParseError)?;
172                let seconds = it.next().ok_or(HeaderParseError)?;
173
174                if let Some((seconds, subseconds)) = split_once(seconds, '.') {
175                    let seconds = seconds.parse::<u8>().map_err(|_| HeaderParseError)?;
176                    let digits = subseconds.len();
177                    if digits > 9 || digits == 0 {
178                        return Err(HeaderParseError);
179                    }
180                    let subseconds = subseconds.parse::<u32>().map_err(|_| HeaderParseError)?;
181
182                    let nanoseconds = subseconds * u32::pow(10, 9 - digits as u32);
183
184                    Ok(NptTime::Hms(hours, minutes, seconds, Some(nanoseconds)))
185                } else {
186                    let seconds = seconds.parse::<u8>().map_err(|_| HeaderParseError)?;
187
188                    Ok(NptTime::Hms(hours, minutes, seconds, None))
189                }
190            }
191        }
192    }
193}
194
195/// SMPTE-Relative Timecode Range ([RFC 7826 section 4.4.1](https://tools.ietf.org/html/rfc7826#section-4.4.1)).
196#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
197#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
198pub enum SmpteRange {
199    /// Empty range.
200    Empty(SmpteType),
201    /// Range starting at a specific time.
202    From(SmpteType, SmpteTime),
203    /// Range from a specific time to another.
204    FromTo(SmpteType, SmpteTime, SmpteTime),
205    /// Range ending at a specific time.
206    To(SmpteType, SmpteTime),
207}
208
209impl fmt::Display for SmpteRange {
210    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
211        match self {
212            SmpteRange::Empty(ty) => write!(fmt, "{ty}"),
213            SmpteRange::From(ty, f) => write!(fmt, "{ty}={f}-"),
214            SmpteRange::FromTo(ty, f, t) => write!(fmt, "{ty}={f}-{t}"),
215            SmpteRange::To(ty, t) => write!(fmt, "{ty}=-{t}"),
216        }
217    }
218}
219
220impl std::str::FromStr for SmpteRange {
221    type Err = HeaderParseError;
222
223    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
224        if let Some((ty, range)) = split_once(s, '=') {
225            let ty = ty.parse()?;
226
227            if range.is_empty() {
228                return Ok(SmpteRange::Empty(ty));
229            }
230
231            let range = range.strip_prefix('=').ok_or(HeaderParseError)?;
232
233            let (from, to) = split_once(range, '-').ok_or(HeaderParseError)?;
234            let from = if from.is_empty() { None } else { Some(from) };
235            let to = if to.is_empty() { None } else { Some(to) };
236
237            let from = from
238                .map(|s| s.parse::<SmpteTime>().map_err(|_| HeaderParseError))
239                .transpose()?;
240            let to = to
241                .map(|s| s.parse::<SmpteTime>().map_err(|_| HeaderParseError))
242                .transpose()?;
243
244            match (from, to) {
245                (Some(from), Some(to)) => Ok(SmpteRange::FromTo(ty, from, to)),
246                (None, Some(to)) => Ok(SmpteRange::To(ty, to)),
247                (Some(from), None) => Ok(SmpteRange::From(ty, from)),
248                (None, None) => Err(HeaderParseError),
249            }
250        } else {
251            Ok(SmpteRange::Empty(s.parse()?))
252        }
253    }
254}
255
256/// SMPTE-Relative Timecode Type ([RFC 7826 section 4.4.1](https://tools.ietf.org/html/rfc7826#section-4.4.1)).
257#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
258#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
259pub enum SmpteType {
260    /// SMPTE 30 frames per second timecodes.
261    Smpte,
262    /// SMPTE 30 frames per second timecodes with drop frames (29.97 fps).
263    Smpte30Drop,
264    /// SMPTE 25 frames per second timecodes.
265    Smpte25,
266    /// Other SMPTE timecode type.
267    Other(String),
268}
269
270impl fmt::Display for SmpteType {
271    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272        match self {
273            SmpteType::Smpte => f.write_str("smpte"),
274            SmpteType::Smpte30Drop => f.write_str("smpte-30-drop"),
275            SmpteType::Smpte25 => f.write_str("smpte-25"),
276            SmpteType::Other(o) => f.write_str(o),
277        }
278    }
279}
280
281impl std::str::FromStr for SmpteType {
282    type Err = HeaderParseError;
283
284    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
285        let stripped = s.strip_prefix("smpte").ok_or(HeaderParseError)?;
286        match stripped {
287            "" => Ok(SmpteType::Smpte),
288            "-30-drop" => Ok(SmpteType::Smpte30Drop),
289            "-25" => Ok(SmpteType::Smpte25),
290            _ => Ok(SmpteType::Other(s.into())),
291        }
292    }
293}
294
295/// SMPTE-Relative Timecode ([RFC 7826 section 4.4.1](https://tools.ietf.org/html/rfc7826#section-4.4.1)).
296#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
297#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
298pub struct SmpteTime {
299    /// Hours (0-23).
300    pub hours: u8,
301    /// Minutes (0-59).
302    pub minutes: u8,
303    /// Seconds (0-59).
304    pub seconds: u8,
305    /// Frames and subframes (0-framerate, 0-99).
306    pub frames: Option<(u8, Option<u8>)>,
307}
308
309impl fmt::Display for SmpteTime {
310    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311        match self.frames {
312            None => write!(
313                f,
314                "{:02}:{:02}:{:02}",
315                self.hours, self.minutes, self.seconds
316            ),
317            Some((frames, None)) => write!(
318                f,
319                "{:02}:{:02}:{:02}:{:02}",
320                self.hours, self.minutes, self.seconds, frames
321            ),
322            Some((frames, Some(subframes))) => write!(
323                f,
324                "{:02}:{:02}:{:02}:{:02}.{:02}",
325                self.hours, self.minutes, self.seconds, frames, subframes
326            ),
327        }
328    }
329}
330
331impl std::str::FromStr for SmpteTime {
332    type Err = HeaderParseError;
333
334    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
335        let mut s = s.split(':');
336
337        let hours = s
338            .next()
339            .and_then(|s| s.parse::<u8>().ok())
340            .ok_or(HeaderParseError)?;
341        let minutes = s
342            .next()
343            .and_then(|s| s.parse::<u8>().ok())
344            .ok_or(HeaderParseError)?;
345        let seconds = s
346            .next()
347            .and_then(|s| s.parse::<u8>().ok())
348            .ok_or(HeaderParseError)?;
349
350        let frames = match s.next() {
351            Some(frames) => frames,
352            None => {
353                return Ok(SmpteTime {
354                    hours,
355                    minutes,
356                    seconds,
357                    frames: None,
358                })
359            }
360        };
361
362        if s.next().is_some() {
363            return Err(HeaderParseError);
364        }
365
366        if let Some((frames, subframes)) = split_once(frames, '.') {
367            let frames = frames.parse::<u8>().map_err(|_| HeaderParseError)?;
368            let digits = subframes.len();
369
370            let factor = match digits {
371                1 => 10,
372                2 => 1,
373                _ => return Err(HeaderParseError),
374            };
375
376            let subframes = subframes.parse::<u8>().map_err(|_| HeaderParseError)? * factor;
377
378            Ok(SmpteTime {
379                hours,
380                minutes,
381                seconds,
382                frames: Some((frames, Some(subframes))),
383            })
384        } else {
385            let frames = frames.parse::<u8>().map_err(|_| HeaderParseError)?;
386
387            Ok(SmpteTime {
388                hours,
389                minutes,
390                seconds,
391                frames: Some((frames, None)),
392            })
393        }
394    }
395}
396
397/// Absolute Time (UTC) Time Range ([RFC 7826 section 4.4.3](https://tools.ietf.org/html/rfc7826#section-4.4.3)).
398#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
399#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
400pub enum UtcRange {
401    /// Empty range.
402    Empty,
403    /// Range starting at a specific time.
404    From(UtcTime),
405    /// Range from a specific time to another.
406    FromTo(UtcTime, UtcTime),
407    /// Range ending at a specific time.
408    To(UtcTime),
409}
410
411impl fmt::Display for UtcRange {
412    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
413        match self {
414            UtcRange::Empty => fmt.write_str("clock"),
415            UtcRange::From(f) => write!(fmt, "clock={f}-"),
416            UtcRange::FromTo(f, t) => write!(fmt, "clock={f}-{t}"),
417            UtcRange::To(t) => write!(fmt, "clock=-{t}"),
418        }
419    }
420}
421
422impl std::str::FromStr for UtcRange {
423    type Err = HeaderParseError;
424
425    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
426        let s = s.strip_prefix("clock").ok_or(HeaderParseError)?;
427
428        if s.is_empty() {
429            return Ok(UtcRange::Empty);
430        }
431
432        let s = s.strip_prefix('=').ok_or(HeaderParseError)?;
433
434        let (from, to) = split_once(s, '-').ok_or(HeaderParseError)?;
435        let from = if from.is_empty() { None } else { Some(from) };
436        let to = if to.is_empty() { None } else { Some(to) };
437
438        let from = from
439            .map(|s| s.parse::<UtcTime>().map_err(|_| HeaderParseError))
440            .transpose()?;
441        let to = to
442            .map(|s| s.parse::<UtcTime>().map_err(|_| HeaderParseError))
443            .transpose()?;
444
445        match (from, to) {
446            (Some(from), Some(to)) => Ok(UtcRange::FromTo(from, to)),
447            (None, Some(to)) => Ok(UtcRange::To(to)),
448            (Some(from), None) => Ok(UtcRange::From(from)),
449            (None, None) => Err(HeaderParseError),
450        }
451    }
452}
453
454/// Absolute Time (UTC) Time ([RFC 7826 section 4.4.3](https://tools.ietf.org/html/rfc7826#section-4.4.3)).
455#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
456#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
457pub struct UtcTime {
458    /// YYYYMMDD date.
459    pub date: u32,
460    /// HHMMSS time.
461    pub time: u32,
462    /// Nanoseconds.
463    pub nanoseconds: Option<u32>,
464}
465
466impl fmt::Display for UtcTime {
467    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
468        if let Some(ns) = self.nanoseconds {
469            write!(f, "{:08}T{:06}.{:09}Z", self.date, self.time, ns)
470        } else {
471            write!(f, "{:08}T{:06}Z", self.date, self.time)
472        }
473    }
474}
475
476impl std::str::FromStr for UtcTime {
477    type Err = HeaderParseError;
478
479    fn from_str(s: &str) -> Result<Self, HeaderParseError> {
480        let (date, time) = split_once(s, 'T').ok_or(HeaderParseError)?;
481        let time = time.strip_suffix('Z').ok_or(HeaderParseError)?;
482
483        let date = date.parse::<u32>().map_err(|_| HeaderParseError)?;
484        let (time, nanoseconds) = if let Some((time, subseconds)) = split_once(time, '.') {
485            let time = time.parse::<u32>().map_err(|_| HeaderParseError)?;
486            let digits = subseconds.len();
487            if digits > 9 || digits == 0 {
488                return Err(HeaderParseError);
489            }
490            let subseconds = subseconds.parse::<u32>().map_err(|_| HeaderParseError)?;
491
492            let nanoseconds = subseconds * u32::pow(10, 9 - digits as u32);
493
494            (time, Some(nanoseconds))
495        } else {
496            let time = time.parse::<u32>().map_err(|_| HeaderParseError)?;
497
498            (time, None)
499        };
500
501        Ok(UtcTime {
502            date,
503            time,
504            nanoseconds,
505        })
506    }
507}
508
509impl super::TypedHeader for Range {
510    fn from_headers(headers: impl AsRef<Headers>) -> Result<Option<Self>, HeaderParseError> {
511        let headers = headers.as_ref();
512
513        let header = match headers.get(&RANGE) {
514            None => return Ok(None),
515            Some(header) => header,
516        };
517
518        Ok(Some(header.as_str().parse()?))
519    }
520
521    fn insert_into(&self, mut headers: impl AsMut<Headers>) {
522        let headers = headers.as_mut();
523        headers.insert(RANGE, self.to_string());
524    }
525}
526
527#[cfg(test)]
528mod tests {
529    use super::*;
530
531    #[test]
532    fn test_range() {
533        let headers = [
534            ("npt", Range::Npt(NptRange::Empty), None),
535            ("npt=now-", Range::Npt(NptRange::From(NptTime::Now)), None),
536            (
537                "npt=123-456",
538                Range::Npt(NptRange::FromTo(
539                    NptTime::Seconds(123, None),
540                    NptTime::Seconds(456, None),
541                )),
542                None,
543            ),
544            (
545                "npt=-456",
546                Range::Npt(NptRange::To(NptTime::Seconds(456, None))),
547                None,
548            ),
549            (
550                "npt=123-",
551                Range::Npt(NptRange::From(NptTime::Seconds(123, None))),
552                None,
553            ),
554            (
555                "npt=123.5-456.567",
556                Range::Npt(NptRange::FromTo(
557                    NptTime::Seconds(123, Some(500_000_000)),
558                    NptTime::Seconds(456, Some(567_000_000)),
559                )),
560                Some("npt=123.500000000-456.567000000"),
561            ),
562            (
563                "npt=43:53:10-1:17:59.123",
564                Range::Npt(NptRange::FromTo(
565                    NptTime::Hms(43, 53, 10, None),
566                    NptTime::Hms(1, 17, 59, Some(123_000_000)),
567                )),
568                Some("npt=43:53:10-01:17:59.123000000"),
569            ),
570        ];
571
572        for (header, expected, serialized) in &headers {
573            let request = crate::Request::builder(crate::Method::Play, crate::Version::V2_0)
574                .header(crate::headers::RANGE, *header)
575                .empty();
576
577            let range = request
578                .typed_header::<super::Range>()
579                .unwrap_or_else(|_| panic!("couldn't parse {}", header))
580                .unwrap();
581
582            assert_eq!(range, *expected, "{header}");
583
584            let request2 = crate::Request::builder(crate::Method::Play, crate::Version::V2_0)
585                .typed_header(&range)
586                .empty();
587
588            let range = request2.header(&crate::headers::RANGE).unwrap();
589
590            assert_eq!(range, serialized.unwrap_or(header), "{header}");
591        }
592    }
593}