mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-01-02 23:38:45 +00:00
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:
parent
44405e0cd7
commit
0f383a6545
3 changed files with 68 additions and 8 deletions
|
@ -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,
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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![],
|
||||
|
|
Loading…
Reference in a new issue