mirror of
https://github.com/sile/hls_m3u8.git
synced 2024-06-09 16:59:34 +00:00
Add master_playlist
module
This commit is contained in:
parent
2111d047c3
commit
a3b1b67447
|
@ -5,6 +5,7 @@ extern crate trackable;
|
||||||
|
|
||||||
use std::io::{self, Read};
|
use std::io::{self, Read};
|
||||||
use clap::{App, Arg};
|
use clap::{App, Arg};
|
||||||
|
use hls_m3u8::master_playlist::MasterPlaylist;
|
||||||
use hls_m3u8::media_playlist::MediaPlaylist;
|
use hls_m3u8::media_playlist::MediaPlaylist;
|
||||||
use trackable::error::Failure;
|
use trackable::error::Failure;
|
||||||
|
|
||||||
|
@ -30,7 +31,10 @@ fn main() {
|
||||||
let playlist: MediaPlaylist = track_try_unwrap!(m3u8.parse());
|
let playlist: MediaPlaylist = track_try_unwrap!(m3u8.parse());
|
||||||
println!("{}", playlist);
|
println!("{}", playlist);
|
||||||
}
|
}
|
||||||
"master" => unimplemented!(),
|
"master" => {
|
||||||
|
let playlist: MasterPlaylist = track_try_unwrap!(m3u8.parse());
|
||||||
|
println!("{}", playlist);
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate trackable;
|
extern crate trackable;
|
||||||
|
|
||||||
// pub mod playlist;
|
|
||||||
// pub mod master_playlist;
|
|
||||||
pub use error::{Error, ErrorKind};
|
pub use error::{Error, ErrorKind};
|
||||||
|
|
||||||
pub mod attribute;
|
pub mod attribute;
|
||||||
|
pub mod master_playlist;
|
||||||
pub mod media_playlist;
|
pub mod media_playlist;
|
||||||
pub mod media_segment;
|
pub mod media_segment;
|
||||||
pub mod string;
|
pub mod string;
|
||||||
|
|
159
src/master_playlist.rs
Normal file
159
src/master_playlist.rs
Normal file
|
@ -0,0 +1,159 @@
|
||||||
|
use std::fmt;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use {Error, ErrorKind, Result};
|
||||||
|
use line::{Line, Lines};
|
||||||
|
use tag::{ExtM3u, ExtXIFrameStreamInf, ExtXIndependentSegments, ExtXMedia, ExtXSessionData,
|
||||||
|
ExtXSessionKey, ExtXStart, ExtXStreamInf, ExtXVersion, Tag};
|
||||||
|
use version::ProtocolVersion;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ExtXStreamInfWithUri {
|
||||||
|
pub inf: ExtXStreamInf,
|
||||||
|
pub uri: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MasterPlaylist {
|
||||||
|
pub version: ExtXVersion,
|
||||||
|
pub media_tags: Vec<ExtXMedia>,
|
||||||
|
pub stream_infs: Vec<ExtXStreamInfWithUri>,
|
||||||
|
pub i_frame_stream_infs: Vec<ExtXIFrameStreamInf>,
|
||||||
|
|
||||||
|
// TODO: A Playlist MUST NOT contain more than one EXT-X-
|
||||||
|
// SESSION-DATA tag with the same DATA-ID attribute and the same
|
||||||
|
// LANGUAGE attribute.
|
||||||
|
pub session_data_tags: Vec<ExtXSessionData>,
|
||||||
|
|
||||||
|
// TODO: A Master Playlist MUST NOT contain more than one EXT-X-SESSION-KEY
|
||||||
|
// tag with the same METHOD, URI, IV, KEYFORMAT, and KEYFORMATVERSIONS
|
||||||
|
// attribute values.
|
||||||
|
pub session_keys: Vec<ExtXSessionKey>,
|
||||||
|
|
||||||
|
pub independent_segments: Option<ExtXIndependentSegments>,
|
||||||
|
pub start: Option<ExtXStart>,
|
||||||
|
}
|
||||||
|
impl fmt::Display for MasterPlaylist {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
writeln!(f, "{}", ExtM3u)?;
|
||||||
|
if self.version.value() != ProtocolVersion::V1 {
|
||||||
|
writeln!(f, "{}", self.version)?;
|
||||||
|
}
|
||||||
|
for t in &self.media_tags {
|
||||||
|
writeln!(f, "{}", t)?;
|
||||||
|
}
|
||||||
|
for t in &self.stream_infs {
|
||||||
|
writeln!(f, "{}", t.inf)?;
|
||||||
|
writeln!(f, "{}", t.uri)?;
|
||||||
|
}
|
||||||
|
for t in &self.i_frame_stream_infs {
|
||||||
|
writeln!(f, "{}", t)?;
|
||||||
|
}
|
||||||
|
for t in &self.session_data_tags {
|
||||||
|
writeln!(f, "{}", t)?;
|
||||||
|
}
|
||||||
|
for t in &self.session_keys {
|
||||||
|
writeln!(f, "{}", t)?;
|
||||||
|
}
|
||||||
|
if let Some(ref t) = self.independent_segments {
|
||||||
|
writeln!(f, "{}", t)?;
|
||||||
|
}
|
||||||
|
if let Some(ref t) = self.start {
|
||||||
|
writeln!(f, "{}", t)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl FromStr for MasterPlaylist {
|
||||||
|
type Err = Error;
|
||||||
|
fn from_str(s: &str) -> Result<Self> {
|
||||||
|
let mut version = None;
|
||||||
|
let mut media_tags = Vec::new();
|
||||||
|
let mut stream_infs = Vec::new();
|
||||||
|
let mut i_frame_stream_infs = Vec::new();
|
||||||
|
let mut session_data_tags = Vec::new();
|
||||||
|
let mut session_keys = Vec::new();
|
||||||
|
let mut independent_segments = None;
|
||||||
|
let mut start = None;
|
||||||
|
|
||||||
|
let mut last_stream_inf = None;
|
||||||
|
for (i, line) in Lines::new(s).enumerate() {
|
||||||
|
match track!(line)? {
|
||||||
|
Line::Blank | Line::Comment(_) => {}
|
||||||
|
Line::Tag(tag) => {
|
||||||
|
if i == 0 {
|
||||||
|
track_assert_eq!(tag, Tag::ExtM3u(ExtM3u), ErrorKind::InvalidInput);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match tag {
|
||||||
|
Tag::ExtM3u(_) => unreachable!(),
|
||||||
|
Tag::ExtXVersion(t) => {
|
||||||
|
track_assert_eq!(version, None, ErrorKind::InvalidInput);
|
||||||
|
version = Some(t);
|
||||||
|
}
|
||||||
|
Tag::ExtInf(_)
|
||||||
|
| Tag::ExtXByteRange(_)
|
||||||
|
| Tag::ExtXDiscontinuity(_)
|
||||||
|
| Tag::ExtXKey(_)
|
||||||
|
| Tag::ExtXMap(_)
|
||||||
|
| Tag::ExtXProgramDateTime(_)
|
||||||
|
| Tag::ExtXDateRange(_)
|
||||||
|
| Tag::ExtXTargetDuration(_)
|
||||||
|
| Tag::ExtXMediaSequence(_)
|
||||||
|
| Tag::ExtXDiscontinuitySequence(_)
|
||||||
|
| Tag::ExtXEndList(_)
|
||||||
|
| Tag::ExtXPlaylistType(_)
|
||||||
|
| Tag::ExtXIFramesOnly(_) => {
|
||||||
|
track_panic!(ErrorKind::InvalidInput, "{}", tag)
|
||||||
|
}
|
||||||
|
Tag::ExtXMedia(t) => {
|
||||||
|
media_tags.push(t);
|
||||||
|
}
|
||||||
|
Tag::ExtXStreamInf(t) => {
|
||||||
|
track_assert_eq!(last_stream_inf, None, ErrorKind::InvalidInput);
|
||||||
|
last_stream_inf = Some((i, t));
|
||||||
|
}
|
||||||
|
Tag::ExtXIFrameStreamInf(t) => {
|
||||||
|
i_frame_stream_infs.push(t);
|
||||||
|
}
|
||||||
|
Tag::ExtXSessionData(t) => {
|
||||||
|
session_data_tags.push(t);
|
||||||
|
}
|
||||||
|
Tag::ExtXSessionKey(t) => {
|
||||||
|
session_keys.push(t);
|
||||||
|
}
|
||||||
|
Tag::ExtXIndependentSegments(t) => {
|
||||||
|
track_assert_eq!(independent_segments, None, ErrorKind::InvalidInput);
|
||||||
|
independent_segments = Some(t);
|
||||||
|
}
|
||||||
|
Tag::ExtXStart(t) => {
|
||||||
|
track_assert_eq!(start, None, ErrorKind::InvalidInput);
|
||||||
|
start = Some(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Line::Uri(uri) => {
|
||||||
|
let (line, inf) = track_assert_some!(last_stream_inf, ErrorKind::InvalidInput);
|
||||||
|
track_assert_eq!(line + 1, i, ErrorKind::InvalidInput);
|
||||||
|
stream_infs.push(ExtXStreamInfWithUri {
|
||||||
|
inf,
|
||||||
|
uri: uri.to_owned(),
|
||||||
|
});
|
||||||
|
last_stream_inf = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: check compatibility
|
||||||
|
Ok(MasterPlaylist {
|
||||||
|
version: version.unwrap_or_else(|| ExtXVersion::new(ProtocolVersion::V1)),
|
||||||
|
media_tags,
|
||||||
|
stream_infs,
|
||||||
|
i_frame_stream_infs,
|
||||||
|
session_data_tags,
|
||||||
|
session_keys,
|
||||||
|
independent_segments,
|
||||||
|
start,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue