Compare commits

...

29 commits
main ... 0.12.2

Author SHA1 Message Date
Sebastian Dröge 55b74c9a9a Update Cargo.lock 2024-02-26 14:56:22 +02:00
Sebastian Dröge c6841e1e74 Update versions to 0.12.2 2024-02-26 14:55:51 +02:00
Sebastian Dröge 3a50489dac Update CHANGELOG.md for 0.12.2 2024-02-26 14:55:29 +02:00
Sebastian Dröge 078c76c260 deny: Add zerocopy 0.6 duplicate override for librespot
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:26:36 +02:00
Sebastian Dröge 3ac010bfb6 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:25:33 +02:00
Xavier Claessens 7edf94f98b janusvr: Add string-ids property
It forces usage of strings even if it can be parsed into an integer.
This allows joining room `"133"` in a server configured with string
room ids.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:24:29 +02:00
Xavier Claessens ea59544c71 janusvr: Room IDs can be strings
Sponsored-by: Netflix Inc.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:24:25 +02:00
Sebastian Dröge 0b96457395 fmp4mux: Update to dash-mpd 0.15
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:24:17 +02:00
Xavier Claessens 5888f5aa5f meson: Fix error when default_library=both
Skip duplicated plugin_name when we have both the static and shared
plugin in the plugins list.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:24:08 +02:00
Maksym Khomenko 4e86b0f3c8 webrtcsink: extensions: separate API and signal checks
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:24:02 +02:00
Maksym Khomenko 98411e97f1 webrtcsink: apply rustfmt
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:23:56 +02:00
Mathieu Duponchelle 8211c253a8 textwrap: don't split on all whitespaces ..
but only on ASCII whitespaces, as we want to honor non-breaking
whitespaces (\u{a0})

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:23:50 +02:00
Xavier Claessens 3a0f30be96 janusvr: Add secret-key property
Every API calls have an optional "apisecret" argument.

Sponsored-by: Netflix Inc.
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:23:45 +02:00
Sebastian Dröge de7e4806e5 deny: Add winnow 0.5 override
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:23:38 +02:00
Sebastian Dröge 27d806ae85 Remove Cargo.lock from .gitignore
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:21:00 +02:00
Sebastian Dröge 3679db5740 rtpgccbwe: Don't reset PTS/DTS to None
The element is usually placed before `rtpsession`, and `rtpsession`
needs the PTS/DTS for correctly determining the running time. The
running time is then used to produce correct RTCP SR, and to potentially
update an NTP-64 RTP header extension if existing on the packets.

Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/496

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1476>
2024-02-26 14:20:53 +02:00
Sebastian Dröge 2f2aac55a3 Update version to 0.12.1 2024-02-13 13:02:27 +02:00
Sebastian Dröge 31dfcd0a78 Update CHANGELOG.md for 0.12.1 2024-02-13 13:01:46 +02:00
Sebastian Dröge b3e233f0c5 Update Cargo.lock
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1460>
2024-02-13 12:37:23 +02:00
Sebastian Dröge 58a065caf3 textwrap: Remove unnecessary to_string() in debug output of a string
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1460>
2024-02-13 12:35:40 +02:00
Jordan Yelloz 606352d7cf webrtcsink: Added sinkpad with "msid" property
This forwards to the webrtcbin sinkpad's msid when specified.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1460>
2024-02-12 18:11:42 +02:00
Sebastian Dröge aa2d056ea1 Update to async-tungstenite 0.25
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1460>
2024-02-12 18:11:31 +02:00
Sebastian Dröge 3f9d5cf2f0 gtk4: Create a window if running from gst-launch-1.0 or GST_GTK4_WINDOW=1 is set
Fixes https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/issues/482

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1460>
2024-02-12 18:11:25 +02:00
Sebastian Dröge 149eff08b7 utils: Update for renamed clippy lint in 1.76
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1460>
2024-02-12 18:11:19 +02:00
Sebastian Dröge c4e3fff2a2 Update Cargo.lock
Downgrade clap_derive to 4.4.7 to not require Rust 1.74 or newer.
2024-02-08 20:52:14 +02:00
Sebastian Dröge 16e001e3f2 Update dependency versions for gtk-rs-core / gtk4-rs / gstreamer-rs and local crates 2024-02-08 19:40:08 +02:00
Sebastian Dröge af694e8bc1 ci: Use 0.22 branch of gstreamer-rs images templates 2024-02-08 19:35:05 +02:00
Sebastian Dröge 66f2969eb9 Update Cargo.lock 2024-02-08 19:33:32 +02:00
Sebastian Dröge 50efdf6a64 Update version to 0.12.0 2024-02-08 19:33:09 +02:00
26 changed files with 800 additions and 531 deletions

1
.gitignore vendored
View file

@ -1,4 +1,3 @@
Cargo.lock
target
*~
*.bk

View file

@ -6,7 +6,7 @@ include:
file: '/templates/debian.yml'
- project: 'gstreamer/gstreamer-rs'
ref: main
ref: '0.22'
file: '/ci/images_template.yml'
- project: 'gstreamer/gstreamer'

View file

