1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-05-23 18:58:19 +00:00
hls_m3u8/src/tags/media_segment/inf.rs
2019-09-06 12:55:00 +02:00

115 lines
3.3 KiB
Rust

use crate::types::{DecimalFloatingPoint, ProtocolVersion, SingleLineString};
use crate::{Error, ErrorKind, Result};
use std::fmt;
use std::str::FromStr;
use std::time::Duration;
use trackable::error::ErrorKindExt;
/// [4.3.2.1. EXTINF]
///
/// [4.3.2.1. EXTINF]: https://tools.ietf.org/html/rfc8216#section-4.3.2.1
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ExtInf {
duration: Duration,
title: Option<SingleLineString>,
}
impl ExtInf {
pub(crate) const PREFIX: &'static str = "#EXTINF:";
/// Makes a new `ExtInf` tag.
pub fn new(duration: Duration) -> Self {
ExtInf {
duration,
title: None,
}
}
/// Makes a new `ExtInf` tag with the given title.
pub fn with_title(duration: Duration, title: SingleLineString) -> Self {
ExtInf {
duration,
title: Some(title),
}
}
/// Returns the duration of the associated media segment.
pub fn duration(&self) -> Duration {
self.duration
}
/// Returns the title of the associated media segment.
pub fn title(&self) -> Option<&SingleLineString> {
self.title.as_ref()
}
/// Returns the protocol compatibility version that this tag requires.
pub fn requires_version(&self) -> ProtocolVersion {
if self.duration.subsec_nanos() == 0 {
ProtocolVersion::V1
} else {
ProtocolVersion::V3
}
}
}
impl fmt::Display for ExtInf {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", Self::PREFIX)?;
let duration = (self.duration.as_secs() as f64)
+ (f64::from(self.duration.subsec_nanos()) / 1_000_000_000.0);
write!(f, "{}", duration)?;
if let Some(ref title) = self.title {
write!(f, ",{}", title)?;
}
Ok(())
}
}
impl FromStr for ExtInf {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
track_assert!(s.starts_with(Self::PREFIX), ErrorKind::InvalidInput);
let mut tokens = s.split_at(Self::PREFIX.len()).1.splitn(2, ',');
let seconds: DecimalFloatingPoint =
may_invalid!(tokens.next().expect("Never fails").parse())?;
let duration = seconds.to_duration();
let title = if let Some(title) = tokens.next() {
Some(track!(SingleLineString::new(title))?)
} else {
None
};
Ok(ExtInf { duration, title })
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn extinf() {
let tag = ExtInf::new(Duration::from_secs(5));
assert_eq!("#EXTINF:5".parse().ok(), Some(tag.clone()));
assert_eq!(tag.to_string(), "#EXTINF:5");
assert_eq!(tag.requires_version(), ProtocolVersion::V1);
let tag = ExtInf::with_title(
Duration::from_secs(5),
SingleLineString::new("foo").unwrap(),
);
assert_eq!("#EXTINF:5,foo".parse().ok(), Some(tag.clone()));
assert_eq!(tag.to_string(), "#EXTINF:5,foo");
assert_eq!(tag.requires_version(), ProtocolVersion::V1);
let tag = ExtInf::new(Duration::from_millis(1234));
assert_eq!("#EXTINF:1.234".parse().ok(), Some(tag.clone()));
assert_eq!(tag.to_string(), "#EXTINF:1.234");
assert_eq!(tag.requires_version(), ProtocolVersion::V3);
}
}