mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-11-25 08:31:00 +00:00
parent
6b717f97c2
commit
93283f61f1
33 changed files with 666 additions and 266 deletions
|
@ -90,11 +90,11 @@ fn split(value: &str, terminator: char) -> Vec<String> {
|
|||
temp_string.push(c);
|
||||
}
|
||||
k if (k == terminator) => {
|
||||
if !inside_quotes {
|
||||
if inside_quotes {
|
||||
temp_string.push(c);
|
||||
} else {
|
||||
result.push(temp_string);
|
||||
temp_string = String::new();
|
||||
} else {
|
||||
temp_string.push(c);
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
|
10
src/error.rs
10
src/error.rs
|
@ -5,7 +5,7 @@ use failure::{Backtrace, Context, Fail};
|
|||
/// This crate specific `Result` type.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
/// The ErrorKind.
|
||||
/// The [`ErrorKind`].
|
||||
#[derive(Debug, Fail, Clone, PartialEq, Eq)]
|
||||
pub enum ErrorKind {
|
||||
#[fail(display = "ChronoParseError: {}", _0)]
|
||||
|
@ -72,6 +72,10 @@ pub enum ErrorKind {
|
|||
/// An attribute is missing.
|
||||
MissingAttribute(String),
|
||||
|
||||
#[fail(display = "Unexpected Attribute: {:?}", _0)]
|
||||
/// An unexpected value.
|
||||
UnexpectedAttribute(String),
|
||||
|
||||
/// Hints that destructuring should not be exhaustive.
|
||||
///
|
||||
/// This enum may grow additional variants, so this makes sure clients
|
||||
|
@ -121,6 +125,10 @@ impl Error {
|
|||
Self::from(ErrorKind::MissingValue(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn unexpected_attribute<T: ToString>(value: T) -> Self {
|
||||
Self::from(ErrorKind::UnexpectedAttribute(value.to_string()))
|
||||
}
|
||||
|
||||
pub(crate) fn invalid_input() -> Self {
|
||||
Self::from(ErrorKind::InvalidInput)
|
||||
}
|
||||
|
|
92
src/line.rs
92
src/line.rs
|
@ -154,53 +154,53 @@ impl fmt::Display for Tag {
|
|||
impl FromStr for Tag {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if s.starts_with(tags::ExtM3u::PREFIX) {
|
||||
s.parse().map(Tag::ExtM3u)
|
||||
} else if s.starts_with(tags::ExtXVersion::PREFIX) {
|
||||
s.parse().map(Tag::ExtXVersion)
|
||||
} else if s.starts_with(tags::ExtInf::PREFIX) {
|
||||
s.parse().map(Tag::ExtInf)
|
||||
} else if s.starts_with(tags::ExtXByteRange::PREFIX) {
|
||||
s.parse().map(Tag::ExtXByteRange)
|
||||
} else if s.starts_with(tags::ExtXDiscontinuity::PREFIX) {
|
||||
s.parse().map(Tag::ExtXDiscontinuity)
|
||||
} else if s.starts_with(tags::ExtXKey::PREFIX) {
|
||||
s.parse().map(Tag::ExtXKey)
|
||||
} else if s.starts_with(tags::ExtXMap::PREFIX) {
|
||||
s.parse().map(Tag::ExtXMap)
|
||||
} else if s.starts_with(tags::ExtXProgramDateTime::PREFIX) {
|
||||
s.parse().map(Tag::ExtXProgramDateTime)
|
||||
} else if s.starts_with(tags::ExtXTargetDuration::PREFIX) {
|
||||
s.parse().map(Tag::ExtXTargetDuration)
|
||||
} else if s.starts_with(tags::ExtXDateRange::PREFIX) {
|
||||
s.parse().map(Tag::ExtXDateRange)
|
||||
} else if s.starts_with(tags::ExtXMediaSequence::PREFIX) {
|
||||
s.parse().map(Tag::ExtXMediaSequence)
|
||||
} else if s.starts_with(tags::ExtXDiscontinuitySequence::PREFIX) {
|
||||
s.parse().map(Tag::ExtXDiscontinuitySequence)
|
||||
} else if s.starts_with(tags::ExtXEndList::PREFIX) {
|
||||
s.parse().map(Tag::ExtXEndList)
|
||||
} else if s.starts_with(tags::ExtXPlaylistType::PREFIX) {
|
||||
s.parse().map(Tag::ExtXPlaylistType)
|
||||
} else if s.starts_with(tags::ExtXIFramesOnly::PREFIX) {
|
||||
s.parse().map(Tag::ExtXIFramesOnly)
|
||||
} else if s.starts_with(tags::ExtXMedia::PREFIX) {
|
||||
s.parse().map(Tag::ExtXMedia)
|
||||
} else if s.starts_with(tags::ExtXStreamInf::PREFIX) {
|
||||
s.parse().map(Tag::ExtXStreamInf)
|
||||
} else if s.starts_with(tags::ExtXIFrameStreamInf::PREFIX) {
|
||||
s.parse().map(Tag::ExtXIFrameStreamInf)
|
||||
} else if s.starts_with(tags::ExtXSessionData::PREFIX) {
|
||||
s.parse().map(Tag::ExtXSessionData)
|
||||
} else if s.starts_with(tags::ExtXSessionKey::PREFIX) {
|
||||
s.parse().map(Tag::ExtXSessionKey)
|
||||
} else if s.starts_with(tags::ExtXIndependentSegments::PREFIX) {
|
||||
s.parse().map(Tag::ExtXIndependentSegments)
|
||||
} else if s.starts_with(tags::ExtXStart::PREFIX) {
|
||||
s.parse().map(Tag::ExtXStart)
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
if input.starts_with(tags::ExtM3u::PREFIX) {
|
||||
input.parse().map(Tag::ExtM3u)
|
||||
} else if input.starts_with(tags::ExtXVersion::PREFIX) {
|
||||
input.parse().map(Tag::ExtXVersion)
|
||||
} else if input.starts_with(tags::ExtInf::PREFIX) {
|
||||
input.parse().map(Tag::ExtInf)
|
||||
} else if input.starts_with(tags::ExtXByteRange::PREFIX) {
|
||||
input.parse().map(Tag::ExtXByteRange)
|
||||
} else if input.starts_with(tags::ExtXDiscontinuity::PREFIX) {
|
||||
input.parse().map(Tag::ExtXDiscontinuity)
|
||||
} else if input.starts_with(tags::ExtXKey::PREFIX) {
|
||||
input.parse().map(Tag::ExtXKey)
|
||||
} else if input.starts_with(tags::ExtXMap::PREFIX) {
|
||||
input.parse().map(Tag::ExtXMap)
|
||||
} else if input.starts_with(tags::ExtXProgramDateTime::PREFIX) {
|
||||
input.parse().map(Tag::ExtXProgramDateTime)
|
||||
} else if input.starts_with(tags::ExtXTargetDuration::PREFIX) {
|
||||
input.parse().map(Tag::ExtXTargetDuration)
|
||||
} else if input.starts_with(tags::ExtXDateRange::PREFIX) {
|
||||
input.parse().map(Tag::ExtXDateRange)
|
||||
} else if input.starts_with(tags::ExtXMediaSequence::PREFIX) {
|
||||
input.parse().map(Tag::ExtXMediaSequence)
|
||||
} else if input.starts_with(tags::ExtXDiscontinuitySequence::PREFIX) {
|
||||
input.parse().map(Tag::ExtXDiscontinuitySequence)
|
||||
} else if input.starts_with(tags::ExtXEndList::PREFIX) {
|
||||
input.parse().map(Tag::ExtXEndList)
|
||||
} else if input.starts_with(tags::ExtXPlaylistType::PREFIX) {
|
||||
input.parse().map(Tag::ExtXPlaylistType)
|
||||
} else if input.starts_with(tags::ExtXIFramesOnly::PREFIX) {
|
||||
input.parse().map(Tag::ExtXIFramesOnly)
|
||||
} else if input.starts_with(tags::ExtXMedia::PREFIX) {
|
||||
input.parse().map(Tag::ExtXMedia).map_err(Error::custom)
|
||||
} else if input.starts_with(tags::ExtXStreamInf::PREFIX) {
|
||||
input.parse().map(Tag::ExtXStreamInf)
|
||||
} else if input.starts_with(tags::ExtXIFrameStreamInf::PREFIX) {
|
||||
input.parse().map(Tag::ExtXIFrameStreamInf)
|
||||
} else if input.starts_with(tags::ExtXSessionData::PREFIX) {
|
||||
input.parse().map(Tag::ExtXSessionData)
|
||||
} else if input.starts_with(tags::ExtXSessionKey::PREFIX) {
|
||||
input.parse().map(Tag::ExtXSessionKey)
|
||||
} else if input.starts_with(tags::ExtXIndependentSegments::PREFIX) {
|
||||
input.parse().map(Tag::ExtXIndependentSegments)
|
||||
} else if input.starts_with(tags::ExtXStart::PREFIX) {
|
||||
input.parse().map(Tag::ExtXStart)
|
||||
} else {
|
||||
Ok(Tag::Unknown(s.to_string()))
|
||||
Ok(Tag::Unknown(input.to_string()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,17 +5,35 @@ use crate::types::{ProtocolVersion, RequiredVersion};
|
|||
use crate::utils::tag;
|
||||
use crate::Error;
|
||||
|
||||
/// # [4.3.1.1. EXTM3U]
|
||||
/// The [ExtM3u] tag indicates that the file is an Extended [M3U]
|
||||
/// # [4.4.1.1. EXTM3U]
|
||||
/// The [`ExtM3u`] tag indicates that the file is an **Ext**ended **[`M3U`]**
|
||||
/// Playlist file.
|
||||
/// It is the at the start of every [`Media Playlist`] and [`Master Playlist`].
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
/// #EXTM3U
|
||||
/// # Examples
|
||||
/// Parsing from a [`str`]:
|
||||
/// ```
|
||||
/// # use failure::Error;
|
||||
/// # use hls_m3u8::tags::ExtM3u;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// assert_eq!("#EXTM3U".parse::<ExtM3u>()?, ExtM3u);
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
/// Converting to a [`str`]:
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtM3u;
|
||||
/// #
|
||||
/// assert_eq!("#EXTM3U".to_string(), ExtM3u.to_string());
|
||||
/// ```
|
||||
///
|
||||
/// [M3U]: https://en.wikipedia.org/wiki/M3U
|
||||
/// [4.3.1.1. EXTM3U]: https://tools.ietf.org/html/rfc8216#section-4.3.1.1
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [`Master Playlist`]: crate::MasterPlaylist
|
||||
/// [`M3U`]: https://en.wikipedia.org/wiki/M3U
|
||||
/// [4.4.1.1. EXTM3U]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-4.4.1.1
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
||||
pub struct ExtM3u;
|
||||
|
||||
|
@ -23,6 +41,7 @@ impl ExtM3u {
|
|||
pub(crate) const PREFIX: &'static str = "#EXTM3U";
|
||||
}
|
||||
|
||||
/// This tag requires [`ProtocolVersion::V1`].
|
||||
impl RequiredVersion for ExtM3u {
|
||||
fn required_version(&self) -> ProtocolVersion {
|
||||
ProtocolVersion::V1
|
||||
|
@ -31,7 +50,7 @@ impl RequiredVersion for ExtM3u {
|
|||
|
||||
impl fmt::Display for ExtM3u {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
Self::PREFIX.fmt(f)
|
||||
write!(f, "{}", Self::PREFIX)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,7 +59,7 @@ impl FromStr for ExtM3u {
|
|||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
tag(input, Self::PREFIX)?;
|
||||
Ok(ExtM3u)
|
||||
Ok(Self)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,40 +5,64 @@ use crate::types::{ProtocolVersion, RequiredVersion};
|
|||
use crate::utils::tag;
|
||||
use crate::Error;
|
||||
|
||||
/// # [4.3.1.2. EXT-X-VERSION]
|
||||
/// The [ExtXVersion] tag indicates the compatibility version of the
|
||||
/// Playlist file, its associated media, and its server.
|
||||
/// # [4.4.1.2. EXT-X-VERSION]
|
||||
/// The [`ExtXVersion`] tag indicates the compatibility version of the
|
||||
/// [`Master Playlist`] or [`Media Playlist`] file.
|
||||
/// It applies to the entire Playlist.
|
||||
///
|
||||
/// The [ExtXVersion] tag applies to the entire Playlist file. Its
|
||||
/// format is:
|
||||
///
|
||||
/// ```text
|
||||
/// #EXT-X-VERSION:<n>
|
||||
/// # Examples
|
||||
/// Parsing from a [`str`]:
|
||||
/// ```
|
||||
/// where `n` is an integer indicating the protocol compatibility version
|
||||
/// number.
|
||||
/// # use failure::Error;
|
||||
/// # use hls_m3u8::tags::ExtXVersion;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Error> {
|
||||
/// use hls_m3u8::types::ProtocolVersion;
|
||||
///
|
||||
/// [4.3.1.2. EXT-X-VERSION]: https://tools.ietf.org/html/rfc8216#section-4.3.1.2
|
||||
/// assert_eq!(
|
||||
/// "#EXT-X-VERSION:5".parse::<ExtXVersion>()?,
|
||||
/// ExtXVersion::new(ProtocolVersion::V5)
|
||||
/// );
|
||||
/// #
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
/// Converting to a [`str`]:
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXVersion;
|
||||
/// #
|
||||
/// use hls_m3u8::types::ProtocolVersion;
|
||||
///
|
||||
/// assert_eq!(
|
||||
/// "#EXT-X-VERSION:5".to_string(),
|
||||
/// ExtXVersion::new(ProtocolVersion::V5).to_string()
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [`Master Playlist`]: crate::MasterPlaylist
|
||||
/// [4.4.1.2. EXT-X-VERSION]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-4.4.1.2
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
pub struct ExtXVersion(ProtocolVersion);
|
||||
|
||||
impl ExtXVersion {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-VERSION:";
|
||||
|
||||
/// Makes a new [ExtXVersion] tag.
|
||||
/// Makes a new [`ExtXVersion`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXVersion;
|
||||
/// use hls_m3u8::types::ProtocolVersion;
|
||||
///
|
||||
/// let version_tag = ExtXVersion::new(ProtocolVersion::V2);
|
||||
/// let version = ExtXVersion::new(ProtocolVersion::V2);
|
||||
/// ```
|
||||
pub const fn new(version: ProtocolVersion) -> Self {
|
||||
Self(version)
|
||||
}
|
||||
|
||||
/// Returns the protocol compatibility version of the playlist, containing this tag.
|
||||
/// Returns the [`ProtocolVersion`] of the playlist, containing this tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -55,6 +79,7 @@ impl ExtXVersion {
|
|||
}
|
||||
}
|
||||
|
||||
/// This tag requires [`ProtocolVersion::V1`].
|
||||
impl RequiredVersion for ExtXVersion {
|
||||
fn required_version(&self) -> ProtocolVersion {
|
||||
ProtocolVersion::V1
|
||||
|
@ -84,7 +109,7 @@ impl FromStr for ExtXVersion {
|
|||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let version = tag(input, Self::PREFIX)?.parse()?;
|
||||
Ok(ExtXVersion::new(version))
|
||||
Ok(Self::new(version))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,4 +140,12 @@ mod test {
|
|||
ProtocolVersion::V1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_default_and_from() {
|
||||
assert_eq!(
|
||||
ExtXVersion::default(),
|
||||
ExtXVersion::from(ProtocolVersion::V1)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,20 +7,17 @@ use crate::types::{ProtocolVersion, RequiredVersion, StreamInf};
|
|||
use crate::utils::{quote, tag, unquote};
|
||||
use crate::Error;
|
||||
|
||||
/// # [4.3.4.3. EXT-X-I-FRAME-STREAM-INF]
|
||||
/// The [ExtXIFrameStreamInf] tag identifies a [Media Playlist] file
|
||||
/// containing the I-frames of a multimedia presentation. It stands
|
||||
/// alone, in that it does not apply to a particular `URI` in the [Master Playlist].
|
||||
/// # [4.4.5.3. EXT-X-I-FRAME-STREAM-INF]
|
||||
/// The [`ExtXIFrameStreamInf`] tag identifies a [`Media Playlist`] file,
|
||||
/// containing the I-frames of a multimedia presentation.
|
||||
///
|
||||
/// Its format is:
|
||||
/// I-frames are encoded video frames, whose decoding
|
||||
/// does not depend on any other frame.
|
||||
///
|
||||
/// ```text
|
||||
/// #EXT-X-I-FRAME-STREAM-INF:<attribute-list>
|
||||
/// ```
|
||||
///
|
||||
/// [Master Playlist]: crate::MasterPlaylist
|
||||
/// [Media Playlist]: crate::MediaPlaylist
|
||||
/// [4.3.4.3. EXT-X-I-FRAME-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.3
|
||||
/// [`Master Playlist`]: crate::MasterPlaylist
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [4.4.5.3. EXT-X-I-FRAME-STREAM-INF]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-4.4.5.3
|
||||
#[derive(PartialOrd, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ExtXIFrameStreamInf {
|
||||
uri: String,
|
||||
|
@ -30,7 +27,7 @@ pub struct ExtXIFrameStreamInf {
|
|||
impl ExtXIFrameStreamInf {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-I-FRAME-STREAM-INF:";
|
||||
|
||||
/// Makes a new [ExtXIFrameStreamInf] tag.
|
||||
/// Makes a new [`ExtXIFrameStreamInf`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -44,7 +41,7 @@ impl ExtXIFrameStreamInf {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the `URI`, that identifies the associated media playlist.
|
||||
/// Returns the `URI`, that identifies the associated [`media playlist`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -52,11 +49,13 @@ impl ExtXIFrameStreamInf {
|
|||
/// let stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
/// assert_eq!(stream.uri(), &"https://www.example.com".to_string());
|
||||
/// ```
|
||||
///
|
||||
/// [`media playlist`]: crate::MediaPlaylist
|
||||
pub const fn uri(&self) -> &String {
|
||||
&self.uri
|
||||
}
|
||||
|
||||
/// Sets the `URI`, that identifies the associated media playlist.
|
||||
/// Sets the `URI`, that identifies the associated [`media playlist`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -67,12 +66,15 @@ impl ExtXIFrameStreamInf {
|
|||
/// stream.set_uri("../new/uri");
|
||||
/// assert_eq!(stream.uri(), &"../new/uri".to_string());
|
||||
/// ```
|
||||
///
|
||||
/// [`media playlist`]: crate::MediaPlaylist
|
||||
pub fn set_uri<T: ToString>(&mut self, value: T) -> &mut Self {
|
||||
self.uri = value.to_string();
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// This tag requires [`ProtocolVersion::V1`].
|
||||
impl RequiredVersion for ExtXIFrameStreamInf {
|
||||
fn required_version(&self) -> ProtocolVersion {
|
||||
ProtocolVersion::V1
|
||||
|
|
|
@ -4,83 +4,120 @@ use std::str::FromStr;
|
|||
use derive_builder::Builder;
|
||||
|
||||
use crate::attribute::AttributePairs;
|
||||
use crate::types::{InStreamId, MediaType, ProtocolVersion, RequiredVersion};
|
||||
use crate::types::{Channels, InStreamId, MediaType, ProtocolVersion, RequiredVersion};
|
||||
use crate::utils::{parse_yes_or_no, quote, tag, unquote};
|
||||
use crate::Error;
|
||||
|
||||
/// # [4.4.4.1. EXT-X-MEDIA]
|
||||
/// The [ExtXMedia] tag is used to relate [Media Playlist]s that contain
|
||||
/// alternative Renditions of the same content. For
|
||||
/// example, three [ExtXMedia] tags can be used to identify audio-only
|
||||
/// [Media Playlist]s, that contain English, French, and Spanish Renditions
|
||||
/// of the same presentation. Or, two [ExtXMedia] tags can be used to
|
||||
/// identify video-only [Media Playlist]s that show two different camera
|
||||
/// # [4.4.5.1. EXT-X-MEDIA]
|
||||
/// The [`ExtXMedia`] tag is used to relate [`Media Playlist`]s,
|
||||
/// that contain alternative Renditions of the same content.
|
||||
///
|
||||
/// For
|
||||
/// example, three [`ExtXMedia`] tags can be used to identify audio-only
|
||||
/// [`Media Playlist`]s, that contain English, French, and Spanish Renditions
|
||||
/// of the same presentation. Or, two [`ExtXMedia`] tags can be used to
|
||||
/// identify video-only [`Media Playlist`]s that show two different camera
|
||||
/// angles.
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
/// #EXT-X-MEDIA:<attribute-list>
|
||||
/// ```
|
||||
///
|
||||
/// [Media Playlist]: crate::MediaPlaylist
|
||||
/// [4.4.4.1. EXT-X-MEDIA]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.4.1
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [4.4.5.1. EXT-X-MEDIA]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-4.4.5.1
|
||||
#[derive(Builder, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[builder(setter(into))]
|
||||
#[builder(build_fn(validate = "Self::validate"))]
|
||||
pub struct ExtXMedia {
|
||||
/// Sets the media type of the rendition.
|
||||
/// Sets the [`MediaType`] of the rendition.
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **required**.
|
||||
media_type: MediaType,
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
/// Sets the URI that identifies the media playlist.
|
||||
/// Sets the `URI` that identifies the [`Media Playlist`].
|
||||
///
|
||||
/// # Note
|
||||
/// - This attribute is **required**, if the [`MediaType`] is [`MediaType::Subtitles`].
|
||||
/// - This attribute is **not allowed**, if the [`MediaType`] is
|
||||
/// [`MediaType::ClosedCaptions`].
|
||||
///
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
uri: Option<String>,
|
||||
/// Sets the identifier that specifies the group to which the rendition belongs.
|
||||
/// Sets the identifier, that specifies the group to which the rendition belongs.
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **required**.
|
||||
group_id: String,
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
/// Sets the name of the primary language used in the rendition.
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
/// The value has to conform to [`RFC5646`].
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **optional**.
|
||||
///
|
||||
/// [`RFC5646`]: https://tools.ietf.org/html/rfc5646
|
||||
language: Option<String>,
|
||||
/// Sets the name of a language associated with the rendition.
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
/// Sets the name of a language associated with the rendition.
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **optional**.
|
||||
///
|
||||
/// [`language`]: #method.language
|
||||
assoc_language: Option<String>,
|
||||
/// Sets a human-readable description of the rendition.
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **required**.
|
||||
///
|
||||
/// If the [`language`] attribute is present, this attribute should be in that language.
|
||||
///
|
||||
/// [`language`]: #method.language
|
||||
name: String,
|
||||
#[builder(default)]
|
||||
/// Sets the value of the `default` flag.
|
||||
#[builder(default)]
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **optional**, its absence indicates an implicit value of `false`.
|
||||
is_default: bool,
|
||||
#[builder(default)]
|
||||
/// Sets the value of the `autoselect` flag.
|
||||
#[builder(default)]
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **optional**, its absence indicates an implicit value of `false`.
|
||||
is_autoselect: bool,
|
||||
/// Sets the value of the `forced` flag.
|
||||
#[builder(default)]
|
||||
/// Sets the value of the `forced` flag.
|
||||
is_forced: bool,
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
/// Sets the identifier that specifies a rendition within the segments in the media playlist.
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
instream_id: Option<InStreamId>,
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
/// Sets the string that represents uniform type identifiers (UTI).
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
characteristics: Option<String>,
|
||||
/// Sets the string that represents the parameters of the rendition.
|
||||
#[builder(setter(strip_option, into), default)]
|
||||
channels: Option<String>,
|
||||
/// Sets the parameters of the rendition.
|
||||
channels: Option<Channels>,
|
||||
}
|
||||
|
||||
impl ExtXMediaBuilder {
|
||||
fn validate(&self) -> Result<(), String> {
|
||||
// A MediaType is always required!
|
||||
let media_type = self
|
||||
.media_type
|
||||
.ok_or_else(|| Error::missing_attribute("MEDIA-TYPE").to_string())?;
|
||||
|
||||
if MediaType::ClosedCaptions == media_type {
|
||||
if media_type == MediaType::Subtitles && self.uri.is_none() {
|
||||
return Err(Error::missing_attribute("URI").to_string());
|
||||
}
|
||||
|
||||
if media_type == MediaType::ClosedCaptions {
|
||||
if self.uri.is_some() {
|
||||
return Err(Error::custom(
|
||||
"Unexpected attribute: \"URL\" for MediaType::ClosedCaptions!",
|
||||
)
|
||||
.to_string());
|
||||
return Err(Error::unexpected_attribute("URI").to_string());
|
||||
}
|
||||
if self.instream_id.is_none() {
|
||||
return Err(Error::missing_attribute("INSTREAM-ID").to_string());
|
||||
}
|
||||
self.instream_id
|
||||
.ok_or_else(|| Error::missing_attribute("INSTREAM-ID").to_string())?;
|
||||
} else if self.instream_id.is_some() {
|
||||
return Err(Error::custom("Unexpected attribute: \"INSTREAM-ID\"!").to_string());
|
||||
return Err(Error::unexpected_attribute("INSTREAM-ID").to_string());
|
||||
}
|
||||
|
||||
if self.is_default.unwrap_or(false) && !self.is_autoselect.unwrap_or(false) {
|
||||
|
@ -89,7 +126,7 @@ impl ExtXMediaBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
if MediaType::Subtitles != media_type && self.is_forced.is_some() {
|
||||
if media_type != MediaType::Subtitles && self.is_forced.is_some() {
|
||||
return Err(Error::invalid_input().to_string());
|
||||
}
|
||||
|
||||
|
@ -100,7 +137,7 @@ impl ExtXMediaBuilder {
|
|||
impl ExtXMedia {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA:";
|
||||
|
||||
/// Makes a new [ExtXMedia] tag.
|
||||
/// Makes a new [`ExtXMedia`] tag.
|
||||
pub fn new<T: ToString>(media_type: MediaType, group_id: T, name: T) -> Self {
|
||||
Self {
|
||||
media_type,
|
||||
|
@ -118,7 +155,7 @@ impl ExtXMedia {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a builder for [ExtXMedia].
|
||||
/// Returns a builder for [`ExtXMedia`].
|
||||
pub fn builder() -> ExtXMediaBuilder {
|
||||
ExtXMediaBuilder::default()
|
||||
}
|
||||
|
@ -215,6 +252,9 @@ impl ExtXMedia {
|
|||
|
||||
/// Sets a human-readable description of the rendition.
|
||||
///
|
||||
/// # Note
|
||||
/// If the [`language`] attribute is present, this attribute should be in that language.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXMedia;
|
||||
|
@ -229,12 +269,14 @@ impl ExtXMedia {
|
|||
/// &"new_name".to_string()
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// [`language`]: #method.language
|
||||
pub fn set_name<T: Into<String>>(&mut self, value: T) -> &mut Self {
|
||||
self.name = value.into();
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the `URI`, that identifies the [MediaPlaylist].
|
||||
/// Returns the `URI`, that identifies the [`Media Playlist`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -251,11 +293,18 @@ impl ExtXMedia {
|
|||
/// &Some("https://www.example.com/".into())
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
pub const fn uri(&self) -> &Option<String> {
|
||||
&self.uri
|
||||
}
|
||||
|
||||
/// Sets the `URI`, that identifies the [MediaPlaylist].
|
||||
/// Sets the `URI`, that identifies the [`Media Playlist`].
|
||||
///
|
||||
/// # Note
|
||||
/// This attribute is **required**, if the [`MediaType`] is [`MediaType::Subtitles`].
|
||||
/// This attribute is **not allowed**, if the [`MediaType`] is
|
||||
/// [`MediaType::ClosedCaptions`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -272,6 +321,8 @@ impl ExtXMedia {
|
|||
/// &Some("https://www.example.com/".into())
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
pub fn set_uri<T: Into<String>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.uri = value.map(|v| v.into());
|
||||
self
|
||||
|
@ -299,6 +350,7 @@ impl ExtXMedia {
|
|||
}
|
||||
|
||||
/// Sets the name of the primary language used in the rendition.
|
||||
/// The value has to conform to [`RFC5646`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -315,6 +367,8 @@ impl ExtXMedia {
|
|||
/// &Some("english".into())
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// [`RFC5646`]: https://tools.ietf.org/html/rfc5646
|
||||
pub fn set_language<T: Into<String>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.language = value.map(|v| v.into());
|
||||
self
|
||||
|
@ -342,6 +396,9 @@ impl ExtXMedia {
|
|||
}
|
||||
|
||||
/// Sets the name of a language associated with the rendition.
|
||||
/// An associated language is often used in a different role, than the
|
||||
/// language specified by the [`language`] attribute (e.g., written versus
|
||||
/// spoken, or a fallback dialect).
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -358,12 +415,14 @@ impl ExtXMedia {
|
|||
/// &Some("spanish".into())
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// [`language`]: #method.language
|
||||
pub fn set_assoc_language<T: Into<String>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.assoc_language = value.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns whether this is the default rendition.
|
||||
/// Returns whether this is the `default` rendition.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -385,6 +444,9 @@ impl ExtXMedia {
|
|||
}
|
||||
|
||||
/// Sets the `default` flag.
|
||||
/// A value of `true` indicates, that the client should play
|
||||
/// this rendition of the content in the absence of information
|
||||
/// from the user indicating a different choice.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -479,7 +541,7 @@ impl ExtXMedia {
|
|||
}
|
||||
|
||||
/// Returns the identifier that specifies a rendition within the segments in the
|
||||
/// [MediaPlaylist].
|
||||
/// [`Media Playlist`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -493,11 +555,14 @@ impl ExtXMedia {
|
|||
///
|
||||
/// assert_eq!(media.instream_id(), Some(InStreamId::Cc1));
|
||||
/// ```
|
||||
///
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
pub const fn instream_id(&self) -> Option<InStreamId> {
|
||||
self.instream_id
|
||||
}
|
||||
|
||||
/// Sets the [InStreamId].
|
||||
/// Sets the [`InStreamId`], that specifies a rendition within the
|
||||
/// segments in the [`Media Playlist`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -536,7 +601,20 @@ impl ExtXMedia {
|
|||
&self.characteristics
|
||||
}
|
||||
|
||||
/// Sets the characteristics.
|
||||
/// Sets the characteristics attribute, containing one or more Uniform Type
|
||||
/// Identifiers separated by comma.
|
||||
/// Each [`UTI`] indicates an individual characteristic of the Rendition.
|
||||
///
|
||||
/// A [`subtitles`] Rendition may include the following characteristics:
|
||||
/// "public.accessibility.transcribes-spoken-dialog",
|
||||
/// "public.accessibility.describes-music-and-sound", and
|
||||
/// "public.easy-to-read" (which indicates that the subtitles have
|
||||
/// been edited for ease of reading).
|
||||
///
|
||||
/// An AUDIO Rendition MAY include the following characteristic:
|
||||
/// "public.accessibility.describes-video".
|
||||
///
|
||||
/// The characteristics attribute may include private UTIs.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -550,26 +628,29 @@ impl ExtXMedia {
|
|||
///
|
||||
/// assert_eq!(media.characteristics(), &Some("characteristic".into()));
|
||||
/// ```
|
||||
///
|
||||
/// [`UTI`]: https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#ref-UTI
|
||||
/// [`subtitles`]: crate::types::MediaType::Subtitles
|
||||
pub fn set_characteristics<T: Into<String>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.characteristics = value.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns a [String] that represents the parameters of the rendition.
|
||||
/// Returns the channels.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXMedia;
|
||||
/// use hls_m3u8::types::MediaType;
|
||||
/// use hls_m3u8::types::{Channels, MediaType};
|
||||
///
|
||||
/// let mut media = ExtXMedia::new(MediaType::Audio, "audio", "name");
|
||||
/// # assert_eq!(media.channels(), &None);
|
||||
///
|
||||
/// media.set_channels(Some("channel"));
|
||||
/// media.set_channels(Some(Channels::new(6)));
|
||||
///
|
||||
/// assert_eq!(media.channels(), &Some("channel".into()));
|
||||
/// assert_eq!(media.channels(), &Some(Channels::new(6)));
|
||||
/// ```
|
||||
pub const fn channels(&self) -> &Option<String> {
|
||||
pub const fn channels(&self) -> &Option<Channels> {
|
||||
&self.channels
|
||||
}
|
||||
|
||||
|
@ -578,16 +659,16 @@ impl ExtXMedia {
|
|||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXMedia;
|
||||
/// use hls_m3u8::types::MediaType;
|
||||
/// use hls_m3u8::types::{Channels, MediaType};
|
||||
///
|
||||
/// let mut media = ExtXMedia::new(MediaType::Audio, "audio", "name");
|
||||
/// # assert_eq!(media.characteristics(), &None);
|
||||
/// # assert_eq!(media.channels(), &None);
|
||||
///
|
||||
/// media.set_characteristics(Some("characteristic"));
|
||||
/// media.set_channels(Some(Channels::new(6)));
|
||||
///
|
||||
/// assert_eq!(media.characteristics(), &Some("characteristic".into()));
|
||||
/// assert_eq!(media.channels(), &Some(Channels::new(6)));
|
||||
/// ```
|
||||
pub fn set_channels<T: Into<String>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
pub fn set_channels<T: Into<Channels>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.channels = value.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
|
@ -687,7 +768,7 @@ impl FromStr for ExtXMedia {
|
|||
builder.characteristics(unquote(value));
|
||||
}
|
||||
"CHANNELS" => {
|
||||
builder.channels(unquote(value));
|
||||
builder.channels(unquote(value).parse::<Channels>()?);
|
||||
}
|
||||
_ => {
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
|
@ -695,6 +776,7 @@ impl FromStr for ExtXMedia {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.build().map_err(Error::builder_error)
|
||||
}
|
||||
}
|
||||
|
@ -915,7 +997,7 @@ mod test {
|
|||
.name("English")
|
||||
.is_autoselect(true)
|
||||
.is_default(true)
|
||||
.channels("2")
|
||||
.channels(Channels::new(2))
|
||||
.build()
|
||||
.unwrap()
|
||||
.to_string(),
|
||||
|
@ -1196,7 +1278,7 @@ mod test {
|
|||
.name("English")
|
||||
.is_autoselect(true)
|
||||
.is_default(true)
|
||||
.channels("2")
|
||||
.channels(Channels::new(2))
|
||||
.build()
|
||||
.unwrap(),
|
||||
"#EXT-X-MEDIA:\
|
||||
|
@ -1262,7 +1344,30 @@ mod test {
|
|||
"#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID=\"foo\",NAME=\"bar\""
|
||||
.parse()
|
||||
.unwrap()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser_error() {
|
||||
assert!("".parse::<ExtXMedia>().is_err());
|
||||
assert!("garbage".parse::<ExtXMedia>().is_err());
|
||||
|
||||
assert!(
|
||||
"#EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS,URI=\"http://www.example.com\""
|
||||
.parse::<ExtXMedia>()
|
||||
.is_err()
|
||||
);
|
||||
assert!("#EXT-X-MEDIA:TYPE=AUDIO,INSTREAM-ID=CC1"
|
||||
.parse::<ExtXMedia>()
|
||||
.is_err());
|
||||
|
||||
assert!("#EXT-X-MEDIA:TYPE=AUDIO,DEFAULT=YES,AUTOSELECT=NO"
|
||||
.parse::<ExtXMedia>()
|
||||
.is_err());
|
||||
|
||||
assert!("#EXT-X-MEDIA:TYPE=AUDIO,FORCED=YES"
|
||||
.parse::<ExtXMedia>()
|
||||
.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
@ -11,37 +11,41 @@ use crate::Error;
|
|||
/// The data of an [ExtXSessionData] tag.
|
||||
#[derive(Hash, Eq, Ord, Debug, PartialEq, Clone, PartialOrd)]
|
||||
pub enum SessionData {
|
||||
/// A String, that contains the data identified by [data_id](ExtXSessionData::data_id).
|
||||
/// If a [language](ExtXSessionData::language) is specified, the value should
|
||||
/// A String, that contains the data identified by [`data_id`](ExtXSessionData::data_id).
|
||||
/// If a [`language`](ExtXSessionData::language) is specified, the value should
|
||||
/// contain a human-readable string written in the specified language.
|
||||
Value(String),
|
||||
/// An [uri], which points to a [json].
|
||||
/// An [`uri`], which points to a [`json`].
|
||||
///
|
||||
/// [json]: https://tools.ietf.org/html/rfc8259
|
||||
/// [uri]: https://tools.ietf.org/html/rfc3986
|
||||
/// [`json`]: https://tools.ietf.org/html/rfc8259
|
||||
/// [`uri`]: https://tools.ietf.org/html/rfc3986
|
||||
Uri(String),
|
||||
}
|
||||
|
||||
/// # [4.3.4.4. EXT-X-SESSION-DATA]
|
||||
///
|
||||
/// The [ExtXSessionData] tag allows arbitrary session data to be
|
||||
/// carried in a [Master Playlist].
|
||||
/// The [`ExtXSessionData`] tag allows arbitrary session data to be
|
||||
/// carried in a [`Master Playlist`].
|
||||
///
|
||||
/// [Master Playlist]: crate::MasterPlaylist
|
||||
/// [`Master Playlist`]: crate::MasterPlaylist
|
||||
/// [4.3.4.4. EXT-X-SESSION-DATA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.4
|
||||
#[derive(Builder, Hash, Eq, Ord, Debug, PartialEq, Clone, PartialOrd)]
|
||||
#[builder(setter(into))]
|
||||
pub struct ExtXSessionData {
|
||||
/// The identifier of the data. For more information look [here](ExtXSessionData::set_data_id).
|
||||
/// The identifier of the data.
|
||||
/// For more information look [`here`](ExtXSessionData::set_data_id).
|
||||
///
|
||||
/// # Note
|
||||
/// This field is required.
|
||||
data_id: String,
|
||||
/// The data associated with the [data_id](ExtXSessionDataBuilder::data_id).
|
||||
/// For more information look [here](SessionData).
|
||||
/// The data associated with the
|
||||
/// [`data_id`](ExtXSessionDataBuilder::data_id).
|
||||
/// For more information look [`here`](SessionData).
|
||||
///
|
||||
/// # Note
|
||||
/// This field is required.
|
||||
data: SessionData,
|
||||
/// The language of the [data](ExtXSessionDataBuilder::data).
|
||||
/// The language of the [`data`](ExtXSessionDataBuilder::data).
|
||||
#[builder(setter(into, strip_option), default)]
|
||||
language: Option<String>,
|
||||
}
|
||||
|
@ -49,7 +53,7 @@ pub struct ExtXSessionData {
|
|||
impl ExtXSessionData {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-SESSION-DATA:";
|
||||
|
||||
/// Makes a new [ExtXSessionData] tag.
|
||||
/// Makes a new [`ExtXSessionData`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -68,7 +72,7 @@ impl ExtXSessionData {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns a new Builder for [ExtXSessionData].
|
||||
/// Returns a new Builder for [`ExtXSessionData`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -94,7 +98,7 @@ impl ExtXSessionData {
|
|||
ExtXSessionDataBuilder::default()
|
||||
}
|
||||
|
||||
/// Makes a new [ExtXSessionData] tag, with the given language.
|
||||
/// Makes a new [`ExtXSessionData`] tag, with the given language.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -154,7 +158,7 @@ impl ExtXSessionData {
|
|||
&self.data
|
||||
}
|
||||
|
||||
/// Returns the `language` tag, that identifies the language of [SessionData].
|
||||
/// Returns the `language` tag, that identifies the language of [`SessionData`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -175,7 +179,7 @@ impl ExtXSessionData {
|
|||
&self.language
|
||||
}
|
||||
|
||||
/// Sets the `language` attribute, that identifies the language of [SessionData].
|
||||
/// Sets the `language` attribute, that identifies the language of [`SessionData`].
|
||||
/// See [rfc5646](https://tools.ietf.org/html/rfc5646).
|
||||
///
|
||||
/// # Example
|
||||
|
@ -224,7 +228,7 @@ impl ExtXSessionData {
|
|||
self
|
||||
}
|
||||
|
||||
/// Sets the [data](ExtXSessionData::data) of this tag.
|
||||
/// Sets the [`data`](ExtXSessionData::data) of this tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
|
@ -7,9 +7,9 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.3.4.5. EXT-X-SESSION-KEY]
|
||||
/// The [ExtXSessionKey] tag allows encryption keys from [Media Playlist]s
|
||||
/// to be specified in a [Master Playlist]. This allows the client to
|
||||
/// preload these keys without having to read the [Media Playlist]s
|
||||
/// The [`ExtXSessionKey`] tag allows encryption keys from [`Media Playlist`]s
|
||||
/// to be specified in a [`Master Playlist`]. This allows the client to
|
||||
/// preload these keys without having to read the [`Media Playlist`]s
|
||||
/// first.
|
||||
///
|
||||
/// Its format is:
|
||||
|
@ -17,8 +17,8 @@ use crate::Error;
|
|||
/// #EXT-X-SESSION-KEY:<attribute-list>
|
||||
/// ```
|
||||
///
|
||||
/// [Media Playlist]: crate::MediaPlaylist
|
||||
/// [Master Playlist]: crate::MasterPlaylist
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [`Master Playlist`]: crate::MasterPlaylist
|
||||
/// [4.3.4.5. EXT-X-SESSION-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.4.5
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ExtXSessionKey(DecryptionKey);
|
||||
|
@ -26,11 +26,13 @@ pub struct ExtXSessionKey(DecryptionKey);
|
|||
impl ExtXSessionKey {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-SESSION-KEY:";
|
||||
|
||||
/// Makes a new [ExtXSessionKey] tag.
|
||||
/// Makes a new [`ExtXSessionKey`] tag.
|
||||
///
|
||||
/// # Panic
|
||||
/// An [ExtXSessionKey] should only be used, if the segments of the stream are encrypted.
|
||||
/// Therefore this function will panic, if the `method` is [EncryptionMethod::None].
|
||||
/// An [`ExtXSessionKey`] should only be used,
|
||||
/// if the segments of the stream are encrypted.
|
||||
/// Therefore this function will panic,
|
||||
/// if the `method` is [`EncryptionMethod::None`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
|
@ -25,7 +25,7 @@ pub struct ExtXStreamInf {
|
|||
impl ExtXStreamInf {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-STREAM-INF:";
|
||||
|
||||
/// Creates a new [ExtXStreamInf] tag.
|
||||
/// Creates a new [`ExtXStreamInf`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -164,7 +164,7 @@ impl ExtXStreamInf {
|
|||
self
|
||||
}
|
||||
|
||||
/// Returns the value of [ClosedCaptions] attribute.
|
||||
/// Returns the value of [`ClosedCaptions`] attribute.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -181,7 +181,7 @@ impl ExtXStreamInf {
|
|||
&self.closed_captions
|
||||
}
|
||||
|
||||
/// Returns the value of [ClosedCaptions] attribute.
|
||||
/// Returns the value of [`ClosedCaptions`] attribute.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
|
@ -6,9 +6,9 @@ use crate::utils::tag;
|
|||
|
||||
/// # [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]
|
||||
///
|
||||
/// The [ExtXDiscontinuitySequence] tag allows synchronization between
|
||||
/// The [`ExtXDiscontinuitySequence`] tag allows synchronization between
|
||||
/// different Renditions of the same Variant Stream or different Variant
|
||||
/// Streams that have [ExtXDiscontinuity] tags in their [Media Playlist]s.
|
||||
/// Streams that have [`ExtXDiscontinuity`] tags in their [`Media Playlist`]s.
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
|
@ -16,8 +16,8 @@ use crate::utils::tag;
|
|||
/// ```
|
||||
/// where `number` is a [u64].
|
||||
///
|
||||
/// [ExtXDiscontinuity]: crate::tags::ExtXDiscontinuity
|
||||
/// [Media Playlist]: crate::MediaPlaylist
|
||||
/// [`ExtXDiscontinuity`]: crate::tags::ExtXDiscontinuity
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.3.3
|
||||
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
|
||||
|
|
|
@ -6,16 +6,16 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.4.3.4. EXT-X-ENDLIST]
|
||||
/// The [ExtXEndList] tag indicates, that no more [Media Segment]s will be
|
||||
/// added to the [Media Playlist] file.
|
||||
/// The [`ExtXEndList`] tag indicates, that no more [`Media Segment`]s will be
|
||||
/// added to the [`Media Playlist`] file.
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
/// #EXT-X-ENDLIST
|
||||
/// ```
|
||||
///
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [Media Playlist]: crate::MediaPlaylist
|
||||
/// [`Media Segment`]: crate::MediaSegment
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [4.4.3.4. EXT-X-ENDLIST]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.3.4
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.4.3.6. EXT-X-I-FRAMES-ONLY]
|
||||
/// The [ExtXIFramesOnly] tag indicates that each [Media Segment] in the
|
||||
/// The [`ExtXIFramesOnly`] tag indicates that each [`Media Segment`] in the
|
||||
/// Playlist describes a single I-frame. I-frames are encoded video
|
||||
/// frames, whose decoding does not depend on any other frame. I-frame
|
||||
/// Playlists can be used for trick play, such as fast forward, rapid
|
||||
|
@ -17,7 +17,7 @@ use crate::Error;
|
|||
/// #EXT-X-I-FRAMES-ONLY
|
||||
/// ```
|
||||
///
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [`Media Segment`]: crate::MediaSegment
|
||||
/// [4.4.3.6. EXT-X-I-FRAMES-ONLY]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.3.6
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -6,14 +6,14 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.4.3.2. EXT-X-MEDIA-SEQUENCE]
|
||||
/// The [ExtXMediaSequence] tag indicates the Media Sequence Number of
|
||||
/// the first [Media Segment] that appears in a Playlist file.
|
||||
/// The [`ExtXMediaSequence`] tag indicates the Media Sequence Number of
|
||||
/// the first [`Media Segment`] that appears in a Playlist file.
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
/// #EXT-X-MEDIA-SEQUENCE:<number>
|
||||
/// ```
|
||||
/// where `number` is a [u64].
|
||||
/// where `number` is a [`u64`].
|
||||
///
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [4.4.3.2. EXT-X-MEDIA-SEQUENCE]:
|
||||
|
@ -24,7 +24,7 @@ pub struct ExtXMediaSequence(u64);
|
|||
impl ExtXMediaSequence {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA-SEQUENCE:";
|
||||
|
||||
/// Makes a new [ExtXMediaSequence] tag.
|
||||
/// Makes a new [`ExtXMediaSequence`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
|
@ -7,8 +7,8 @@ use crate::Error;
|
|||
|
||||
/// # [4.4.3.5. EXT-X-PLAYLIST-TYPE]
|
||||
///
|
||||
/// The [ExtXPlaylistType] tag provides mutability information about the
|
||||
/// [Media Playlist]. It applies to the entire [Media Playlist].
|
||||
/// The [`ExtXPlaylistType`] tag provides mutability information about the
|
||||
/// [`Media Playlist`]. It applies to the entire [`Media Playlist`].
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
|
@ -20,10 +20,10 @@ use crate::Error;
|
|||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.3.5
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum ExtXPlaylistType {
|
||||
/// If the [ExtXPlaylistType] is Event, Media Segments can only be added to
|
||||
/// the end of the Media Playlist.
|
||||
/// If the [`ExtXPlaylistType`] is Event, Media Segments
|
||||
/// can only be added to the end of the Media Playlist.
|
||||
Event,
|
||||
/// If the [ExtXPlaylistType] is Video On Demand (Vod),
|
||||
/// If the [`ExtXPlaylistType`] is Video On Demand (Vod),
|
||||
/// the Media Playlist cannot change.
|
||||
Vod,
|
||||
}
|
||||
|
|
|
@ -7,39 +7,52 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.4.3.1. EXT-X-TARGETDURATION]
|
||||
/// The [ExtXTargetDuration] tag specifies the maximum [Media Segment]
|
||||
/// The [`ExtXTargetDuration`] tag specifies the maximum [`Media Segment`]
|
||||
/// duration.
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
/// #EXT-X-TARGETDURATION:<s>
|
||||
/// ```
|
||||
/// where `s` is the target [Duration] in seconds.
|
||||
///
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [`Media Segment`]: crate::MediaSegment
|
||||
/// [4.4.3.1. EXT-X-TARGETDURATION]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.3.1
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-4.4.3.1
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
|
||||
pub struct ExtXTargetDuration(Duration);
|
||||
|
||||
impl ExtXTargetDuration {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-TARGETDURATION:";
|
||||
|
||||
/// Makes a new [ExtXTargetduration] tag.
|
||||
/// Makes a new [`ExtXTargetDuration`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXTargetDuration;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let target_duration = ExtXTargetDuration::new(Duration::from_secs(20));
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
/// The nanoseconds part of the [Duration] will be discarded.
|
||||
/// The nanoseconds part of the [`Duration`] will be discarded.
|
||||
pub const fn new(duration: Duration) -> Self {
|
||||
// TOOD: round instead of discarding?
|
||||
Self(Duration::from_secs(duration.as_secs()))
|
||||
}
|
||||
|
||||
/// Returns the maximum media segment duration in the associated playlist.
|
||||
/// Returns the maximum media segment duration.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXTargetDuration;
|
||||
/// use std::time::Duration;
|
||||
///
|
||||
/// let target_duration = ExtXTargetDuration::new(Duration::from_nanos(2_000_000_000));
|
||||
///
|
||||
/// assert_eq!(target_duration.duration(), Duration::from_secs(2));
|
||||
/// ```
|
||||
pub const fn duration(&self) -> Duration {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
/// This tag requires [`ProtocolVersion::V1`].
|
||||
impl RequiredVersion for ExtXTargetDuration {
|
||||
fn required_version(&self) -> ProtocolVersion {
|
||||
ProtocolVersion::V1
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::Error;
|
|||
|
||||
/// # [4.4.2.2. EXT-X-BYTERANGE]
|
||||
///
|
||||
/// The [ExtXByteRange] tag indicates that a [Media Segment] is a sub-range
|
||||
/// The [`ExtXByteRange`] tag indicates that a [`Media Segment`] is a sub-range
|
||||
/// of the resource identified by its `URI`.
|
||||
///
|
||||
/// Its format is:
|
||||
|
@ -20,7 +20,7 @@ use crate::Error;
|
|||
/// If present, `o` is a [usize] indicating the start of the sub-range,
|
||||
/// as a byte offset from the beginning of the resource.
|
||||
///
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [`Media Segment`]: crate::MediaSegment
|
||||
/// [4.4.2.2. EXT-X-BYTERANGE]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.2.2
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -29,7 +29,7 @@ pub struct ExtXByteRange(ByteRange);
|
|||
impl ExtXByteRange {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-BYTERANGE:";
|
||||
|
||||
/// Makes a new [ExtXByteRange] tag.
|
||||
/// Makes a new [`ExtXByteRange`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -40,7 +40,7 @@ impl ExtXByteRange {
|
|||
Self(ByteRange::new(length, start))
|
||||
}
|
||||
|
||||
/// Converts the [ExtXByteRange] to a [ByteRange].
|
||||
/// Converts the [`ExtXByteRange`] to a [`ByteRange`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
|
@ -10,7 +10,7 @@ use crate::types::{ProtocolVersion, RequiredVersion};
|
|||
use crate::utils::{quote, tag, unquote};
|
||||
use crate::Error;
|
||||
|
||||
/// [4.3.2.7. EXT-X-DATERANGE]
|
||||
/// # [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
|
||||
///
|
||||
|
@ -18,7 +18,7 @@ use crate::Error;
|
|||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ExtXDateRange {
|
||||
/// A string that uniquely identifies a [ExtXDateRange] in the Playlist.
|
||||
/// A string that uniquely identifies a [`ExtXDateRange`] in the Playlist.
|
||||
/// This attribute is required.
|
||||
id: String,
|
||||
/// A client-defined string that specifies some set of attributes and their associated value
|
||||
|
@ -64,7 +64,7 @@ pub struct ExtXDateRange {
|
|||
impl ExtXDateRange {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-DATERANGE:";
|
||||
|
||||
/// Makes a new [ExtXDateRange] tag.
|
||||
/// Makes a new [`ExtXDateRange`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -74,7 +74,7 @@ impl ExtXDateRange {
|
|||
///
|
||||
/// const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
|
||||
///
|
||||
/// ExtXDateRange::new("id", FixedOffset::east(8 * HOURS_IN_SECS)
|
||||
/// let date_range = ExtXDateRange::new("id", FixedOffset::east(8 * HOURS_IN_SECS)
|
||||
/// .ymd(2010, 2, 19)
|
||||
/// .and_hms_milli(14, 54, 23, 31));
|
||||
/// ```
|
||||
|
@ -95,6 +95,22 @@ impl ExtXDateRange {
|
|||
}
|
||||
}
|
||||
|
||||
/// This tag requires [`ProtocolVersion::V1`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXDateRange;
|
||||
/// use hls_m3u8::types::{RequiredVersion, ProtocolVersion};
|
||||
/// use chrono::{DateTime, FixedOffset};
|
||||
/// use chrono::offset::TimeZone;
|
||||
///
|
||||
/// const HOURS_IN_SECS: i32 = 3600; // 1 hour = 3600 seconds
|
||||
///
|
||||
/// let date_range = ExtXDateRange::new("id", FixedOffset::east(8 * HOURS_IN_SECS)
|
||||
/// .ymd(2010, 2, 19)
|
||||
/// .and_hms_milli(14, 54, 23, 31));
|
||||
/// assert_eq!(date_range.required_version(), ProtocolVersion::V1);
|
||||
/// ```
|
||||
impl RequiredVersion for ExtXDateRange {
|
||||
fn required_version(&self) -> ProtocolVersion {
|
||||
ProtocolVersion::V1
|
||||
|
|
|
@ -6,15 +6,15 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.4.2.3. EXT-X-DISCONTINUITY]
|
||||
/// The [ExtXDiscontinuity] tag indicates a discontinuity between the
|
||||
/// [Media Segment] that follows it and the one that preceded it.
|
||||
/// The [`ExtXDiscontinuity`] tag indicates a discontinuity between the
|
||||
/// [`Media Segment`] that follows it and the one that preceded it.
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
/// #EXT-X-DISCONTINUITY
|
||||
/// ```
|
||||
///
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [`Media Segment`]: crate::MediaSegment
|
||||
/// [4.4.2.3. EXT-X-DISCONTINUITY]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.2.3
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -8,8 +8,8 @@ use crate::Error;
|
|||
|
||||
/// # [4.4.2.1. EXTINF]
|
||||
///
|
||||
/// The [ExtInf] tag specifies the duration of a [Media Segment]. It applies
|
||||
/// only to the next [Media Segment].
|
||||
/// The [`ExtInf`] tag specifies the duration of a [`Media Segment`]. It applies
|
||||
/// only to the next [`Media Segment`].
|
||||
///
|
||||
/// Its format is:
|
||||
/// ```text
|
||||
|
@ -17,7 +17,7 @@ use crate::Error;
|
|||
/// ```
|
||||
/// The title is an optional informative title about the [Media Segment].
|
||||
///
|
||||
/// [Media Segment]: crate::media_segment::MediaSegment
|
||||
/// [`Media Segment`]: crate::media_segment::MediaSegment
|
||||
/// [4.4.2.1. EXTINF]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.2.1
|
||||
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
|
@ -29,7 +29,7 @@ pub struct ExtInf {
|
|||
impl ExtInf {
|
||||
pub(crate) const PREFIX: &'static str = "#EXTINF:";
|
||||
|
||||
/// Makes a new [ExtInf] tag.
|
||||
/// Makes a new [`ExtInf`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
@ -45,7 +45,7 @@ impl ExtInf {
|
|||
}
|
||||
}
|
||||
|
||||
/// Makes a new [ExtInf] tag with the given title.
|
||||
/// Makes a new [`ExtInf`] tag with the given title.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
|
@ -7,11 +7,11 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.4.2.4. EXT-X-KEY]
|
||||
/// [Media Segment]s may be encrypted. The [ExtXKey] tag specifies how to
|
||||
/// decrypt them. It applies to every [Media Segment] and to every Media
|
||||
/// Initialization Section declared by an [ExtXMap] tag, that appears
|
||||
/// between it and the next [ExtXKey] tag in the Playlist file with the
|
||||
/// same [KeyFormat] attribute (or the end of the Playlist file).
|
||||
/// [`Media Segment`]s may be encrypted. The [`ExtXKey`] tag specifies how to
|
||||
/// decrypt them. It applies to every [`Media Segment`] and to every Media
|
||||
/// Initialization Section declared by an [`ExtXMap`] tag, that appears
|
||||
/// between it and the next [`ExtXKey`] tag in the Playlist file with the
|
||||
/// same [`KeyFormat`] attribute (or the end of the Playlist file).
|
||||
///
|
||||
/// The format is:
|
||||
/// ```text
|
||||
|
@ -19,11 +19,12 @@ use crate::Error;
|
|||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
/// In case of an empty key (`EncryptionMethod::None`), all attributes will be ignored.
|
||||
/// In case of an empty key (`EncryptionMethod::None`),
|
||||
/// all attributes will be ignored.
|
||||
///
|
||||
/// [KeyFormat]: crate::types::KeyFormat
|
||||
/// [ExtXMap]: crate::tags::ExtXMap
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [`KeyFormat`]: crate::types::KeyFormat
|
||||
/// [`ExtXMap`]: crate::tags::ExtXMap
|
||||
/// [`Media Segment`]: crate::MediaSegment
|
||||
/// [4.4.2.4. EXT-X-KEY]:
|
||||
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.2.4
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
@ -32,8 +33,9 @@ pub struct ExtXKey(DecryptionKey);
|
|||
impl ExtXKey {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-KEY:";
|
||||
|
||||
/// Makes a new `ExtXKey` tag.
|
||||
/// # Examples
|
||||
/// Makes a new [`ExtXKey`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use hls_m3u8::tags::ExtXKey;
|
||||
/// use hls_m3u8::types::EncryptionMethod;
|
||||
|
@ -52,8 +54,9 @@ impl ExtXKey {
|
|||
Self(DecryptionKey::new(method, uri))
|
||||
}
|
||||
|
||||
/// Makes a new `ExtXKey` tag without a decryption key.
|
||||
/// # Examples
|
||||
/// Makes a new [`ExtXKey`] tag without a decryption key.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use hls_m3u8::tags::ExtXKey;
|
||||
///
|
||||
|
@ -74,8 +77,10 @@ impl ExtXKey {
|
|||
})
|
||||
}
|
||||
|
||||
/// Returns whether the [EncryptionMethod] is [None](EncryptionMethod::None).
|
||||
/// # Examples
|
||||
/// Returns whether the [`EncryptionMethod`] is
|
||||
/// [`None`](EncryptionMethod::None).
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// use hls_m3u8::tags::ExtXKey;
|
||||
/// use hls_m3u8::types::EncryptionMethod;
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::utils::{quote, tag, unquote};
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.4.2.5. EXT-X-MAP]
|
||||
/// The [ExtXMap] tag specifies how to obtain the Media Initialization
|
||||
/// The [`ExtXMap`] tag specifies how to obtain the Media Initialization
|
||||
/// Section, required to parse the applicable [Media Segment]s.
|
||||
///
|
||||
/// Its format is:
|
||||
|
@ -27,7 +27,7 @@ pub struct ExtXMap {
|
|||
impl ExtXMap {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-MAP:";
|
||||
|
||||
/// Makes a new `ExtXMap` tag.
|
||||
/// Makes a new [`ExtXMap`] tag.
|
||||
pub fn new<T: ToString>(uri: T) -> Self {
|
||||
ExtXMap {
|
||||
uri: uri.to_string(),
|
||||
|
@ -35,7 +35,7 @@ impl ExtXMap {
|
|||
}
|
||||
}
|
||||
|
||||
/// Makes a new `ExtXMap` tag with the given range.
|
||||
/// Makes a new [`ExtXMap`] tag with the given range.
|
||||
pub fn with_range<T: ToString>(uri: T, range: ByteRange) -> Self {
|
||||
ExtXMap {
|
||||
uri: uri.to_string(),
|
||||
|
@ -43,7 +43,8 @@ impl ExtXMap {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns the URI that identifies a resource that contains the media initialization section.
|
||||
/// Returns the `URI` that identifies a resource,
|
||||
/// that contains the media initialization section.
|
||||
pub const fn uri(&self) -> &String {
|
||||
&self.uri
|
||||
}
|
||||
|
|
|
@ -9,10 +9,10 @@ use crate::utils::tag;
|
|||
use crate::Error;
|
||||
|
||||
/// # [4.3.2.6. EXT-X-PROGRAM-DATE-TIME]
|
||||
/// The [ExtXProgramDateTime] tag associates the first sample of a
|
||||
/// [Media Segment] with an absolute date and/or time.
|
||||
/// The [`ExtXProgramDateTime`] tag associates the first sample of a
|
||||
/// [`Media Segment`] with an absolute date and/or time.
|
||||
///
|
||||
/// [Media Segment]: crate::MediaSegment
|
||||
/// [`Media Segment`]: crate::MediaSegment
|
||||
/// [4.3.2.6. EXT-X-PROGRAM-DATE-TIME]: https://tools.ietf.org/html/rfc8216#section-4.3.2.6
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct ExtXProgramDateTime(DateTime<FixedOffset>);
|
||||
|
@ -20,7 +20,7 @@ pub struct ExtXProgramDateTime(DateTime<FixedOffset>);
|
|||
impl ExtXProgramDateTime {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-PROGRAM-DATE-TIME:";
|
||||
|
||||
/// Makes a new `ExtXProgramDateTime` tag.
|
||||
/// Makes a new [`ExtXProgramDateTime`] tag.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
|
@ -18,7 +18,7 @@ pub struct ExtXStart {
|
|||
impl ExtXStart {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-START:";
|
||||
|
||||
/// Makes a new [ExtXStart] tag.
|
||||
/// Makes a new [`ExtXStart`] tag.
|
||||
///
|
||||
/// # Panic
|
||||
/// Panics if the time_offset value is infinite.
|
||||
|
@ -35,7 +35,7 @@ impl ExtXStart {
|
|||
}
|
||||
}
|
||||
|
||||
/// Makes a new [ExtXStart] tag with the given `precise` flag.
|
||||
/// Makes a new [`ExtXStart`] tag with the given `precise` flag.
|
||||
///
|
||||
/// # Panic
|
||||
/// Panics if the time_offset value is infinite.
|
||||
|
|
|
@ -15,7 +15,7 @@ pub struct ByteRange {
|
|||
}
|
||||
|
||||
impl ByteRange {
|
||||
/// Creates a new [ByteRange].
|
||||
/// Creates a new [`ByteRange`].
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
188
src/types/channels.rs
Normal file
188
src/types/channels.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
use core::fmt;
|
||||
use core::str::FromStr;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// Specifies a list of parameters.
|
||||
///
|
||||
/// # `MediaType::Audio`
|
||||
/// The first parameter is a count of audio channels expressed as a [`u64`],
|
||||
/// indicating the maximum number of independent, simultaneous audio channels
|
||||
/// present in any [`MediaSegment`] in the rendition. For example, an
|
||||
/// `AC-3 5.1` rendition would have a `CHANNELS="6"` attribute.
|
||||
///
|
||||
/// The second parameter identifies the encoding of object-based audio used by the
|
||||
/// rendition. This parameter is a comma-separated list of Audio
|
||||
/// Object Coding Identifiers. It is optional. An Audio Object
|
||||
/// Coding Identifier is a string containing characters from the set
|
||||
/// `[A..Z]`, `[0..9]`, and `'-'`. They are codec-specific. A parameter
|
||||
/// value of consisting solely of the dash character (`'-'`) indicates
|
||||
/// that the audio is not object-based.
|
||||
///
|
||||
/// # Example
|
||||
/// Creating a `CHANNELS="6"` attribute
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::Channels;
|
||||
/// let mut channels = Channels::new(6);
|
||||
///
|
||||
/// assert_eq!(format!("CHANNELS=\"{}\"", channels), "CHANNELS=\"6\"".to_string());
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
/// Currently there are no example playlists in the documentation,
|
||||
/// or in popular m3u8 libraries, showing a usage for the second parameter
|
||||
/// of [`Channels`], so if you have one please open an issue on github!
|
||||
///
|
||||
/// [`MediaSegment`]: crate::MediaSegment
|
||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Ord, PartialOrd, Hash)]
|
||||
pub struct Channels {
|
||||
first_parameter: u64,
|
||||
second_parameter: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl Channels {
|
||||
/// Makes a new [`Channels`] struct.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::Channels;
|
||||
/// let mut channels = Channels::new(6);
|
||||
/// ```
|
||||
pub const fn new(value: u64) -> Self {
|
||||
Self {
|
||||
first_parameter: value,
|
||||
second_parameter: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the first parameter.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::Channels;
|
||||
/// let mut channels = Channels::new(6);
|
||||
///
|
||||
/// assert_eq!(channels.first_parameter(), 6);
|
||||
/// ```
|
||||
pub const fn first_parameter(&self) -> u64 {
|
||||
self.first_parameter
|
||||
}
|
||||
|
||||
/// Sets the first parameter.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::Channels;
|
||||
/// let mut channels = Channels::new(3);
|
||||
///
|
||||
/// channels.set_first_parameter(6);
|
||||
/// assert_eq!(channels.first_parameter(), 6)
|
||||
/// ```
|
||||
pub fn set_first_parameter(&mut self, value: u64) -> &mut Self {
|
||||
self.first_parameter = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the second parameter, if there is any!
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::Channels;
|
||||
/// let mut channels = Channels::new(3);
|
||||
/// # assert_eq!(channels.second_parameter(), &None);
|
||||
///
|
||||
/// channels.set_second_parameter(Some(vec!["AAC","MP3"]));
|
||||
/// assert_eq!(channels.second_parameter(), &Some(vec!["AAC".to_string(),"MP3".to_string()]))
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
/// Currently there is no use for this parameter.
|
||||
pub const fn second_parameter(&self) -> &Option<Vec<String>> {
|
||||
&self.second_parameter
|
||||
}
|
||||
|
||||
/// Sets the second parameter.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::Channels;
|
||||
/// let mut channels = Channels::new(3);
|
||||
/// # assert_eq!(channels.second_parameter(), &None);
|
||||
///
|
||||
/// channels.set_second_parameter(Some(vec!["AAC","MP3"]));
|
||||
/// assert_eq!(channels.second_parameter(), &Some(vec!["AAC".to_string(),"MP3".to_string()]))
|
||||
/// ```
|
||||
///
|
||||
/// # Note
|
||||
/// Currently there is no use for this parameter.
|
||||
pub fn set_second_parameter<T: ToString>(&mut self, value: Option<Vec<T>>) -> &mut Self {
|
||||
self.second_parameter = value.map(|v| v.into_iter().map(|s| s.to_string()).collect());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Channels {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let parameters = input.split('/').collect::<Vec<_>>();
|
||||
let first_parameter = parameters
|
||||
.first()
|
||||
.ok_or_else(|| Error::missing_attribute("First parameter of channels!"))?
|
||||
.parse()?;
|
||||
|
||||
let second_parameter = parameters
|
||||
.get(1)
|
||||
.map(|v| v.split(',').map(|v| v.to_string()).collect());
|
||||
|
||||
Ok(Self {
|
||||
first_parameter,
|
||||
second_parameter,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Channels {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.first_parameter)?;
|
||||
|
||||
if let Some(second) = &self.second_parameter {
|
||||
write!(f, "/{}", second.join(","))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_display() {
|
||||
let mut channels = Channels::new(6);
|
||||
assert_eq!(channels.to_string(), "6".to_string());
|
||||
|
||||
channels.set_first_parameter(7);
|
||||
assert_eq!(channels.to_string(), "7".to_string());
|
||||
|
||||
assert_eq!(
|
||||
"6/P,K,J".to_string(),
|
||||
Channels::new(6)
|
||||
.set_second_parameter(Some(vec!["P", "K", "J"]))
|
||||
.to_string()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parser() {
|
||||
assert_eq!("6".parse::<Channels>().unwrap(), Channels::new(6));
|
||||
let mut result = Channels::new(6);
|
||||
result.set_second_parameter(Some(vec!["P", "K", "J"]));
|
||||
|
||||
assert_eq!("6/P,K,J".parse::<Channels>().unwrap(), result);
|
||||
|
||||
assert!("garbage".parse::<Channels>().is_err());
|
||||
assert!("".parse::<Channels>().is_err());
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ use crate::Error;
|
|||
pub(crate) struct DecimalFloatingPoint(f64);
|
||||
|
||||
impl DecimalFloatingPoint {
|
||||
/// Makes a new [DecimalFloatingPoint] instance.
|
||||
/// Makes a new [`DecimalFloatingPoint`] instance.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
|
@ -32,7 +32,7 @@ impl DecimalFloatingPoint {
|
|||
Self(value)
|
||||
}
|
||||
|
||||
/// Converts [DecimalFloatingPoint] to [f64].
|
||||
/// Converts [`DecimalFloatingPoint`] to [`f64`].
|
||||
pub const fn as_f64(self) -> f64 {
|
||||
self.0
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ impl DecimalFloatingPoint {
|
|||
|
||||
impl Eq for DecimalFloatingPoint {}
|
||||
|
||||
// this trait is implemented manually, so it doesn't construct a [DecimalFloatingPoint],
|
||||
// this trait is implemented manually, so it doesn't construct a [`DecimalFloatingPoint`],
|
||||
// with a negative value.
|
||||
impl FromStr for DecimalFloatingPoint {
|
||||
type Err = Error;
|
||||
|
|
|
@ -17,7 +17,7 @@ pub(crate) struct DecimalResolution {
|
|||
}
|
||||
|
||||
impl DecimalResolution {
|
||||
/// Creates a new DecimalResolution.
|
||||
/// Creates a new [`DecimalResolution`].
|
||||
pub const fn new(width: usize, height: usize) -> Self {
|
||||
Self { width, height }
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ impl DecimalResolution {
|
|||
}
|
||||
}
|
||||
|
||||
/// [DecimalResolution] can be constructed from a tuple; `(width, height)`.
|
||||
/// [`DecimalResolution`] can be constructed from a tuple; `(width, height)`.
|
||||
impl From<(usize, usize)> for DecimalResolution {
|
||||
fn from(value: (usize, usize)) -> Self {
|
||||
DecimalResolution::new(value.0, value.1)
|
||||
|
|
|
@ -13,10 +13,10 @@ use crate::Error;
|
|||
|
||||
#[derive(Builder, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
#[builder(setter(into), build_fn(validate = "Self::validate"))]
|
||||
/// [DecryptionKey] contains data, that is shared between [ExtXSessionKey] and [ExtXKey].
|
||||
/// [`DecryptionKey`] contains data, that is shared between [`ExtXSessionKey`] and [`ExtXKey`].
|
||||
///
|
||||
/// [ExtXSessionKey]: crate::tags::ExtXSessionKey
|
||||
/// [ExtXKey]: crate::tags::ExtXKey
|
||||
/// [`ExtXSessionKey`]: crate::tags::ExtXSessionKey
|
||||
/// [`ExtXKey`]: crate::tags::ExtXKey
|
||||
pub struct DecryptionKey {
|
||||
/// The [EncryptionMethod].
|
||||
pub(crate) method: EncryptionMethod,
|
||||
|
|
|
@ -6,7 +6,8 @@ use crate::utils::{quote, tag, unquote};
|
|||
use crate::Error;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
/// KeyFormat specifies, how the key is represented in the resource identified by the URI
|
||||
/// [`KeyFormat`] specifies, how the key is represented in the
|
||||
/// resource identified by the `URI`.
|
||||
pub enum KeyFormat {
|
||||
/// The key is a single packed array of 16 octets in binary format.
|
||||
Identity,
|
||||
|
|
|
@ -8,9 +8,9 @@ use crate::Error;
|
|||
|
||||
/// A list of [usize], that can be used to indicate which version(s)
|
||||
/// this instance complies with, if more than one version of a particular
|
||||
/// [KeyFormat] is defined.
|
||||
/// [`KeyFormat`] is defined.
|
||||
///
|
||||
/// [KeyFormat]: crate::types::KeyFormat
|
||||
/// [`KeyFormat`]: crate::types::KeyFormat
|
||||
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
|
||||
pub struct KeyFormatVersions(Vec<usize>);
|
||||
|
||||
|
@ -21,12 +21,12 @@ impl Default for KeyFormatVersions {
|
|||
}
|
||||
|
||||
impl KeyFormatVersions {
|
||||
/// Makes a new [KeyFormatVersions].
|
||||
/// Makes a new [`KeyFormatVersions`].
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
/// Add a value to the [KeyFormatVersions].
|
||||
/// Add a value to the [`KeyFormatVersions`].
|
||||
pub fn push(&mut self, value: usize) {
|
||||
if self.is_default() {
|
||||
self.0 = vec![value];
|
||||
|
@ -35,7 +35,7 @@ impl KeyFormatVersions {
|
|||
}
|
||||
}
|
||||
|
||||
/// Returns `true`, if [KeyFormatVersions] has the default value of `vec![1]`.
|
||||
/// Returns `true`, if [`KeyFormatVersions`] has the default value of `vec![1]`.
|
||||
pub fn is_default(&self) -> bool {
|
||||
self.0 == vec![1] && self.0.len() == 1 || self.0.is_empty()
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
//! Miscellaneous types.
|
||||
mod byte_range;
|
||||
mod channels;
|
||||
mod closed_captions;
|
||||
mod decimal_floating_point;
|
||||
mod decimal_resolution;
|
||||
|
@ -16,6 +17,7 @@ mod signed_decimal_floating_point;
|
|||
mod stream_inf;
|
||||
|
||||
pub use byte_range::*;
|
||||
pub use channels::*;
|
||||
pub use closed_captions::*;
|
||||
pub(crate) use decimal_floating_point::*;
|
||||
pub(crate) use decimal_resolution::*;
|
||||
|
|
|
@ -28,7 +28,7 @@ pub trait RequiredVersion {
|
|||
}
|
||||
|
||||
/// # [7. Protocol Version Compatibility]
|
||||
/// The [ProtocolVersion] specifies, which m3u8 revision is required, to parse
|
||||
/// The [`ProtocolVersion`] specifies, which m3u8 revision is required, to parse
|
||||
/// a certain tag correctly.
|
||||
///
|
||||
/// [7. Protocol Version Compatibility]:
|
||||
|
@ -46,7 +46,8 @@ pub enum ProtocolVersion {
|
|||
}
|
||||
|
||||
impl ProtocolVersion {
|
||||
/// Returns the newest [ProtocolVersion], that is supported by this library.
|
||||
/// Returns the newest [`ProtocolVersion`], that is supported by
|
||||
/// this library.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
|
|
Loading…
Reference in a new issue