mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-11-26 00:51:00 +00:00
Refactor types
module
This commit is contained in:
parent
80537c3e43
commit
9f6d4e7ed7
5 changed files with 169 additions and 136 deletions
|
@ -4,8 +4,9 @@ use std::str::FromStr;
|
|||
use {Error, ErrorKind, Result};
|
||||
use attribute::{AttributePairs, DecimalFloatingPoint, DecimalInteger, DecimalResolution,
|
||||
QuotedString};
|
||||
use types::{ClosedCaptions, DecryptionKey, HdcpLevel, InStreamId, M3u8String, MediaType,
|
||||
ProtocolVersion, SessionData, YesOrNo};
|
||||
use types::{ClosedCaptions, DecryptionKey, HdcpLevel, InStreamId, MediaType, ProtocolVersion,
|
||||
SessionData, SingleLineString};
|
||||
use super::parse_yes_or_no;
|
||||
|
||||
/// [4.3.4.1. EXT-X-MEDIA]
|
||||
///
|
||||
|
@ -20,9 +21,9 @@ pub struct ExtXMedia {
|
|||
language: Option<QuotedString>,
|
||||
assoc_language: Option<QuotedString>,
|
||||
name: QuotedString,
|
||||
default: YesOrNo,
|
||||
autoselect: YesOrNo,
|
||||
forced: YesOrNo,
|
||||
default: bool,
|
||||
autoselect: bool,
|
||||
forced: bool,
|
||||
instream_id: Option<InStreamId>,
|
||||
characteristics: Option<QuotedString>,
|
||||
channels: Option<QuotedString>,
|
||||
|
@ -39,9 +40,9 @@ impl ExtXMedia {
|
|||
language: None,
|
||||
assoc_language: None,
|
||||
name,
|
||||
default: YesOrNo::No,
|
||||
autoselect: YesOrNo::No,
|
||||
forced: YesOrNo::No,
|
||||
default: false,
|
||||
autoselect: false,
|
||||
forced: false,
|
||||
instream_id: None,
|
||||
characteristics: None,
|
||||
channels: None,
|
||||
|
@ -79,18 +80,18 @@ impl ExtXMedia {
|
|||
}
|
||||
|
||||
/// Returns whether this is the default rendition.
|
||||
pub fn default(&self) -> YesOrNo {
|
||||
pub fn default(&self) -> bool {
|
||||
self.default
|
||||
}
|
||||
|
||||
/// Returns whether the client may choose to
|
||||
/// play this rendition in the absence of explicit user preference.
|
||||
pub fn autoselect(&self) -> YesOrNo {
|
||||
pub fn autoselect(&self) -> bool {
|
||||
self.autoselect
|
||||
}
|
||||
|
||||
/// Returns whether the rendition contains content that is considered essential to play.
|
||||
pub fn forced(&self) -> YesOrNo {
|
||||
pub fn forced(&self) -> bool {
|
||||
self.forced
|
||||
}
|
||||
|
||||
|
@ -138,13 +139,13 @@ impl fmt::Display for ExtXMedia {
|
|||
write!(f, ",ASSOC-LANGUAGE={}", x)?;
|
||||
}
|
||||
write!(f, ",NAME={}", self.name)?;
|
||||
if YesOrNo::Yes == self.default {
|
||||
if self.default {
|
||||
write!(f, ",DEFAULT=YES")?;
|
||||
}
|
||||
if YesOrNo::Yes == self.autoselect {
|
||||
if self.autoselect {
|
||||
write!(f, ",AUTOSELECT=YES")?;
|
||||
}
|
||||
if YesOrNo::Yes == self.forced {
|
||||
if self.forced {
|
||||
write!(f, ",FORCED=YES")?;
|
||||
}
|
||||
if let Some(ref x) = self.instream_id {
|
||||
|
@ -170,7 +171,7 @@ impl FromStr for ExtXMedia {
|
|||
let mut language = None;
|
||||
let mut assoc_language = None;
|
||||
let mut name = None;
|
||||
let mut default = None;
|
||||
let mut default = false;
|
||||
let mut autoselect = None;
|
||||
let mut forced = None;
|
||||
let mut instream_id = None;
|
||||
|
@ -186,9 +187,9 @@ impl FromStr for ExtXMedia {
|
|||
"LANGUAGE" => language = Some(track!(value.parse())?),
|
||||
"ASSOC-LANGUAGE" => assoc_language = Some(track!(value.parse())?),
|
||||
"NAME" => name = Some(track!(value.parse())?),
|
||||
"DEFAULT" => default = Some(track!(value.parse())?),
|
||||
"AUTOSELECT" => autoselect = Some(track!(value.parse())?),
|
||||
"FORCED" => forced = Some(track!(value.parse())?),
|
||||
"DEFAULT" => default = track!(parse_yes_or_no(value))?,
|
||||
"AUTOSELECT" => autoselect = Some(track!(parse_yes_or_no(value))?),
|
||||
"FORCED" => forced = Some(track!(parse_yes_or_no(value))?),
|
||||
"INSTREAM-ID" => {
|
||||
let s: QuotedString = track!(value.parse())?;
|
||||
instream_id = Some(track!(s.as_str().parse())?);
|
||||
|
@ -210,8 +211,8 @@ impl FromStr for ExtXMedia {
|
|||
} else {
|
||||
track_assert!(instream_id.is_none(), ErrorKind::InvalidInput);
|
||||
}
|
||||
if default == Some(YesOrNo::Yes) && autoselect.is_some() {
|
||||
track_assert_eq!(autoselect, Some(YesOrNo::Yes), ErrorKind::InvalidInput);
|
||||
if default && autoselect.is_some() {
|
||||
track_assert_eq!(autoselect, Some(true), ErrorKind::InvalidInput);
|
||||
}
|
||||
if MediaType::Subtitles != media_type {
|
||||
track_assert_eq!(forced, None, ErrorKind::InvalidInput);
|
||||
|
@ -223,9 +224,9 @@ impl FromStr for ExtXMedia {
|
|||
language,
|
||||
assoc_language,
|
||||
name,
|
||||
default: default.unwrap_or(YesOrNo::No),
|
||||
autoselect: autoselect.unwrap_or(YesOrNo::No),
|
||||
forced: forced.unwrap_or(YesOrNo::No),
|
||||
default,
|
||||
autoselect: autoselect.unwrap_or(false),
|
||||
forced: forced.unwrap_or(false),
|
||||
instream_id,
|
||||
characteristics,
|
||||
channels,
|
||||
|
@ -238,7 +239,7 @@ impl FromStr for ExtXMedia {
|
|||
/// [4.3.4.2. EXT-X-STREAM-INF]: https://tools.ietf.org/html/rfc8216#section-4.3.4.2
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct ExtXStreamInf {
|
||||
uri: M3u8String,
|
||||
uri: SingleLineString,
|
||||
bandwidth: DecimalInteger,
|
||||
average_bandwidth: Option<DecimalInteger>,
|
||||
codecs: Option<QuotedString>,
|
||||
|
@ -254,7 +255,7 @@ impl ExtXStreamInf {
|
|||
pub(crate) const PREFIX: &'static str = "#EXT-X-STREAM-INF:";
|
||||
|
||||
/// Makes a new `ExtXStreamInf` tag.
|
||||
pub fn new(uri: M3u8String, bandwidth: DecimalInteger) -> Self {
|
||||
pub fn new(uri: SingleLineString, bandwidth: DecimalInteger) -> Self {
|
||||
ExtXStreamInf {
|
||||
uri,
|
||||
bandwidth,
|
||||
|
@ -271,7 +272,7 @@ impl ExtXStreamInf {
|
|||
}
|
||||
|
||||
/// Returns the URI that identifies the associated media playlist.
|
||||
pub fn uri(&self) -> &M3u8String {
|
||||
pub fn uri(&self) -> &SingleLineString {
|
||||
&self.uri
|
||||
}
|
||||
|
||||
|
@ -376,7 +377,7 @@ impl FromStr for ExtXStreamInf {
|
|||
first_line.starts_with(Self::PREFIX),
|
||||
ErrorKind::InvalidInput
|
||||
);
|
||||
let uri = track!(M3u8String::new(second_line))?;
|
||||
let uri = track!(SingleLineString::new(second_line))?;
|
||||
let mut bandwidth = None;
|
||||
let mut average_bandwidth = None;
|
||||
let mut codecs = None;
|
||||
|
@ -722,7 +723,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn ext_x_stream_inf() {
|
||||
let tag = ExtXStreamInf::new(M3u8String::new("foo").unwrap(), DecimalInteger(1000));
|
||||
let tag = ExtXStreamInf::new(SingleLineString::new("foo").unwrap(), DecimalInteger(1000));
|
||||
let text = "#EXT-X-STREAM-INF:BANDWIDTH=1000\nfoo";
|
||||
assert_eq!(text.parse().ok(), Some(tag.clone()));
|
||||
assert_eq!(tag.to_string(), text);
|
||||
|
|
|
@ -3,7 +3,8 @@ use std::str::FromStr;
|
|||
|
||||
use {Error, ErrorKind, Result};
|
||||
use attribute::{AttributePairs, SignedDecimalFloatingPoint};
|
||||
use types::{ProtocolVersion, YesOrNo};
|
||||
use types::ProtocolVersion;
|
||||
use super::parse_yes_or_no;
|
||||
|
||||
/// [4.3.5.1. EXT-X-INDEPENDENT-SEGMENTS]
|
||||
///
|
||||
|
@ -37,7 +38,7 @@ impl FromStr for ExtXIndependentSegments {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct ExtXStart {
|
||||
time_offset: SignedDecimalFloatingPoint,
|
||||
precise: YesOrNo,
|
||||
precise: bool,
|
||||
}
|
||||
impl ExtXStart {
|
||||
pub(crate) const PREFIX: &'static str = "#EXT-X-START:";
|
||||
|
@ -46,12 +47,12 @@ impl ExtXStart {
|
|||
pub fn new(time_offset: SignedDecimalFloatingPoint) -> Self {
|
||||
ExtXStart {
|
||||
time_offset,
|
||||
precise: YesOrNo::No,
|
||||
precise: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a new `ExtXStart` tag with the given `precise` flag.
|
||||
pub fn with_precise(time_offset: SignedDecimalFloatingPoint, precise: YesOrNo) -> Self {
|
||||
pub fn with_precise(time_offset: SignedDecimalFloatingPoint, precise: bool) -> Self {
|
||||
ExtXStart {
|
||||
time_offset,
|
||||
precise,
|
||||
|
@ -65,7 +66,7 @@ impl ExtXStart {
|
|||
|
||||
/// Returns whether clients should not render media stream whose presentation times are
|
||||
/// prior to the specified time offset.
|
||||
pub fn precise(&self) -> YesOrNo {
|
||||
pub fn precise(&self) -> bool {
|
||||
self.precise
|
||||
}
|
||||
|
||||
|
@ -78,8 +79,8 @@ impl fmt::Display for ExtXStart {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", Self::PREFIX)?;
|
||||
write!(f, "TIME-OFFSET={}", self.time_offset)?;
|
||||
if self.precise == YesOrNo::Yes {
|
||||
write!(f, ",PRECISE={}", self.precise)?;
|
||||
if self.precise {
|
||||
write!(f, ",PRECISE=YES")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -90,13 +91,13 @@ impl FromStr for ExtXStart {
|
|||
track_assert!(s.starts_with(Self::PREFIX), ErrorKind::InvalidInput);
|
||||
|
||||
let mut time_offset = None;
|
||||
let mut precise = None;
|
||||
let mut precise = false;
|
||||
let attrs = AttributePairs::parse(s.split_at(Self::PREFIX.len()).1);
|
||||
for attr in attrs {
|
||||
let (key, value) = track!(attr)?;
|
||||
match key {
|
||||
"TIME-OFFSET" => time_offset = Some(track!(value.parse())?),
|
||||
"PRECISE" => precise = Some(track!(value.parse())?),
|
||||
"PRECISE" => precise = track!(parse_yes_or_no(value))?,
|
||||
_ => {
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
||||
|
@ -107,7 +108,7 @@ impl FromStr for ExtXStart {
|
|||
let time_offset = track_assert_some!(time_offset, ErrorKind::InvalidInput);
|
||||
Ok(ExtXStart {
|
||||
time_offset,
|
||||
precise: precise.unwrap_or(YesOrNo::No),
|
||||
precise,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use trackable::error::ErrorKindExt;
|
|||
|
||||
use {Error, ErrorKind, Result};
|
||||
use attribute::{AttributePairs, DecimalFloatingPoint, QuotedString};
|
||||
use types::{ByteRange, DecryptionKey, M3u8String, ProtocolVersion, Yes};
|
||||
use types::{ByteRange, DecryptionKey, ProtocolVersion, SingleLineString};
|
||||
|
||||
/// [4.3.2.1. EXTINF]
|
||||
///
|
||||
|
@ -15,7 +15,7 @@ use types::{ByteRange, DecryptionKey, M3u8String, ProtocolVersion, Yes};
|
|||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ExtInf {
|
||||
duration: Duration,
|
||||
title: Option<M3u8String>,
|
||||
title: Option<SingleLineString>,
|
||||
}
|
||||
impl ExtInf {
|
||||
pub(crate) const PREFIX: &'static str = "#EXTINF:";
|
||||
|
@ -29,7 +29,7 @@ impl ExtInf {
|
|||
}
|
||||
|
||||
/// Makes a new `ExtInf` tag with the given title.
|
||||
pub fn with_title(duration: Duration, title: M3u8String) -> Self {
|
||||
pub fn with_title(duration: Duration, title: SingleLineString) -> Self {
|
||||
ExtInf {
|
||||
duration,
|
||||
title: Some(title),
|
||||
|
@ -42,7 +42,7 @@ impl ExtInf {
|
|||
}
|
||||
|
||||
/// Returns the title of the associated media segment.
|
||||
pub fn title(&self) -> Option<&M3u8String> {
|
||||
pub fn title(&self) -> Option<&SingleLineString> {
|
||||
self.title.as_ref()
|
||||
}
|
||||
|
||||
|
@ -80,7 +80,7 @@ impl FromStr for ExtInf {
|
|||
let duration = seconds.to_duration();
|
||||
|
||||
let title = if let Some(title) = tokens.next() {
|
||||
Some(track!(M3u8String::new(title))?)
|
||||
Some(track!(SingleLineString::new(title))?)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -355,7 +355,7 @@ pub struct ExtXDateRange {
|
|||
pub scte35_cmd: Option<QuotedString>,
|
||||
pub scte35_out: Option<QuotedString>,
|
||||
pub scte35_in: Option<QuotedString>,
|
||||
pub end_on_next: Option<Yes>,
|
||||
pub end_on_next: bool,
|
||||
pub client_attributes: BTreeMap<String, String>,
|
||||
}
|
||||
impl ExtXDateRange {
|
||||
|
@ -375,11 +375,11 @@ impl fmt::Display for ExtXDateRange {
|
|||
}
|
||||
write!(
|
||||
f,
|
||||
",START_DATE={:?}",
|
||||
",START-DATE={:?}",
|
||||
self.start_date.format("%Y-%m-%d").to_string()
|
||||
)?;
|
||||
if let Some(ref x) = self.end_date {
|
||||
write!(f, ",END_DATE={:?}", x.format("%Y-%m-%d").to_string())?;
|
||||
write!(f, ",END-DATE={:?}", x.format("%Y-%m-%d").to_string())?;
|
||||
}
|
||||
if let Some(x) = self.duration {
|
||||
write!(f, ",DURATION={}", DecimalFloatingPoint::from_duration(x))?;
|
||||
|
@ -387,21 +387,21 @@ impl fmt::Display for ExtXDateRange {
|
|||
if let Some(x) = self.planned_duration {
|
||||
write!(
|
||||
f,
|
||||
",PLANNED_DURATION={}",
|
||||
",PLANNED-DURATION={}",
|
||||
DecimalFloatingPoint::from_duration(x)
|
||||
)?;
|
||||
}
|
||||
if let Some(ref x) = self.scte35_cmd {
|
||||
write!(f, ",SCTE35_CMD={}", x)?;
|
||||
write!(f, ",SCTE35-CMD={}", x)?;
|
||||
}
|
||||
if let Some(ref x) = self.scte35_out {
|
||||
write!(f, ",SCTE35_OUT={}", x)?;
|
||||
write!(f, ",SCTE35-OUT={}", x)?;
|
||||
}
|
||||
if let Some(ref x) = self.scte35_in {
|
||||
write!(f, ",SCTE35_IN={}", x)?;
|
||||
write!(f, ",SCTE35-IN={}", x)?;
|
||||
}
|
||||
if let Some(ref x) = self.end_on_next {
|
||||
write!(f, ",END_ON_NEXT={}", x)?;
|
||||
if self.end_on_next {
|
||||
write!(f, ",END-ON-NEXT=YES",)?;
|
||||
}
|
||||
for (k, v) in &self.client_attributes {
|
||||
write!(f, ",{}={}", k, v)?;
|
||||
|
@ -423,7 +423,7 @@ impl FromStr for ExtXDateRange {
|
|||
let mut scte35_cmd = None;
|
||||
let mut scte35_out = None;
|
||||
let mut scte35_in = None;
|
||||
let mut end_on_next = None;
|
||||
let mut end_on_next = false;
|
||||
let mut client_attributes = BTreeMap::new();
|
||||
let attrs = AttributePairs::parse(s.split_at(Self::PREFIX.len()).1);
|
||||
for attr in attrs {
|
||||
|
@ -456,7 +456,10 @@ impl FromStr for ExtXDateRange {
|
|||
"SCTE35-CMD" => scte35_cmd = Some(track!(value.parse())?),
|
||||
"SCTE35-OUT" => scte35_out = Some(track!(value.parse())?),
|
||||
"SCTE35-IN" => scte35_in = Some(track!(value.parse())?),
|
||||
"END-ON-NEXT" => end_on_next = Some(track!(value.parse())?),
|
||||
"END-ON-NEXT" => {
|
||||
track_assert_eq!(value, "YES", ErrorKind::InvalidInput);
|
||||
end_on_next = true;
|
||||
}
|
||||
_ => {
|
||||
if key.starts_with("X-") {
|
||||
client_attributes.insert(key.split_at(2).1.to_owned(), value.to_owned());
|
||||
|
@ -470,7 +473,7 @@ impl FromStr for ExtXDateRange {
|
|||
|
||||
let id = track_assert_some!(id, ErrorKind::InvalidInput);
|
||||
let start_date = track_assert_some!(start_date, ErrorKind::InvalidInput);
|
||||
if end_on_next.is_some() {
|
||||
if end_on_next {
|
||||
track_assert!(class.is_some(), ErrorKind::InvalidInput);
|
||||
}
|
||||
Ok(ExtXDateRange {
|
||||
|
@ -504,7 +507,10 @@ mod test {
|
|||
assert_eq!(tag.to_string(), "#EXTINF:5");
|
||||
assert_eq!(tag.requires_version(), ProtocolVersion::V1);
|
||||
|
||||
let tag = ExtInf::with_title(Duration::from_secs(5), M3u8String::new("foo").unwrap());
|
||||
let tag = ExtInf::with_title(
|
||||
Duration::from_secs(5),
|
||||
SingleLineString::new("foo").unwrap(),
|
||||
);
|
||||
assert_eq!("#EXTINF:5,foo".parse().ok(), Some(tag.clone()));
|
||||
assert_eq!(tag.to_string(), "#EXTINF:5,foo");
|
||||
assert_eq!(tag.requires_version(), ProtocolVersion::V1);
|
||||
|
|
|
@ -229,3 +229,11 @@ impl FromStr for Tag {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_yes_or_no(s: &str) -> Result<bool> {
|
||||
match s {
|
||||
"YES" => Ok(true),
|
||||
"NO" => Ok(false),
|
||||
_ => track_panic!(ErrorKind::InvalidInput, "Unexpected value: {:?}", s),
|
||||
}
|
||||
}
|
||||
|
|
177
src/types.rs
177
src/types.rs
|
@ -7,51 +7,47 @@ use trackable::error::ErrorKindExt;
|
|||
use {Error, ErrorKind, Result};
|
||||
use attribute::{AttributePairs, HexadecimalSequence, QuotedString};
|
||||
|
||||
// TODO: rename
|
||||
/// String that represents a single line in a playlist file.
|
||||
///
|
||||
/// See: [4.1. Definition of a Playlist]
|
||||
///
|
||||
/// [4.1. Definition of a Playlist]: https://tools.ietf.org/html/rfc8216#section-4.1
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct M3u8String(String);
|
||||
impl M3u8String {
|
||||
pub struct SingleLineString(String);
|
||||
impl SingleLineString {
|
||||
/// Makes a new `SingleLineString` instance.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If the given string contains any control characters,
|
||||
/// this function will return an error which has the kind `ErrorKind::InvalidInput`.
|
||||
pub fn new<T: Into<String>>(s: T) -> Result<Self> {
|
||||
// TODO: validate
|
||||
Ok(M3u8String(s.into()))
|
||||
}
|
||||
pub unsafe fn new_unchecked<T: Into<String>>(s: T) -> Self {
|
||||
M3u8String(s.into())
|
||||
let s = s.into();
|
||||
track_assert!(!s.chars().any(|c| c.is_control()), ErrorKind::InvalidInput);
|
||||
Ok(SingleLineString(s))
|
||||
}
|
||||
}
|
||||
impl Deref for M3u8String {
|
||||
impl Deref for SingleLineString {
|
||||
type Target = str;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl AsRef<str> for M3u8String {
|
||||
impl AsRef<str> for SingleLineString {
|
||||
fn as_ref(&self) -> &str {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
impl fmt::Display for M3u8String {
|
||||
impl fmt::Display for SingleLineString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct Yes;
|
||||
impl fmt::Display for Yes {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
"YES".fmt(f)
|
||||
}
|
||||
}
|
||||
impl FromStr for Yes {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
track_assert_eq!(s, "YES", ErrorKind::InvalidInput);
|
||||
Ok(Yes)
|
||||
}
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc8216#section-7
|
||||
/// [7. Protocol Version Compatibility]
|
||||
///
|
||||
/// [7. Protocol Version Compatibility]: https://tools.ietf.org/html/rfc8216#section-7
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub enum ProtocolVersion {
|
||||
V1,
|
||||
|
@ -92,6 +88,12 @@ impl FromStr for ProtocolVersion {
|
|||
}
|
||||
}
|
||||
|
||||
/// Byte range.
|
||||
///
|
||||
/// See: [4.3.2.2. EXT-X-BYTERANGE]
|
||||
///
|
||||
/// [4.3.2.2. EXT-X-BYTERANGE]: https://tools.ietf.org/html/rfc8216#section-4.3.2.2
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct ByteRange {
|
||||
pub length: usize,
|
||||
|
@ -125,16 +127,22 @@ impl FromStr for ByteRange {
|
|||
}
|
||||
}
|
||||
|
||||
/// Decryption 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
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct DecryptionKey {
|
||||
pub method: EncryptionMethod,
|
||||
pub uri: QuotedString,
|
||||
pub iv: Option<HexadecimalSequence>,
|
||||
pub iv: Option<HexadecimalSequence>, // TODO: iv
|
||||
pub key_format: Option<QuotedString>,
|
||||
pub key_format_versions: Option<QuotedString>,
|
||||
}
|
||||
impl DecryptionKey {
|
||||
pub fn requires_version(&self) -> ProtocolVersion {
|
||||
pub(crate) fn requires_version(&self) -> ProtocolVersion {
|
||||
if self.key_format.is_some() | self.key_format_versions.is_some() {
|
||||
ProtocolVersion::V5
|
||||
} else if self.iv.is_some() {
|
||||
|
@ -172,29 +180,14 @@ impl FromStr for DecryptionKey {
|
|||
for attr in attrs {
|
||||
let (key, value) = track!(attr)?;
|
||||
match key {
|
||||
"METHOD" => {
|
||||
track_assert_eq!(method, None, ErrorKind::InvalidInput);
|
||||
method = Some(track!(value.parse())?);
|
||||
}
|
||||
"URI" => {
|
||||
track_assert_eq!(uri, None, ErrorKind::InvalidInput);
|
||||
uri = Some(track!(value.parse())?);
|
||||
}
|
||||
"IV" => {
|
||||
// TODO: validate length(128-bit)
|
||||
track_assert_eq!(iv, None, ErrorKind::InvalidInput);
|
||||
iv = Some(track!(value.parse())?);
|
||||
}
|
||||
"KEYFORMAT" => {
|
||||
track_assert_eq!(key_format, None, ErrorKind::InvalidInput);
|
||||
key_format = Some(track!(value.parse())?);
|
||||
}
|
||||
"KEYFORMATVERSIONS" => {
|
||||
track_assert_eq!(key_format_versions, None, ErrorKind::InvalidInput);
|
||||
key_format_versions = Some(track!(value.parse())?);
|
||||
}
|
||||
"METHOD" => method = Some(track!(value.parse())?),
|
||||
"URI" => uri = Some(track!(value.parse())?),
|
||||
"IV" => iv = Some(track!(value.parse())?),
|
||||
"KEYFORMAT" => key_format = Some(track!(value.parse())?),
|
||||
"KEYFORMATVERSIONS" => key_format_versions = Some(track!(value.parse())?),
|
||||
_ => {
|
||||
// [6.3.1] ignore any attribute/value pair with an unrecognized AttributeName.
|
||||
// [6.3.1. General Client Responsibilities]
|
||||
// > ignore any attribute/value pair with an unrecognized AttributeName.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -210,6 +203,12 @@ impl FromStr for DecryptionKey {
|
|||
}
|
||||
}
|
||||
|
||||
/// Encryption method.
|
||||
///
|
||||
/// 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
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum EncryptionMethod {
|
||||
Aes128,
|
||||
|
@ -238,6 +237,12 @@ impl FromStr for EncryptionMethod {
|
|||
}
|
||||
}
|
||||
|
||||
/// Playlist type.
|
||||
///
|
||||
/// See: [4.3.3.5. EXT-X-PLAYLIST-TYPE]
|
||||
///
|
||||
/// [4.3.3.5. EXT-X-PLAYLIST-TYPE]: https://tools.ietf.org/html/rfc8216#section-4.3.3.5
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum PlaylistType {
|
||||
Event,
|
||||
|
@ -262,6 +267,12 @@ impl FromStr for PlaylistType {
|
|||
}
|
||||
}
|
||||
|
||||
/// Media type.
|
||||
///
|
||||
/// 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
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum MediaType {
|
||||
Audio,
|
||||
|
@ -292,35 +303,12 @@ impl FromStr for MediaType {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: remove
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum YesOrNo {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
impl fmt::Display for YesOrNo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
YesOrNo::Yes => "YES".fmt(f),
|
||||
YesOrNo::No => "NO".fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl FromStr for YesOrNo {
|
||||
type Err = Error;
|
||||
fn from_str(s: &str) -> Result<Self> {
|
||||
match s {
|
||||
"YES" => Ok(YesOrNo::Yes),
|
||||
"NO" => Ok(YesOrNo::No),
|
||||
_ => track_panic!(
|
||||
ErrorKind::InvalidInput,
|
||||
"Unexpected enumerated-string: {:?}",
|
||||
s
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Identifier of a rendition within the segments in a media playlist.
|
||||
///
|
||||
/// 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
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum InStreamId {
|
||||
Cc1,
|
||||
|
@ -472,6 +460,12 @@ impl FromStr for InStreamId {
|
|||
}
|
||||
}
|
||||
|
||||
/// HDCP level.
|
||||
///
|
||||
/// 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
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum HdcpLevel {
|
||||
Type0,
|
||||
|
@ -496,6 +490,12 @@ impl FromStr for HdcpLevel {
|
|||
}
|
||||
}
|
||||
|
||||
/// The identifier of a closed captions group or its absence.
|
||||
///
|
||||
/// 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
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum ClosedCaptions {
|
||||
GroupId(QuotedString),
|
||||
|
@ -520,8 +520,25 @@ impl FromStr for ClosedCaptions {
|
|||
}
|
||||
}
|
||||
|
||||
/// Session data.
|
||||
///
|
||||
/// See: [4.3.4.4. EXT-X-SESSION-DATA]
|
||||
///
|
||||
/// [4.3.4.4. EXT-X-SESSION-DATA]: https://tools.ietf.org/html/rfc8216#section-4.3.4.4
|
||||
#[allow(missing_docs)]
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum SessionData {
|
||||
Value(QuotedString),
|
||||
Uri(QuotedString),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn single_line_string() {
|
||||
assert!(SingleLineString::new("foo").is_ok());
|
||||
assert!(SingleLineString::new("b\rar").is_err());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue