mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-11-21 14:50:59 +00:00
commit
ed64ed15d3
12 changed files with 359 additions and 352 deletions
|
@ -64,6 +64,7 @@ impl FromStr for AttributePairs {
|
|||
result.insert(key.to_string(), value.to_string());
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
dbg!(&result);
|
||||
Ok(result)
|
||||
}
|
||||
|
|
|
@ -369,13 +369,13 @@ mod tests {
|
|||
#[test]
|
||||
fn test_parser() {
|
||||
r#"#EXTM3U
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=150000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=416x234
|
||||
http://example.com/low/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=240000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=240000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=416x234
|
||||
http://example.com/lo_mid/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=440000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=440000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=416x234
|
||||
http://example.com/hi_mid/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=640000,RESOLUTION=640x360,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=640000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=640x360
|
||||
http://example.com/high/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS="mp4a.40.5"
|
||||
http://example.com/audio/index.m3u8
|
||||
|
@ -387,13 +387,13 @@ http://example.com/audio/index.m3u8
|
|||
#[test]
|
||||
fn test_display() {
|
||||
let input = r#"#EXTM3U
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=150000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=150000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=416x234
|
||||
http://example.com/low/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=240000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=240000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=416x234
|
||||
http://example.com/lo_mid/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=440000,RESOLUTION=416x234,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=440000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=416x234
|
||||
http://example.com/hi_mid/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=640000,RESOLUTION=640x360,CODECS="avc1.42e00a,mp4a.40.2"
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=640000,CODECS="avc1.42e00a,mp4a.40.2",RESOLUTION=640x360
|
||||
http://example.com/high/index.m3u8
|
||||
#EXT-X-STREAM-INF:BANDWIDTH=64000,CODECS="mp4a.40.5"
|
||||
http://example.com/audio/index.m3u8
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::attribute::AttributePairs;
|
||||
use crate::types::{DecimalResolution, HdcpLevel, ProtocolVersion};
|
||||
use crate::utils::parse_u64;
|
||||
use crate::types::{ProtocolVersion, StreamInf};
|
||||
use crate::utils::{quote, tag, unquote};
|
||||
use crate::Error;
|
||||
|
||||
|
@ -13,12 +13,7 @@ use crate::Error;
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ExtXIFrameStreamInf {
|
||||
uri: String,
|
||||
bandwidth: u64,
|
||||
average_bandwidth: Option<u64>,
|
||||
codecs: Option<String>,
|
||||
resolution: Option<DecimalResolution>,
|
||||
hdcp_level: Option<HdcpLevel>,
|
||||
video: Option<String>,
|
||||
stream_inf: StreamInf,
|
||||
}
|
||||
|
||||
impl ExtXIFrameStreamInf {
|
||||
|
@ -28,12 +23,7 @@ impl ExtXIFrameStreamInf {
|
|||
pub fn new<T: ToString>(uri: T, bandwidth: u64) -> Self {
|
||||
ExtXIFrameStreamInf {
|
||||
uri: uri.to_string(),
|
||||
bandwidth,
|
||||
average_bandwidth: None,
|
||||
codecs: None,
|
||||
resolution: None,
|
||||
hdcp_level: None,
|
||||
video: None,
|
||||
stream_inf: StreamInf::new(bandwidth),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,189 +56,6 @@ impl ExtXIFrameStreamInf {
|
|||
self
|
||||
}
|
||||
|
||||
/// Returns the peak segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
/// assert_eq!(stream.bandwidth(), 20);
|
||||
/// ```
|
||||
pub const fn bandwidth(&self) -> u64 {
|
||||
self.bandwidth
|
||||
}
|
||||
|
||||
/// Sets the group identifier for the video in the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let mut stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
///
|
||||
/// stream.set_video(Some("video"));
|
||||
/// assert_eq!(stream.video(), &Some("video".to_string()));
|
||||
/// ```
|
||||
pub fn set_video<T: ToString>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.video = value.map(|v| v.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the group identifier for the video in the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
/// assert_eq!(stream.video(), &None);
|
||||
/// ```
|
||||
pub const fn video(&self) -> &Option<String> {
|
||||
&self.video
|
||||
}
|
||||
|
||||
/// Sets the peak segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let mut stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
///
|
||||
/// stream.set_bandwidth(5);
|
||||
/// assert_eq!(stream.bandwidth(), 5);
|
||||
/// ```
|
||||
pub fn set_bandwidth(&mut self, value: u64) -> &mut Self {
|
||||
self.bandwidth = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the average segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
/// assert_eq!(stream.average_bandwidth(), None);
|
||||
/// ```
|
||||
pub const fn average_bandwidth(&self) -> Option<u64> {
|
||||
self.average_bandwidth
|
||||
}
|
||||
|
||||
/// Sets the average segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let mut stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
///
|
||||
/// stream.set_average_bandwidth(Some(300));
|
||||
/// assert_eq!(stream.average_bandwidth(), Some(300));
|
||||
/// ```
|
||||
pub fn set_average_bandwidth(&mut self, value: Option<u64>) -> &mut Self {
|
||||
self.average_bandwidth = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// A string that represents the list of codec types contained the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
/// assert_eq!(stream.codecs(), &None);
|
||||
/// ```
|
||||
pub const fn codecs(&self) -> &Option<String> {
|
||||
&self.codecs
|
||||
}
|
||||
|
||||
/// A string that represents the list of codec types contained the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let mut stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
///
|
||||
/// stream.set_uri("../new/uri");
|
||||
/// assert_eq!(stream.uri(), &"../new/uri".to_string());
|
||||
/// ```
|
||||
pub fn set_codecs<T: ToString>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.codecs = value.map(|v| v.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the resolution of the stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
/// assert_eq!(stream.resolution(), None);
|
||||
/// ```
|
||||
pub fn resolution(&self) -> Option<(usize, usize)> {
|
||||
if let Some(res) = &self.resolution {
|
||||
Some((res.width(), res.height()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the resolution of the stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let mut stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
///
|
||||
/// stream.set_resolution(1920, 1080);
|
||||
/// assert_eq!(stream.resolution(), Some((1920, 1080)));
|
||||
/// ```
|
||||
pub fn set_resolution(&mut self, width: usize, height: usize) -> &mut Self {
|
||||
if let Some(res) = &mut self.resolution {
|
||||
res.set_width(width);
|
||||
res.set_height(height);
|
||||
} else {
|
||||
self.resolution = Some(DecimalResolution::new(width, height));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// The HDCP level of the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
/// assert_eq!(stream.uri(), &"https://www.example.com".to_string());
|
||||
/// ```
|
||||
pub const fn hdcp_level(&self) -> Option<HdcpLevel> {
|
||||
self.hdcp_level
|
||||
}
|
||||
|
||||
/// The HDCP level of the variant stream.
|
||||
///
|
||||
/// # Example
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXIFrameStreamInf;
|
||||
/// #
|
||||
/// let mut stream = ExtXIFrameStreamInf::new("https://www.example.com", 20);
|
||||
///
|
||||
/// stream.set_uri("../new/uri");
|
||||
/// assert_eq!(stream.uri(), &"../new/uri".to_string());
|
||||
/// ```
|
||||
pub fn set_hdcp_level<T: Into<HdcpLevel>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.hdcp_level = value.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the protocol compatibility version that this tag requires.
|
||||
pub const fn requires_version(&self) -> ProtocolVersion {
|
||||
ProtocolVersion::V1
|
||||
|
@ -258,24 +65,7 @@ impl ExtXIFrameStreamInf {
|
|||
impl fmt::Display for ExtXIFrameStreamInf {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Self::PREFIX)?;
|
||||
write!(f, "URI={}", quote(&self.uri))?;
|
||||
write!(f, ",BANDWIDTH={}", self.bandwidth)?;
|
||||
|
||||
if let Some(value) = &self.average_bandwidth {
|
||||
write!(f, ",AVERAGE-BANDWIDTH={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.codecs {
|
||||
write!(f, ",CODECS={}", quote(value))?;
|
||||
}
|
||||
if let Some(value) = &self.resolution {
|
||||
write!(f, ",RESOLUTION={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.hdcp_level {
|
||||
write!(f, ",HDCP-LEVEL={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.video {
|
||||
write!(f, ",VIDEO={}", quote(value))?;
|
||||
}
|
||||
write!(f, "URI={},{}", quote(&self.uri), self.stream_inf)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -287,44 +77,37 @@ impl FromStr for ExtXIFrameStreamInf {
|
|||
let input = tag(input, Self::PREFIX)?;
|
||||
|
||||
let mut uri = None;
|
||||
let mut bandwidth = None;
|
||||
let mut average_bandwidth = None;
|
||||
let mut codecs = None;
|
||||
let mut resolution = None;
|
||||
let mut hdcp_level = None;
|
||||
let mut video = None;
|
||||
|
||||
for (key, value) in input.parse::<AttributePairs>()? {
|
||||
match key.as_str() {
|
||||
"URI" => uri = Some(unquote(value)),
|
||||
"BANDWIDTH" => bandwidth = Some(parse_u64(value)?),
|
||||
"AVERAGE-BANDWIDTH" => average_bandwidth = Some(parse_u64(value)?),
|
||||
"CODECS" => codecs = Some(unquote(value)),
|
||||
"RESOLUTION" => resolution = Some(value.parse()?),
|
||||
"HDCP-LEVEL" => hdcp_level = Some(value.parse()?),
|
||||
"VIDEO" => video = Some(unquote(value)),
|
||||
_ => {
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let uri = uri.ok_or(Error::missing_value("URI"))?;
|
||||
let bandwidth = bandwidth.ok_or(Error::missing_value("BANDWIDTH"))?;
|
||||
|
||||
Ok(ExtXIFrameStreamInf {
|
||||
Ok(Self {
|
||||
uri,
|
||||
bandwidth,
|
||||
average_bandwidth,
|
||||
codecs,
|
||||
resolution,
|
||||
hdcp_level,
|
||||
video,
|
||||
stream_inf: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ExtXIFrameStreamInf {
|
||||
type Target = StreamInf;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.stream_inf
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ExtXIFrameStreamInf {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.stream_inf
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
use std::fmt;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::attribute::AttributePairs;
|
||||
use crate::types::{
|
||||
ClosedCaptions, DecimalFloatingPoint, DecimalResolution, HdcpLevel, ProtocolVersion,
|
||||
};
|
||||
use crate::utils::{parse_u64, quote, tag, unquote};
|
||||
use crate::types::{ClosedCaptions, DecimalFloatingPoint, ProtocolVersion, StreamInf};
|
||||
use crate::utils::{quote, tag, unquote};
|
||||
use crate::Error;
|
||||
|
||||
/// [4.3.4.2. EXT-X-STREAM-INF]
|
||||
|
@ -14,35 +13,30 @@ use crate::Error;
|
|||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExtXStreamInf {
|
||||
uri: String,
|
||||
bandwidth: u64,
|
||||
average_bandwidth: Option<u64>,
|
||||
codecs: Option<String>,
|
||||
resolution: Option<DecimalResolution>,
|
||||
frame_rate: Option<DecimalFloatingPoint>,
|
||||
hdcp_level: Option<HdcpLevel>,
|
||||
audio: Option<String>,
|
||||
video: Option<String>,
|
||||
subtitles: Option<String>,
|
||||
closed_captions: Option<ClosedCaptions>,
|
||||
stream_inf: StreamInf,
|
||||
}
|
||||
|
||||
impl ExtXStreamInf {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-STREAM-INF:";
|
||||
|
||||
/// Makes a new `ExtXStreamInf` tag.
|
||||
/// Makes a new [ExtXStreamInf] tag.
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtXStreamInf;
|
||||
/// #
|
||||
/// let stream = ExtXStreamInf::new("https://www.example.com/", 20);
|
||||
/// ```
|
||||
pub fn new<T: ToString>(uri: T, bandwidth: u64) -> Self {
|
||||
ExtXStreamInf {
|
||||
uri: uri.to_string(),
|
||||
bandwidth,
|
||||
average_bandwidth: None,
|
||||
codecs: None,
|
||||
resolution: None,
|
||||
frame_rate: None,
|
||||
hdcp_level: None,
|
||||
audio: None,
|
||||
video: None,
|
||||
subtitles: None,
|
||||
closed_captions: None,
|
||||
stream_inf: StreamInf::new(bandwidth),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -51,51 +45,16 @@ impl ExtXStreamInf {
|
|||
&self.uri
|
||||
}
|
||||
|
||||
/// Returns the peak segment bit rate of the variant stream.
|
||||
pub const fn bandwidth(&self) -> u64 {
|
||||
self.bandwidth
|
||||
}
|
||||
|
||||
/// Returns the average segment bit rate of the variant stream.
|
||||
pub const fn average_bandwidth(&self) -> Option<u64> {
|
||||
self.average_bandwidth
|
||||
}
|
||||
|
||||
/// Returns a string that represents the list of codec types contained the variant stream.
|
||||
pub fn codecs(&self) -> Option<&String> {
|
||||
self.codecs.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the optimal pixel resolution at which to display all the video in the variant
|
||||
/// stream.
|
||||
pub fn resolution(&self) -> Option<(usize, usize)> {
|
||||
if let Some(res) = &self.resolution {
|
||||
Some((res.width(), res.height()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the maximum frame rate for all the video in the variant stream.
|
||||
pub fn frame_rate(&self) -> Option<f64> {
|
||||
self.frame_rate.map_or(None, |v| Some(v.as_f64()))
|
||||
}
|
||||
|
||||
/// Returns the HDCP level of the variant stream.
|
||||
pub const fn hdcp_level(&self) -> Option<HdcpLevel> {
|
||||
self.hdcp_level
|
||||
}
|
||||
|
||||
/// Returns the group identifier for the audio in the variant stream.
|
||||
pub fn audio(&self) -> Option<&String> {
|
||||
self.audio.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the group identifier for the video in the variant stream.
|
||||
pub fn video(&self) -> Option<&String> {
|
||||
self.video.as_ref()
|
||||
}
|
||||
|
||||
/// Returns the group identifier for the subtitles in the variant stream.
|
||||
pub fn subtitles(&self) -> Option<&String> {
|
||||
self.subtitles.as_ref()
|
||||
|
@ -114,29 +73,13 @@ impl ExtXStreamInf {
|
|||
|
||||
impl fmt::Display for ExtXStreamInf {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Self::PREFIX)?;
|
||||
write!(f, "BANDWIDTH={}", self.bandwidth)?;
|
||||
if let Some(value) = &self.average_bandwidth {
|
||||
write!(f, ",AVERAGE-BANDWIDTH={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.resolution {
|
||||
write!(f, ",RESOLUTION={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.codecs {
|
||||
write!(f, ",CODECS={}", quote(value))?;
|
||||
}
|
||||
write!(f, "{}{}", Self::PREFIX, self.stream_inf)?;
|
||||
if let Some(value) = &self.frame_rate {
|
||||
write!(f, ",FRAME-RATE={:.3}", value.as_f64())?;
|
||||
}
|
||||
if let Some(value) = &self.hdcp_level {
|
||||
write!(f, ",HDCP-LEVEL={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.audio {
|
||||
write!(f, ",AUDIO={}", quote(value))?;
|
||||
}
|
||||
if let Some(value) = &self.video {
|
||||
write!(f, ",VIDEO={}", quote(value))?;
|
||||
}
|
||||
if let Some(value) = &self.subtitles {
|
||||
write!(f, ",SUBTITLES={}", quote(value))?;
|
||||
}
|
||||
|
@ -154,58 +97,50 @@ impl FromStr for ExtXStreamInf {
|
|||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let mut lines = input.lines();
|
||||
let first_line = lines.next().ok_or(Error::missing_value("first_line"))?;
|
||||
let uri = lines.next().ok_or(Error::missing_value("second_line"))?;
|
||||
let uri = lines.next().ok_or(Error::missing_value("URI"))?;
|
||||
|
||||
let first_line = tag(first_line, Self::PREFIX)?;
|
||||
let input = tag(first_line, Self::PREFIX)?;
|
||||
|
||||
let mut bandwidth = None;
|
||||
let mut average_bandwidth = None;
|
||||
let mut codecs = None;
|
||||
let mut resolution = None;
|
||||
let mut frame_rate = None;
|
||||
let mut hdcp_level = None;
|
||||
let mut audio = None;
|
||||
let mut video = None;
|
||||
let mut subtitles = None;
|
||||
let mut closed_captions = None;
|
||||
|
||||
for (key, value) in first_line.parse::<AttributePairs>()? {
|
||||
for (key, value) in input.parse::<AttributePairs>()? {
|
||||
match key.as_str() {
|
||||
"BANDWIDTH" => bandwidth = Some((parse_u64(value))?),
|
||||
"AVERAGE-BANDWIDTH" => average_bandwidth = Some((parse_u64(value))?),
|
||||
"CODECS" => codecs = Some(unquote(value)),
|
||||
"RESOLUTION" => resolution = Some((value.parse())?),
|
||||
"FRAME-RATE" => frame_rate = Some((value.parse())?),
|
||||
"HDCP-LEVEL" => hdcp_level = Some((value.parse())?),
|
||||
"AUDIO" => audio = Some(unquote(value)),
|
||||
"VIDEO" => video = Some(unquote(value)),
|
||||
"SUBTITLES" => subtitles = Some(unquote(value)),
|
||||
"CLOSED-CAPTIONS" => closed_captions = Some((value.parse())?),
|
||||
_ => {
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
let bandwidth = bandwidth.ok_or(Error::missing_value("EXT-X-BANDWIDTH"))?;
|
||||
|
||||
Ok(ExtXStreamInf {
|
||||
Ok(Self {
|
||||
uri: uri.to_string(),
|
||||
bandwidth,
|
||||
average_bandwidth,
|
||||
codecs,
|
||||
resolution,
|
||||
frame_rate,
|
||||
hdcp_level,
|
||||
audio,
|
||||
video,
|
||||
subtitles,
|
||||
closed_captions,
|
||||
stream_inf: input.parse()?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ExtXStreamInf {
|
||||
type Target = StreamInf;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.stream_inf
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for ExtXStreamInf {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.stream_inf
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
|
|
@ -18,7 +18,7 @@ impl ExtXKey {
|
|||
pub(crate) const PREFIX: &'static str = "#EXT-X-KEY:";
|
||||
|
||||
/// Makes a new `ExtXKey` tag.
|
||||
/// # Example
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use hls_m3u8::tags::ExtXKey;
|
||||
/// use hls_m3u8::types::EncryptionMethod;
|
||||
|
@ -38,7 +38,7 @@ impl ExtXKey {
|
|||
}
|
||||
|
||||
/// Makes a new `ExtXKey` tag without a decryption key.
|
||||
/// # Example
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use hls_m3u8::tags::ExtXKey;
|
||||
///
|
||||
|
@ -60,7 +60,7 @@ impl ExtXKey {
|
|||
}
|
||||
|
||||
/// Returns whether the [EncryptionMethod] is [None](EncryptionMethod::None).
|
||||
/// # Example
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// use hls_m3u8::tags::ExtXKey;
|
||||
/// use hls_m3u8::types::EncryptionMethod;
|
||||
|
|
|
@ -50,7 +50,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
fn test_parser() {
|
||||
let closed_captions = ClosedCaptions::None;
|
||||
assert_eq!(closed_captions, "NONE".parse::<ClosedCaptions>().unwrap());
|
||||
|
||||
|
|
|
@ -84,7 +84,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
pub fn test_parse() {
|
||||
pub fn test_parser() {
|
||||
let decimal_floating_point = DecimalFloatingPoint::new(22.0).unwrap();
|
||||
assert_eq!(
|
||||
decimal_floating_point,
|
||||
|
|
|
@ -90,7 +90,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
fn test_parser() {
|
||||
assert_eq!(
|
||||
DecimalResolution::new(1920, 1080),
|
||||
"1920x1080".parse::<DecimalResolution>().unwrap()
|
||||
|
|
|
@ -89,7 +89,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
fn test_parser() {
|
||||
assert_eq!(
|
||||
EncryptionMethod::Aes128,
|
||||
"AES-128".parse::<EncryptionMethod>().unwrap()
|
||||
|
|
|
@ -50,7 +50,7 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
fn test_parser() {
|
||||
let level = HdcpLevel::Type0;
|
||||
assert_eq!(level, "TYPE-0".parse::<HdcpLevel>().unwrap());
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ mod initialization_vector;
|
|||
mod media_type;
|
||||
mod protocol_version;
|
||||
mod signed_decimal_floating_point;
|
||||
mod stream_inf;
|
||||
|
||||
pub use byte_range::*;
|
||||
pub use closed_captions::*;
|
||||
|
@ -24,3 +25,4 @@ pub use initialization_vector::*;
|
|||
pub use media_type::*;
|
||||
pub use protocol_version::*;
|
||||
pub(crate) use signed_decimal_floating_point::*;
|
||||
pub use stream_inf::*;
|
||||
|
|
286
src/types/stream_inf.rs
Normal file
286
src/types/stream_inf.rs
Normal file
|
@ -0,0 +1,286 @@
|
|||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::attribute::AttributePairs;
|
||||
use crate::types::{DecimalResolution, HdcpLevel};
|
||||
use crate::utils::parse_u64;
|
||||
use crate::utils::{quote, unquote};
|
||||
use crate::Error;
|
||||
|
||||
/// [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
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct StreamInf {
|
||||
bandwidth: u64,
|
||||
average_bandwidth: Option<u64>,
|
||||
codecs: Option<String>,
|
||||
resolution: Option<DecimalResolution>,
|
||||
hdcp_level: Option<HdcpLevel>,
|
||||
video: Option<String>,
|
||||
}
|
||||
|
||||
impl StreamInf {
|
||||
/// Creates a new [StreamInf].
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let stream = StreamInf::new(20);
|
||||
/// ```
|
||||
pub const fn new(bandwidth: u64) -> Self {
|
||||
Self {
|
||||
bandwidth,
|
||||
average_bandwidth: None,
|
||||
codecs: None,
|
||||
resolution: None,
|
||||
hdcp_level: None,
|
||||
video: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the peak segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let stream = StreamInf::new(20);
|
||||
/// assert_eq!(stream.bandwidth(), 20);
|
||||
/// ```
|
||||
pub const fn bandwidth(&self) -> u64 {
|
||||
self.bandwidth
|
||||
}
|
||||
|
||||
/// Sets the peak segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let mut stream = StreamInf::new(20);
|
||||
///
|
||||
/// stream.set_bandwidth(5);
|
||||
/// assert_eq!(stream.bandwidth(), 5);
|
||||
/// ```
|
||||
pub fn set_bandwidth(&mut self, value: u64) -> &mut Self {
|
||||
self.bandwidth = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the group identifier for the video in the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let stream = StreamInf::new(20);
|
||||
/// assert_eq!(stream.video(), &None);
|
||||
/// ```
|
||||
pub const fn video(&self) -> &Option<String> {
|
||||
&self.video
|
||||
}
|
||||
|
||||
/// Sets the group identifier for the video in the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let mut stream = StreamInf::new(20);
|
||||
///
|
||||
/// stream.set_video(Some("video"));
|
||||
/// assert_eq!(stream.video(), &Some("video".to_string()));
|
||||
/// ```
|
||||
pub fn set_video<T: ToString>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.video = value.map(|v| v.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the average segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let stream = StreamInf::new(20);
|
||||
/// assert_eq!(stream.average_bandwidth(), None);
|
||||
/// ```
|
||||
pub const fn average_bandwidth(&self) -> Option<u64> {
|
||||
self.average_bandwidth
|
||||
}
|
||||
|
||||
/// Sets the average segment bit rate of the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let mut stream = StreamInf::new(20);
|
||||
///
|
||||
/// stream.set_average_bandwidth(Some(300));
|
||||
/// assert_eq!(stream.average_bandwidth(), Some(300));
|
||||
/// ```
|
||||
pub fn set_average_bandwidth(&mut self, value: Option<u64>) -> &mut Self {
|
||||
self.average_bandwidth = value;
|
||||
self
|
||||
}
|
||||
|
||||
/// A string that represents the list of codec types contained the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let stream = StreamInf::new(20);
|
||||
/// assert_eq!(stream.codecs(), &None);
|
||||
/// ```
|
||||
pub const fn codecs(&self) -> &Option<String> {
|
||||
&self.codecs
|
||||
}
|
||||
|
||||
/// A string that represents the list of codec types contained the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let mut stream = StreamInf::new(20);
|
||||
///
|
||||
/// stream.set_codecs(Some("mp4a.40.2,avc1.4d401e"));
|
||||
/// assert_eq!(stream.codecs(), &Some("mp4a.40.2,avc1.4d401e".to_string()));
|
||||
/// ```
|
||||
pub fn set_codecs<T: ToString>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.codecs = value.map(|v| v.to_string());
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the resolution of the stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let stream = StreamInf::new(20);
|
||||
/// assert_eq!(stream.resolution(), None);
|
||||
/// ```
|
||||
pub fn resolution(&self) -> Option<(usize, usize)> {
|
||||
if let Some(res) = &self.resolution {
|
||||
Some((res.width(), res.height()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the resolution of the stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let mut stream = StreamInf::new(20);
|
||||
///
|
||||
/// stream.set_resolution(1920, 1080);
|
||||
/// assert_eq!(stream.resolution(), Some((1920, 1080)));
|
||||
/// ```
|
||||
pub fn set_resolution(&mut self, width: usize, height: usize) -> &mut Self {
|
||||
if let Some(res) = &mut self.resolution {
|
||||
res.set_width(width);
|
||||
res.set_height(height);
|
||||
} else {
|
||||
self.resolution = Some(DecimalResolution::new(width, height));
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
/// The HDCP level of the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::StreamInf;
|
||||
/// #
|
||||
/// let stream = StreamInf::new(20);
|
||||
/// assert_eq!(stream.hdcp_level(), None);
|
||||
/// ```
|
||||
pub const fn hdcp_level(&self) -> Option<HdcpLevel> {
|
||||
self.hdcp_level
|
||||
}
|
||||
|
||||
/// The HDCP level of the variant stream.
|
||||
///
|
||||
/// # Examples
|
||||
/// ```
|
||||
/// # use hls_m3u8::types::{HdcpLevel, StreamInf};
|
||||
/// #
|
||||
/// let mut stream = StreamInf::new(20);
|
||||
///
|
||||
/// stream.set_hdcp_level(Some(HdcpLevel::None));
|
||||
/// assert_eq!(stream.hdcp_level(), Some(HdcpLevel::None));
|
||||
/// ```
|
||||
pub fn set_hdcp_level<T: Into<HdcpLevel>>(&mut self, value: Option<T>) -> &mut Self {
|
||||
self.hdcp_level = value.map(|v| v.into());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for StreamInf {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "BANDWIDTH={}", self.bandwidth)?;
|
||||
|
||||
if let Some(value) = &self.average_bandwidth {
|
||||
write!(f, ",AVERAGE-BANDWIDTH={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.codecs {
|
||||
write!(f, ",CODECS={}", quote(value))?;
|
||||
}
|
||||
if let Some(value) = &self.resolution {
|
||||
write!(f, ",RESOLUTION={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.hdcp_level {
|
||||
write!(f, ",HDCP-LEVEL={}", value)?;
|
||||
}
|
||||
if let Some(value) = &self.video {
|
||||
write!(f, ",VIDEO={}", quote(value))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for StreamInf {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let mut bandwidth = None;
|
||||
let mut average_bandwidth = None;
|
||||
let mut codecs = None;
|
||||
let mut resolution = None;
|
||||
let mut hdcp_level = None;
|
||||
let mut video = None;
|
||||
|
||||
for (key, value) in input.parse::<AttributePairs>()? {
|
||||
match key.as_str() {
|
||||
"BANDWIDTH" => bandwidth = Some(parse_u64(value)?),
|
||||
"AVERAGE-BANDWIDTH" => average_bandwidth = Some(parse_u64(value)?),
|
||||
"CODECS" => codecs = Some(unquote(value)),
|
||||
"RESOLUTION" => resolution = Some(value.parse()?),
|
||||
"HDCP-LEVEL" => hdcp_level = Some(value.parse()?),
|
||||
"VIDEO" => video = Some(unquote(value)),
|
||||
_ => {
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let bandwidth = bandwidth.ok_or(Error::missing_value("BANDWIDTH"))?;
|
||||
|
||||
Ok(Self {
|
||||
bandwidth,
|
||||
average_bandwidth,
|
||||
codecs,
|
||||
resolution,
|
||||
hdcp_level,
|
||||
video,
|
||||
})
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue