hlssink3: Allow setting i-frame-only playlist.

HLS allows manifest where all segments are single ifames.
This manifest requires `EXT-X-I-FRAMES-ONLY` tag in the
manifest.
I-FRAMES-ONLY playlist segments are video only segments.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1070>
This commit is contained in:
rajneeshksoni 2023-01-31 21:34:04 +04:00 committed by GStreamer Marge Bot
parent 44405e0cd7
commit 0f383a6545
3 changed files with 68 additions and 8 deletions

View file

@ -2041,6 +2041,18 @@
}
},
"properties": {
"i-frames-only": {
"blurb": "Each video segments is single iframe, So put EXT-X-I-FRAMES-ONLY tag in the playlist",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "false",
"mutable": "null",
"readable": true,
"type": "gboolean",
"writable": true
},
"location": {
"blurb": "Location of the file to write",
"conditionally-available": false,

View file

@ -25,6 +25,7 @@ const DEFAULT_MAX_NUM_SEGMENT_FILES: u32 = 10;
const DEFAULT_TARGET_DURATION: u32 = 15;
const DEFAULT_PLAYLIST_LENGTH: u32 = 5;
const DEFAULT_PLAYLIST_TYPE: HlsSink3PlaylistType = HlsSink3PlaylistType::Unspecified;
const DEFAULT_I_FRAMES_ONLY_PLAYLIST: bool = false;
const DEFAULT_SEND_KEYFRAME_REQUESTS: bool = true;
const SIGNAL_GET_PLAYLIST_STREAM: &str = "get-playlist-stream";
@ -66,6 +67,7 @@ struct Settings {
playlist_type: Option<MediaPlaylistType>,
max_num_segment_files: usize,
target_duration: u32,
i_frames_only: bool,
send_keyframe_requests: bool,
splitmuxsink: gst::Element,
@ -94,6 +96,7 @@ impl Default for Settings {
max_num_segment_files: DEFAULT_MAX_NUM_SEGMENT_FILES as usize,
target_duration: DEFAULT_TARGET_DURATION,
send_keyframe_requests: DEFAULT_SEND_KEYFRAME_REQUESTS,
i_frames_only: DEFAULT_I_FRAMES_ONLY_PLAYLIST,
splitmuxsink,
giostreamsink,
@ -111,9 +114,13 @@ pub(crate) struct StartedState {
}
impl StartedState {
fn new(target_duration: f32, playlist_type: Option<MediaPlaylistType>) -> Self {
fn new(
target_duration: f32,
playlist_type: Option<MediaPlaylistType>,
i_frames_only: bool,
) -> Self {
Self {
playlist: Playlist::new(target_duration, playlist_type),
playlist: Playlist::new(target_duration, playlist_type, i_frames_only),
current_segment_location: None,
fragment_opened_at: None,
old_segment_locations: Vec::new(),
@ -148,17 +155,22 @@ impl HlsSink3 {
fn start(&self) {
gst::info!(CAT, imp: self, "Starting");
let (target_duration, playlist_type) = {
let (target_duration, playlist_type, i_frames_only) = {
let settings = self.settings.lock().unwrap();
(
settings.target_duration as f32,
settings.playlist_type.clone(),
settings.i_frames_only,
)
};
let mut state = self.state.lock().unwrap();
if let State::Stopped = *state {
*state = State::Started(StartedState::new(target_duration, playlist_type));
*state = State::Started(StartedState::new(
target_duration,
playlist_type,
i_frames_only,
));
}
}
@ -452,6 +464,11 @@ impl ObjectImpl for HlsSink3 {
.nick("Playlist Type")
.blurb("The type of the playlist to use. When VOD type is set, the playlist will be live until the pipeline ends execution.")
.build(),
glib::ParamSpecBoolean::builder("i-frames-only")
.nick("I-Frames only playlist")
.blurb("Each video segments is single iframe, So put EXT-X-I-FRAMES-ONLY tag in the playlist")
.default_value(DEFAULT_I_FRAMES_ONLY_PLAYLIST)
.build(),
glib::ParamSpecBoolean::builder("send-keyframe-requests")
.nick("Send Keyframe Requests")
.blurb("Send keyframe requests to ensure correct fragmentation. If this is disabled then the input must have keyframes in regular intervals.")
@ -509,6 +526,17 @@ impl ObjectImpl for HlsSink3 {
.expect("type checked upstream")
.into();
}
"i-frames-only" => {
settings.i_frames_only = value.get().expect("type checked upstream");
if settings.i_frames_only && settings.audio_sink {
gst::element_error!(
self.obj(),
gst::StreamError::WrongType,
("Invalid configuration"),
["Audio not allowed for i-frames-only-stream"]
);
}
}
"send-keyframe-requests" => {
settings.send_keyframe_requests = value.get().expect("type checked upstream");
settings
@ -535,6 +563,7 @@ impl ObjectImpl for HlsSink3 {
let playlist_type: HlsSink3PlaylistType = settings.playlist_type.as_ref().into();
playlist_type.to_value()
}
"i-frames-only" => settings.i_frames_only.to_value(),
"send-keyframe-requests" => settings.send_keyframe_requests.to_value(),
_ => unimplemented!(),
}
@ -767,6 +796,15 @@ impl ElementImpl for HlsSink3 {
);
return None;
}
if settings.i_frames_only {
gst::element_error!(
self.obj(),
gst::StreamError::WrongType,
("Invalid configuration"),
["Audio not allowed for i-frames-only-stream"]
);
return None;
}
let peer_pad = settings.splitmuxsink.request_pad_simple("audio_0").unwrap();
let sink_pad =

View file

@ -11,7 +11,8 @@ use once_cell::sync::Lazy;
use regex::Regex;
use std::io::Write;
const GST_M3U8_PLAYLIST_VERSION: usize = 3;
const GST_M3U8_PLAYLIST_V3: usize = 3;
const GST_M3U8_PLAYLIST_V4: usize = 4;
static SEGMENT_IDX_PATTERN: Lazy<regex::Regex> = Lazy::new(|| Regex::new(r"(%0(\d+)d)").unwrap());
@ -29,7 +30,11 @@ pub struct Playlist {
}
impl Playlist {
pub fn new(target_duration: f32, playlist_type: Option<MediaPlaylistType>) -> Self {
pub fn new(
target_duration: f32,
playlist_type: Option<MediaPlaylistType>,
i_frames_only: bool,
) -> Self {
let mut turn_vod = false;
let playlist_type = if playlist_type == Some(MediaPlaylistType::Vod) {
turn_vod = true;
@ -37,16 +42,21 @@ impl Playlist {
} else {
playlist_type
};
let m3u8_version = if i_frames_only {
GST_M3U8_PLAYLIST_V4
} else {
GST_M3U8_PLAYLIST_V3
};
Self {
inner: MediaPlaylist {
version: Some(GST_M3U8_PLAYLIST_VERSION),
version: Some(m3u8_version),
target_duration,
media_sequence: 0,
segments: vec![],
discontinuity_sequence: 0,
end_list: false,
playlist_type,
i_frames_only: false,
i_frames_only,
start: None,
independent_segments: false,
unknown_tags: vec![],