mirror of
https://github.com/sile/hls_m3u8.git
synced 2025-02-16 13:15:14 +00:00
remove unnecessary allocations
This commit is contained in:
parent
aae3809545
commit
1b0eb56224
10 changed files with 59 additions and 134 deletions
46
src/line.rs
46
src/line.rs
|
@ -1,3 +1,4 @@
|
|||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::str::FromStr;
|
||||
|
||||
|
@ -12,7 +13,7 @@ pub(crate) struct Lines<'a> {
|
|||
}
|
||||
|
||||
impl<'a> Iterator for Lines<'a> {
|
||||
type Item = crate::Result<Line>;
|
||||
type Item = crate::Result<Line<'a>>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut stream_inf = false;
|
||||
|
@ -32,10 +33,7 @@ impl<'a> Iterator for Lines<'a> {
|
|||
|
||||
continue;
|
||||
} else if line.starts_with("#EXT") {
|
||||
match line.parse() {
|
||||
Ok(value) => return Some(Ok(Line::Tag(value))),
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
return Some(Tag::try_from(line).map(Line::Tag));
|
||||
} else if line.starts_with('#') {
|
||||
continue; // ignore comments
|
||||
} else {
|
||||
|
@ -44,17 +42,15 @@ impl<'a> Iterator for Lines<'a> {
|
|||
stream_inf = false;
|
||||
|
||||
if let Some(first_line) = stream_inf_line {
|
||||
match format!("{}\n{}", first_line, line).parse() {
|
||||
Ok(value) => {
|
||||
return Some(Ok(Line::Tag(value)));
|
||||
}
|
||||
Err(e) => return Some(Err(e)),
|
||||
}
|
||||
return Some(
|
||||
tags::ExtXStreamInf::from_str(&format!("{}\n{}", first_line, line))
|
||||
.map(|v| Line::Tag(Tag::ExtXStreamInf(v))),
|
||||
);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
return Some(Ok(Line::Uri(line.to_string())));
|
||||
return Some(Ok(Line::Uri(line)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,15 +69,14 @@ impl<'a> From<&'a str> for Lines<'a> {
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum Line {
|
||||
Tag(Tag),
|
||||
Uri(String),
|
||||
pub(crate) enum Line<'a> {
|
||||
Tag(Tag<'a>),
|
||||
Uri(&'a str),
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub(crate) enum Tag {
|
||||
ExtM3u(tags::ExtM3u),
|
||||
pub(crate) enum Tag<'a> {
|
||||
ExtXVersion(tags::ExtXVersion),
|
||||
ExtInf(tags::ExtInf),
|
||||
ExtXByteRange(tags::ExtXByteRange),
|
||||
|
@ -103,13 +98,12 @@ pub(crate) enum Tag {
|
|||
ExtXSessionKey(tags::ExtXSessionKey),
|
||||
ExtXIndependentSegments(tags::ExtXIndependentSegments),
|
||||
ExtXStart(tags::ExtXStart),
|
||||
Unknown(String),
|
||||
Unknown(&'a str),
|
||||
}
|
||||
|
||||
impl fmt::Display for Tag {
|
||||
impl<'a> fmt::Display for Tag<'a> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match &self {
|
||||
Self::ExtM3u(value) => value.fmt(f),
|
||||
Self::ExtXVersion(value) => value.fmt(f),
|
||||
Self::ExtInf(value) => value.fmt(f),
|
||||
Self::ExtXByteRange(value) => value.fmt(f),
|
||||
|
@ -136,13 +130,11 @@ impl fmt::Display for Tag {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for Tag {
|
||||
type Err = Error;
|
||||
impl<'a> TryFrom<&'a str> for Tag<'a> {
|
||||
type Error = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
if input.starts_with(tags::ExtM3u::PREFIX) {
|
||||
input.parse().map(Self::ExtM3u)
|
||||
} else if input.starts_with(tags::ExtXVersion::PREFIX) {
|
||||
fn try_from(input: &'a str) -> Result<Self, Self::Error> {
|
||||
if input.starts_with(tags::ExtXVersion::PREFIX) {
|
||||
input.parse().map(Self::ExtXVersion)
|
||||
} else if input.starts_with(tags::ExtInf::PREFIX) {
|
||||
input.parse().map(Self::ExtInf)
|
||||
|
@ -185,7 +177,7 @@ impl FromStr for Tag {
|
|||
} else if input.starts_with(tags::ExtXStart::PREFIX) {
|
||||
input.parse().map(Self::ExtXStart)
|
||||
} else {
|
||||
Ok(Self::Unknown(input.to_string()))
|
||||
Ok(Self::Unknown(input))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@ use crate::tags::{
|
|||
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion,
|
||||
};
|
||||
use crate::types::{ClosedCaptions, MediaType, ProtocolVersion};
|
||||
use crate::utils::tag;
|
||||
use crate::{Error, RequiredVersion};
|
||||
|
||||
/// Master playlist.
|
||||
|
@ -263,6 +264,7 @@ impl FromStr for MasterPlaylist {
|
|||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let input = tag(input, ExtM3u::PREFIX)?;
|
||||
let mut builder = Self::builder();
|
||||
|
||||
let mut media_tags = vec![];
|
||||
|
@ -272,23 +274,15 @@ impl FromStr for MasterPlaylist {
|
|||
let mut session_key_tags = vec![];
|
||||
let mut unknown_tags = vec![];
|
||||
|
||||
for (i, line) in Lines::from(input).enumerate() {
|
||||
for line in Lines::from(input) {
|
||||
match line? {
|
||||
Line::Tag(tag) => {
|
||||
if i == 0 {
|
||||
if tag != Tag::ExtM3u(ExtM3u) {
|
||||
return Err(Error::invalid_input());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
match tag {
|
||||
Tag::ExtM3u(_) => {
|
||||
return Err(Error::invalid_input());
|
||||
}
|
||||
Tag::ExtXVersion(_) => {
|
||||
// This tag can be ignored, because the
|
||||
// MasterPlaylist will automatically set the
|
||||
// ExtXVersion tag to correct version!
|
||||
// ExtXVersion tag to the minimum required version
|
||||
// TODO: this might be verified?
|
||||
}
|
||||
Tag::ExtInf(_)
|
||||
| Tag::ExtXByteRange(_)
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::tags::{
|
|||
ExtXMediaSequence, ExtXPlaylistType, ExtXStart, ExtXTargetDuration, ExtXVersion,
|
||||
};
|
||||
use crate::types::ProtocolVersion;
|
||||
use crate::utils::tag;
|
||||
use crate::{Encrypted, Error, RequiredVersion};
|
||||
|
||||
/// Media playlist.
|
||||
|
@ -276,6 +277,8 @@ fn parse_media_playlist(
|
|||
input: &str,
|
||||
builder: &mut MediaPlaylistBuilder,
|
||||
) -> crate::Result<MediaPlaylist> {
|
||||
let input = tag(input, "#EXTM3U")?;
|
||||
|
||||
let mut segment = MediaSegment::builder();
|
||||
let mut segments = vec![];
|
||||
|
||||
|
@ -285,17 +288,10 @@ fn parse_media_playlist(
|
|||
|
||||
let mut available_key_tags: Vec<crate::tags::ExtXKey> = vec![];
|
||||
|
||||
for (i, line) in Lines::from(input).enumerate() {
|
||||
for line in Lines::from(input) {
|
||||
match line? {
|
||||
Line::Tag(tag) => {
|
||||
if i == 0 {
|
||||
if tag != Tag::ExtM3u(ExtM3u) {
|
||||
return Err(Error::custom("m3u8 doesn't start with #EXTM3U"));
|
||||
}
|
||||
continue;
|
||||
}
|
||||
match tag {
|
||||
Tag::ExtM3u(_) => return Err(Error::invalid_input()),
|
||||
Tag::ExtInf(t) => {
|
||||
has_partial_segment = true;
|
||||
segment.inf_tag(t);
|
||||
|
|
|
@ -11,31 +11,12 @@ use crate::{Error, RequiredVersion};
|
|||
/// Playlist file.
|
||||
/// It is the at the start of every [`Media Playlist`] and [`Master Playlist`].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Parsing from a [`str`]:
|
||||
///
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtM3u;
|
||||
/// #
|
||||
/// assert_eq!("#EXTM3U".parse::<ExtM3u>()?, ExtM3u);
|
||||
/// # Ok::<(), Box<dyn ::std::error::Error>>(())
|
||||
/// ```
|
||||
///
|
||||
/// Converting to a [`str`]:
|
||||
///
|
||||
/// ```
|
||||
/// # use hls_m3u8::tags::ExtM3u;
|
||||
/// #
|
||||
/// assert_eq!("#EXTM3U".to_string(), ExtM3u.to_string());
|
||||
/// ```
|
||||
///
|
||||
/// [`Media Playlist`]: crate::MediaPlaylist
|
||||
/// [`Master Playlist`]: crate::MasterPlaylist
|
||||
/// [`M3U`]: https://en.wikipedia.org/wiki/M3U
|
||||
/// [4.3.1.1. EXTM3U]: https://tools.ietf.org/html/rfc8216#section-4.3.1.1
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
|
||||
pub struct ExtM3u;
|
||||
pub(crate) struct ExtM3u;
|
||||
|
||||
impl ExtM3u {
|
||||
pub(crate) const PREFIX: &'static str = "#EXTM3U";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
mod m3u;
|
||||
mod version;
|
||||
|
||||
pub use m3u::*;
|
||||
pub(crate) use m3u::*;
|
||||
pub use version::*;
|
||||
|
|
|
@ -66,23 +66,7 @@ impl FromStr for ExtXByteRange {
|
|||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let input = tag(input, Self::PREFIX)?;
|
||||
|
||||
let tokens = input.splitn(2, '@').collect::<Vec<_>>();
|
||||
|
||||
if tokens.is_empty() {
|
||||
return Err(Error::invalid_input());
|
||||
}
|
||||
|
||||
let length = tokens[0].parse()?;
|
||||
|
||||
let start = {
|
||||
if tokens.len() == 2 {
|
||||
Some(tokens[1].parse()?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self::new(length, start))
|
||||
Ok(Self(ByteRange::from_str(input)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -151,29 +151,14 @@ impl FromStr for ExtInf {
|
|||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let input = tag(input, Self::PREFIX)?;
|
||||
let tokens = input.splitn(2, ',').collect::<Vec<_>>();
|
||||
let mut input = tag(input, Self::PREFIX)?.splitn(2, ',');
|
||||
|
||||
if tokens.is_empty() {
|
||||
return Err(Error::custom(format!(
|
||||
"failed to parse #EXTINF tag, couldn't split input: {:?}",
|
||||
input
|
||||
)));
|
||||
}
|
||||
|
||||
let duration = Duration::from_secs_f64(tokens[0].parse()?);
|
||||
|
||||
let title = {
|
||||
if tokens.len() >= 2 {
|
||||
if tokens[1].trim().is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(tokens[1].to_string())
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let duration = Duration::from_secs_f64(input.next().unwrap().parse()?);
|
||||
let title = input
|
||||
.next()
|
||||
.map(|value| value.trim())
|
||||
.filter(|value| !value.is_empty())
|
||||
.map(|value| value.to_string());
|
||||
|
||||
Ok(Self { duration, title })
|
||||
}
|
||||
|
|
|
@ -74,22 +74,15 @@ impl fmt::Display for ByteRange {
|
|||
impl FromStr for ByteRange {
|
||||
type Err = Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let tokens = s.splitn(2, '@').collect::<Vec<_>>();
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let mut input = input.splitn(2, '@');
|
||||
|
||||
if tokens.is_empty() {
|
||||
return Err(Error::invalid_input());
|
||||
}
|
||||
let length = input
|
||||
.next()
|
||||
.ok_or_else(|| Error::custom("missing length for #EXT-X-BYTERANGE"))
|
||||
.and_then(|s| s.parse().map_err(Error::parse_int))?;
|
||||
|
||||
let length = tokens[0].parse()?;
|
||||
|
||||
let start = {
|
||||
if tokens.len() == 2 {
|
||||
Some(tokens[1].parse()?)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
let start = input.next().map(str::parse).transpose()?;
|
||||
|
||||
Ok(Self::new(length, start))
|
||||
}
|
||||
|
|
|
@ -83,17 +83,17 @@ impl FromStr for Channels {
|
|||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let parameters = input.split('/').collect::<Vec<_>>();
|
||||
let mut parameters = input.split('/');
|
||||
|
||||
let channel_number = parameters
|
||||
.first()
|
||||
.next()
|
||||
.ok_or_else(|| Error::missing_attribute("first parameter of channels"))?
|
||||
.parse()
|
||||
.map_err(Error::parse_int)?;
|
||||
|
||||
Ok(Self {
|
||||
channel_number,
|
||||
unknown: parameters[1..].iter().map(|v| v.to_string()).collect(),
|
||||
unknown: parameters.map(|v| (*v).to_string()).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,19 +36,19 @@ impl FromStr for Resolution {
|
|||
type Err = Error;
|
||||
|
||||
fn from_str(input: &str) -> Result<Self, Self::Err> {
|
||||
let tokens = input.splitn(2, 'x').collect::<Vec<_>>();
|
||||
let mut input = input.splitn(2, 'x');
|
||||
|
||||
if tokens.len() != 2 {
|
||||
return Err(Error::custom(format!(
|
||||
"InvalidInput: Expected input format: [width]x[height] (ex. 1920x1080), got {:?}",
|
||||
input,
|
||||
)));
|
||||
}
|
||||
let width = input
|
||||
.next()
|
||||
.ok_or_else(|| Error::custom("missing width for `Resolution` or an invalid input"))
|
||||
.and_then(|v| v.parse().map_err(Error::parse_int))?;
|
||||
|
||||
Ok(Self {
|
||||
width: tokens[0].parse().map_err(Error::parse_int)?,
|
||||
height: tokens[1].parse().map_err(Error::parse_int)?,
|
||||
})
|
||||
let height = input
|
||||
.next()
|
||||
.ok_or_else(|| Error::custom("missing height for `Resolution` or an invalid input"))
|
||||
.and_then(|v| v.parse().map_err(Error::parse_int))?;
|
||||
|
||||
Ok(Self { width, height })
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue