1
0
Fork 0
mirror of https://github.com/sile/hls_m3u8.git synced 2024-05-18 08:22:59 +00:00

Add media_playlist module

This commit is contained in:
Takeru Ohta 2018-02-14 00:25:33 +09:00
parent f5393cb209
commit 2111d047c3
5 changed files with 258 additions and 4 deletions

View file

@ -5,3 +5,6 @@ authors = ["Takeru Ohta <phjgt308@gmail.com>"]
[dependencies]
trackable = "0.2"
[dev-dependencies]
clap = "2"

View file

@ -1,18 +1,36 @@
extern crate clap;
extern crate hls_m3u8;
#[macro_use]
extern crate trackable;
use std::io::{self, Read};
use clap::{App, Arg};
use hls_m3u8::media_playlist::MediaPlaylist;
use trackable::error::Failure;
fn main() {
let matches = App::new("parse")
.arg(
Arg::with_name("M3U8_TYPE")
.long("m3u8-type")
.takes_value(true)
.default_value("media")
.possible_values(&["media", "master"]),
)
.get_matches();
let mut m3u8 = String::new();
track_try_unwrap!(
io::stdin()
.read_to_string(&mut m3u8)
.map_err(Failure::from_error)
);
for line in hls_m3u8::line::Lines::new(&m3u8) {
println!("{:?}", track_try_unwrap!(line));
match matches.value_of("M3U8_TYPE").unwrap() {
"media" => {
let playlist: MediaPlaylist = track_try_unwrap!(m3u8.parse());
println!("{}", playlist);
}
"master" => unimplemented!(),
_ => unreachable!(),
}
}

View file

@ -2,11 +2,11 @@
extern crate trackable;
// pub mod playlist;
// pub mod media_playlist;
// pub mod master_playlist;
pub use error::{Error, ErrorKind};
pub mod attribute;
pub mod media_playlist;
pub mod media_segment;
pub mod string;
pub mod tag;

191
src/media_playlist.rs Normal file
View file

@ -0,0 +1,191 @@
use std::fmt;
use std::str::FromStr;
use {Error, ErrorKind, Result};
use line::{Line, Lines};
use media_segment::{MediaSegment, MediaSegmentBuilder};
use tag::{ExtM3u, ExtXDiscontinuitySequence, ExtXEndList, ExtXIFramesOnly,
ExtXIndependentSegments, ExtXMediaSequence, ExtXPlaylistType, ExtXStart,
ExtXTargetDuration, ExtXVersion, Tag};
use version::ProtocolVersion;
// TODO: There MUST NOT be more than one Media Playlist tag of each type in any Media Playlist.
// TODO: A Media Playlist tag MUST NOT appear in a Master Playlist.
#[derive(Debug, Clone)]
pub struct MediaPlaylist {
pub version: ExtXVersion,
// TODO: The EXTINF duration of each Media Segment in the Playlist
// file, when rounded to the nearest integer, MUST be less than or equal
// to the target duration
pub target_duration: ExtXTargetDuration,
// TODO: The EXT-X-MEDIA-SEQUENCE tag MUST appear before the first Media
// Segment in the Playlist.
pub media_sequence: Option<ExtXMediaSequence>,
// TODO: The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before the first
// Media Segment in the Playlist.
//
// TODO: The EXT-X-DISCONTINUITY-SEQUENCE tag MUST appear before any EXT-
// X-DISCONTINUITY tag.
pub discontinuity_sequence: Option<ExtXDiscontinuitySequence>,
pub playlist_type: Option<ExtXPlaylistType>,
pub i_frames_only: Option<ExtXIFramesOnly>,
pub independent_segments: Option<ExtXIndependentSegments>,
pub start: Option<ExtXStart>,
pub segments: Vec<MediaSegment>,
pub end_list: Option<ExtXEndList>,
}
impl fmt::Display for MediaPlaylist {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
writeln!(f, "{}", ExtM3u)?;
if self.version.value() != ProtocolVersion::V1 {
writeln!(f, "{}", self.version)?;
}
writeln!(f, "{}", self.target_duration)?;
if let Some(ref t) = self.media_sequence {
writeln!(f, "{}", t)?;
}
if let Some(ref t) = self.discontinuity_sequence {
writeln!(f, "{}", t)?;
}
if let Some(ref t) = self.playlist_type {
writeln!(f, "{}", t)?;
}
if let Some(ref t) = self.i_frames_only {
writeln!(f, "{}", t)?;
}
if let Some(ref t) = self.independent_segments {
writeln!(f, "{}", t)?;
}
if let Some(ref t) = self.start {
writeln!(f, "{}", t)?;
}
for segment in &self.segments {
writeln!(f, "{}", segment)?;
}
if let Some(ref t) = self.end_list {
writeln!(f, "{}", t)?;
}
Ok(())
}
}
impl FromStr for MediaPlaylist {
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
let mut version = None;
let mut target_duration = None;
let mut media_sequence = None;
let mut discontinuity_sequence = None;
let mut playlist_type = None;
let mut i_frames_only = None;
let mut independent_segments = None;
let mut start = None;
let mut end_list = None;
let mut segment = MediaSegmentBuilder::new();
let mut segments = Vec::new();
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(t) => {
segment.tag(t);
}
Tag::ExtXByteRange(t) => {
segment.tag(t);
}
Tag::ExtXDiscontinuity(t) => {
segment.tag(t);
}
Tag::ExtXKey(t) => {
segment.tag(t);
}
Tag::ExtXMap(t) => {
segment.tag(t);
}
Tag::ExtXProgramDateTime(t) => {
segment.tag(t);
}
Tag::ExtXDateRange(t) => {
segment.tag(t);
}
Tag::ExtXTargetDuration(t) => {
track_assert_eq!(target_duration, None, ErrorKind::InvalidInput);
target_duration = Some(t);
}
Tag::ExtXMediaSequence(t) => {
track_assert_eq!(media_sequence, None, ErrorKind::InvalidInput);
media_sequence = Some(t);
}
Tag::ExtXDiscontinuitySequence(t) => {
track_assert_eq!(discontinuity_sequence, None, ErrorKind::InvalidInput);
discontinuity_sequence = Some(t);
}
Tag::ExtXEndList(t) => {
track_assert_eq!(end_list, None, ErrorKind::InvalidInput);
end_list = Some(t);
}
Tag::ExtXPlaylistType(t) => {
track_assert_eq!(playlist_type, None, ErrorKind::InvalidInput);
playlist_type = Some(t);
}
Tag::ExtXIFramesOnly(t) => {
track_assert_eq!(i_frames_only, None, ErrorKind::InvalidInput);
i_frames_only = Some(t);
}
Tag::ExtXMedia(_)
| Tag::ExtXStreamInf(_)
| Tag::ExtXIFrameStreamInf(_)
| Tag::ExtXSessionData(_)
| Tag::ExtXSessionKey(_) => {
track_panic!(ErrorKind::InvalidInput, "{}", tag)
}
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) => {
segment.uri(uri.to_owned());
segments.push(track!(segment.finish())?);
segment = MediaSegmentBuilder::new();
}
}
}
let target_duration = track_assert_some!(target_duration, ErrorKind::InvalidInput);
// TODO: check compatibility
Ok(MediaPlaylist {
version: version.unwrap_or_else(|| ExtXVersion::new(ProtocolVersion::V1)),
target_duration,
media_sequence,
discontinuity_sequence,
playlist_type,
i_frames_only,
independent_segments,
start,
segments,
end_list,
})
}
}

