diff --git a/src/lib.rs b/src/lib.rs index cdb2765..b91f567 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,12 +83,12 @@ extern crate nom; pub mod playlist; -use nom::character::complete::{digit1, multispace0, space0 }; -use nom::{IResult}; -use nom::{ delimited,none_of,peek,is_a,is_not,complete,terminated,tag, +use self::nom::character::complete::{digit1, multispace0, space0 }; +use self::nom::{IResult}; +use self::nom::{ delimited,none_of,peek,is_a,is_not,complete,terminated,tag, alt,do_parse,opt,named,map,map_res,eof,many0,take,take_until,char}; -use nom::combinator::map; -use nom::character::complete::{line_ending, not_line_ending}; +use self::nom::combinator::map; +use self::nom::character::complete::{line_ending, not_line_ending}; use std::str; use std::f32; use std::string; @@ -163,7 +163,7 @@ pub fn parse_playlist_res(input: &[u8]) -> Result IResult<&[u8], MasterPlaylist> { - map(parse_master_playlist_tags, MasterPlaylist::from_tags)(input) + map(parse_master_playlist_tags, master_playlist_from_tags)(input) } /// Parse input as a master playlist @@ -177,7 +177,7 @@ pub fn parse_master_playlist_res(input: &[u8]) -> Result IResult<&[u8], MediaPlaylist> { - map(parse_media_playlist_tags, MediaPlaylist::from_tags)(input) + map(parse_media_playlist_tags, media_playlist_from_tags)(input) } /// Parse input as a media playlist @@ -303,6 +303,48 @@ pub fn master_playlist_tag(input: &[u8]) -> IResult<&[u8], MasterPlaylistTag> { ) } +pub fn master_playlist_from_tags(mut tags: Vec) -> MasterPlaylist { + let mut master_playlist = MasterPlaylist::default(); + + while let Some(tag) = tags.pop() { + match tag { + MasterPlaylistTag::Version(v) => { + master_playlist.version = v; + } + MasterPlaylistTag::AlternativeMedia(v) => { + master_playlist.alternatives.push(v); + } + MasterPlaylistTag::VariantStream(stream) => { + master_playlist.variants.push(stream); + } + MasterPlaylistTag::Uri(uri) => { + if let Some(stream) = master_playlist.get_newest_variant() { + stream.uri = uri; + } + } + MasterPlaylistTag::SessionData(data) => { + master_playlist.session_data.push(data); + } + MasterPlaylistTag::SessionKey(key) => { + master_playlist.session_key.push(key); + } + MasterPlaylistTag::Start(s) => { + master_playlist.start = Some(s); + } + MasterPlaylistTag::IndependentSegments => { + master_playlist.independent_segments = true; + } + MasterPlaylistTag::Unknown(unknown) => { + master_playlist.unknown_tags.push(unknown); + } + _ => (), + } + } + + master_playlist +} + + named!(pub variant_stream_tag, do_parse!(tag!("#EXT-X-STREAM-INF:") >> attributes: key_value_pairs >> ( VariantStream::from_hashmap(attributes, false))) @@ -375,6 +417,87 @@ pub fn media_playlist_tag(input: &[u8]) -> IResult<&[u8], MediaPlaylistTag> { ) } +pub fn media_playlist_from_tags(mut tags: Vec) -> MediaPlaylist { + let mut media_playlist = MediaPlaylist::default(); + let mut next_segment = MediaSegment::empty(); + let mut encryption_key = None; + let mut map = None; + + while let Some(tag) = tags.pop() { + + match tag { + MediaPlaylistTag::Version(v) => { + media_playlist.version = v; + } + MediaPlaylistTag::TargetDuration(d) => { + media_playlist.target_duration = d; + } + MediaPlaylistTag::MediaSequence(n) => { + media_playlist.media_sequence = n; + } + MediaPlaylistTag::DiscontinuitySequence(n) => { + media_playlist.discontinuity_sequence = n; + } + MediaPlaylistTag::EndList => { + media_playlist.end_list = true; + } + MediaPlaylistTag::PlaylistType(t) => { + media_playlist.playlist_type = Some(t); + } + MediaPlaylistTag::IFramesOnly => { + media_playlist.i_frames_only = true; + } + MediaPlaylistTag::Start(s) => { + media_playlist.start = Some(s); + } + MediaPlaylistTag::IndependentSegments => { + media_playlist.independent_segments = true; + } + MediaPlaylistTag::Segment(segment_tag) => { + match segment_tag { + SegmentTag::Extinf(d, t) => { + next_segment.duration = d; + next_segment.title = t; + } + SegmentTag::ByteRange(b) => { + next_segment.byte_range = Some(b); + } + SegmentTag::Discontinuity => { + next_segment.discontinuity = true; + } + SegmentTag::Key(k) => { + encryption_key = Some(k); + } + SegmentTag::Map(m) => { + map = Some(m); + } + SegmentTag::ProgramDateTime(d) => { + next_segment.program_date_time = Some(d); + } + SegmentTag::DateRange(d) => { + next_segment.daterange = Some(d); + } + SegmentTag::Unknown(t) => { + media_playlist.unknown_tags.push(t); + } + SegmentTag::Uri(u) => { + next_segment.key = encryption_key.clone(); + next_segment.map = map.clone(); + next_segment.uri = u; + media_playlist.segments.push(next_segment); + next_segment = MediaSegment::empty(); + encryption_key = None; + map = None; + } + _ => (), + } + } + _ => (), + } + } + media_playlist +} + named!(pub playlist_type, map_res!( do_parse!( @@ -436,7 +559,15 @@ named!(pub duration_title_tag<(f32, Option)>, named!(pub key, map!(key_value_pairs, Key::from_hashmap)); -named!(pub extmap, map!(key_value_pairs, Map::from_hashmap)); +named!(pub extmap, map!(key_value_pairs, |attrs| Map { + uri: attrs.get("URI").map(|u| u.clone()).unwrap_or_default(), + byte_range: attrs.get("BYTERANGE").map(|range| { + match byte_range_val(range.as_bytes()) { + IResult::Ok((_, br)) => br, + _ => panic!("Should not happen"), + } + }), +})); // ----------------------------------------------------------------------------------------------- // Basic tags diff --git a/src/playlist.rs b/src/playlist.rs index af937c1..91384b1 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -7,7 +7,6 @@ use std::io::Write; use std::collections::HashMap; use std::str::FromStr; use std::fmt; -use super::*; use std::f32; macro_rules! write_some_attribute_quoted { @@ -74,48 +73,8 @@ pub struct MasterPlaylist { } impl MasterPlaylist { - pub fn from_tags(mut tags: Vec) -> MasterPlaylist { - let mut master_playlist = MasterPlaylist::default(); - while let Some(tag) = tags.pop() { - match tag { - MasterPlaylistTag::Version(v) => { - master_playlist.version = v; - } - MasterPlaylistTag::AlternativeMedia(v) => { - master_playlist.alternatives.push(v); - } - MasterPlaylistTag::VariantStream(stream) => { - master_playlist.variants.push(stream); - } - MasterPlaylistTag::Uri(uri) => { - if let Some(stream) = master_playlist.get_newest_variant() { - stream.uri = uri; - } - } - MasterPlaylistTag::SessionData(data) => { - master_playlist.session_data.push(data); - } - MasterPlaylistTag::SessionKey(key) => { - master_playlist.session_key.push(key); - } - MasterPlaylistTag::Start(s) => { - master_playlist.start = Some(s); - } - MasterPlaylistTag::IndependentSegments => { - master_playlist.independent_segments = true; - } - MasterPlaylistTag::Unknown(unknown) => { - master_playlist.unknown_tags.push(unknown); - } - _ => (), - } - } - - master_playlist - } - - fn get_newest_variant(&mut self) -> Option<&mut VariantStream> { + pub fn get_newest_variant(&mut self) -> Option<&mut VariantStream> { self.variants.iter_mut().rev().find(|v| !v.is_i_frame) } @@ -445,87 +404,6 @@ pub struct MediaPlaylist { impl MediaPlaylist { - pub fn from_tags(mut tags: Vec) -> MediaPlaylist { - let mut media_playlist = MediaPlaylist::default(); - let mut next_segment = MediaSegment::empty(); - let mut encryption_key = None; - let mut map = None; - - while let Some(tag) = tags.pop() { - - match tag { - MediaPlaylistTag::Version(v) => { - media_playlist.version = v; - } - MediaPlaylistTag::TargetDuration(d) => { - media_playlist.target_duration = d; - } - MediaPlaylistTag::MediaSequence(n) => { - media_playlist.media_sequence = n; - } - MediaPlaylistTag::DiscontinuitySequence(n) => { - media_playlist.discontinuity_sequence = n; - } - MediaPlaylistTag::EndList => { - media_playlist.end_list = true; - } - MediaPlaylistTag::PlaylistType(t) => { - media_playlist.playlist_type = Some(t); - } - MediaPlaylistTag::IFramesOnly => { - media_playlist.i_frames_only = true; - } - MediaPlaylistTag::Start(s) => { - media_playlist.start = Some(s); - } - MediaPlaylistTag::IndependentSegments => { - media_playlist.independent_segments = true; - } - MediaPlaylistTag::Segment(segment_tag) => { - match segment_tag { - SegmentTag::Extinf(d, t) => { - next_segment.duration = d; - next_segment.title = t; - } - SegmentTag::ByteRange(b) => { - next_segment.byte_range = Some(b); - } - SegmentTag::Discontinuity => { - next_segment.discontinuity = true; - } - SegmentTag::Key(k) => { - encryption_key = Some(k); - } - SegmentTag::Map(m) => { - map = Some(m); - } - SegmentTag::ProgramDateTime(d) => { - next_segment.program_date_time = Some(d); - } - SegmentTag::DateRange(d) => { - next_segment.daterange = Some(d); - } - SegmentTag::Unknown(t) => { - media_playlist.unknown_tags.push(t); - } - SegmentTag::Uri(u) => { - next_segment.key = encryption_key.clone(); - next_segment.map = map.clone(); - next_segment.uri = u; - media_playlist.segments.push(next_segment); - next_segment = MediaSegment::empty(); - encryption_key = None; - map = None; - } - _ => (), - } - } - _ => (), - } - } - media_playlist - } - pub fn write_to(&self, w: &mut T) -> std::io::Result<()> { writeln!(w, "{}" ,"#EXTM3U")?; writeln!(w, "#EXT-X-VERSION:{}", self.version)?; @@ -724,13 +602,6 @@ pub struct Map { } impl Map { - pub fn from_hashmap(mut attrs: HashMap) -> Map { - Map { - uri: attrs.remove("URI").unwrap_or_default(), - byte_range: attrs.remove("BYTERANGE").map(ByteRange::from), - } - } - pub fn write_attributes_to(&self, w: &mut T) -> std::io::Result<()> { write!(w, "URI=\"{}\"", self.uri)?; if let Some(ref byte_range) = self.byte_range { @@ -764,22 +635,6 @@ impl ByteRange { } } -impl From for ByteRange { - fn from(s: String) -> Self { - let w: &str = &s; - ByteRange::from(w) - } -} - -impl<'a> From<&'a str> for ByteRange { - fn from(s: &'a str) -> Self { - match byte_range_val(s.as_bytes()) { - IResult::Ok((_, br)) => br, - _ => panic!("Should not happen"), - } - } -} - /// [`#EXT-X-DATERANGE:`] /// (https://tools.ietf.org/html/draft-pantos-http-live-streaming-19#section-4.3.2.7) diff --git a/tests/lib.rs b/tests/lib.rs index 601ff43..76a21dc 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -405,7 +405,7 @@ fn create_and_parse_media_playlist_full() { uri: "20140311T113819-01-338559live.ts".into(), duration: 2.002, title: Some("338559".into()), - byte_range: Some(ByteRange::from("137116@497036")), + byte_range: Some(ByteRange { length: 137116, offset: Some(4559) }), discontinuity: true, key: Some(Key { method: "AES-128".into(), @@ -416,7 +416,7 @@ fn create_and_parse_media_playlist_full() { }), map: Some(Map { uri: "www.map-uri.com".into(), - byte_range: Some(ByteRange::from("137116@497036")), + byte_range: Some(ByteRange { length: 137116, offset: Some(4559) }), }), program_date_time: Some("broodlordinfestorgg".into()), daterange: None,