@ -5,6 +5,24 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html),
specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-version-field).
## [0.12.2] - 2024-02-26
### Fixed
- rtpgccbwe: Don't reset PTS/DTS to `None` as otherwise `rtpsession` won't be
able to generate valid RTCP.
- webrtcsink: Fix usage with 1.22.
### Added
- janusvrwebrtcsink: Add `secret-key` property.
- janusvrwebrtcsink: Allow for string room ids and add `string-ids` property.
- textwrap: Don't split on all whitespaces, especially not on non-breaking
whitespace.
## [0.12.1] - 2024-02-13
### Added
- gtk4: Create a window for testing purposes when running in `gst-launch-1.0`
or if `GST_GTK4_WINDOW=1` is set.
- webrtcsink: Add `msid` property.
## [0.12.0] - 2024-02-08
### Changed
- ndi: `ndisrc` passes received data downstream without an additional copy, if
@ -344,7 +362,9 @@ specifically the [variant used by Rust](http://doc.crates.io/manifest.html#the-v
- webrtcsink: Make the `turn-server` property a `turn-servers` list
- webrtcsink: Move from async-std to tokio
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.0...HEAD
[Unreleased]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.2...HEAD
[0.12.2]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.1...0.12.2
[0.12.1]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.12.0...0.12.1
[0.12.0]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.11.3...0.12.0
[0.11.3]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.11.2...0.11.3
[0.11.2]: https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/compare/0.11.1...0.11.2

828
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -113,36 +113,36 @@ panic = 'unwind'
opt-level = 1
[workspace.package]
version = "0.12.0-alpha.1"
version = "0.12.2"
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
edition = "2021"
rust-version = "1.70"
[workspace.dependencies]
once_cell = "1"
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master", features=["use_glib"] }
pango = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
pangocairo = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "master" }
gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gdk-wayland = { package = "gdk4-wayland", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gdk-x11 = { package = "gdk4-x11", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gdk-win32 = { package = "gdk4-win32", git = "https://github.com/gtk-rs/gtk4-rs", branch = "master"}
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl = { package = "gstreamer-gl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl-egl = { package = "gstreamer-gl-egl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl-wayland = { package = "gstreamer-gl-wayland", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-gl-x11 = { package = "gstreamer-gl-x11", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-net = { package = "gstreamer-net", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-pbutils = { package = "gstreamer-pbutils", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-plugin-version-helper = { path="./version-helper" }
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-sdp = { package = "gstreamer-sdp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-utils = { package = "gstreamer-utils", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
gst-webrtc = { package = "gstreamer-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "main" }
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.19", version = "0.19" }
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.19", version = "0.19" }
cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.19", version = "0.19", features=["use_glib"] }
pango = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.19", version = "0.19" }
pangocairo = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.19", version = "0.19" }
gtk = { package = "gtk4", git = "https://github.com/gtk-rs/gtk4-rs", branch = "0.8", version = "0.8"}
gdk-wayland = { package = "gdk4-wayland", git = "https://github.com/gtk-rs/gtk4-rs", branch = "0.8", version = "0.8"}
gdk-x11 = { package = "gdk4-x11", git = "https://github.com/gtk-rs/gtk4-rs", branch = "0.8", version = "0.8"}
gdk-win32 = { package = "gdk4-win32", git = "https://github.com/gtk-rs/gtk4-rs", branch = "0.8", version = "0.8"}
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-app = { package = "gstreamer-app", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-check = { package = "gstreamer-check", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-gl = { package = "gstreamer-gl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-gl-egl = { package = "gstreamer-gl-egl", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-gl-wayland = { package = "gstreamer-gl-wayland", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-gl-x11 = { package = "gstreamer-gl-x11", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-net = { package = "gstreamer-net", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-pbutils = { package = "gstreamer-pbutils", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-plugin-version-helper = { path="./version-helper", version = "0.8" }
gst-rtp = { package = "gstreamer-rtp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-sdp = { package = "gstreamer-sdp", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-utils = { package = "gstreamer-utils", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }
gst-webrtc = { package = "gstreamer-webrtc", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", branch = "0.22", version = "0.22" }

View file

@ -70,6 +70,9 @@ version = "0.9"
[[bans.skip]]
name = "hmac"
version = "0.11"
[[bans.skip]]
name = "zerocopy"
version = "0.6"
# field-offset and nix depend on an older memoffset
# https://github.com/Diggsey/rust-field-offset/pull/23
@ -184,6 +187,9 @@ version = "0.2"
[[bans.skip]]
name = "toml_edit"
version = "0.21"
[[bans.skip]]
name = "winnow"
version = "0.5"
[sources]
unknown-registry = "deny"

View file

@ -6704,12 +6704,14 @@
"audio_%%u": {
"caps": "audio/x-raw:\naudio/x-opus:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
},
"video_%%u": {
"caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
}
},
"rank": "none"
@ -6735,12 +6737,14 @@
"audio_%%u": {
"caps": "audio/x-raw:\naudio/x-opus:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
},
"video_%%u": {
"caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
}
},
"rank": "none"
@ -6766,12 +6770,14 @@
"audio_%%u": {
"caps": "audio/x-raw:\naudio/x-opus:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
},
"video_%%u": {
"caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
}
},
"rank": "none"
@ -6797,12 +6803,14 @@
"audio_%%u": {
"caps": "audio/x-raw:\naudio/x-opus:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
},
"video_%%u": {
"caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
}
},
"rank": "none"
@ -6862,12 +6870,14 @@
"audio_%%u": {
"caps": "audio/x-raw:\naudio/x-opus:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
},
"video_%%u": {
"caps": "video/x-raw:\n\nvideo/x-raw(memory:CUDAMemory):\n\nvideo/x-raw(memory:GLMemory):\n\nvideo/x-raw(memory:NVMM):\n\nvideo/x-raw(memory:D3D11Memory):\nvideo/x-vp8:\nvideo/x-h264:\nvideo/x-vp9:\nvideo/x-h265:\n",
"direction": "sink",
"presence": "request"
"presence": "request",
"type": "GstWebRTCSinkPad"
}
},
"rank": "none"
@ -7590,6 +7600,32 @@
}
]
},
"GstWebRTCSinkPad": {
"hierarchy": [
"GstWebRTCSinkPad",
"GstGhostPad",
"GstProxyPad",
"GstPad",
"GstObject",
"GInitiallyUnowned",
"GObject"
],
"kind": "object",
"properties": {
"msid": {
"blurb": "Remote MediaStream ID in use for this pad",
"conditionally-available": false,
"construct": false,
"construct-only": false,
"controllable": false,
"default": "NULL",
"mutable": "ready",
"readable": true,
"type": "gchararray",
"writable": true
}
}
},
"GstWebRTCSrcPad": {
"hierarchy": [
"GstWebRTCSrcPad",

View file

@ -1,7 +1,7 @@
project('gst-plugins-rs',
'rust',
'c',
version: '0.12.0-alpha.1',
version: '0.12.2',
meson_version : '>= 1.1')
# dependencies.py needs a toml parsing module
@ -489,6 +489,16 @@ foreach plugin : plugins
plugin_name = plugin_name.substring(3)
endif
plugin_display_name = plugin_name
if plugin_name.startswith('gst')
plugin_display_name = plugin_name.substring(3)
endif
if plugin_display_name in plugin_names
# When default_library=both plugins are duplicated.
continue
endif
plugin_names += plugin_display_name
option_name = plugin_name.substring(3)
if option_name.startswith('rs')
option_name = option_name.substring(2)
@ -533,13 +543,7 @@ foreach plugin : plugins
warning('Static plugin @0@ is known to fail. It will not be included in libgstreamer-full.'.format(plugin_name))
else
gst_plugins += dep
pc_files += [plugin_name + '.pc']
if plugin_name.startswith('gst')
plugin_names += [plugin_name.substring(3)]
else
plugin_names += [plugin_name]
endif
endif
endforeach

View file

@ -27,7 +27,7 @@ gst-app = { workspace = true, features = ["v1_18"] }
gst-check = { workspace = true, features = ["v1_18"] }
m3u8-rs = "5.0"
chrono = "0.4"
dash-mpd = { version = "0.14", default-features = false }
dash-mpd = { version = "0.15", default-features = false }
quick-xml = { version = "0.31", features = ["serialize"] }
serde = "1"

View file

@ -182,8 +182,8 @@ fn main() -> Result<(), Error> {
let mut write_segment =
|start: gst::ClockTime, duration: gst::ClockTime, repeat: usize| {
let mut s = dash_mpd::S {
t: Some(start.mseconds() as i64),
d: duration.mseconds() as i64,
t: Some(start.mseconds()),
d: duration.mseconds(),
..Default::default()
};
if repeat > 0 {

View file

@ -1146,15 +1146,12 @@ impl ObjectSubclass for BandwidthEstimator {
fn with_class(klass: &Self::Class) -> Self {
let templ = klass.pad_template("sink").unwrap();
let sinkpad = gst::Pad::builder_from_template(&templ)
.chain_function(|_pad, parent, mut buffer| {
.chain_function(|_pad, parent, buffer| {
BandwidthEstimator::catch_panic_pad_function(
parent,
|| Err(gst::FlowError::Error),
|this| {
let mut state = this.state.lock().unwrap();
let mutbuf = buffer.make_mut();
mutbuf.set_pts(None);
mutbuf.set_dts(None);
state.buffers.push_front(buffer);
state.flow_return

View file

@ -25,11 +25,11 @@ futures = "0.3"
tokio = { version = "1", features = ["fs", "macros", "rt-multi-thread", "time"] }
tokio-native-tls = "0.3.0"
tokio-stream = "0.1.11"
async-tungstenite = { version = "0.24", features = ["tokio-runtime", "tokio-native-tls"] }
async-tungstenite = { version = "0.25", features = ["tokio-runtime", "tokio-native-tls"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
fastrand = "2.0"
gst_plugin_webrtc_protocol = { path="protocol", package = "gst-plugin-webrtc-signalling-protocol" }
gst_plugin_webrtc_protocol = { path="protocol", package = "gst-plugin-webrtc-signalling-protocol", version = "0.12" }
human_bytes = "0.4"
url = "2"

View file

@ -12,7 +12,7 @@ rust-version.workspace = true
anyhow = "1"
tokio = { version = "1", features = ["fs", "io-util", "macros", "rt-multi-thread", "time"] }
tokio-native-tls = "0.3.0"
async-tungstenite = { version = "0.24", features = ["tokio-runtime", "tokio-native-tls"] }
async-tungstenite = { version = "0.25", features = ["tokio-runtime", "tokio-native-tls"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
clap = { version = "4", features = ["derive"] }
@ -24,7 +24,7 @@ uuid = { version = "1", features = ["v4"] }
thiserror = "1"
test-log = { version = "0.2", features = ["trace"], default-features = false }
pin-project-lite = "0.2"
gst_plugin_webrtc_protocol = { path="../protocol", package = "gst-plugin-webrtc-signalling-protocol" }
gst_plugin_webrtc_protocol = { path="../protocol", package = "gst-plugin-webrtc-signalling-protocol", version = "0.12" }
[[bin]]
name = "gst-webrtc-signalling-server"

View file

@ -42,17 +42,26 @@ fn feed_id() -> u32 {
thread_rng().gen()
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
#[serde(untagged)]
enum RoomId {
Str(String),
Num(u32),
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct KeepAliveMsg {
janus: String,
transaction: String,
session_id: u64,
apisecret: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct CreateSessionMsg {
janus: String,
transaction: String,
apisecret: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
@ -61,14 +70,15 @@ struct AttachPluginMsg {
transaction: String,
plugin: String,
session_id: u64,
apisecret: Option<String>,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq)]
struct RoomRequestBody {
request: String,
ptype: String,
room: u64,
id: u32,
room: RoomId,
id: RoomId,
#[serde(skip_serializing_if = "Option::is_none")]
display: Option<String>,
}
@ -79,6 +89,7 @@ struct RoomRequestMsg {
transaction: String,
session_id: u64,
handle_id: u64,
apisecret: Option<String>,
body: RoomRequestBody,
}
@ -102,6 +113,7 @@ struct PublishMsg {
transaction: String,
session_id: u64,
handle_id: u64,
apisecret: Option<String>,
body: PublishBody,
jsep: Jsep,
}
@ -119,6 +131,7 @@ struct TrickleMsg {
transaction: String,
session_id: u64,
handle_id: u64,
apisecret: Option<String>,
candidate: Candidate,
}
@ -141,12 +154,12 @@ struct InnerError {
#[derive(Serialize, Deserialize, Debug)]
struct RoomJoined {
room: Option<u64>,
room: Option<RoomId>,
}
#[derive(Serialize, Deserialize, Debug)]
struct RoomEvent {
room: Option<u64>,
room: Option<RoomId>,
error_code: Option<i32>,
error: Option<String>,
}
@ -213,14 +226,18 @@ struct State {
session_id: Option<u64>,
handle_id: Option<u64>,
transaction_id: Option<String>,
room_id: Option<RoomId>,
feed_id: Option<RoomId>,
}
#[derive(Clone)]
struct Settings {
janus_endpoint: String,
room_id: Option<String>,
feed_id: u32,
feed_id: String,
display_name: Option<String>,
secret_key: Option<String>,
string_ids: bool,
}
impl Default for Settings {
@ -228,8 +245,10 @@ impl Default for Settings {
Self {
janus_endpoint: "ws://127.0.0.1:8188".to_string(),
room_id: None,
feed_id: feed_id(),
feed_id: feed_id().to_string(),
display_name: None,
secret_key: None,
string_ids: false,
}
}
}
@ -240,8 +259,10 @@ pub struct Signaller {
state: Mutex<State>,
#[property(name="janus-endpoint", get, set, type = String, member = janus_endpoint, blurb = "The Janus server endpoint to POST SDP offer to")]
#[property(name="room-id", get, set, type = String, member = room_id, blurb = "The Janus Room ID that will be joined to")]
#[property(name="feed-id", get, set, type = u32, member = feed_id, blurb = "The Janus Feed ID to identify where the track is coming from")]
#[property(name="feed-id", get, set, type = String, member = feed_id, blurb = "The Janus Feed ID to identify where the track is coming from")]
#[property(name="display-name", get, set, type = String, member = display_name, blurb = "The name of the publisher in the Janus Video Room")]
#[property(name="secret-key", get, set, type = String, member = secret_key, blurb = "The secret API key to communicate with Janus server")]
#[property(name="string-ids", get, set, type = bool, member = string_ids, blurb = "Force passing room-id and feed-id as string even if they can be parsed into an integer")]
settings: Mutex<Settings>,
}
@ -292,15 +313,20 @@ impl Signaller {
},
_ = tokio::time::sleep(Duration::from_secs(10)) => {
if let Some(ref this) = this {
let (transaction, session_id) = {
let (transaction, session_id, apisecret) = {
let state = this.state.lock().unwrap();
(state.transaction_id.clone().unwrap(),
state.session_id.unwrap())
let settings = this.settings.lock().unwrap();
(
state.transaction_id.clone().unwrap(),
state.session_id.unwrap(),
settings.secret_key.clone(),
)
};
let msg = OutgoingMessage::KeepAlive(KeepAliveMsg {
janus: "keepalive".to_string(),
transaction,
session_id,
apisecret,
});
res = ws_sink
.send(WsMessage::Text(serde_json::to_string(&msg).unwrap()))
@ -402,7 +428,7 @@ impl Signaller {
match plugindata {
VideoRoomData::Joined(joined) => {
if let Some(room) = joined.room {
gst::trace!(CAT, imp: self, "Joined room {} successfully", room);
gst::trace!(CAT, imp: self, "Joined room {room:?} successfully");
self.session_requested();
}
}
@ -466,9 +492,12 @@ impl Signaller {
fn create_session(&self) {
let transaction = transaction_id();
self.set_transaction_id(transaction.clone());
let settings = self.settings.lock().unwrap();
let apisecret = settings.secret_key.clone();
self.send(OutgoingMessage::CreateSession(CreateSessionMsg {
janus: "create".to_string(),
transaction,
apisecret,
}));
}
@ -481,12 +510,14 @@ impl Signaller {
}
fn attach_plugin(&self) {
let (transaction, session_id) = {
let (transaction, session_id, apisecret) = {
let state = self.state.lock().unwrap();
let settings = self.settings.lock().unwrap();
(
state.transaction_id.clone().unwrap(),
state.session_id.unwrap(),
settings.secret_key.clone(),
)
};
self.send(OutgoingMessage::AttachPlugin(AttachPluginMsg {
@ -494,12 +525,13 @@ impl Signaller {
transaction,
plugin: "janus.plugin.videoroom".to_string(),
session_id,
apisecret,
}));
}
fn join_room(&self) {
let (transaction, session_id, handle_id, room, feed_id, display) = {
let state = self.state.lock().unwrap();
let (transaction, session_id, handle_id, room, feed_id, display, apisecret) = {
let mut state = self.state.lock().unwrap();
let settings = self.settings.lock().unwrap();
if settings.room_id.is_none() {
@ -507,13 +539,38 @@ impl Signaller {
return;
}
/* room_id and feed_id can be either a string or integer depending
* on server configuration. The property is always a string, if we
* can parse it to integer then assume that's what the server expects,
* unless string-ids=true is set to force usage of strings.
* Save parsed value in state to not have to parse it again for future
* API calls.
*/
if settings.string_ids {
state.room_id = Some(RoomId::Str(settings.room_id.clone().unwrap()));
state.feed_id = Some(RoomId::Str(settings.feed_id.clone()));
} else {
let room_id_str = settings.room_id.as_ref().unwrap();
match room_id_str.parse::<u32>() {
Ok(n) => {
state.room_id = Some(RoomId::Num(n));
state.feed_id = Some(RoomId::Num(settings.feed_id.parse().unwrap()));
}
Err(_) => {
state.room_id = Some(RoomId::Str(room_id_str.clone()));
state.feed_id = Some(RoomId::Str(settings.feed_id.clone()));
}
};
}
(
state.transaction_id.clone().unwrap(),
state.session_id.unwrap(),
state.handle_id.unwrap(),
settings.room_id.as_ref().unwrap().parse().unwrap(),
settings.feed_id,
state.room_id.clone().unwrap(),
state.feed_id.clone().unwrap(),
settings.display_name.clone(),
settings.secret_key.clone(),
)
};
self.send(OutgoingMessage::RoomRequest(RoomRequestMsg {
@ -521,6 +578,7 @@ impl Signaller {
transaction,
session_id,
handle_id,
apisecret,
body: RoomRequestBody {
request: "join".to_string(),
ptype: "publisher".to_string(),
@ -532,7 +590,7 @@ impl Signaller {
}
fn leave_room(&self) {
let (transaction, session_id, handle_id, room, feed_id, display) = {
let (transaction, session_id, handle_id, room, feed_id, display, apisecret) = {
let state = self.state.lock().unwrap();
let settings = self.settings.lock().unwrap();
@ -545,9 +603,10 @@ impl Signaller {
state.transaction_id.clone().unwrap(),
state.session_id.unwrap(),
state.handle_id.unwrap(),
settings.room_id.as_ref().unwrap().parse().unwrap(),
settings.feed_id,
state.room_id.clone().unwrap(),
state.feed_id.clone().unwrap(),
settings.display_name.clone(),
settings.secret_key.clone(),
)
};
self.send_blocking(OutgoingMessage::RoomRequest(RoomRequestMsg {
@ -555,6 +614,7 @@ impl Signaller {
transaction,
session_id,
handle_id,
apisecret,
body: RoomRequestBody {
request: "leave".to_string(),
ptype: "publisher".to_string(),
@ -566,7 +626,7 @@ impl Signaller {
}
fn publish(&self, offer: &gst_webrtc::WebRTCSessionDescription) {
let (transaction, session_id, handle_id) = {
let (transaction, session_id, handle_id, apisecret) = {
let state = self.state.lock().unwrap();
let settings = self.settings.lock().unwrap();
@ -579,6 +639,7 @@ impl Signaller {
state.transaction_id.clone().unwrap(),
state.session_id.unwrap(),
state.handle_id.unwrap(),
settings.secret_key.clone(),
)
};
let sdp_data = offer.sdp().as_text().unwrap();
@ -587,6 +648,7 @@ impl Signaller {
transaction,
session_id,
handle_id,
apisecret,
body: PublishBody {
request: "publish".to_string(),
audio: true,
@ -601,7 +663,7 @@ impl Signaller {
}
fn trickle(&self, candidate: &str, sdp_m_line_index: u32) {
let (transaction, session_id, handle_id) = {
let (transaction, session_id, handle_id, apisecret) = {
let state = self.state.lock().unwrap();
let settings = self.settings.lock().unwrap();
@ -614,6 +676,7 @@ impl Signaller {
state.transaction_id.clone().unwrap(),
state.session_id.unwrap(),
state.handle_id.unwrap(),
settings.secret_key.clone(),
)
};
self.send(OutgoingMessage::Trickle(TrickleMsg {
@ -621,6 +684,7 @@ impl Signaller {
transaction,
session_id,
handle_id,
apisecret,
candidate: Candidate {
candidate: candidate.to_string(),
sdp_m_line_index,

View file

@ -20,7 +20,9 @@ use std::ops::Mul;
use std::sync::{mpsc, Arc, Condvar, Mutex};
use super::homegrown_cc::CongestionController;
use super::{WebRTCSinkCongestionControl, WebRTCSinkError, WebRTCSinkMitigationMode};
use super::{
WebRTCSinkCongestionControl, WebRTCSinkError, WebRTCSinkMitigationMode, WebRTCSinkPad,
};
use crate::aws_kvs_signaller::AwsKvsSignaller;
use crate::janusvr_signaller::JanusVRSignaller;
use crate::livekit_signaller::LiveKitSignaller;
@ -186,7 +188,7 @@ impl futures::stream::FusedStream for CustomBusStream {
/// Wrapper around our sink pads
#[derive(Debug, Clone)]
struct InputStream {
sink_pad: gst::GhostPad,
sink_pad: WebRTCSinkPad,
producer: Option<StreamProducer>,
/// The (fixed) caps coming in
in_caps: Option<gst::Caps>,
@ -554,8 +556,8 @@ fn make_converter_for_video_caps(caps: &gst::Caps, codec: &Codec) -> Result<gst:
// NVIDIA V4L2 encoders require NVMM memory as input and that requires using the
// corresponding converter
|| codec
.encoder_factory()
.map_or(false, |factory| factory.name().starts_with("nvv4l2"))
.encoder_factory()
.map_or(false, |factory| factory.name().starts_with("nvv4l2"))
{
let queue = make_element("queue", None)?;
let nvconvert = if let Ok(nvconvert) = make_element("nvvideoconvert", None) {
@ -1434,6 +1436,10 @@ impl InputStream {
),
)
}
fn msid(&self) -> Option<String> {
self.sink_pad.property("msid")
}
}
impl NavigationEventHandler {
@ -1537,12 +1543,13 @@ impl BaseWebRTCSink {
match extension_configuration_type {
ExtensionConfigurationType::Auto => {
// GstRTPBasePayload::extensions property is only available since GStreamer 1.24
if !payloader.has_property("extensions", Some(gst::Array::static_type()))
&& self.has_connected_payloader_setup_slots()
{
gst::warning!(CAT, "'extensions' property is not available: TWCC extension ID will default to 1. \
if !payloader.has_property("extensions", Some(gst::Array::static_type())) {
if self.has_connected_payloader_setup_slots() {
gst::warning!(CAT, "'extensions' property is not available: TWCC extension ID will default to 1. \
Application code must ensure to pick non-conflicting IDs for any additionally configured extensions. \
Please consider updating GStreamer to 1.24.");
}
return Some(1);
}
@ -1738,6 +1745,11 @@ impl BaseWebRTCSink {
return;
};
if let Some(msid) = stream.msid() {
gst::trace!(CAT, obj: element, "forwarding msid={msid:?} to webrtcbin sinkpad");
pad.set_property("msid", &msid);
}
let transceiver = pad.property::<gst_webrtc::WebRTCRTPTransceiver>("transceiver");
transceiver.set_property(
@ -1867,7 +1879,7 @@ impl BaseWebRTCSink {
gst::StreamError::Failed,
["Signalling error: {}", error]
);
})
}),
),
request_meta: signaler.connect_closure(
@ -1877,7 +1889,7 @@ impl BaseWebRTCSink {
let meta = instance.imp().settings.lock().unwrap().meta.clone();
meta
})
}),
),
session_requested: signaler.connect_closure(
@ -1887,7 +1899,7 @@ impl BaseWebRTCSink {
if let Err(err) = instance.imp().start_session(session_id, peer_id, offer) {
gst::warning!(CAT, "{}", err);
}
})
}),
),
session_description: signaler.connect_closure(
@ -1908,9 +1920,9 @@ impl BaseWebRTCSink {
),
handle_ice: signaler.connect_closure(
"handle-ice",
false,
glib::closure!(@watch instance => move |
"handle-ice",
false,
glib::closure!(@watch instance => move |
_signaler: glib::Object,
session_id: &str,
sdp_m_line_index: u32,
@ -1920,7 +1932,7 @@ impl BaseWebRTCSink {
.imp()
.handle_ice(session_id, Some(sdp_m_line_index), None, candidate);
}),
),
),
session_ended: signaler.connect_closure(
"session-ended",
@ -1930,7 +1942,7 @@ impl BaseWebRTCSink {
gst::warning!(CAT, "{}", err);
}
false
})
}),
),
shutdown: signaler.connect_closure(
@ -1938,7 +1950,7 @@ impl BaseWebRTCSink {
false,
glib::closure!(@watch instance => move |_signaler: glib::Object|{
instance.imp().shutdown(instance);
})
}),
),
});
}
@ -2585,7 +2597,7 @@ impl BaseWebRTCSink {
if session.congestion_controller.is_some() {
let session_id_str = session_id.to_string();
rtpbin.connect_closure("on-new-ssrc", true,
glib::closure!(@weak-allow-none element,
glib::closure!(@weak-allow-none element,
=> move |rtpbin: gst::Object, session_id: u32, _src: u32| {
let rtp_session = rtpbin.emit_by_name::<gst::Element>("get-session", &[&session_id]);
@ -2606,8 +2618,8 @@ impl BaseWebRTCSink {
));
}
}
})
);
}),
);
}
let clock = element.clock();
@ -3718,7 +3730,7 @@ impl ObjectImpl for BaseWebRTCSink {
.mutable_ready()
.build(),
glib::ParamSpecObject::builder::<Signallable>("signaller")
.flags(glib::ParamFlags::READABLE | gst::PARAM_FLAG_MUTABLE_READY)
.flags(glib::ParamFlags::READABLE | gst::PARAM_FLAG_MUTABLE_READY)
.blurb("The Signallable object to use to handle WebRTC Signalling")
.build(),
]
@ -4071,11 +4083,12 @@ impl ElementImpl for BaseWebRTCSink {
caps_builder = caps_builder.structure(codec.caps.structure(0).unwrap().to_owned());
}
let video_pad_template = gst::PadTemplate::new(
let video_pad_template = gst::PadTemplate::with_gtype(
"video_%u",
gst::PadDirection::Sink,
gst::PadPresence::Request,
&caps_builder.build(),
WebRTCSinkPad::static_type(),
)
.unwrap();
@ -4084,11 +4097,12 @@ impl ElementImpl for BaseWebRTCSink {
for codec in Codecs::audio_codecs() {
caps_builder = caps_builder.structure(codec.caps.structure(0).unwrap().to_owned());
}
let audio_pad_template = gst::PadTemplate::new(
let audio_pad_template = gst::PadTemplate::with_gtype(
"audio_%u",
gst::PadDirection::Sink,
gst::PadPresence::Request,
&caps_builder.build(),
WebRTCSinkPad::static_type(),
)
.unwrap();
@ -4127,13 +4141,13 @@ impl ElementImpl for BaseWebRTCSink {
(name, false)
};
let sink_pad = gst::GhostPad::builder_from_template(templ)
let sink_pad = gst::PadBuilder::<WebRTCSinkPad>::from_template(templ)
.name(name.as_str())
.chain_function(|pad, parent, buffer| {
BaseWebRTCSink::catch_panic_pad_function(
parent,
|| Err(gst::FlowError::Error),
|this| this.chain(pad, buffer),
|this| this.chain(pad.upcast_ref(), buffer),
)
})
.event_function(|pad, parent, event| {
@ -4250,7 +4264,7 @@ impl ChildProxyImpl for BaseWebRTCSink {
fn child_by_name(&self, name: &str) -> Option<glib::Object> {
match name {
"signaller" => Some(self.settings.lock().unwrap().signaller.clone().upcast()),
_ => None,
_ => self.obj().static_pad(name).map(|pad| pad.upcast()),
}
}
}

View file

@ -39,11 +39,16 @@ use gst::subclass::prelude::*;
mod homegrown_cc;
mod imp;
mod pad;
glib::wrapper! {
pub struct BaseWebRTCSink(ObjectSubclass<imp::BaseWebRTCSink>) @extends gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy, gst_video::Navigation;
}
glib::wrapper! {
pub struct WebRTCSinkPad(ObjectSubclass<pad::WebRTCSinkPad>) @extends gst::GhostPad, gst::ProxyPad, gst::Pad, gst::Object;
}
glib::wrapper! {
pub struct WebRTCSink(ObjectSubclass<imp::WebRTCSink>) @extends BaseWebRTCSink, gst::Bin, gst::Element, gst::Object, @implements gst::ChildProxy, gst_video::Navigation;
}
@ -124,6 +129,7 @@ enum WebRTCSinkMitigationMode {
}
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
WebRTCSinkPad::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
BaseWebRTCSink::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
WebRTCSinkCongestionControl::static_type().mark_as_plugin_api(gst::PluginAPIFlags::empty());
gst::Element::register(

View file

@ -0,0 +1,57 @@
// SPDX-License-Identifier: MPL-2.0
use gst::{glib, prelude::*, subclass::prelude::*};
use once_cell::sync::Lazy;
use std::sync::Mutex;
#[derive(Default)]
pub struct WebRTCSinkPad {
settings: Mutex<Settings>,
}
#[derive(Debug, Default)]
struct Settings {
msid: Option<String>,
}
#[glib::object_subclass]
impl ObjectSubclass for WebRTCSinkPad {
const NAME: &'static str = "GstWebRTCSinkPad";
type Type = super::WebRTCSinkPad;
type ParentType = gst::GhostPad;
}
impl ObjectImpl for WebRTCSinkPad {
fn properties() -> &'static [glib::ParamSpec] {
static PROPS: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
vec![glib::ParamSpecString::builder("msid")
.flags(glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY)
.blurb("Remote MediaStream ID in use for this pad")
.build()]
});
PROPS.as_ref()
}
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
let mut settings = self.settings.lock().unwrap();
match pspec.name() {
"msid" => {
settings.msid = value
.get::<Option<String>>()
.expect("type checked upstream")
}
name => panic!("no writable property {name:?}"),
}
}
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
let settings = self.settings.lock().unwrap();
match pspec.name() {
"msid" => settings.msid.to_value(),
name => panic!("no readable property {name:?}"),
}
}
}
impl GstObjectImpl for WebRTCSinkPad {}
impl PadImpl for WebRTCSinkPad {}
impl ProxyPadImpl for WebRTCSinkPad {}
impl GhostPadImpl for WebRTCSinkPad {}

View file

@ -174,7 +174,7 @@ impl TextWrap {
CAT,
imp: self,
"Outputting contents {}, ts: {}, duration: {}",
drained.to_string(),
drained,
state.start_ts.display(),
duration.display(),
);
@ -199,7 +199,7 @@ impl TextWrap {
state.end_ts = buffer.pts();
let words = data.split_whitespace();
let words = data.split_ascii_whitespace();
let mut current_text = state.current_text.to_string();
for word in words {

View file

@ -13,7 +13,7 @@ gst.workspace = true
gst-base.workspace = true
gst-audio.workspace = true
gst-video.workspace = true
gst-plugin-gtk4 = { path = "../../video/gtk4", optional = true }
gst-plugin-gtk4 = { path = "../../video/gtk4", optional = true, version = "0.12" }
gtk = { workspace = true, optional = true }
gio = { workspace = true, optional = true }
parking_lot = "0.12"

View file

@ -534,7 +534,7 @@ impl ObjectImpl for FallbackSrc {
// Called whenever a value of a property is read. It can be called
// at any time from any thread.
#[allow(clippy::blocks_in_if_conditions)]
#[allow(clippy::block_in_conditions)]
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
match pspec.name() {
"enable-audio" => {
@ -3237,7 +3237,7 @@ impl FallbackSrc {
});
}
#[allow(clippy::blocks_in_if_conditions)]
#[allow(clippy::block_in_conditions)]
fn schedule_source_restart_timeout(
&self,
state: &mut State,
@ -3400,7 +3400,7 @@ impl FallbackSrc {
source.restart_timeout = Some(timeout);
}
#[allow(clippy::blocks_in_if_conditions)]
#[allow(clippy::block_in_conditions)]
fn have_fallback_activated(&self, state: &State) -> bool {
let mut have_audio = false;
let mut have_video = false;

View file

@ -771,7 +771,7 @@ impl FallbackSwitch {
is_active
);
#[allow(clippy::blocks_in_if_conditions)]
#[allow(clippy::block_in_conditions)]
let output_clockid = if is_active {
pad_state.schedule_clock(
self,

View file

@ -12,7 +12,7 @@ rust-version.workspace = true
gio = { workspace = true, optional = true }
gst.workspace = true
gst-audio.workspace = true
gst-plugin-gtk4 = { path = "../../video/gtk4", optional = true }
gst-plugin-gtk4 = { path = "../../video/gtk4", optional = true, version = "0.12" }
gtk = { workspace = true, optional = true }
num-rational = { version = "0.4", default-features = false, features = [] }
once_cell.workspace = true

View file

@ -12,7 +12,7 @@ rust-version.workspace = true
gst.workspace = true
gst-audio.workspace = true
gst-video.workspace = true
gst-plugin-gtk4 = { path = "../../video/gtk4", optional = true }
gst-plugin-gtk4 = { path = "../../video/gtk4", optional = true, version = "0.12" }
gtk = { workspace = true, optional = true }
gio = { workspace = true, optional = true }
parking_lot = "0.12"

View file

@ -693,7 +693,7 @@ impl ToggleRecord {
}
}
#[allow(clippy::blocks_in_if_conditions)]
#[allow(clippy::block_in_conditions)]
fn handle_secondary_stream<T: HandleData>(
&self,
pad: &gst::Pad,

View file

@ -1,6 +1,6 @@
[package]
name = "gst-plugin-version-helper"
version = "0.8.0"
version = "0.8.1"
authors = ["Sajeer Ahamed <ahamedsajeer.15.15@cse.mrt.ac.lk>",
"Sebastian Dröge <sebastian@centricular.com>"]
categories = ["development-tools"]

View file

@ -61,6 +61,7 @@ pub(crate) static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
#[derive(Default)]
pub struct PaintableSink {
paintable: Mutex<Option<ThreadGuard<Paintable>>>,
window: Mutex<Option<ThreadGuard<gtk::Window>>>,
info: Mutex<Option<gst_video::VideoInfo>>,
sender: Mutex<Option<async_channel::Sender<SinkEvent>>>,
pending_frame: Mutex<Option<Frame>>,
@ -231,6 +232,18 @@ impl ElementImpl for PaintableSink {
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
match transition {
gst::StateChange::NullToReady => {
let create_window = glib::program_name().as_deref() == Some("gst-launch-1.0")
|| std::env::var("GST_GTK4_WINDOW").as_deref() == Ok("1");
if create_window {
let res = utils::invoke_on_main_thread(gtk::init);
if let Err(err) = res {
gst::error!(CAT, imp: self, "Failed to create initialize GTK: {err}");
return Err(gst::StateChangeError);
}
}
let mut paintable = self.paintable.lock().unwrap();
if paintable.is_none() {
@ -273,6 +286,10 @@ impl ElementImpl for PaintableSink {
);
}
}
if create_window {
self.create_window();
}
}
_ => (),
}
@ -294,6 +311,17 @@ impl ElementImpl for PaintableSink {
}
});
}
gst::StateChange::ReadyToNull => {
let mut window_guard = self.window.lock().unwrap();
if let Some(window) = window_guard.take() {
drop(window_guard);
glib::MainContext::default().invoke(move || {
let window = window.get_ref();
window.close();
});
}
}
_ => (),
}
@ -520,6 +548,46 @@ impl PaintableSink {
.replace(tmp_caps);
}
fn create_window(&self) {
let self_ = self.to_owned();
glib::MainContext::default().invoke(move || {
let mut window_guard = self_.window.lock().unwrap();
if window_guard.is_some() {
return;
}
let paintable = match &*self_.paintable.lock().unwrap() {
Some(paintable) => paintable.get_ref().clone(),
None => return,
};
let window = gtk::Window::new();
let picture = gtk::Picture::new();
picture.set_paintable(Some(&paintable));
window.set_child(Some(&picture));
window.set_default_size(640, 480);
window.connect_close_request({
let self_ = self_.clone();
move |_window| {
if self_.window.lock().unwrap().is_some() {
gst::element_imp_error!(
self_,
gst::ResourceError::NotFound,
("Output window was closed")
);
}
glib::Propagation::Proceed
}
});
window.show();
*window_guard = Some(ThreadGuard::new(window));
});
}
fn create_paintable(&self, paintable_storage: &mut MutexGuard<Option<ThreadGuard<Paintable>>>) {
#[cfg(any(target_os = "macos", target_os = "windows", feature = "gst-gl"))]
{