View file

@ -15,6 +15,15 @@ macro_rules! may_invalid {
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TagKind {
Basic,
MediaSegment,
MediaPlaylist,
MasterPlaylist,
MediaOrMasterPlaylist,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum MediaSegmentTag {
ExtInf(ExtInf),
@ -150,6 +159,32 @@ pub enum Tag {
ExtXIndependentSegments(ExtXIndependentSegments),
ExtXStart(ExtXStart),
}
impl Tag {
pub fn kind(&self) -> TagKind {
match *self {
Tag::ExtM3u(_) | Tag::ExtXVersion(_) => TagKind::Basic,
Tag::ExtInf(_)
| Tag::ExtXByteRange(_)
| Tag::ExtXDiscontinuity(_)
| Tag::ExtXKey(_)
| Tag::ExtXMap(_)
| Tag::ExtXProgramDateTime(_)
| Tag::ExtXDateRange(_) => TagKind::MediaSegment,
Tag::ExtXTargetDuration(_)
| Tag::ExtXMediaSequence(_)
| Tag::ExtXDiscontinuitySequence(_)
| Tag::ExtXEndList(_)
| Tag::ExtXPlaylistType(_)
| Tag::ExtXIFramesOnly(_) => TagKind::MediaPlaylist,
Tag::ExtXMedia(_)
| Tag::ExtXStreamInf(_)
| Tag::ExtXIFrameStreamInf(_)
| Tag::ExtXSessionData(_)
| Tag::ExtXSessionKey(_) => TagKind::MasterPlaylist,
Tag::ExtXIndependentSegments(_) | Tag::ExtXStart(_) => TagKind::MediaOrMasterPlaylist,
}
}
}
impl fmt::Display for Tag {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
@ -255,10 +290,17 @@ impl FromStr for ExtM3u {
// TODO: A Playlist file MUST NOT contain more than one EXT-X-VERSION tag
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtXVersion {
pub version: ProtocolVersion,
version: ProtocolVersion,
}
impl ExtXVersion {
const PREFIX: &'static str = "#EXT-X-VERSION:";
pub fn new(version: ProtocolVersion) -> Self {
ExtXVersion { version }
}
pub fn value(&self) -> ProtocolVersion {
self.version
}
}
impl fmt::Display for ExtXVersion {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {