diff --git a/src/line.rs b/src/line.rs index 386cc1a..de07d08 100644 --- a/src/line.rs +++ b/src/line.rs @@ -90,6 +90,8 @@ pub enum Tag { ExtXKey(tags::ExtXKey), ExtXMap(tags::ExtXMap), ExtXProgramDateTime(tags::ExtXProgramDateTime), + ExtXCueOut(tags::ExtXCueOut), + ExtXCueIn(tags::ExtXCueIn), ExtXDateRange(tags::ExtXDateRange), ExtXTargetDuration(tags::ExtXTargetDuration), ExtXMediaSequence(tags::ExtXMediaSequence), @@ -117,6 +119,8 @@ impl fmt::Display for Tag { Tag::ExtXKey(ref t) => t.fmt(f), Tag::ExtXMap(ref t) => t.fmt(f), Tag::ExtXProgramDateTime(ref t) => t.fmt(f), + Tag::ExtXCueOut(ref t) => t.fmt(f), + Tag::ExtXCueIn(ref t) => t.fmt(f), Tag::ExtXDateRange(ref t) => t.fmt(f), Tag::ExtXTargetDuration(ref t) => t.fmt(f), Tag::ExtXMediaSequence(ref t) => t.fmt(f), @@ -154,6 +158,10 @@ impl FromStr for Tag { track!(s.parse().map(Tag::ExtXMap)) } else if s.starts_with(tags::ExtXProgramDateTime::PREFIX) { track!(s.parse().map(Tag::ExtXProgramDateTime)) + } else if s.starts_with(tags::ExtXCueOut::PREFIX) { + track!(s.parse().map(Tag::ExtXCueOut)) + } else if s.starts_with(tags::ExtXCueIn::PREFIX) { + track!(s.parse().map(Tag::ExtXCueIn)) } else if s.starts_with(tags::ExtXTargetDuration::PREFIX) { track!(s.parse().map(Tag::ExtXTargetDuration)) } else if s.starts_with(tags::ExtXDateRange::PREFIX) { diff --git a/src/master_playlist.rs b/src/master_playlist.rs index e96a837..0cc9bb8 100644 --- a/src/master_playlist.rs +++ b/src/master_playlist.rs @@ -329,6 +329,8 @@ impl FromStr for MasterPlaylist { | Tag::ExtXKey(_) | Tag::ExtXMap(_) | Tag::ExtXProgramDateTime(_) + | Tag::ExtXCueOut(_) + | Tag::ExtXCueIn(_) | Tag::ExtXDateRange(_) | Tag::ExtXTargetDuration(_) | Tag::ExtXMediaSequence(_) diff --git a/src/media_playlist.rs b/src/media_playlist.rs index 0d876ed..ae6e241 100644 --- a/src/media_playlist.rs +++ b/src/media_playlist.rs @@ -56,7 +56,6 @@ impl MediaPlaylistBuilder { self.version = Some(version); self } - /// Sets the given tag to the resulting playlist. pub fn tag>(&mut self, tag: T) -> &mut Self { match tag.into() { @@ -365,6 +364,14 @@ impl MediaPlaylistOptions { has_partial_segment = true; segment.tag(t); } + Tag::ExtXCueOut(t) => { + has_partial_segment = true; + segment.tag(t); + } + Tag::ExtXCueIn(t) => { + has_partial_segment = true; + segment.tag(t); + } Tag::ExtXDateRange(t) => { has_partial_segment = true; segment.tag(t); diff --git a/src/media_segment.rs b/src/media_segment.rs index c3012de..fe3ff58 100644 --- a/src/media_segment.rs +++ b/src/media_segment.rs @@ -2,8 +2,8 @@ use std::fmt; use std::iter; use tags::{ - ExtInf, ExtXByteRange, ExtXDateRange, ExtXDiscontinuity, ExtXKey, ExtXMap, ExtXProgramDateTime, - MediaSegmentTag, + ExtInf, ExtXByteRange, ExtXCueIn, ExtXCueOut, ExtXDateRange, ExtXDiscontinuity, ExtXKey, + ExtXMap, ExtXProgramDateTime, MediaSegmentTag, }; use types::{ProtocolVersion, SingleLineString}; use {ErrorKind, Result}; @@ -17,6 +17,8 @@ pub struct MediaSegmentBuilder { date_range_tag: Option, discontinuity_tag: Option, program_date_time_tag: Option, + cue_out_tag: Option, + cue_in_tag: Option, inf_tag: Option, uri: Option, } @@ -30,6 +32,8 @@ impl MediaSegmentBuilder { date_range_tag: None, discontinuity_tag: None, program_date_time_tag: None, + cue_out_tag: None, + cue_in_tag: None, inf_tag: None, uri: None, } @@ -51,6 +55,8 @@ impl MediaSegmentBuilder { MediaSegmentTag::ExtXKey(t) => self.key_tags.push(t), MediaSegmentTag::ExtXMap(t) => self.map_tag = Some(t), MediaSegmentTag::ExtXProgramDateTime(t) => self.program_date_time_tag = Some(t), + MediaSegmentTag::ExtXCueOut(t) => self.cue_out_tag = Some(t), + MediaSegmentTag::ExtXCueIn(t) => self.cue_in_tag = Some(t), } self } @@ -66,6 +72,8 @@ impl MediaSegmentBuilder { date_range_tag: self.date_range_tag, discontinuity_tag: self.discontinuity_tag, program_date_time_tag: self.program_date_time_tag, + cue_out_tag: self.cue_out_tag, + cue_in_tag: self.cue_in_tag, inf_tag, uri, }) @@ -86,6 +94,8 @@ pub struct MediaSegment { date_range_tag: Option, discontinuity_tag: Option, program_date_time_tag: Option, + cue_out_tag: Option, + cue_in_tag: Option, inf_tag: ExtInf, uri: SingleLineString, } @@ -109,6 +119,12 @@ impl fmt::Display for MediaSegment { if let Some(ref t) = self.program_date_time_tag { writeln!(f, "{}", t)?; } + if let Some(ref t) = self.cue_out_tag { + writeln!(f, "{}", t)?; + } + if let Some(ref t) = self.cue_in_tag { + writeln!(f, "{}", t)?; + } writeln!(f, "{}", self.inf_tag)?; writeln!(f, "{}", self.uri)?; Ok(()) @@ -145,6 +161,14 @@ impl MediaSegment { self.program_date_time_tag.as_ref() } + /// Returns the `EXT-X-CUE-OUT` tag associated with the media segment. + pub fn cue_out_tag(&self) -> Option<&ExtXCueOut> { + self.cue_out_tag.as_ref() + } + /// Returns the `EXT-X-CUE-IN` tag associated with the media segment. + pub fn cue_in_tag(&self) -> Option<&ExtXCueIn> { + self.cue_in_tag.as_ref() + } /// Returns the `EXT-X-MAP` tag associated with the media segment. pub fn map_tag(&self) -> Option<&ExtXMap> { self.map_tag.as_ref() diff --git a/src/tags/media_segment.rs b/src/tags/media_segment.rs index 32b7fb6..c67138f 100644 --- a/src/tags/media_segment.rs +++ b/src/tags/media_segment.rs @@ -339,6 +339,71 @@ impl FromStr for ExtXProgramDateTime { } } +/// [EXT-X-CUE-OUT] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExtXCueOut { + ///seconds in ad pod + duration: Duration, +} +impl ExtXCueOut { + pub(crate) const PREFIX: &'static str = "#EXT-X-CUE-OUT:"; + + /// Makes a new `ExtXCueOut` tag. + pub fn new(duration: Duration) -> Self { + let duration = Duration::from_secs(duration.as_secs()); + ExtXCueOut { duration } + } + + /// Returns the date-time of the first sample of the associated media segment. + pub fn duration(&self) -> &Duration { + &self.duration + } + + /// Returns the protocol compatibility version that this tag requires. + pub fn requires_version(&self) -> ProtocolVersion { + ProtocolVersion::V1 + } +} +impl fmt::Display for ExtXCueOut { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}{}", Self::PREFIX, self.duration.as_secs()) + } +} +impl FromStr for ExtXCueOut { + type Err = Error; + fn from_str(s: &str) -> Result { + track_assert!(s.starts_with(Self::PREFIX), ErrorKind::InvalidInput); + let duration = may_invalid!(s.split_at(Self::PREFIX.len()).1.parse())?; + Ok(ExtXCueOut { + duration: Duration::from_secs(duration), + }) + } +} +///CUE-IN +/// + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ExtXCueIn; +impl ExtXCueIn { + pub(crate) const PREFIX: &'static str = "#EXT-X-CUE-IN"; + + /// Returns the protocol compatibility version that this tag requires. + pub fn requires_version(self) -> ProtocolVersion { + ProtocolVersion::V1 + } +} +impl fmt::Display for ExtXCueIn { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + Self::PREFIX.fmt(f) + } +} +impl FromStr for ExtXCueIn { + type Err = Error; + fn from_str(s: &str) -> Result { + track_assert_eq!(s, Self::PREFIX, ErrorKind::InvalidInput); + Ok(ExtXCueIn) + } +} /// [4.3.2.7. EXT-X-DATERANGE] /// /// [4.3.2.7. EXT-X-DATERANGE]: https://tools.ietf.org/html/rfc8216#section-4.3.2.7 @@ -439,7 +504,10 @@ impl FromStr for ExtXDateRange { planned_duration = Some(seconds.to_duration()); } "SCTE35-CMD" => scte35_cmd = Some(track!(value.parse())?), - "SCTE35-OUT" => scte35_out = Some(track!(value.parse())?), + "SCTE35-OUT" => { + scte35_out = Some(QuotedString::new(value.to_string())?); + // scte35_out = Some(track!(value.parse())?), + } "SCTE35-IN" => scte35_in = Some(track!(value.parse())?), "END-ON-NEXT" => { track_assert_eq!(value, "YES", ErrorKind::InvalidInput); @@ -602,7 +670,6 @@ mod test { assert_eq!(tag.to_string(), text); assert_eq!(tag.requires_version(), ProtocolVersion::V6); } - #[test] fn ext_x_program_date_time() { let text = "#EXT-X-PROGRAM-DATE-TIME:2010-02-19T14:54:23.031+08:00"; @@ -612,4 +679,20 @@ mod test { assert_eq!(tag.to_string(), text); assert_eq!(tag.requires_version(), ProtocolVersion::V1); } + #[test] + fn ext_x_cue_out() { + let tag = ExtXCueOut::new(Duration::from_secs(24)); + let text = "#EXT-X-CUE-OUT:24"; + assert_eq!(text.parse().ok(), Some(tag)); + assert_eq!(tag.to_string(), text); + assert_eq!(tag.requires_version(), ProtocolVersion::V1); + } + #[test] + fn ext_x_cue_in() { + let tag = ExtXCueIn::new(); + let text = "#EXT-X-CUE-OUT"; + assert_eq!(text.parse().ok(), Some(tag)); + assert_eq!(tag.to_string(), text); + assert_eq!(tag.requires_version(), ProtocolVersion::V1); + } } diff --git a/src/tags/mod.rs b/src/tags/mod.rs index 6b28f8b..9008b8b 100644 --- a/src/tags/mod.rs +++ b/src/tags/mod.rs @@ -31,7 +31,8 @@ pub use self::media_playlist::{ ExtXTargetDuration, }; pub use self::media_segment::{ - ExtInf, ExtXByteRange, ExtXDateRange, ExtXDiscontinuity, ExtXKey, ExtXMap, ExtXProgramDateTime, + ExtInf, ExtXByteRange, ExtXCueIn, ExtXCueOut, ExtXDateRange, ExtXDiscontinuity, ExtXKey, + ExtXMap, ExtXProgramDateTime, }; mod basic; @@ -107,6 +108,8 @@ pub enum MediaSegmentTag { ExtXKey(ExtXKey), ExtXMap(ExtXMap), ExtXProgramDateTime(ExtXProgramDateTime), + ExtXCueOut(ExtXCueOut), + ExtXCueIn(ExtXCueIn), } impl_from!(MediaSegmentTag, ExtInf); impl_from!(MediaSegmentTag, ExtXByteRange); @@ -115,6 +118,8 @@ impl_from!(MediaSegmentTag, ExtXDiscontinuity); impl_from!(MediaSegmentTag, ExtXKey); impl_from!(MediaSegmentTag, ExtXMap); impl_from!(MediaSegmentTag, ExtXProgramDateTime); +impl_from!(MediaSegmentTag, ExtXCueOut); +impl_from!(MediaSegmentTag, ExtXCueIn); fn parse_yes_or_no(s: &str) -> Result { match s {