mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-09-02 17:53:48 +00:00
Add HLS sink with multi-variant playlist support
`hlsmultivariantsink` adds support for the following as per RFC 8216 - Multivariant/master playlist - Alternate Renditions - Variant Streams Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1515>
This commit is contained in:
parent
f7ba4c40a7
commit
4218e88fce
10 changed files with 3451 additions and 0 deletions
57
Cargo.lock
generated
57
Cargo.lock
generated
|
@ -917,6 +917,15 @@ version = "2.7.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1be3f42a67d6d345ecd59f675f3f012d6974981560836e938c22b424b85ce1be"
|
||||
|
||||
[[package]]
|
||||
name = "bitreader"
|
||||
version = "0.3.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "886559b1e163d56c765bc3a985febb4eee8009f625244511d8ee3c432e08c066"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitstream-io"
|
||||
version = "2.6.0"
|
||||
|
@ -1333,6 +1342,22 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cros-codecs"
|
||||
version = "0.0.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "277a30a0ddadfa014380ee30cc60330d260369855417c492fa94421d7c7e9229"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bitreader",
|
||||
"byteorder",
|
||||
"bytes",
|
||||
"crc32fast",
|
||||
"enumn",
|
||||
"log",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
|
@ -1783,6 +1808,17 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumn"
|
||||
version = "0.1.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote 1.0.38",
|
||||
"syn 2.0.96",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "env_filter"
|
||||
version = "0.1.3"
|
||||
|
@ -2688,6 +2724,27 @@ dependencies = [
|
|||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gst-plugin-hlsmultivariantsink"
|
||||
version = "0.14.0-alpha.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"chrono",
|
||||
"cros-codecs",
|
||||
"gio",
|
||||
"gst-plugin-fmp4",
|
||||
"gst-plugin-hlssink3",
|
||||
"gst-plugin-version-helper",
|
||||
"gstreamer",
|
||||
"gstreamer-app",
|
||||
"gstreamer-audio",
|
||||
"gstreamer-check",
|
||||
"gstreamer-pbutils",
|
||||
"gstreamer-video",
|
||||
"m3u8-rs",
|
||||
"sprintf",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gst-plugin-hlssink3"
|
||||
version = "0.14.0-alpha.1"
|
||||
|
|
|
@ -25,6 +25,7 @@ members = [
|
|||
"mux/mp4",
|
||||
|
||||
"net/aws",
|
||||
"net/hlsmultivariantsink",
|
||||
"net/hlssink3",
|
||||
"net/mpegtslive",
|
||||
"net/ndi",
|
||||
|
|
|
@ -3680,6 +3680,267 @@
|
|||
"tracers": {},
|
||||
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
},
|
||||
"hlsmultivariantsink": {
|
||||
"description": "GStreamer HLS (HTTP Live Streaming) multi-variant sink Plugin",
|
||||
"elements": {
|
||||
"hlsmultivariantsink": {
|
||||
"author": "Sanchayan Maity <sanchayan@asymptotic.io>",
|
||||
"description": "HTTP Live Streaming sink",
|
||||
"hierarchy": [
|
||||
"GstHlsMultivariantSink",
|
||||
"GstBin",
|
||||
"GstElement",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"interfaces": [
|
||||
"GstChildProxy"
|
||||
],
|
||||
"klass": "Sink/Muxer",
|
||||
"pad-templates": {
|
||||
"audio_%%u": {
|
||||
"caps": "ANY",
|
||||
"direction": "sink",
|
||||
"presence": "request",
|
||||
"type": "HlsMultivariantSinkPad"
|
||||
},
|
||||
"video_%%u": {
|
||||
"caps": "ANY",
|
||||
"direction": "sink",
|
||||
"presence": "request",
|
||||
"type": "HlsMultivariantSinkPad"
|
||||
}
|
||||
},
|
||||
"properties": {
|
||||
"max-files": {
|
||||
"blurb": "Maximum number of files to keep on disk. Once the maximum is reached, old files start to be deleted to make room for new ones.",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "10",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"multivariant-playlist-location": {
|
||||
"blurb": "Location of the multivariant playlist file to write",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "multivariant.m3u8",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gchararray",
|
||||
"writable": true
|
||||
},
|
||||
"muxer-type": {
|
||||
"blurb": "The muxer to use, cmafmux or mpegtsmux, accordingly selects hlssink3 or hlscmafsink",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "cmaf (0)",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstHlsMultivariantSinkMuxerType",
|
||||
"writable": true
|
||||
},
|
||||
"playlist-length": {
|
||||
"blurb": "Length of HLS playlist. To allow players to conform to section 6.3.3 of the HLS specification, this should be at least 3. If set to 0, the playlist will be infinite.",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "5",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
},
|
||||
"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.",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "unspecified (0)",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "GstHlsMultivariantSinkPlaylistType",
|
||||
"writable": true
|
||||
},
|
||||
"send-keyframe-requests": {
|
||||
"blurb": "Send keyframe requests to ensure correct fragmentation. If this is disabled then the input must have keyframes in regular intervals.",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "true",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "gboolean",
|
||||
"writable": true
|
||||
},
|
||||
"target-duration": {
|
||||
"blurb": "The target duration in seconds of a segment/file. (0 - disabled, useful for management of segment duration by the streaming server)",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "15",
|
||||
"max": "-1",
|
||||
"min": "0",
|
||||
"mutable": "null",
|
||||
"readable": true,
|
||||
"type": "guint",
|
||||
"writable": true
|
||||
}
|
||||
},
|
||||
"rank": "none",
|
||||
"signals": {
|
||||
"delete-fragment": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "gchararray"
|
||||
}
|
||||
],
|
||||
"return-type": "gboolean",
|
||||
"when": "last"
|
||||
},
|
||||
"get-fragment-stream": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "gchararray"
|
||||
}
|
||||
],
|
||||
"return-type": "GOutputStream",
|
||||
"when": "last"
|
||||
},
|
||||
"get-init-stream": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "gchararray"
|
||||
}
|
||||
],
|
||||
"return-type": "GOutputStream",
|
||||
"when": "last"
|
||||
},
|
||||
"get-multivariant-playlist-stream": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "gchararray"
|
||||
}
|
||||
],
|
||||
"return-type": "GOutputStream",
|
||||
"when": "last"
|
||||
},
|
||||
"get-playlist-stream": {
|
||||
"args": [
|
||||
{
|
||||
"name": "arg0",
|
||||
"type": "gchararray"
|
||||
}
|
||||
],
|
||||
"return-type": "GOutputStream",
|
||||
"when": "last"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"filename": "gsthlsmultivariantsink",
|
||||
"license": "MPL",
|
||||
"other-types": {
|
||||
"GstHlsMultivariantSinkMuxerType": {
|
||||
"kind": "enum",
|
||||
"values": [
|
||||
{
|
||||
"desc": "cmaf",
|
||||
"name": "cmaf",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"desc": "mpegts",
|
||||
"name": "mpegts",
|
||||
"value": "1"
|
||||
}
|
||||
]
|
||||
},
|
||||
"GstHlsMultivariantSinkPlaylistType": {
|
||||
"kind": "enum",
|
||||
"values": [
|
||||
{
|
||||
"desc": "Unspecified: The tag `#EXT-X-PLAYLIST-TYPE` won't be present in the playlist during the pipeline processing.",
|
||||
"name": "unspecified",
|
||||
"value": "0"
|
||||
},
|
||||
{
|
||||
"desc": "Event: No segments will be removed from the playlist. At the end of the processing, the tag `#EXT-X-ENDLIST` is added to the playlist. The tag `#EXT-X-PLAYLIST-TYPE:EVENT` will be present in the playlist.",
|
||||
"name": "event",
|
||||
"value": "1"
|
||||
},
|
||||
{
|
||||
"desc": "Vod: The playlist behaves like the `event` option (a live event), but at the end of the processing, the playlist will be set to `#EXT-X-PLAYLIST-TYPE:VOD`.",
|
||||
"name": "vod",
|
||||
"value": "2"
|
||||
}
|
||||
]
|
||||
},
|
||||
"HlsMultivariantSinkPad": {
|
||||
"hierarchy": [
|
||||
"HlsMultivariantSinkPad",
|
||||
"GstGhostPad",
|
||||
"GstProxyPad",
|
||||
"GstPad",
|
||||
"GstObject",
|
||||
"GInitiallyUnowned",
|
||||
"GObject"
|
||||
],
|
||||
"kind": "object",
|
||||
"properties": {
|
||||
"alternate-rendition": {
|
||||
"blurb": "Alternate Rendition",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "variant-stream, is-i-frame=(boolean)false, uri=(string)\"\", bandwidth=(guint64)0, codecs=(string)NULL, audio=(string)NULL, video=(string)NULL;",
|
||||
"mutable": "ready",
|
||||
"readable": true,
|
||||
"type": "GstStructure",
|
||||
"writable": true
|
||||
},
|
||||
"variant": {
|
||||
"blurb": "Variant Stream",
|
||||
"conditionally-available": false,
|
||||
"construct": false,
|
||||
"construct-only": false,
|
||||
"controllable": false,
|
||||
"default": "variant-stream, is-i-frame=(boolean)false, uri=(string)\"\", bandwidth=(guint64)0, codecs=(string)NULL, audio=(string)NULL, video=(string)NULL;",
|
||||
"mutable": "ready",
|
||||
"readable": true,
|
||||
"type": "GstStructure",
|
||||
"writable": true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"package": "gst-plugin-hlsmultivariantsink",
|
||||
"source": "gst-plugin-hlsmultivariantsink",
|
||||
"tracers": {},
|
||||
"url": "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
},
|
||||
"hlssink3": {
|
||||
"description": "GStreamer HLS (HTTP Live Streaming) Plugin",
|
||||
"elements": {
|
||||
|
|
|
@ -151,6 +151,7 @@ plugins = {
|
|||
'extra-deps': {'openssl': ['>=1.1']},
|
||||
},
|
||||
'mpegtslive': {'library': 'libgstmpegtslive'},
|
||||
'hlsmultivariantsink': {'library': 'libgsthlsmultivariantsink'},
|
||||
'hlssink3': {'library': 'libgsthlssink3'},
|
||||
'ndi': {'library': 'libgstndi'},
|
||||
'onvif': {
|
||||
|
|
|
@ -27,6 +27,7 @@ option('mp4', type: 'feature', value: 'auto', description: 'Build mp4 plugin')
|
|||
|
||||
# net
|
||||
option('aws', type: 'feature', value: 'auto', description: 'Build aws plugin')
|
||||
option('hlsmultivariantsink', type: 'feature', value: 'auto', description: 'Build hlsmultivariantsink plugin')
|
||||
option('hlssink3', type: 'feature', value: 'auto', description: 'Build hlssink3 plugin')
|
||||
option('mpegtslive', type: 'feature', value: 'auto', description: 'Build mpegtslive plugin')
|
||||
option('ndi', type: 'feature', value: 'auto', description: 'Build ndi plugin')
|
||||
|
|
53
net/hlsmultivariantsink/Cargo.toml
Normal file
53
net/hlsmultivariantsink/Cargo.toml
Normal file
|
@ -0,0 +1,53 @@
|
|||
[package]
|
||||
name = "gst-plugin-hlsmultivariantsink"
|
||||
description = "GStreamer HLS (HTTP Live Streaming) multi-variant sink Plugin"
|
||||
repository.workspace = true
|
||||
version.workspace = true
|
||||
authors = ["Sanchayan Maity <sanchayan@asymptotic.io>"]
|
||||
edition.workspace = true
|
||||
license = "MPL-2.0"
|
||||
rust-version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
gst.workspace = true
|
||||
gst-app.workspace = true
|
||||
gio.workspace = true
|
||||
m3u8-rs = "5.0"
|
||||
chrono = "0.4"
|
||||
sprintf = "0.2"
|
||||
gst-pbutils = { workspace = true, features = ["v1_22"] }
|
||||
cros-codecs = { version = "0.0.4", default-features = false }
|
||||
|
||||
[dev-dependencies]
|
||||
gst-audio.workspace = true
|
||||
gst-video.workspace = true
|
||||
gst-check.workspace = true
|
||||
m3u8-rs = "5.0"
|
||||
anyhow = "1"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper.workspace = true
|
||||
|
||||
[lib]
|
||||
name = "gsthlsmultivariantsink"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
static = []
|
||||
capi = []
|
||||
doc = ["gst/v1_18"]
|
||||
|
||||
[package.metadata.capi]
|
||||
min_version = "0.9.21"
|
||||
|
||||
[package.metadata.capi.header]
|
||||
enabled = false
|
||||
|
||||
[package.metadata.capi.library]
|
||||
install_subdir = "gstreamer-1.0"
|
||||
versioning = false
|
||||
import_library = false
|
||||
|
||||
[package.metadata.capi.pkg_config]
|
||||
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
|
12
net/hlsmultivariantsink/build.rs
Normal file
12
net/hlsmultivariantsink/build.rs
Normal file
|
@ -0,0 +1,12 @@
|
|||
// Copyright (C) 2024, Asymptotic Inc.
|
||||
// Author: Sanchayan Maity <sanchayan@asymptotic.io>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
fn main() {
|
||||
gst_plugin_version_helper::info()
|
||||
}
|
1810
net/hlsmultivariantsink/src/imp.rs
Normal file
1810
net/hlsmultivariantsink/src/imp.rs
Normal file
File diff suppressed because it is too large
Load diff
104
net/hlsmultivariantsink/src/lib.rs
Normal file
104
net/hlsmultivariantsink/src/lib.rs
Normal file
|
@ -0,0 +1,104 @@
|
|||
// Copyright (C) 2024, Asymptotic Inc.
|
||||
// Author: Sanchayan Maity <sanchayan@asymptotic.io>
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
|
||||
// If a copy of the MPL was not distributed with this file, You can obtain one at
|
||||
// <https://mozilla.org/MPL/2.0/>.
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use gst::glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "GstHlsMultivariantSinkMuxerType")]
|
||||
#[non_exhaustive]
|
||||
pub enum HlsMultivariantSinkMuxerType {
|
||||
#[default]
|
||||
#[enum_value(name = "cmaf", nick = "cmaf")]
|
||||
Cmaf = 0,
|
||||
|
||||
#[enum_value(name = "mpegts", nick = "mpegts")]
|
||||
MpegTs = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "GstHlsMultivariantSinkAlternativeMediaType")]
|
||||
#[non_exhaustive]
|
||||
pub enum HlsMultivariantSinkAlternativeMediaType {
|
||||
#[default]
|
||||
#[enum_value(name = "AUDIO", nick = "audio")]
|
||||
Audio = 0,
|
||||
|
||||
#[enum_value(name = "VIDEO", nick = "video")]
|
||||
Video = 1,
|
||||
}
|
||||
|
||||
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::Enum)]
|
||||
#[repr(u32)]
|
||||
#[enum_type(name = "GstHlsMultivariantSinkPlaylistType")]
|
||||
#[non_exhaustive]
|
||||
pub enum HlsMultivariantSinkPlaylistType {
|
||||
#[enum_value(
|
||||
name = "Unspecified: The tag `#EXT-X-PLAYLIST-TYPE` won't be present in the playlist during the pipeline processing.",
|
||||
nick = "unspecified"
|
||||
)]
|
||||
Unspecified = 0,
|
||||
|
||||
#[enum_value(
|
||||
name = "Event: No segments will be removed from the playlist. At the end of the processing, the tag `#EXT-X-ENDLIST` is added to the playlist. The tag `#EXT-X-PLAYLIST-TYPE:EVENT` will be present in the playlist.",
|
||||
nick = "event"
|
||||
)]
|
||||
Event = 1,
|
||||
|
||||
#[enum_value(
|
||||
name = "Vod: The playlist behaves like the `event` option (a live event), but at the end of the processing, the playlist will be set to `#EXT-X-PLAYLIST-TYPE:VOD`.",
|
||||
nick = "vod"
|
||||
)]
|
||||
Vod = 2,
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct HlsMultivariantSink(ObjectSubclass<imp::HlsMultivariantSink>) @extends gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy;
|
||||
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub(crate) struct HlsMultivariantSinkPad(ObjectSubclass<imp::HlsMultivariantSinkPad>) @extends gst::GhostPad, gst::ProxyPad, gst::Pad, gst::Object;
|
||||
}
|
||||
|
||||
pub fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
#[cfg(feature = "doc")]
|
||||
{
|
||||
HlsMultivariantSinkPad::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
|
||||
HlsMultivariantSinkMuxerType::static_type()
|
||||
.mark_as_plugin_api(gst::PluginAPIFlags::empty());
|
||||
HlsMultivariantSinkPlaylistType::static_type()
|
||||
.mark_as_plugin_api(gst::PluginAPIFlags::empty());
|
||||
HlsMultivariantSinkAlternativeMediaType::static_type()
|
||||
.mark_as_plugin_api(gst::PluginAPIFlags::empty());
|
||||
}
|
||||
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"hlsmultivariantsink",
|
||||
gst::Rank::NONE,
|
||||
HlsMultivariantSink::static_type(),
|
||||
)
|
||||
}
|
||||
|
||||
gst::plugin_define!(
|
||||
hlsmultivariantsink,
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
plugin_init,
|
||||
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
|
||||
"MPL",
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_REPOSITORY"),
|
||||
env!("BUILD_REL_DATE")
|
||||
);
|
1151
net/hlsmultivariantsink/tests/hlsmultivariantsink.rs
Normal file
1151
net/hlsmultivariantsink/tests/hlsmultivariantsink.rs
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue