From fe333063abfac5924206d1913bb2c6f0e6642be3 Mon Sep 17 00:00:00 2001 From: Seungha Yang Date: Sun, 24 Sep 2023 17:51:45 +0900 Subject: hlssink3: Use sprintf for segment name formatting The zero-padded naming requirement is unnecessary. Use simple sprintf instead Part-of: --- Cargo.lock | 8 ++- net/hlssink3/Cargo.toml | 2 +- net/hlssink3/src/imp.rs | 16 +++--- net/hlssink3/src/playlist.rs | 120 ------------------------------------------- 4 files changed, 17 insertions(+), 129 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d711f1409..1c84535e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2227,7 +2227,7 @@ dependencies = [ "gstreamer-check", "m3u8-rs", "once_cell", - "regex", + "sprintf", ] [[package]] @@ -5333,6 +5333,12 @@ dependencies = [ "lock_api", ] +[[package]] +name = "sprintf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c0cdea5a20a06e7c57f627094e7b1618e5665592cd88f2d45fa4014e348db58" + [[package]] name = "strength_reduce" version = "0.2.4" diff --git a/net/hlssink3/Cargo.toml b/net/hlssink3/Cargo.toml index d3f8ae0a6..bdba7923c 100644 --- a/net/hlssink3/Cargo.toml +++ b/net/hlssink3/Cargo.toml @@ -15,7 +15,7 @@ glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.16", version gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.16", version = "0.16" } once_cell = "1.7.2" m3u8-rs = "5.0" -regex = "1" +sprintf = "0.1.3" [dev-dependencies] gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.19", version = "0.19" } diff --git a/net/hlssink3/src/imp.rs b/net/hlssink3/src/imp.rs index 9f4492701..f9737afaf 100644 --- a/net/hlssink3/src/imp.rs +++ b/net/hlssink3/src/imp.rs @@ -6,7 +6,7 @@ // // SPDX-License-Identifier: MPL-2.0 -use crate::playlist::{Playlist, SegmentFormatter}; +use crate::playlist::Playlist; use crate::HlsSink3PlaylistType; use gio::prelude::*; use glib::subclass::prelude::*; @@ -60,7 +60,6 @@ impl From> for HlsSink3PlaylistType { struct Settings { location: String, - segment_formatter: SegmentFormatter, playlist_location: String, playlist_root: Option, playlist_length: u32, @@ -88,7 +87,6 @@ impl Default for Settings { .expect("Could not make element giostreamsink"); Self { location: String::from(DEFAULT_LOCATION), - segment_formatter: SegmentFormatter::new(DEFAULT_LOCATION).unwrap(), playlist_location: String::from(DEFAULT_PLAYLIST_LOCATION), playlist_root: None, playlist_length: DEFAULT_PLAYLIST_LENGTH, @@ -190,7 +188,14 @@ impl HlsSink3 { }; let settings = self.settings.lock().unwrap(); - let segment_file_location = settings.segment_formatter.segment(fragment_id); + let segment_file_location = match sprintf::sprintf!(&settings.location, fragment_id) { + Ok(file_name) => file_name, + Err(err) => { + gst::error!(CAT, imp: self, "Couldn't build file name, err: {:?}", err,); + return Err(String::from("Invalid init segment file pattern")); + } + }; + gst::trace!( CAT, imp: self, @@ -493,9 +498,6 @@ impl ObjectImpl for HlsSink3 { .get::>() .expect("type checked upstream") .unwrap_or_else(|| DEFAULT_LOCATION.into()); - settings.segment_formatter = SegmentFormatter::new(&settings.location).expect( - "A string containing `%03d` pattern must be used (can be any number from 0-9)", - ); settings .splitmuxsink .set_property("location", &settings.location); diff --git a/net/hlssink3/src/playlist.rs b/net/hlssink3/src/playlist.rs index ee3297eb0..a541b3187 100644 --- a/net/hlssink3/src/playlist.rs +++ b/net/hlssink3/src/playlist.rs @@ -7,15 +7,11 @@ // SPDX-License-Identifier: MPL-2.0 use m3u8_rs::{MediaPlaylist, MediaPlaylistType, MediaSegment}; -use once_cell::sync::Lazy; -use regex::Regex; use std::io::Write; const GST_M3U8_PLAYLIST_V3: usize = 3; const GST_M3U8_PLAYLIST_V4: usize = 4; -static SEGMENT_IDX_PATTERN: Lazy = Lazy::new(|| Regex::new(r"(%0(\d+)d)").unwrap()); - /// An HLS playlist. /// /// Controls the changes that needs to happen in the playlist as new segments are added. This @@ -155,119 +151,3 @@ pub enum PlaylistRenderState { Init, Started, } - -/// A formatter for segment locations. -/// -/// The formatting is based on a string that must contain the placeholder `%0Xd` where `X` is a -/// the number of zero prefixes you want to have in the segment name. The placeholder is only -/// replaced once in the string, other placements are not going to be processed. -/// -/// # Examples -/// -/// In this example we want to have segment files with the following names: -/// ```text -/// part001.ts -/// part002.ts -/// part003.ts -/// part004.ts -/// ``` -/// Then we can use the segment pattern value as `"part%03d.ts"`: -/// -/// ```rust,ignore -/// let formatter = SegmentFormatter::new("part%03d.ts").unwrap(); -/// assert_eq!(formatter.segment(1), "part001.ts"); -/// assert_eq!(formatter.segment(2), "part002.ts"); -/// assert_eq!(formatter.segment(3), "part003.ts"); -/// assert_eq!(formatter.segment(4), "part004.ts"); -/// ``` -pub struct SegmentFormatter { - prefix: String, - suffix: String, - padding_len: u32, -} - -impl SegmentFormatter { - /// Processes the segment name containing a placeholder. It can be used - /// repeatedly to format segment names. - /// - /// If an invalid placeholder is provided, then `None` is returned. - pub fn new>(segment_pattern: S) -> Option { - let segment_pattern = segment_pattern.as_ref(); - let caps = SEGMENT_IDX_PATTERN.captures(segment_pattern)?; - let number_placement_match = caps.get(1)?; - let zero_pad_match = caps.get(2)?; - let padding_len = zero_pad_match - .as_str() - .parse::() - .expect("valid number matched by regex"); - let prefix = segment_pattern[..number_placement_match.start()].to_string(); - let suffix = segment_pattern[number_placement_match.end()..].to_string(); - Some(Self { - prefix, - suffix, - padding_len, - }) - } - - /// Returns the segment location formatted for the provided id. - #[inline] - pub fn segment(&self, id: u32) -> String { - let padded_number = left_pad_zeroes(self.padding_len, id); - format!("{}{}{}", self.prefix, padded_number, self.suffix) - } -} - -/// Transforms a number to a zero padded string representation. -/// -/// The zero padding is added to the left of the number which is converted to a string. For the -/// case that the length number converted to string is larger than the requested padding, the -/// number representation is returned and no padding is added. The length of the returned string is -/// the maximum value between the desired padding and the length of the number. -/// -/// # Examples -/// -/// ```rust,ignore -/// let padded_number = left_pad_zeroes(4, 10); -/// assert_eq!(padded_number, "0010"); -/// ``` -#[inline] -pub(crate) fn left_pad_zeroes(padding: u32, number: u32) -> String { - let numerical_repr = number.to_string(); - let mut padded = String::with_capacity(padding.max(numerical_repr.len() as u32) as usize); - let pad_zeroes = padding as i32 - numerical_repr.len() as i32; - if pad_zeroes > 0 { - for _ in 0..pad_zeroes { - padded.push('0'); - } - } - padded.push_str(&numerical_repr); - padded -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn segment_is_correctly_formatted() { - let formatter = SegmentFormatter::new("segment%05d.ts").unwrap(); - - assert_eq!("segment00001.ts", formatter.segment(1)); - assert_eq!("segment00016.ts", formatter.segment(16)); - assert_eq!("segment01827.ts", formatter.segment(1827)); - assert_eq!("segment98765.ts", formatter.segment(98765)); - - let formatter = SegmentFormatter::new("part-%03d.ts").unwrap(); - assert_eq!("part-010.ts", formatter.segment(10)); - assert_eq!("part-9999.ts", formatter.segment(9999)); - } - - #[test] - fn padding_numbers() { - assert_eq!("001", left_pad_zeroes(3, 1)); - assert_eq!("010", left_pad_zeroes(3, 10)); - assert_eq!("100", left_pad_zeroes(3, 100)); - assert_eq!("1000", left_pad_zeroes(3, 1000)); - assert_eq!("987654321", left_pad_zeroes(3, 987654321)); - } -} -- cgit v1.2.3