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