From c7419c864fc076bb39df44e44aeddc1db75a34b1 Mon Sep 17 00:00:00 2001 From: Luro02 <24826124+Luro02@users.noreply.github.com> Date: Mon, 24 Feb 2020 14:09:26 +0100 Subject: [PATCH] improve StreamData --- src/master_playlist.rs | 20 ++++---- src/types/codecs.rs | 99 ++++++++++++++++++++++++++++++++++++++++ src/types/mod.rs | 2 + src/types/stream_data.rs | 37 +++++++++------ tests/rfc8216.rs | 18 ++++---- 5 files changed, 142 insertions(+), 34 deletions(-) create mode 100644 src/types/codecs.rs diff --git a/src/master_playlist.rs b/src/master_playlist.rs index 37d79c4..1376e10 100644 --- a/src/master_playlist.rs +++ b/src/master_playlist.rs @@ -418,7 +418,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(150000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((416, 234)) .build() .unwrap() @@ -431,7 +431,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(240000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((416, 234)) .build() .unwrap() @@ -444,7 +444,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(440000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((416, 234)) .build() .unwrap() @@ -457,7 +457,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(640000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((640, 360)) .build() .unwrap() @@ -470,7 +470,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(64000) - .codecs("mp4a.40.5") + .codecs(&["mp4a.40.5"]) .build() .unwrap() }, @@ -493,7 +493,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(150000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((416, 234)) .build() .unwrap() @@ -506,7 +506,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(240000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((416, 234)) .build() .unwrap() @@ -519,7 +519,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(440000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((416, 234)) .build() .unwrap() @@ -532,7 +532,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(640000) - .codecs("avc1.42e00a,mp4a.40.2") + .codecs(&["avc1.42e00a", "mp4a.40.2"]) .resolution((640, 360)) .build() .unwrap() @@ -545,7 +545,7 @@ mod tests { closed_captions: None, stream_data: StreamData::builder() .bandwidth(64000) - .codecs("mp4a.40.5") + .codecs(&["mp4a.40.5"]) .build() .unwrap() }, diff --git a/src/types/codecs.rs b/src/types/codecs.rs new file mode 100644 index 0000000..f9cf17f --- /dev/null +++ b/src/types/codecs.rs @@ -0,0 +1,99 @@ +use core::fmt; +use core::str::FromStr; + +use derive_more::{AsMut, AsRef, Deref, DerefMut}; + +use crate::Error; + +/// A list of formats, where each format specifies a media sample type that is +/// present in one or more renditions specified by the [`VariantStream`]. +/// +/// Valid format identifiers are those in the ISO Base Media File Format Name +/// Space defined by "The 'Codecs' and 'Profiles' Parameters for "Bucket" Media +/// Types" ([RFC6381]). +/// +/// For example, a stream containing AAC low complexity (AAC-LC) audio and H.264 +/// Main Profile Level 3.0 video would be +/// +/// ``` +/// # use hls_m3u8::types::Codecs; +/// let codecs = Codecs::from(&["mp4a.40.2", "avc1.4d401e"]); +/// ``` +/// +/// [RFC6381]: https://tools.ietf.org/html/rfc6381 +/// [`VariantStream`]: crate::tags::VariantStream +#[derive(AsMut, AsRef, Deref, DerefMut, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Codecs { + list: Vec, +} + +impl Codecs { + /// Makes a new (empty) [`Codecs`] struct. + /// + /// # Example + /// + /// ``` + /// # use hls_m3u8::types::Codecs; + /// let codecs = Codecs::new(); + /// ``` + #[inline] + #[must_use] + pub fn new() -> Self { Self { list: Vec::new() } } +} + +impl fmt::Display for Codecs { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if let Some(codec) = self.list.iter().next() { + write!(f, "{}", codec)?; + + for codec in self.list.iter().skip(1) { + write!(f, ",{}", codec)?; + } + } + + Ok(()) + } +} +impl FromStr for Codecs { + type Err = Error; + + fn from_str(input: &str) -> Result { + Ok(Self { + list: input.split(',').map(|s| s.into()).collect(), + }) + } +} + +impl, I: IntoIterator> From for Codecs { + fn from(value: I) -> Self { + Self { + list: value.into_iter().map(|s| s.as_ref().to_string()).collect(), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_from() { + assert_eq!(Codecs::from(Vec::<&str>::new()), Codecs::new()); + } + + #[test] + fn test_display() { + assert_eq!( + Codecs::from(vec!["mp4a.40.2", "avc1.4d401e"]).to_string(), + "mp4a.40.2,avc1.4d401e".to_string() + ); + } + + #[test] + fn test_parser() { + assert_eq!( + Codecs::from_str("mp4a.40.2,avc1.4d401e").unwrap(), + Codecs::from(vec!["mp4a.40.2", "avc1.4d401e"]) + ); + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs index 5bb0b04..65ffcf4 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -2,6 +2,7 @@ mod byte_range; mod channels; mod closed_captions; +mod codecs; mod encryption_method; mod hdcp_level; mod in_stream_id; @@ -19,6 +20,7 @@ mod ufloat; pub use byte_range::*; pub use channels::*; pub use closed_captions::*; +pub use codecs::*; pub use encryption_method::*; pub use hdcp_level::*; pub use in_stream_id::*; diff --git a/src/types/stream_data.rs b/src/types/stream_data.rs index f8f03e0..624fff9 100644 --- a/src/types/stream_data.rs +++ b/src/types/stream_data.rs @@ -5,7 +5,7 @@ use derive_builder::Builder; use shorthand::ShortHand; use crate::attribute::AttributePairs; -use crate::types::{HdcpLevel, ProtocolVersion, Resolution}; +use crate::types::{Codecs, HdcpLevel, ProtocolVersion, Resolution}; use crate::utils::{quote, unquote}; use crate::{Error, RequiredVersion}; @@ -93,27 +93,34 @@ pub struct StreamData { #[builder(default)] #[shorthand(enable(copy), disable(into, option_as_ref))] average_bandwidth: Option, - /// A string that represents a list of formats, where each format specifies - /// a media sample type that is present in one or more renditions specified - /// by the [`VariantStream`]. + /// A list of formats, where each format specifies a media sample type that + /// is present in one or more renditions specified by the [`VariantStream`]. /// /// Valid format identifiers are those in the ISO Base Media File Format /// Name Space defined by "The 'Codecs' and 'Profiles' Parameters for - /// "Bucket" Media Types" [RFC6381]. + /// "Bucket" Media Types" ([RFC6381]). /// /// For example, a stream containing AAC low complexity (AAC-LC) audio and - /// H.264 Main Profile Level 3.0 video would have a codecs value of - /// "mp4a.40.2,avc1.4d401e". + /// H.264 Main Profile Level 3.0 video would be + /// + /// ``` + /// # use hls_m3u8::types::Codecs; + /// let codecs = Codecs::from(&["mp4a.40.2", "avc1.4d401e"]); + /// ``` /// /// # Example /// /// ``` /// # use hls_m3u8::types::StreamData; - /// # + /// use hls_m3u8::types::Codecs; + /// /// let mut stream = StreamData::new(20); /// - /// stream.set_codecs(Some("mp4a.40.2,avc1.4d401e")); - /// assert_eq!(stream.codecs(), Some(&"mp4a.40.2,avc1.4d401e".to_string())); + /// stream.set_codecs(Some(&["mp4a.40.2", "avc1.4d401e"])); + /// assert_eq!( + /// stream.codecs(), + /// Some(&Codecs::from(&["mp4a.40.2", "avc1.4d401e"])) + /// ); /// ``` /// /// # Note @@ -126,7 +133,7 @@ pub struct StreamData { /// crate::tags::VariantStream::ExtXStreamInf /// [RFC6381]: https://tools.ietf.org/html/rfc6381 #[builder(default, setter(into))] - codecs: Option, + codecs: Option, /// The resolution of the stream. /// /// # Example @@ -237,7 +244,7 @@ impl StreamData { /// StreamData::builder() /// .bandwidth(200) /// .average_bandwidth(15) - /// .codecs("mp4a.40.2,avc1.4d401e") + /// .codecs(&["mp4a.40.2", "avc1.4d401e"]) /// .resolution((1920, 1080)) /// .hdcp_level(HdcpLevel::Type0) /// .video("video_01") @@ -297,7 +304,7 @@ impl FromStr for StreamData { .map_err(|e| Error::parse_int(value, e))?, ) } - "CODECS" => codecs = Some(unquote(value)), + "CODECS" => codecs = Some(unquote(value).parse()?), "RESOLUTION" => resolution = Some(value.parse()?), "HDCP-LEVEL" => { hdcp_level = Some(value.parse::().map_err(Error::strum)?) @@ -346,7 +353,7 @@ mod tests { fn test_display() { let mut stream_data = StreamData::new(200); stream_data.set_average_bandwidth(Some(15)); - stream_data.set_codecs(Some("mp4a.40.2,avc1.4d401e")); + stream_data.set_codecs(Some(&["mp4a.40.2", "avc1.4d401e"])); stream_data.set_resolution(Some((1920, 1080))); stream_data.set_hdcp_level(Some(HdcpLevel::Type0)); stream_data.set_video(Some("video")); @@ -369,7 +376,7 @@ mod tests { fn test_parser() { let mut stream_data = StreamData::new(200); stream_data.set_average_bandwidth(Some(15)); - stream_data.set_codecs(Some("mp4a.40.2,avc1.4d401e")); + stream_data.set_codecs(Some(&["mp4a.40.2", "avc1.4d401e"])); stream_data.set_resolution(Some((1920, 1080))); stream_data.set_hdcp_level(Some(HdcpLevel::Type0)); stream_data.set_video(Some("video")); diff --git a/tests/rfc8216.rs b/tests/rfc8216.rs index 5390f55..707329a 100644 --- a/tests/rfc8216.rs +++ b/tests/rfc8216.rs @@ -214,7 +214,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(65000) - .codecs("mp4a.40.5") + .codecs(&["mp4a.40.5"]) .build() .unwrap() }, @@ -280,7 +280,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(65000) - .codecs("mp4a.40.5") + .codecs(&["mp4a.40.5"]) .build() .unwrap() }, @@ -345,7 +345,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(1280000) - .codecs("...") + .codecs(&["..."]) .build() .unwrap() }, @@ -357,7 +357,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(2560000) - .codecs("...") + .codecs(&["..."]) .build() .unwrap() }, @@ -369,7 +369,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(7680000) - .codecs("...") + .codecs(&["..."]) .build() .unwrap() }, @@ -381,7 +381,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(65000) - .codecs("mp4a.40.5") + .codecs(&["mp4a.40.5"]) .build() .unwrap() }, @@ -518,7 +518,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(1280000) - .codecs("...") + .codecs(&["..."]) .video("low") .build() .unwrap() @@ -531,7 +531,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(2560000) - .codecs("...") + .codecs(&["..."]) .video("mid") .build() .unwrap() @@ -544,7 +544,7 @@ generate_tests! [ closed_captions: None, stream_data: StreamData::builder() .bandwidth(7680000) - .codecs("...") + .codecs(&["..."]) .video("hi") .build() .unwrap()