1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-11-25 16:41:00 +00:00

improve MediaSegment

This commit is contained in:
Luro02 2020-03-25 12:18:34 +01:00
parent 42e1afaa47
commit ca302ef543
No known key found for this signature in database
GPG key ID: B66FD4F74501A9CF
4 changed files with 196 additions and 42 deletions

View file

@ -9,39 +9,176 @@ use crate::tags::{
use crate::types::{DecryptionKey, ProtocolVersion}; use crate::types::{DecryptionKey, ProtocolVersion};
use crate::{Decryptable, RequiredVersion}; use crate::{Decryptable, RequiredVersion};
/// Media segment. /// A video is split into smaller chunks called [`MediaSegment`]s, which are
#[derive(ShortHand, Debug, Clone, Builder, PartialEq, PartialOrd)] /// specified by a uri and optionally a byte range.
#[builder(setter(into, strip_option))] ///
#[shorthand(enable(must_use, get_mut, collection_magic))] /// Each `MediaSegment` must carry the continuation of the encoded bitstream
/// from the end of the segment with the previous [`MediaSegment::number`],
/// where values in a series such as timestamps and continuity counters must
/// continue uninterrupted. The only exceptions are the first [`MediaSegment`]
/// ever to appear in a [`MediaPlaylist`] and [`MediaSegment`]s that are
/// explicitly signaled as discontinuities.
/// Unmarked media discontinuities can trigger playback errors.
///
/// Any `MediaSegment` that contains video should include enough information
/// to initialize a video decoder and decode a continuous set of frames that
/// includes the final frame in the segment; network efficiency is optimized if
/// there is enough information in the segment to decode all frames in the
/// segment.
///
/// For example, any `MediaSegment` containing H.264 video should
/// contain an Instantaneous Decoding Refresh (IDR); frames prior to the first
/// IDR will be downloaded but possibly discarded.
///
/// [`MediaPlaylist`]: crate::MediaPlaylist
#[derive(ShortHand, Debug, Clone, Builder, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[builder(setter(strip_option))]
#[shorthand(enable(must_use))]
pub struct MediaSegment { pub struct MediaSegment {
/// All [`ExtXKey`] tags. /// Each [`MediaSegment`] has a number, which allows synchronization between
/// different variants.
///
/// ## Note
///
/// This number must not be specified, because it will be assigned
/// automatically by [`MediaPlaylistBuilder::segments`]. The first
/// [`MediaSegment::number`] in a [`MediaPlaylist`] will either be 0 or the
/// number returned by the [`ExtXDiscontinuitySequence`] if one is
/// provided.
/// The following segments will be the previous segment number + 1.
///
/// [`MediaPlaylistBuilder::segments`]:
/// crate::builder::MediaPlaylistBuilder::segments
/// [`MediaPlaylist`]: crate::MediaPlaylist
/// [`ExtXMediaSequence`]: crate::tags::ExtXMediaSequence
/// [`ExtXDiscontinuitySequence`]: crate::tags::ExtXDiscontinuitySequence
#[builder(default, setter(custom))]
#[shorthand(disable(set))]
pub(crate) number: usize,
#[builder(default, setter(custom))]
#[shorthand(enable(skip))]
pub(crate) explicit_number: bool,
/// This field specifies how to decrypt a [`MediaSegment`], which can only
/// be encrypted with one [`EncryptionMethod`], using one [`DecryptionKey`]
/// and [`DecryptionKey::iv`].
///
/// However, a server may offer multiple ways to retrieve that key by
/// providing multiple keys with different [`DecryptionKey::format`]s.
///
/// Any unencrypted segment that is preceded by an encrypted segment must
/// have an [`ExtXKey::empty`]. Otherwise, the client will misinterpret
/// those segments as encrypted.
///
/// The server may set the HTTP Expires header in the key response to
/// indicate the duration for which the key can be cached.
///
/// ## Note
///
/// This field is optional and a missing value or an [`ExtXKey::empty()`]
/// indicates an unencrypted media segment.
///
/// [`ExtXMap`]: crate::tags::ExtXMap
/// [`KeyFormat`]: crate::types::KeyFormat
/// [`EncryptionMethod`]: crate::types::EncryptionMethod
#[builder(default, setter(into))]
#[shorthand(enable(skip))]
pub keys: Vec<ExtXKey>,
/// This field specifies how to obtain the Media Initialization Section
/// required to parse the applicable `MediaSegment`s.
///
/// ## Note
///
/// This field is optional, but should be specified for media segments in
/// playlists with an [`ExtXIFramesOnly`] tag when the first `MediaSegment`
/// in the playlist (or the first segment following a segment marked with
/// [`MediaSegment::has_discontinuity`]) does not immediately follow the
/// Media Initialization Section at the beginning of its resource.
///
/// [`ExtXIFramesOnly`]: crate::tags::ExtXIFramesOnly
#[builder(default)] #[builder(default)]
keys: Vec<ExtXKey>, #[shorthand(enable(skip))]
/// The [`ExtXMap`] tag associated with the media segment. pub map: Option<ExtXMap>,
/// This field indicates that a `MediaSegment` is a sub-range of the
/// resource identified by its URI.
///
/// ## Note
///
/// This field is optional.
#[builder(default, setter(into))]
#[shorthand(enable(skip))]
pub byte_range: Option<ExtXByteRange>,
/// This field associates a date-range (i.e., a range of time defined by a
/// starting and ending date) with a set of attribute/value pairs.
///
/// ## Note
///
/// This field is optional.
#[builder(default)] #[builder(default)]
map: Option<ExtXMap>, #[shorthand(enable(skip))]
/// The [`ExtXByteRange`] tag associated with the [`MediaSegment`]. pub date_range: Option<ExtXDateRange>,
/// This field indicates a discontinuity between the `MediaSegment` that
/// follows it and the one that preceded it.
///
/// ## Note
///
/// This field is required if any of the following characteristics change:
/// - file format
/// - number, type, and identifiers of tracks
/// - timestamp, sequence
///
/// This field should be present if any of the following characteristics
/// change:
/// - encoding parameters
/// - encoding sequence
#[builder(default)] #[builder(default)]
byte_range: Option<ExtXByteRange>, #[shorthand(enable(skip))]
/// The [`ExtXDateRange`] tag associated with the media segment. pub has_discontinuity: bool,
/// This field associates the first sample of a media segment with an
/// absolute date and/or time.
///
/// ## Note
///
/// This field is optional.
#[builder(default)] #[builder(default)]
date_range: Option<ExtXDateRange>, #[shorthand(enable(skip))]
/// The [`ExtXDiscontinuity`] tag associated with the media segment. pub program_date_time: Option<ExtXProgramDateTime>,
#[builder(default)] /// This field indicates the duration of a media segment.
discontinuity: Option<ExtXDiscontinuity>, ///
/// The [`ExtXProgramDateTime`] tag associated with the media /// ## Note
/// segment. ///
#[builder(default)] /// This field is required.
program_date_time: Option<ExtXProgramDateTime>, #[shorthand(enable(skip))]
/// The [`ExtInf`] tag associated with the [`MediaSegment`]. #[builder(setter(into))]
inf: ExtInf, pub inf: ExtInf,
/// The `URI` of the [`MediaSegment`]. /// The URI of a media segment.
///
/// ## Note
///
/// This field is required.
#[builder(setter(into))]
#[shorthand(enable(into))] #[shorthand(enable(into))]
uri: String, uri: String,
} }
impl MediaSegment { impl MediaSegment {
/// Returns a builder for a [`MediaSegment`]. /// Returns a builder for a [`MediaSegment`].
///
/// # Example
///
/// ```
/// # use hls_m3u8::MediaSegment;
/// use hls_m3u8::tags::ExtXMap;
/// use std::time::Duration;
///
/// let segment = MediaSegment::builder()
/// .map(ExtXMap::new("https://www.example.com/"))
/// .byte_range(5..25)
/// .has_discontinuity(true)
/// .inf(Duration::from_secs(4))
/// .uri("http://www.uri.com/")
/// .build()?;
/// # Ok::<(), String>(())
/// ```
#[must_use] #[must_use]
#[inline] #[inline]
pub fn builder() -> MediaSegmentBuilder { MediaSegmentBuilder::default() } pub fn builder() -> MediaSegmentBuilder { MediaSegmentBuilder::default() }
@ -58,11 +195,23 @@ impl MediaSegmentBuilder {
self self
} }
/// The number of a [`MediaSegment`]. Normally this should not be set
/// explicitly, because the [`MediaPlaylist::builder`] will automatically
/// apply the correct number.
///
/// [`MediaPlaylist::builder`]: crate::MediaPlaylist::builder
pub fn number(&mut self, value: Option<usize>) -> &mut Self {
self.number = value;
self.explicit_number = Some(value.is_some());
self
}
} }
impl fmt::Display for MediaSegment { impl fmt::Display for MediaSegment {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
// self.keys will be printed by MediaPlaylist! // NOTE: self.keys will be printed by the `MediaPlaylist` to prevent redundance.
if let Some(value) = &self.map { if let Some(value) = &self.map {
writeln!(f, "{}", value)?; writeln!(f, "{}", value)?;
@ -76,15 +225,15 @@ impl fmt::Display for MediaSegment {
writeln!(f, "{}", value)?; writeln!(f, "{}", value)?;
} }
if let Some(value) = &self.discontinuity { if self.has_discontinuity {
writeln!(f, "{}", value)?; writeln!(f, "{}", ExtXDiscontinuity)?;
} }
if let Some(value) = &self.program_date_time { if let Some(value) = &self.program_date_time {
writeln!(f, "{}", value)?; writeln!(f, "{}", value)?;
} }
writeln!(f, "{}", self.inf)?; // TODO: there might be a `,` missing writeln!(f, "{}", self.inf)?;
writeln!(f, "{}", self.uri)?; writeln!(f, "{}", self.uri)?;
Ok(()) Ok(())
} }
@ -97,7 +246,13 @@ impl RequiredVersion for MediaSegment {
self.map, self.map,
self.byte_range, self.byte_range,
self.date_range, self.date_range,
self.discontinuity, {
if self.has_discontinuity {
Some(ExtXDiscontinuity)
} else {
None
}
},
self.program_date_time, self.program_date_time,
self.inf self.inf
] ]
@ -123,8 +278,7 @@ mod tests {
MediaSegment::builder() MediaSegment::builder()
.map(ExtXMap::new("https://www.example.com/")) .map(ExtXMap::new("https://www.example.com/"))
.byte_range(ExtXByteRange::from(5..25)) .byte_range(ExtXByteRange::from(5..25))
//.date_range() // TODO! .has_discontinuity(true)
.discontinuity(ExtXDiscontinuity)
.inf(ExtInf::new(Duration::from_secs(4))) .inf(ExtInf::new(Duration::from_secs(4)))
.uri("http://www.uri.com/") .uri("http://www.uri.com/")
.build() .build()

View file

@ -1,15 +1,15 @@
mod byte_range; pub(crate) mod byte_range;
mod date_range; pub(crate) mod date_range;
mod discontinuity; pub(crate) mod discontinuity;
mod inf; pub(crate) mod inf;
mod key; pub(crate) mod key;
mod map; pub(crate) mod map;
mod program_date_time; pub(crate) mod program_date_time;
pub use byte_range::*; pub use byte_range::*;
pub use date_range::*; pub use date_range::ExtXDateRange;
pub(crate) use discontinuity::*; pub(crate) use discontinuity::*;
pub use inf::*; pub use inf::*;
pub use key::*; pub use key::ExtXKey;
pub use map::*; pub use map::*;
pub use program_date_time::*; pub use program_date_time::*;

View file

@ -10,7 +10,7 @@ use crate::{Error, RequiredVersion};
/// ///
/// [`MediaSegment`]: crate::MediaSegment /// [`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(crate) struct ExtXIndependentSegments;
impl ExtXIndependentSegments { impl ExtXIndependentSegments {
pub(crate) const PREFIX: &'static str = "#EXT-X-INDEPENDENT-SEGMENTS"; pub(crate) const PREFIX: &'static str = "#EXT-X-INDEPENDENT-SEGMENTS";

View file

@ -1,5 +1,5 @@
mod independent_segments; pub(crate) mod independent_segments;
mod start; pub(crate) mod start;
pub use independent_segments::*; pub(crate) use independent_segments::ExtXIndependentSegments;
pub use start::*; pub use start::*;