1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-06-09 16:59:34 +00:00

improvements to code

This commit is contained in:
Luro02 2020-02-10 13:21:48 +01:00
parent e6f5091f1b
commit 3a388e3985
No known key found for this signature in database
GPG key ID: B66FD4F74501A9CF
22 changed files with 132 additions and 100 deletions

View file

@ -14,7 +14,7 @@
missing_docs, missing_docs,
missing_copy_implementations, missing_copy_implementations,
missing_debug_implementations, missing_debug_implementations,
trivial_casts, // TODO (needed?) trivial_casts,
trivial_numeric_casts trivial_numeric_casts
)] )]
//! [HLS] m3u8 parser/generator. //! [HLS] m3u8 parser/generator.

View file

@ -391,6 +391,7 @@ fn parse_media_playlist(
segment = MediaSegment::builder(); segment = MediaSegment::builder();
has_partial_segment = false; has_partial_segment = false;
} }
_ => {}
} }
} }

View file

@ -12,10 +12,10 @@ use crate::{Error, RequiredVersion};
/// # [4.4.5.1. EXT-X-MEDIA] /// # [4.4.5.1. EXT-X-MEDIA]
/// ///
/// The [`ExtXMedia`] tag is used to relate [`Media Playlist`]s, /// The [`ExtXMedia`] tag is used to relate [`Media Playlist`]s,
/// that contain alternative Renditions of the same content. /// that contain alternative renditions of the same content.
/// ///
/// For example, three [`ExtXMedia`] tags can be used to identify audio-only /// For example, three [`ExtXMedia`] tags can be used to identify audio-only
/// [`Media Playlist`]s, that contain English, French, and Spanish Renditions /// [`Media Playlist`]s, that contain English, French, and Spanish renditions
/// of the same presentation. Or, two [`ExtXMedia`] tags can be used to /// of the same presentation. Or, two [`ExtXMedia`] tags can be used to
/// identify video-only [`Media Playlist`]s that show two different camera /// identify video-only [`Media Playlist`]s that show two different camera
/// angles. /// angles.
@ -35,7 +35,7 @@ pub struct ExtXMedia {
/// This attribute is **required**. /// This attribute is **required**.
#[shorthand(enable(copy))] #[shorthand(enable(copy))]
media_type: MediaType, media_type: MediaType,
/// The `URI` that identifies the [`Media Playlist`]. /// An `URI` to a [`Media Playlist`].
/// ///
/// # Note /// # Note
/// ///
@ -184,14 +184,18 @@ impl ExtXMedia {
pub(crate) const PREFIX: &'static str = "#EXT-X-MEDIA:"; 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 { pub fn new<T, K>(media_type: MediaType, group_id: T, name: K) -> Self
where
T: Into<String>,
K: Into<String>,
{
Self { Self {
media_type, media_type,
uri: None, uri: None,
group_id: group_id.to_string(), group_id: group_id.into(),
language: None, language: None,
assoc_language: None, assoc_language: None,
name: name.to_string(), name: name.into(),
is_default: false, is_default: false,
is_autoselect: false, is_autoselect: false,
is_forced: false, is_forced: false,

View file

@ -13,7 +13,7 @@ use crate::{Error, RequiredVersion};
#[derive(Hash, Eq, Ord, Debug, PartialEq, Clone, PartialOrd)] #[derive(Hash, Eq, Ord, Debug, PartialEq, Clone, PartialOrd)]
pub enum SessionData { pub enum SessionData {
/// A String, that contains the data identified by /// A String, that contains the data identified by
/// [`data_id`]. /// [`data_id`](ExtXSessionData::data_id).
/// If a [`language`] is specified, the value /// If a [`language`] is specified, the value
/// should contain a human-readable string written in the specified /// should contain a human-readable string written in the specified
/// language. /// language.
@ -51,7 +51,7 @@ pub struct ExtXSessionData {
/// ///
/// [reverse DNS]: https://en.wikipedia.org/wiki/Reverse_domain_name_notation /// [reverse DNS]: https://en.wikipedia.org/wiki/Reverse_domain_name_notation
data_id: String, data_id: String,
/// The data associated with the [`data_id`]. /// The data associated with the [`data_id`](ExtXSessionData::data_id).
/// For more information look [`here`](SessionData). /// For more information look [`here`](SessionData).
/// ///
/// # Note /// # Note
@ -70,6 +70,7 @@ impl ExtXSessionData {
/// Makes a new [`ExtXSessionData`] tag. /// Makes a new [`ExtXSessionData`] tag.
/// ///
/// # Example /// # Example
///
/// ``` /// ```
/// use hls_m3u8::tags::{ExtXSessionData, SessionData}; /// use hls_m3u8::tags::{ExtXSessionData, SessionData};
/// ///
@ -78,9 +79,9 @@ impl ExtXSessionData {
/// SessionData::Uri("https://www.example.com/".to_string()), /// SessionData::Uri("https://www.example.com/".to_string()),
/// ); /// );
/// ``` /// ```
pub fn new<T: ToString>(data_id: T, data: SessionData) -> Self { pub fn new<T: Into<String>>(data_id: T, data: SessionData) -> Self {
Self { Self {
data_id: data_id.to_string(), data_id: data_id.into(),
data, data,
language: None, language: None,
} }
@ -89,6 +90,7 @@ impl ExtXSessionData {
/// Returns a new Builder for [`ExtXSessionData`]. /// Returns a new Builder for [`ExtXSessionData`].
/// ///
/// # Example /// # Example
///
/// ``` /// ```
/// use hls_m3u8::tags::{ExtXSessionData, SessionData}; /// use hls_m3u8::tags::{ExtXSessionData, SessionData};
/// ///
@ -123,11 +125,15 @@ impl ExtXSessionData {
/// "english", /// "english",
/// ); /// );
/// ``` /// ```
pub fn with_language<T: ToString>(data_id: T, data: SessionData, language: T) -> Self { pub fn with_language<T, K>(data_id: T, data: SessionData, language: K) -> Self
where
T: Into<String>,
K: Into<String>,
{
Self { Self {
data_id: data_id.to_string(), data_id: data_id.into(),
data, data,
language: Some(language.to_string()), language: Some(language.into()),
} }
} }
} }

View file

@ -40,7 +40,7 @@ impl ExtXSessionKey {
/// ///
/// let session_key = ExtXSessionKey::new(EncryptionMethod::Aes128, "https://www.example.com/"); /// let session_key = ExtXSessionKey::new(EncryptionMethod::Aes128, "https://www.example.com/");
/// ``` /// ```
pub fn new<T: ToString>(method: EncryptionMethod, uri: T) -> Self { pub fn new<T: Into<String>>(method: EncryptionMethod, uri: T) -> Self {
if method == EncryptionMethod::None { if method == EncryptionMethod::None {
panic!("The EncryptionMethod is not allowed to be None"); panic!("The EncryptionMethod is not allowed to be None");
} }
@ -49,8 +49,8 @@ impl ExtXSessionKey {
} }
} }
/// This tag requires the version returned by /// This tag requires the same [`ProtocolVersion`] that is returned by
/// [`DecryptionKey::required_version`]. /// `DecryptionKey::required_version`.
impl RequiredVersion for ExtXSessionKey { impl RequiredVersion for ExtXSessionKey {
fn required_version(&self) -> ProtocolVersion { self.0.required_version() } fn required_version(&self) -> ProtocolVersion { self.0.required_version() }
} }

View file

@ -1,6 +1,8 @@
use std::fmt; use std::fmt;
use std::str::FromStr; use std::str::FromStr;
use shorthand::ShortHand;
use crate::types::ProtocolVersion; use crate::types::ProtocolVersion;
use crate::utils::tag; use crate::utils::tag;
use crate::RequiredVersion; use crate::RequiredVersion;
@ -15,22 +17,9 @@ use crate::RequiredVersion;
/// [`Media Playlist`]: crate::MediaPlaylist /// [`Media Playlist`]: crate::MediaPlaylist
/// [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]: /// [4.4.3.3. EXT-X-DISCONTINUITY-SEQUENCE]:
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-04#section-4.4.3.3 /// 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)] #[derive(ShortHand, Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ExtXDiscontinuitySequence(u64); #[shorthand(enable(must_use))]
pub struct ExtXDiscontinuitySequence {
impl ExtXDiscontinuitySequence {
pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY-SEQUENCE:";
/// Makes a new [ExtXDiscontinuitySequence] tag.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
/// ```
pub const fn new(seq_num: u64) -> Self { Self(seq_num) }
/// Returns the discontinuity sequence number of /// Returns the discontinuity sequence number of
/// the first media segment that appears in the associated playlist. /// the first media segment that appears in the associated playlist.
/// ///
@ -38,27 +27,26 @@ impl ExtXDiscontinuitySequence {
/// ///
/// ``` /// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence; /// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
///
/// assert_eq!(discontinuity_sequence.seq_num(), 5);
/// ```
pub const fn seq_num(self) -> u64 { self.0 }
/// Sets the sequence number.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let mut discontinuity_sequence = ExtXDiscontinuitySequence::new(5); /// let mut discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
/// ///
/// discontinuity_sequence.set_seq_num(10); /// discontinuity_sequence.set_seq_num(10);
/// assert_eq!(discontinuity_sequence.seq_num(), 10); /// assert_eq!(discontinuity_sequence.seq_num(), 10);
/// ``` /// ```
pub fn set_seq_num(&mut self, value: u64) -> &mut Self { seq_num: u64,
self.0 = value; }
self
} impl ExtXDiscontinuitySequence {
pub(crate) const PREFIX: &'static str = "#EXT-X-DISCONTINUITY-SEQUENCE:";
/// Makes a new [`ExtXDiscontinuitySequence`] tag.
///
/// # Example
///
/// ```
/// # use hls_m3u8::tags::ExtXDiscontinuitySequence;
/// let discontinuity_sequence = ExtXDiscontinuitySequence::new(5);
/// ```
pub const fn new(seq_num: u64) -> Self { Self { seq_num } }
} }
/// This tag requires [`ProtocolVersion::V1`]. /// This tag requires [`ProtocolVersion::V1`].
@ -69,7 +57,7 @@ impl RequiredVersion for ExtXDiscontinuitySequence {
impl fmt::Display for ExtXDiscontinuitySequence { impl fmt::Display for ExtXDiscontinuitySequence {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// //
write!(f, "{}{}", Self::PREFIX, self.0) write!(f, "{}{}", Self::PREFIX, self.seq_num)
} }
} }

View file

@ -121,7 +121,7 @@ pub struct ExtXDateRange {
impl ExtXDateRangeBuilder { impl ExtXDateRangeBuilder {
/// Inserts a key value pair. /// Inserts a key value pair.
pub fn insert_client_attribute<K: ToString, V: Into<Value>>( pub fn insert_client_attribute<K: Into<String>, V: Into<Value>>(
&mut self, &mut self,
key: K, key: K,
value: V, value: V,
@ -131,7 +131,7 @@ impl ExtXDateRangeBuilder {
} }
if let Some(client_attributes) = &mut self.client_attributes { if let Some(client_attributes) = &mut self.client_attributes {
client_attributes.insert(key.to_string(), value.into()); client_attributes.insert(key.into(), value.into());
} else { } else {
unreachable!(); unreachable!();
} }
@ -160,9 +160,9 @@ impl ExtXDateRange {
/// .and_hms_milli(14, 54, 23, 31), /// .and_hms_milli(14, 54, 23, 31),
/// ); /// );
/// ``` /// ```
pub fn new<T: ToString>(id: T, start_date: DateTime<FixedOffset>) -> Self { pub fn new<T: Into<String>>(id: T, start_date: DateTime<FixedOffset>) -> Self {
Self { Self {
id: id.to_string(), id: id.into(),
class: None, class: None,
start_date, start_date,
end_date: None, end_date: None,
@ -281,7 +281,7 @@ impl fmt::Display for ExtXDateRange {
write!( write!(
f, f,
",END-DATE={}", ",END-DATE={}",
quote(value.to_rfc3339_opts(SecondsFormat::AutoSi, true)) quote(&value.to_rfc3339_opts(SecondsFormat::AutoSi, true))
)?; )?;
} }

View file

@ -49,10 +49,10 @@ impl ExtInf {
/// ///
/// let ext_inf = ExtInf::with_title(Duration::from_secs(5), "title"); /// let ext_inf = ExtInf::with_title(Duration::from_secs(5), "title");
/// ``` /// ```
pub fn with_title<T: ToString>(duration: Duration, title: T) -> Self { pub fn with_title<T: Into<String>>(duration: Duration, title: T) -> Self {
Self { Self {
duration, duration,
title: Some(title.to_string()), title: Some(title.into()),
} }
} }
@ -156,9 +156,9 @@ impl FromStr for ExtInf {
let duration = Duration::from_secs_f64(input.next().unwrap().parse()?); let duration = Duration::from_secs_f64(input.next().unwrap().parse()?);
let title = input let title = input
.next() .next()
.map(|value| value.trim()) .map(str::trim)
.filter(|value| !value.is_empty()) .filter(|value| !value.is_empty())
.map(|value| value.to_string()); .map(ToString::to_string);
Ok(Self { duration, title }) Ok(Self { duration, title })
} }

View file

@ -45,7 +45,7 @@ impl ExtXKey {
/// "#EXT-X-KEY:METHOD=AES-128,URI=\"https://www.example.com/\"" /// "#EXT-X-KEY:METHOD=AES-128,URI=\"https://www.example.com/\""
/// ); /// );
/// ``` /// ```
pub fn new<T: ToString>(method: EncryptionMethod, uri: T) -> Self { pub fn new<T: Into<String>>(method: EncryptionMethod, uri: T) -> Self {
Self(DecryptionKey::new(method, uri)) Self(DecryptionKey::new(method, uri))
} }
@ -87,8 +87,8 @@ impl ExtXKey {
pub fn is_empty(&self) -> bool { self.0.method() == EncryptionMethod::None } pub fn is_empty(&self) -> bool { self.0.method() == EncryptionMethod::None }
} }
/// This tag requires the [`ProtocolVersion`] returned by /// This tag requires the same [`ProtocolVersion`] that is returned by
/// [`DecryptionKey::required_version`]. /// `DecryptionKey::required_version`.
impl RequiredVersion for ExtXKey { impl RequiredVersion for ExtXKey {
fn required_version(&self) -> ProtocolVersion { self.0.required_version() } fn required_version(&self) -> ProtocolVersion { self.0.required_version() }
} }
@ -103,7 +103,10 @@ impl FromStr for ExtXKey {
} }
impl fmt::Display for ExtXKey { impl fmt::Display for ExtXKey {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}{}", Self::PREFIX, self.0) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//
write!(f, "{}{}", Self::PREFIX, self.0)
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -69,9 +69,9 @@ impl ExtXMap {
/// # use hls_m3u8::tags::ExtXMap; /// # use hls_m3u8::tags::ExtXMap;
/// let map = ExtXMap::new("https://prod.mediaspace.com/init.bin"); /// let map = ExtXMap::new("https://prod.mediaspace.com/init.bin");
/// ``` /// ```
pub fn new<T: ToString>(uri: T) -> Self { pub fn new<T: Into<String>>(uri: T) -> Self {
Self { Self {
uri: uri.to_string(), uri: uri.into(),
range: None, range: None,
keys: vec![], keys: vec![],
} }
@ -90,9 +90,9 @@ impl ExtXMap {
/// ByteRange::new(9, Some(2)), /// ByteRange::new(9, Some(2)),
/// ); /// );
/// ``` /// ```
pub fn with_range<T: ToString>(uri: T, range: ByteRange) -> Self { pub fn with_range<T: Into<String>>(uri: T, range: ByteRange) -> Self {
Self { Self {
uri: uri.to_string(), uri: uri.into(),
range: Some(range), range: Some(range),
keys: vec![], keys: vec![],
} }

View file

@ -7,8 +7,13 @@ use crate::{Error, RequiredVersion};
/// # [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS] /// # [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]
/// ///
/// The [`ExtXIndependentSegments`] tag signals that all media samples in a
/// [`MediaSegment`] can be decoded without information from other segments.
///
/// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]: /// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]:
/// https://tools.ietf.org/html/rfc8216#section-4.3.5.1 /// https://tools.ietf.org/html/rfc8216#section-4.3.5.1
///
/// [`MediaSegment`]: crate::MediaSegment
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub struct ExtXIndependentSegments; pub struct ExtXIndependentSegments;

View file

@ -1,3 +1,11 @@
//! The tags in this section can appear in either Master Playlists or
//! Media Playlists. If one of these tags appears in a Master Playlist,
//! it should not appear in any Media Playlist referenced by that Master
//! Playlist. A tag that appears in both must have the same value;
//! otherwise, clients should ignore the value in the Media Playlist(s).
//!
//! These tags must not appear more than once in a Playlist. If a tag
//! appears more than once, clients must fail to parse the Playlist.
mod independent_segments; mod independent_segments;
mod start; mod start;

View file

@ -18,7 +18,9 @@ use crate::{Error, RequiredVersion};
#[builder(setter(into), build_fn(validate = "Self::validate"))] #[builder(setter(into), build_fn(validate = "Self::validate"))]
#[shorthand(enable(must_use, into))] #[shorthand(enable(must_use, into))]
pub struct DecryptionKey { pub struct DecryptionKey {
/// The [`EncryptionMethod`]. /// HLS supports multiple encryption methods for a segment.
///
/// For example `AES-128`.
/// ///
/// # Example /// # Example
/// ///
@ -41,9 +43,10 @@ pub struct DecryptionKey {
/// This attribute is required. /// This attribute is required.
#[shorthand(enable(copy))] #[shorthand(enable(copy))]
pub(crate) method: EncryptionMethod, pub(crate) method: EncryptionMethod,
/// An `URI`, that specifies how to obtain the key. /// An `URI` that specifies how to obtain the key.
/// ///
/// # Example /// # Example
///
/// ``` /// ```
/// # use hls_m3u8::types::DecryptionKey; /// # use hls_m3u8::types::DecryptionKey;
/// use hls_m3u8::types::EncryptionMethod; /// use hls_m3u8::types::EncryptionMethod;
@ -63,8 +66,10 @@ pub struct DecryptionKey {
/// This attribute is required, if the [`EncryptionMethod`] is not `None`. /// This attribute is required, if the [`EncryptionMethod`] is not `None`.
#[builder(setter(into, strip_option), default)] #[builder(setter(into, strip_option), default)]
pub(crate) uri: Option<String>, pub(crate) uri: Option<String>,
/// The IV (Initialization Vector) for the key. /// An IV (initialization vector) is used to prevent repetitions between
/// segments of encrypted data.
/// ///
/// <https://en.wikipedia.org/wiki/Initialization_vector>
/// ///
/// # Example /// # Example
/// ///
@ -90,7 +95,7 @@ pub struct DecryptionKey {
// TODO: workaround for https://github.com/Luro02/shorthand/issues/20 // TODO: workaround for https://github.com/Luro02/shorthand/issues/20
#[shorthand(enable(copy), disable(option_as_ref))] #[shorthand(enable(copy), disable(option_as_ref))]
pub(crate) iv: Option<[u8; 16]>, pub(crate) iv: Option<[u8; 16]>,
/// [`KeyFormat`] specifies how the key is /// The [`KeyFormat`] specifies how the key is
/// represented in the resource identified by the `URI`. /// represented in the resource identified by the `URI`.
/// ///
/// # Example /// # Example
@ -157,23 +162,25 @@ impl DecryptionKey {
/// ///
/// let key = DecryptionKey::new(EncryptionMethod::Aes128, "https://www.example.com/"); /// let key = DecryptionKey::new(EncryptionMethod::Aes128, "https://www.example.com/");
/// ``` /// ```
pub fn new<T: ToString>(method: EncryptionMethod, uri: T) -> Self { #[doc(hidden)]
pub fn new<T: Into<String>>(method: EncryptionMethod, uri: T) -> Self {
Self { Self {
method, method,
uri: Some(uri.to_string()), uri: Some(uri.into()),
iv: None, iv: None,
key_format: None, key_format: None,
key_format_versions: None, key_format_versions: None,
} }
} }
/// Returns a Builder to build a [DecryptionKey]. /// Returns a Builder to build a [`DecryptionKey`].
pub fn builder() -> DecryptionKeyBuilder { DecryptionKeyBuilder::default() } pub fn builder() -> DecryptionKeyBuilder { DecryptionKeyBuilder::default() }
} }
/// This tag requires [`ProtocolVersion::V5`], if [`KeyFormat`] or /// This tag requires [`ProtocolVersion::V5`], if [`KeyFormat`] or
/// [`KeyFormatVersions`] is specified and [`ProtocolVersion::V2`] if an iv is /// [`KeyFormatVersions`] is specified and [`ProtocolVersion::V2`] if an iv is
/// specified. /// specified.
///
/// Otherwise [`ProtocolVersion::V1`] is required. /// Otherwise [`ProtocolVersion::V1`] is required.
impl RequiredVersion for DecryptionKey { impl RequiredVersion for DecryptionKey {
fn required_version(&self) -> ProtocolVersion { fn required_version(&self) -> ProtocolVersion {
@ -187,6 +194,7 @@ impl RequiredVersion for DecryptionKey {
} }
} }
#[doc(hidden)]
impl FromStr for DecryptionKey { impl FromStr for DecryptionKey {
type Err = Error; type Err = Error;

View file

@ -5,15 +5,16 @@ use strum::{Display, EnumString};
/// See: [4.3.2.4. EXT-X-KEY] /// See: [4.3.2.4. EXT-X-KEY]
/// ///
/// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4 /// [4.3.2.4. EXT-X-KEY]: https://tools.ietf.org/html/rfc8216#section-4.3.2.4
#[non_exhaustive]
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)] #[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[strum(serialize_all = "SCREAMING-KEBAB-CASE")] #[strum(serialize_all = "SCREAMING-KEBAB-CASE")]
pub enum EncryptionMethod { pub enum EncryptionMethod {
/// `None` means that the [`MediaSegment`]s are not encrypted. /// `None` means that the [`MediaSegment`]s are not encrypted.
/// ///
/// [MediaSegment]: crate::MediaSegment /// [`MediaSegment`]: crate::MediaSegment
None, None,
/// `Aes128` signals that the [MediaSegment]s are completely encrypted /// `Aes128` signals that the [`MediaSegment`]s are completely encrypted
/// using the Advanced Encryption Standard ([AES-128]) with a 128-bit /// using the Advanced Encryption Standard ([AES-128]) with a 128-bit
/// key, Cipher Block Chaining (CBC), and /// key, Cipher Block Chaining (CBC), and
/// [Public-Key Cryptography Standards #7 (PKCS7)] padding. /// [Public-Key Cryptography Standards #7 (PKCS7)] padding.
@ -22,14 +23,14 @@ pub enum EncryptionMethod {
/// Initialization Vector (IV) attribute value or the Media Sequence /// Initialization Vector (IV) attribute value or the Media Sequence
/// Number as the IV. /// Number as the IV.
/// ///
/// [MediaSegment]: crate::MediaSegment /// [`MediaSegment`]: crate::MediaSegment
/// [AES_128]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf /// [AES-128]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf
/// [Public-Key Cryptography Standards #7 (PKCS7)]: https://tools.ietf.org/html/rfc5652 /// [Public-Key Cryptography Standards #7 (PKCS7)]: https://tools.ietf.org/html/rfc5652
#[strum(serialize = "AES-128")] #[strum(serialize = "AES-128")]
Aes128, Aes128,
/// `SampleAes` means that the [MediaSegment]s /// `SampleAes` means that the [`MediaSegment`]s
/// contain media samples, such as audio or video, that are encrypted /// contain media samples, such as audio or video, that are encrypted
/// using the Advanced Encryption Standard ([AES_128]). How these media /// using the Advanced Encryption Standard ([`AES-128`]). How these media
/// streams are encrypted and encapsulated in a segment depends on the /// streams are encrypted and encapsulated in a segment depends on the
/// media encoding and the media format of the segment. fMP4 Media /// media encoding and the media format of the segment. fMP4 Media
/// Segments are encrypted using the 'cbcs' scheme of /// Segments are encrypted using the 'cbcs' scheme of
@ -39,7 +40,7 @@ pub enum EncryptionMethod {
/// Live Streaming (HLS) [SampleEncryption specification]. /// Live Streaming (HLS) [SampleEncryption specification].
/// ///
/// [`MediaSegment`]: crate::MediaSegment /// [`MediaSegment`]: crate::MediaSegment
/// [AES-128]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf /// [`AES-128`]: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.197.pdf
/// [Common Encryption]: https://tools.ietf.org/html/rfc8216#ref-COMMON_ENC /// [Common Encryption]: https://tools.ietf.org/html/rfc8216#ref-COMMON_ENC
/// [H.264]: https://tools.ietf.org/html/rfc8216#ref-H_264 /// [H.264]: https://tools.ietf.org/html/rfc8216#ref-H_264
/// [AAC]: https://tools.ietf.org/html/rfc8216#ref-ISO_14496 /// [AAC]: https://tools.ietf.org/html/rfc8216#ref-ISO_14496

View file

@ -5,6 +5,7 @@ use strum::{Display, EnumString};
/// See: [4.3.4.2. EXT-X-STREAM-INF] /// See: [4.3.4.2. EXT-X-STREAM-INF]
/// ///
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2 /// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
#[non_exhaustive]
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)] #[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[strum(serialize_all = "SCREAMING-KEBAB-CASE")] #[strum(serialize_all = "SCREAMING-KEBAB-CASE")]

View file

@ -5,9 +5,11 @@ use strum::{Display, EnumString};
/// See: [4.3.4.1. EXT-X-MEDIA] /// See: [4.3.4.1. EXT-X-MEDIA]
/// ///
/// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1 /// [4.3.4.1. EXT-X-MEDIA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.1
#[non_exhaustive]
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[strum(serialize_all = "UPPERCASE")] #[strum(serialize_all = "UPPERCASE")]
#[derive(Ord, PartialOrd, Debug, Clone, Copy, PartialEq, Eq, Hash, Display, EnumString)]
#[non_exhaustive]
pub enum InStreamId { pub enum InStreamId {
Cc1, Cc1,
Cc2, Cc2,

View file

@ -7,6 +7,7 @@ use crate::{Error, RequiredVersion};
/// [`KeyFormat`] specifies, how the key is represented in the /// [`KeyFormat`] specifies, how the key is represented in the
/// resource identified by the `URI`. /// resource identified by the `URI`.
#[non_exhaustive]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum KeyFormat { pub enum KeyFormat {
/// The key is a single packed array of 16 octets in binary format. /// The key is a single packed array of 16 octets in binary format.
@ -28,7 +29,7 @@ impl FromStr for KeyFormat {
} }
impl fmt::Display for KeyFormat { impl fmt::Display for KeyFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", quote("identity")) } fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", quote(&"identity")) }
} }
/// This tag requires [`ProtocolVersion::V5`]. /// This tag requires [`ProtocolVersion::V5`].

View file

@ -31,7 +31,10 @@ impl KeyFormatVersions {
/// Returns `true`, if [`KeyFormatVersions`] has the default value of /// Returns `true`, if [`KeyFormatVersions`] has the default value of
/// `vec![1]`. /// `vec![1]`.
pub fn is_default(&self) -> bool { self.0 == vec![1] && self.0.len() == 1 || self.0.is_empty() } pub fn is_default(&self) -> bool {
//
self.0 == vec![1] && self.0.len() == 1 || self.0.is_empty()
}
} }
impl Default for KeyFormatVersions { impl Default for KeyFormatVersions {
@ -73,7 +76,7 @@ impl fmt::Display for KeyFormatVersions {
// vec![1, 2, 3] -> "1/2/3" // vec![1, 2, 3] -> "1/2/3"
self.0 self.0
.iter() .iter()
.map(|v| v.to_string()) .map(ToString::to_string)
.collect::<Vec<String>>() .collect::<Vec<String>>()
.join("/") .join("/")
) )

View file

@ -1,6 +1,7 @@
use strum::{Display, EnumString}; use strum::{Display, EnumString};
/// Specifies the media type. /// Specifies the media type.
#[non_exhaustive]
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Ord, PartialOrd, Display, EnumString, Debug, Clone, Copy, PartialEq, Eq, Hash)] #[derive(Ord, PartialOrd, Display, EnumString, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[strum(serialize_all = "SCREAMING-KEBAB-CASE")] #[strum(serialize_all = "SCREAMING-KEBAB-CASE")]

View file

@ -10,6 +10,7 @@ use crate::Error;
/// ///
/// [7. Protocol Version Compatibility]: /// [7. Protocol Version Compatibility]:
/// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-7 /// https://tools.ietf.org/html/draft-pantos-hls-rfc8216bis-05#section-7
#[non_exhaustive]
#[allow(missing_docs)] #[allow(missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum ProtocolVersion { pub enum ProtocolVersion {

View file

@ -6,8 +6,9 @@ use hex;
use crate::utils::{quote, unquote}; use crate::utils::{quote, unquote};
use crate::Error; use crate::Error;
#[derive(Debug, Clone, PartialEq, PartialOrd)]
/// A [`Value`]. /// A [`Value`].
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub enum Value { pub enum Value {
/// A [`String`]. /// A [`String`].
String(String), String(String),

View file

@ -1,4 +1,5 @@
use crate::Error; use crate::Error;
use core::iter;
macro_rules! required_version { macro_rules! required_version {
( $( $tag:expr ),* ) => { ( $( $tag:expr ),* ) => {
@ -22,14 +23,7 @@ pub(crate) fn parse_iv_from_str(input: &str) -> crate::Result<[u8; 16]> {
let mut result = [0; 16]; let mut result = [0; 16];
// TODO: hex::decode_to_slice(&input.as_bytes()[2..], &mut result).map_err(Error::hex)?;
// hex::decode_to_slice(value.as_bytes()[2..], &mut result)?;
for (i, c) in input.as_bytes().chunks(2).skip(1).enumerate() {
let d = core::str::from_utf8(c).map_err(Error::custom)?;
let b = u8::from_str_radix(d, 16).map_err(Error::custom)?;
result[i] = b;
}
Ok(result) Ok(result)
} }
@ -50,19 +44,23 @@ pub(crate) fn parse_yes_or_no<T: AsRef<str>>(s: T) -> crate::Result<bool> {
/// ///
/// Therefore it is safe to simply remove any occurence of those characters. /// Therefore it is safe to simply remove any occurence of those characters.
/// [rfc8216#section-4.2](https://tools.ietf.org/html/rfc8216#section-4.2) /// [rfc8216#section-4.2](https://tools.ietf.org/html/rfc8216#section-4.2)
pub(crate) fn unquote<T: ToString>(value: T) -> String { pub(crate) fn unquote<T: AsRef<str>>(value: T) -> String {
value value
.to_string() .as_ref()
.replace("\"", "") .chars()
.replace("\n", "") .filter(|c| *c != '"' && *c != '\n' && *c != '\r')
.replace("\r", "") .collect()
} }
/// Puts a string inside quotes. /// Puts a string inside quotes.
#[allow(clippy::needless_pass_by_value)]
pub(crate) fn quote<T: ToString>(value: T) -> String { pub(crate) fn quote<T: ToString>(value: T) -> String {
// the replace is for the case, that quote is called on an already quoted // the replace is for the case, that quote is called on an already quoted
// string, which could cause problems! // string, which could cause problems!
format!("\"{}\"", value.to_string().replace("\"", "")) iter::once('"')
.chain(value.to_string().chars().filter(|c| *c != '"'))
.chain(iter::once('"'))
.collect()
} }
/// Checks, if the given tag is at the start of the input. If this is the case, /// Checks, if the given tag is at the start of the input. If this is the case,