diff --git a/sample-playlists/media-playlist-zero-decimal.m3u8 b/sample-playlists/media-playlist-zero-decimal.m3u8 new file mode 100644 index 0000000..b52f251 --- /dev/null +++ b/sample-playlists/media-playlist-zero-decimal.m3u8 @@ -0,0 +1,30 @@ +#EXTM3U +#EXT-X-TARGETDURATION:11 +#EXT-X-VERSION:4 +#EXT-X-MEDIA-SEQUENCE:0 +#EXT-X-PLAYLIST-TYPE:VOD +#EXTINF:9.00000, +#EXT-X-BYTERANGE:86920@0 +main.aac +#EXTINF:10.00000, +#EXT-X-BYTERANGE:136595@86920 +main.aac +#EXTINF:9.00000, +#EXT-X-BYTERANGE:136567@223515 +main.aac +#EXTINF:10.00000, +#EXT-X-BYTERANGE:136954@360082 +main.aac +#EXTINF:10.00000, +#EXT-X-BYTERANGE:137116@497036 +main.aac +#EXTINF:9.00000, +#EXT-X-BYTERANGE:136770@634152 +main.aac +#EXTINF:10.00000, +#EXT-X-BYTERANGE:137219@770922 +main.aac +#EXTINF:10.00000, +#EXT-X-BYTERANGE:137132@908141 +main.acc +#EXT-X-ENDLIST diff --git a/src/lib.rs b/src/lib.rs index 1b740c1..ec65acf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,32 @@ //! //let mut file = std::fs::File::open("playlist.m3u8").unwrap(); //! //playlist.write_to(&mut file).unwrap(); //! ``` +//! +//! Controlling the output precision for floats, such as #EXTINF (default is unset) +//! +//! ``` +//! use std::sync::atomic::Ordering; +//! use m3u8_rs::{WRITE_OPT_FLOAT_PRECISION, MediaPlaylist, MediaSegment}; +//! +//! WRITE_OPT_FLOAT_PRECISION.store(5, Ordering::Relaxed); +//! +//! let playlist = MediaPlaylist { +//! target_duration: 3, +//! segments: vec![ +//! MediaSegment { +//! duration: 2.9, +//! title: Some("title".into()), +//! ..Default::default() +//! }, +//! ], +//! ..Default::default() +//! }; +//! +//! let mut v: Vec = Vec::new(); +//! +//! playlist.write_to(&mut v).unwrap(); +//! let m3u8_str: &str = std::str::from_utf8(&v).unwrap(); +//! assert!(m3u8_str.contains("#EXTINF:2.90000,title")); mod playlist; pub use playlist::*; diff --git a/src/playlist.rs b/src/playlist.rs index 8ba747b..8b3fa0c 100644 --- a/src/playlist.rs +++ b/src/playlist.rs @@ -6,11 +6,16 @@ use crate::QuotedOrUnquoted; use std::collections::HashMap; use std::convert::{TryFrom, TryInto}; -use std::f32; use std::fmt; use std::fmt::Display; use std::io::Write; use std::str::FromStr; +use std::sync::atomic::{AtomicUsize, Ordering}; +use std::usize::MAX; +use std::{f32, usize}; + +/// The output precision for floats, such as #EXTINF (default is unset) +pub static WRITE_OPT_FLOAT_PRECISION: AtomicUsize = AtomicUsize::new(MAX); macro_rules! write_some_attribute_quoted { ($w:expr, $tag:expr, $o:expr) => { @@ -884,7 +889,14 @@ impl MediaSegment { writeln!(w, "{}", unknown_tag)?; } - write!(w, "#EXTINF:{},", self.duration)?; + match WRITE_OPT_FLOAT_PRECISION.load(Ordering::Relaxed) { + MAX => { + write!(w, "#EXTINF:{},", self.duration)?; + } + n => { + write!(w, "#EXTINF:{:.*},", n, self.duration)?; + } + }; if let Some(ref v) = self.title { writeln!(w, "{}", v)?; diff --git a/tests/lib.rs b/tests/lib.rs index 3b95f2d..a388365 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -8,6 +8,7 @@ use std::collections::HashMap; use std::fs::File; use std::io::Read; use std::path; +use std::sync::atomic::Ordering; use std::{fs, io}; fn all_sample_m3u_playlists() -> Vec { @@ -198,6 +199,36 @@ fn create_and_parse_master_playlist_empty() { assert_eq!(playlist_original, playlist_parsed); } +#[test] +fn create_segment_float_inf() { + let playlist = Playlist::MediaPlaylist(MediaPlaylist { + version: Some(6), + target_duration: 3, + media_sequence: 338559, + discontinuity_sequence: 1234, + end_list: true, + playlist_type: Some(MediaPlaylistType::Vod), + segments: vec![MediaSegment { + uri: "20140311T113819-01-338559live.ts".into(), + duration: 2.000f32, + title: Some("title".into()), + ..Default::default() + }], + ..Default::default() + }); + + let mut v: Vec = Vec::new(); + playlist.write_to(&mut v).unwrap(); + let m3u8_str: &str = std::str::from_utf8(&v).unwrap(); + assert!(m3u8_str.contains("#EXTINF:2,title")); + + WRITE_OPT_FLOAT_PRECISION.store(5, Ordering::Relaxed); + + playlist.write_to(&mut v).unwrap(); + let m3u8_str: &str = std::str::from_utf8(&v).unwrap(); + assert!(m3u8_str.contains("#EXTINF:2.00000,title")); +} + #[test] fn create_and_parse_master_playlist_full() { let mut playlist_original = Playlist::MasterPlaylist(MasterPlaylist { @@ -382,6 +413,7 @@ fn create_and_parse_media_playlist_full() { tag: "X-CUE-OUT".into(), rest: Some("DURATION=2.002".into()), }], + ..Default::default() }], unknown_tags: vec![], });