forked from mirrors/gstreamer-rs
Compare commits
108 commits
Author | SHA1 | Date | |
---|---|---|---|
|
6359c2ddf0 | ||
|
8017fa541a | ||
|
9f7eae9693 | ||
|
8c286faaa6 | ||
|
0f6bbcfb6c | ||
|
35ce5d1494 | ||
|
42c028672f | ||
|
2871edeca7 | ||
|
5c8c29f7e4 | ||
|
593de5c551 | ||
|
02f4e7dad0 | ||
|
9f3ae6aa77 | ||
|
ef84e3e71b | ||
|
bd3a053ff1 | ||
|
d1be4756b0 | ||
|
80eaa01ce8 | ||
|
7ea168a6a0 | ||
|
eedac4642c | ||
|
48ee57c63f | ||
|
e982c07255 | ||
|
fc3fb363e7 | ||
|
70ee948623 | ||
|
25e13209db | ||
|
c0a4545c83 | ||
|
408e97ad70 | ||
|
a4c3a484cf | ||
|
db82a4d591 | ||
|
7324d27869 | ||
|
12fd38a33e | ||
|
01c4151870 | ||
|
36f13a8007 | ||
|
905a0b6887 | ||
|
6b95762ea7 | ||
|
cb40917ac9 | ||
|
4c0f03d5fd | ||
|
07d9fba822 | ||
|
8e4a561a41 | ||
|
40637647b9 | ||
|
9b92261c42 | ||
|
a97de245c9 | ||
|
5abf146212 | ||
|
763cddfba6 | ||
|
df223af719 | ||
|
27380d237c | ||
|
cb150485b1 | ||
|
7d77858bc8 | ||
|
437ec48ae1 | ||
|
03c4721aa3 | ||
|
0a82caa706 | ||
|
470b727252 | ||
|
8027269c7b | ||
|
6f52f3e4fa | ||
|
db2b39d382 | ||
|
66e822dbf7 | ||
|
c5262fa69f | ||
|
08ce9f5b2f | ||
|
de87e6061e | ||
|
c2b5341b8f | ||
|
43096963de | ||
|
3b30546461 | ||
|
7975383e96 | ||
|
d3e54789fe | ||
|
f2a5960c36 | ||
|
b1c7d225b1 | ||
|
08efe21002 | ||
|
e531c7f565 | ||
|
7622ceb03a | ||
|
bc69e3dafd | ||
|
7b6ae13008 | ||
|
71bbcc00e2 | ||
|
7d5f8e95bf | ||
|
c407ce825d | ||
|
11699fda0f | ||
|
4e9b155b90 | ||
|
66a0e36e22 | ||
|
f525e7cea7 | ||
|
6a41f4b9b5 | ||
|
44facc5a82 | ||
|
1a4a725793 | ||
|
e1fd8b36c8 | ||
|
73a6aa1f26 | ||
|
6c6384e9cd | ||
|
3c7ace5451 | ||
|
fcad4e5aa3 | ||
|
8c384e387a | ||
|
f8893ec6fb | ||
|
acae1d6037 | ||
|
4cefb512cd | ||
|
ee176b9b07 | ||
|
ac5eeb7259 | ||
|
f5dc2578fa | ||
|
2507d8262f | ||
|
8c40e8b5b8 | ||
|
411b1802ba | ||
|
c38dd726a7 | ||
|
682d0a1ac6 | ||
|
d10b1b2722 | ||
|
b0e5419d7d | ||
|
fd40a98f8c | ||
|
f53b78a0cb | ||
|
5022d85b83 | ||
|
639dbcd6bb | ||
|
7f773090ce | ||
|
cee6d20ad8 | ||
|
9d8fbb7d7c | ||
|
214f050a24 | ||
|
4988cc7bb2 | ||
|
5b928af1f5 |
289 changed files with 8282 additions and 1277 deletions
|
@ -49,15 +49,14 @@ variables:
|
|||
WINDOWS_RUST_STABLE_IMAGE: "$CI_REGISTRY_IMAGE/windows:$GST_RS_IMG_TAG-main-$GST_RS_STABLE"
|
||||
WINDOWS_RUST_STABLE_UPSTREAM_IMAGE: "$CI_REGISTRY/$FDO_UPSTREAM_REPO/windows:$GST_RS_IMG_TAG-main-$GST_RS_STABLE"
|
||||
|
||||
RUST_DOCS_FLAGS: "--extern-html-root-url=muldiv=https://docs.rs/muldiv/1.0.0/muldiv/ -Z unstable-options --generate-link-to-definition"
|
||||
RUST_DOCS_FLAGS: "--cfg docsrs --extern-html-root-url=muldiv=https://docs.rs/muldiv/1.0.0/muldiv/ -Z unstable-options --generate-link-to-definition"
|
||||
NAMESPACE: gstreamer
|
||||
# format is <branch>=<name>
|
||||
# the name is used in the URL
|
||||
# latest release must be at the top
|
||||
# (only relevant on main branch)
|
||||
RELEASES:
|
||||
0.20=0.20
|
||||
0.19=0.19
|
||||
0.21=0.21
|
||||
|
||||
stages:
|
||||
- "trigger"
|
||||
|
@ -96,13 +95,6 @@ trigger:
|
|||
before_script:
|
||||
- source ./ci/env.sh
|
||||
- mkdir .cargo && echo -e "[net]\ngit-fetch-with-cli = true" > .cargo/config
|
||||
# If cargo exists assume we probably will want to update
|
||||
# the lockfile
|
||||
- |
|
||||
if command -v cargo; then
|
||||
cargo generate-lockfile --color=always
|
||||
cargo update --color=always
|
||||
fi
|
||||
|
||||
.debian:12-base:
|
||||
extends: .debian:12
|
||||
|
@ -366,7 +358,7 @@ deny:
|
|||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == "schedule"
|
||||
script:
|
||||
- cargo deny --color=always check
|
||||
- cargo deny --color=always --workspace --all-features check all
|
||||
|
||||
gir-checks:
|
||||
variables:
|
||||
|
|
3066
Cargo.lock
generated
Normal file
3066
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,5 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
|
||||
default-members = [
|
||||
"gstreamer/sys",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
variables:
|
||||
GST_RS_IMG_TAG: "2023-08-07.0"
|
||||
GST_RS_STABLE: "1.71.1"
|
||||
GST_RS_IMG_TAG: "2023-11-16.0"
|
||||
GST_RS_STABLE: "1.74.0"
|
||||
GST_RS_MSRV: "1.70.0"
|
||||
|
|
|
@ -23,7 +23,7 @@ done
|
|||
if [ -n "$EXAMPLES_TUTORIALS" ]; then
|
||||
# Keep in sync with examples/Cargo.toml
|
||||
# List all features except windows/win32
|
||||
EXAMPLES_FEATURES="--features=rtsp-server,rtsp-server-record,pango-cairo,overlay-composition,gl,gst-gl-x11,gst-gl-wayland,gst-gl-egl,allocators,gst-play,gst-player,ges,image,cairo-rs,gst-video/v1_18"
|
||||
EXAMPLES_FEATURES="--features=rtsp-server,rtsp-server-record,pango-cairo,overlay-composition,gl,gst-gl-x11,gst-gl-egl,allocators,gst-play,gst-player,ges,image,cairo-rs,gst-video/v1_18"
|
||||
|
||||
cargo build --locked --color=always --manifest-path examples/Cargo.toml --bins --examples "$EXAMPLES_FEATURES"
|
||||
cargo build --locked --color=always --manifest-path tutorials/Cargo.toml --bins --examples --all-features
|
||||
|
|
|
@ -34,7 +34,7 @@ done
|
|||
|
||||
# Keep in sync with examples/Cargo.toml
|
||||
# List all features except windows/win32
|
||||
EXAMPLES_FEATURES="--features=rtsp-server,rtsp-server-record,pango-cairo,overlay-composition,gl,gst-gl-x11,gst-gl-wayland,gst-gl-egl,allocators,gst-play,gst-player,ges,image,cairo-rs,gst-video/v1_18"
|
||||
EXAMPLES_FEATURES="--features=rtsp-server,rtsp-server-record,pango-cairo,overlay-composition,gl,gst-gl-x11,gst-gl-egl,allocators,gst-play,gst-player,ges,image,cairo-rs,gst-video/v1_18"
|
||||
|
||||
# And also run over all the examples/tutorials
|
||||
cargo clippy --locked --color=always --manifest-path examples/Cargo.toml --all-targets "$EXAMPLES_FEATURES" -- $CLIPPY_LINTS
|
||||
|
|
31
deny.toml
31
deny.toml
|
@ -1,3 +1,8 @@
|
|||
exclude = [
|
||||
"examples",
|
||||
"tutorials",
|
||||
]
|
||||
|
||||
[advisories]
|
||||
db-path = "~/.cargo/advisory-db"
|
||||
db-urls = ["https://github.com/rustsec/advisory-db"]
|
||||
|
@ -8,16 +13,7 @@ ignore = []
|
|||
|
||||
[licenses]
|
||||
unlicensed = "deny"
|
||||
allow = [
|
||||
"Apache-2.0",
|
||||
]
|
||||
deny = [
|
||||
"GPL-1.0",
|
||||
"GPL-2.0",
|
||||
"GPL-3.0",
|
||||
"AGPL-1.0",
|
||||
"AGPL-3.0",
|
||||
]
|
||||
default = "deny"
|
||||
copyleft = "deny"
|
||||
allow-osi-fsf-free = "either"
|
||||
confidence-threshold = 0.8
|
||||
|
@ -27,14 +23,6 @@ multiple-versions = "deny"
|
|||
wildcards = "allow"
|
||||
highlight = "all"
|
||||
|
||||
# Various cocoa crates are in the middle of updating to newer versions
|
||||
[[bans.skip]]
|
||||
name = "foreign-types"
|
||||
version = "0.3"
|
||||
[[bans.skip]]
|
||||
name = "foreign-types-shared"
|
||||
version = "0.1"
|
||||
|
||||
[sources]
|
||||
unknown-registry = "deny"
|
||||
unknown-git = "deny"
|
||||
|
@ -47,7 +35,8 @@ allow-git = [
|
|||
name = "syn"
|
||||
version = "1.0"
|
||||
|
||||
# Various crates depend on an older version of bitflags
|
||||
# proc-macro-crate depends on an older version of toml_edit
|
||||
# https://github.com/bkchr/proc-macro-crate/pull/41
|
||||
[[bans.skip]]
|
||||
name = "bitflags"
|
||||
version = "1.0"
|
||||
name = "toml_edit"
|
||||
version = "0.20"
|
||||
|
|
|
@ -1,46 +1,49 @@
|
|||
[package]
|
||||
name = "examples"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
license = "MIT"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
[dependencies]
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
gst-gl = { package = "gstreamer-gl", path = "../gstreamer-gl", optional = true }
|
||||
gst-gl-egl = { package = "gstreamer-gl-egl", path = "../gstreamer-gl/egl", optional = true }
|
||||
gst-gl-wayland = { package = "gstreamer-gl-wayland", path = "../gstreamer-gl/wayland", optional = true }
|
||||
gst-gl-x11 = { package = "gstreamer-gl-x11", path = "../gstreamer-gl/x11", optional = true }
|
||||
gst-app = { package = "gstreamer-app", path = "../gstreamer-app" }
|
||||
gst-audio = { package = "gstreamer-audio", path = "../gstreamer-audio" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base" }
|
||||
gst-video = { package = "gstreamer-video", path = "../gstreamer-video" }
|
||||
gst-pbutils = { package = "gstreamer-pbutils", path = "../gstreamer-pbutils" }
|
||||
gst-play = { package = "gstreamer-play", path = "../gstreamer-play", optional = true }
|
||||
gst-player = { package = "gstreamer-player", path = "../gstreamer-player", optional = true }
|
||||
ges = { package = "gstreamer-editing-services", path = "../gstreamer-editing-services", optional = true }
|
||||
gst-sdp = { package = "gstreamer-sdp", path = "../gstreamer-sdp", optional = true }
|
||||
gst-rtsp = { package = "gstreamer-rtsp", path = "../gstreamer-rtsp", optional = true }
|
||||
gst-rtsp-server = { package = "gstreamer-rtsp-server", path = "../gstreamer-rtsp-server", optional = true }
|
||||
gst-allocators = { package = "gstreamer-allocators", path = "../gstreamer-allocators", optional = true }
|
||||
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", optional = true }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
gst-gl = { package = "gstreamer-gl", path = "../gstreamer-gl", version = "0.21", optional = true }
|
||||
gst-gl-egl = { package = "gstreamer-gl-egl", path = "../gstreamer-gl/egl", version = "0.21", optional = true }
|
||||
gst-gl-x11 = { package = "gstreamer-gl-x11", path = "../gstreamer-gl/x11", version = "0.21", optional = true }
|
||||
gst-app = { package = "gstreamer-app", path = "../gstreamer-app", version = "0.21" }
|
||||
gst-audio = { package = "gstreamer-audio", path = "../gstreamer-audio", version = "0.21" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base", version = "0.21" }
|
||||
gst-video = { package = "gstreamer-video", path = "../gstreamer-video", version = "0.21" }
|
||||
gst-pbutils = { package = "gstreamer-pbutils", path = "../gstreamer-pbutils", version = "0.21" }
|
||||
gst-play = { package = "gstreamer-play", path = "../gstreamer-play", version = "0.21", optional = true }
|
||||
gst-player = { package = "gstreamer-player", path = "../gstreamer-player", version = "0.21", optional = true }
|
||||
ges = { package = "gstreamer-editing-services", path = "../gstreamer-editing-services", version = "0.21", optional = true }
|
||||
gst-sdp = { package = "gstreamer-sdp", path = "../gstreamer-sdp", version = "0.21", optional = true }
|
||||
gst-rtsp = { package = "gstreamer-rtsp", path = "../gstreamer-rtsp", version = "0.21", optional = true }
|
||||
gst-rtsp-server = { package = "gstreamer-rtsp-server", path = "../gstreamer-rtsp-server", version = "0.21", optional = true }
|
||||
gst-allocators = { package = "gstreamer-allocators", path = "../gstreamer-allocators", version = "0.21", optional = true }
|
||||
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18", optional = true }
|
||||
anyhow = "1.0"
|
||||
byte-slice-cast = "1"
|
||||
cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18", features=["use_glib"], optional = true }
|
||||
derive_more = "0.99.5"
|
||||
futures = "0.3"
|
||||
byte-slice-cast = "1"
|
||||
cairo-rs = { git = "https://github.com/gtk-rs/gtk-rs-core", features=["use_glib"], optional = true }
|
||||
pango = { git = "https://github.com/gtk-rs/gtk-rs-core", optional = true }
|
||||
pangocairo = { git = "https://github.com/gtk-rs/gtk-rs-core", optional = true }
|
||||
glutin = { version = "0.29", optional = true }
|
||||
glutin = { version = "0.31", optional = true, default-features = false }
|
||||
glutin-winit = { version = "0.4", optional = true, default-features = false }
|
||||
image = { version = "0.24", optional = true, default-features = false, features = ["png", "jpeg"] }
|
||||
memmap2 = { version = "0.7", optional = true }
|
||||
memfd = { version = "0.6", optional = true }
|
||||
uds = { version = "0.2", optional = true }
|
||||
memmap2 = { version = "0.9", optional = true }
|
||||
pango = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18", optional = true }
|
||||
pangocairo = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18", optional = true }
|
||||
raw-window-handle = { version = "0.5", optional = true }
|
||||
uds = { version = "0.4", optional = true }
|
||||
winit = { version = "0.29", optional = true, default-features = false, features = ["rwh_05"] }
|
||||
atomic_refcell = "0.1"
|
||||
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows = { version = "0.48", features=["Win32_Graphics_Direct3D11",
|
||||
windows = { version = "0.52", features=["Win32_Graphics_Direct3D11",
|
||||
"Win32_Foundation", "Win32_Graphics_Direct3D", "Win32_Graphics_Dxgi",
|
||||
"Win32_Graphics_Dxgi_Common", "Win32_Graphics_Direct2D",
|
||||
"Win32_Graphics_Direct2D_Common", "Win32_Graphics_DirectWrite",
|
||||
|
@ -58,10 +61,9 @@ rtsp-server = ["gst-rtsp-server", "gst-rtsp", "gst-sdp"]
|
|||
rtsp-server-record = ["gst-rtsp-server", "gst-rtsp", "gio"]
|
||||
pango-cairo = ["pango", "pangocairo", "cairo-rs"]
|
||||
overlay-composition = ["pango", "pangocairo", "cairo-rs"]
|
||||
gl = ["gst-gl", "gl_generator", "glutin"]
|
||||
gst-gl-x11 = ["dep:gst-gl-x11"]
|
||||
gst-gl-egl = ["dep:gst-gl-egl"]
|
||||
gst-gl-wayland = ["dep:gst-gl-wayland"]
|
||||
gl = ["dep:gst-gl", "dep:gl_generator", "dep:glutin", "dep:glutin-winit", "dep:winit", "dep:raw-window-handle"]
|
||||
gst-gl-x11 = ["dep:gst-gl-x11", "glutin-winit?/glx"] # glx turns on x11
|
||||
gst-gl-egl = ["dep:gst-gl-egl", "glutin-winit?/egl", "glutin-winit?/x11", "glutin-winit?/wayland"] # Use X11 or Wayland via EGL
|
||||
allocators = ["gst-allocators", "memmap2", "memfd", "uds"]
|
||||
|
||||
[[bin]]
|
||||
|
|
|
@ -75,31 +75,33 @@ fn example_main() {
|
|||
// Add a pad probe on the sink pad and catch the custom event we sent, then send
|
||||
// an EOS event on the pipeline.
|
||||
sinkpad.add_probe(gst::PadProbeType::EVENT_DOWNSTREAM, move |_, probe_info| {
|
||||
match probe_info.data {
|
||||
Some(gst::PadProbeData::Event(ref ev))
|
||||
if ev.type_() == gst::EventType::CustomDownstream =>
|
||||
{
|
||||
if let Some(custom_event) = ExampleCustomEvent::parse(ev) {
|
||||
if let Some(pipeline) = pipeline_weak.upgrade() {
|
||||
if custom_event.send_eos {
|
||||
/* Send EOS event to shut down the pipeline, but from an async callback, as we're
|
||||
* in a pad probe blocking the stream thread here... */
|
||||
println!("Got custom event with send_eos=true. Sending EOS");
|
||||
let ev = gst::event::Eos::new();
|
||||
let pipeline_weak = pipeline_weak.clone();
|
||||
pipeline.call_async(move |_| {
|
||||
if let Some(pipeline) = pipeline_weak.upgrade() {
|
||||
pipeline.send_event(ev);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
println!("Got custom event, with send_eos=false. Ignoring");
|
||||
}
|
||||
}
|
||||
let Some(event) = probe_info.event() else {
|
||||
return gst::PadProbeReturn::Ok;
|
||||
};
|
||||
|
||||
let Some(custom_event) = ExampleCustomEvent::parse(event) else {
|
||||
return gst::PadProbeReturn::Ok;
|
||||
};
|
||||
|
||||
let Some(pipeline) = pipeline_weak.upgrade() else {
|
||||
return gst::PadProbeReturn::Ok;
|
||||
};
|
||||
|
||||
if custom_event.send_eos {
|
||||
/* Send EOS event to shut down the pipeline, but from an async callback, as we're
|
||||
* in a pad probe blocking the stream thread here... */
|
||||
println!("Got custom event with send_eos=true. Sending EOS");
|
||||
let ev = gst::event::Eos::new();
|
||||
let pipeline_weak = pipeline_weak.clone();
|
||||
pipeline.call_async(move |_| {
|
||||
if let Some(pipeline) = pipeline_weak.upgrade() {
|
||||
pipeline.send_event(ev);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
});
|
||||
} else {
|
||||
println!("Got custom event, with send_eos=false. Ignoring");
|
||||
}
|
||||
|
||||
gst::PadProbeReturn::Ok
|
||||
});
|
||||
|
||||
|
@ -113,9 +115,8 @@ fn example_main() {
|
|||
glib::timeout_add_seconds(2 + i as u32, move || {
|
||||
// Here we temporarily retrieve a strong reference on the pipeline from the weak one
|
||||
// we moved into this callback.
|
||||
let pipeline = match pipeline_weak.upgrade() {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return glib::ControlFlow::Break,
|
||||
let Some(pipeline) = pipeline_weak.upgrade() else {
|
||||
return glib::ControlFlow::Break;
|
||||
};
|
||||
println!("Sending custom event to the pipeline with send_eos={send_eos}");
|
||||
let ev = ExampleCustomEvent::new(*send_eos);
|
||||
|
|
|
@ -305,19 +305,18 @@ fn main() -> Result<()> {
|
|||
let sinkpad = videosink.static_pad("sink").unwrap();
|
||||
let overlay_context_weak = Arc::downgrade(&overlay_context);
|
||||
sinkpad.add_probe(gst::PadProbeType::BUFFER, move |_, probe_info| {
|
||||
if let Some(gst::PadProbeData::Buffer(_)) = probe_info.data {
|
||||
let overlay_context = overlay_context_weak.upgrade().unwrap();
|
||||
let mut context = overlay_context.lock().unwrap();
|
||||
context.timestamp_queue.push_back(SystemTime::now());
|
||||
// Updates framerate per 10 frames
|
||||
if context.timestamp_queue.len() >= 10 {
|
||||
let now = context.timestamp_queue.back().unwrap();
|
||||
let front = context.timestamp_queue.front().unwrap();
|
||||
let duration = now.duration_since(*front).unwrap().as_millis() as f32;
|
||||
context.avg_fps = 1000f32 * (context.timestamp_queue.len() - 1) as f32 / duration;
|
||||
context.timestamp_queue.clear();
|
||||
}
|
||||
let overlay_context = overlay_context_weak.upgrade().unwrap();
|
||||
let mut context = overlay_context.lock().unwrap();
|
||||
context.timestamp_queue.push_back(SystemTime::now());
|
||||
// Updates framerate per 10 frames
|
||||
if context.timestamp_queue.len() >= 10 {
|
||||
let now = context.timestamp_queue.back().unwrap();
|
||||
let front = context.timestamp_queue.front().unwrap();
|
||||
let duration = now.duration_since(*front).unwrap().as_millis() as f32;
|
||||
context.avg_fps = 1000f32 * (context.timestamp_queue.len() - 1) as f32 / duration;
|
||||
context.timestamp_queue.clear();
|
||||
}
|
||||
|
||||
gst::PadProbeReturn::Ok
|
||||
});
|
||||
|
||||
|
|
|
@ -90,9 +90,8 @@ fn example_main() -> Result<(), Error> {
|
|||
decodebin.connect_pad_added(move |dbin, src_pad| {
|
||||
// Here we temporarily retrieve a strong reference on the pipeline from the weak one
|
||||
// we moved into this callback.
|
||||
let pipeline = match pipeline_weak.upgrade() {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return,
|
||||
let Some(pipeline) = pipeline_weak.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// Try to detect whether the raw stream decodebin provided us with
|
||||
|
|
|
@ -120,9 +120,8 @@ fn example_main() -> Result<(), Error> {
|
|||
src.connect_pad_added(move |dbin, dbin_src_pad| {
|
||||
// Here we temporarily retrieve a strong reference on the pipeline from the weak one
|
||||
// we moved into this callback.
|
||||
let pipeline = match pipeline_weak.upgrade() {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return,
|
||||
let Some(pipeline) = pipeline_weak.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
let (is_audio, is_video) = {
|
||||
|
|
|
@ -55,9 +55,8 @@ fn example_main() {
|
|||
glib::timeout_add_seconds(5, move || {
|
||||
// Here we temporarily retrieve a strong reference on the pipeline from the weak one
|
||||
// we moved into this callback.
|
||||
let pipeline = match pipeline_weak.upgrade() {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return glib::ControlFlow::Break,
|
||||
let Some(pipeline) = pipeline_weak.upgrade() else {
|
||||
return glib::ControlFlow::Break;
|
||||
};
|
||||
|
||||
println!("sending eos");
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![allow(clippy::non_send_fields_in_send_ty)]
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
#[path = "../glupload.rs"]
|
||||
mod glupload;
|
||||
use glupload::*;
|
||||
|
@ -161,14 +163,12 @@ mod mirror {
|
|||
}
|
||||
}
|
||||
|
||||
fn example_main() {
|
||||
fn example_main() -> Result<()> {
|
||||
gst::init().unwrap();
|
||||
let glfilter = mirror::GLMirrorFilter::new(Some("foo"));
|
||||
App::new(Some(glfilter.as_ref()))
|
||||
.and_then(main_loop)
|
||||
.unwrap_or_else(|e| eprintln!("Error! {e}"))
|
||||
let glfilter = mirror::GLMirrorFilter::new(Some("Mirror filter"));
|
||||
App::new(Some(glfilter.as_ref())).and_then(main_loop)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
examples_common::run(example_main);
|
||||
fn main() -> Result<()> {
|
||||
examples_common::run(example_main)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#![allow(clippy::non_send_fields_in_send_ty)]
|
||||
|
||||
use anyhow::Result;
|
||||
|
||||
#[path = "../glupload.rs"]
|
||||
mod glupload;
|
||||
use glupload::*;
|
||||
|
@ -7,12 +9,10 @@ use glupload::*;
|
|||
#[path = "../examples-common.rs"]
|
||||
pub mod examples_common;
|
||||
|
||||
fn example_main() {
|
||||
App::new(None)
|
||||
.and_then(main_loop)
|
||||
.unwrap_or_else(|e| eprintln!("Error! {e}"))
|
||||
fn example_main() -> Result<()> {
|
||||
App::new(None).and_then(main_loop)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
examples_common::run(example_main);
|
||||
fn main() -> Result<()> {
|
||||
examples_common::run(example_main)
|
||||
}
|
||||
|
|
|
@ -92,18 +92,18 @@ fn create_pipeline() -> Result<gst::Pipeline, Error> {
|
|||
DWriteCreateFactory::<IDWriteFactory>(DWRITE_FACTORY_TYPE_SHARED).unwrap();
|
||||
let text_format = dwrite_factory
|
||||
.CreateTextFormat(
|
||||
windows::w!("Arial"),
|
||||
windows::core::w!("Arial"),
|
||||
None,
|
||||
DWRITE_FONT_WEIGHT_BOLD,
|
||||
DWRITE_FONT_STYLE_NORMAL,
|
||||
DWRITE_FONT_STRETCH_NORMAL,
|
||||
32f32,
|
||||
windows::w!("en-us"),
|
||||
windows::core::w!("en-us"),
|
||||
)
|
||||
.unwrap();
|
||||
let text_layout = dwrite_factory
|
||||
.CreateTextLayout(
|
||||
windows::w!("GStreamer").as_wide(),
|
||||
windows::core::w!("GStreamer").as_wide(),
|
||||
&text_format,
|
||||
// Size will be updated later on "caps-changed" signal
|
||||
800f32,
|
||||
|
|
|
@ -38,36 +38,38 @@ fn example_main() {
|
|||
// This handler gets called for every buffer that passes the pad we probe.
|
||||
src_pad.add_probe(gst::PadProbeType::BUFFER, |_, probe_info| {
|
||||
// Interpret the data sent over the pad as one buffer
|
||||
if let Some(gst::PadProbeData::Buffer(ref buffer)) = probe_info.data {
|
||||
// At this point, buffer is only a reference to an existing memory region somewhere.
|
||||
// When we want to access its content, we have to map it while requesting the required
|
||||
// mode of access (read, read/write).
|
||||
// This type of abstraction is necessary, because the buffer in question might not be
|
||||
// on the machine's main memory itself, but rather in the GPU's memory.
|
||||
// So mapping the buffer makes the underlying memory region accessible to us.
|
||||
// See: https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/allocation.html
|
||||
let map = buffer.map_readable().unwrap();
|
||||
let Some(buffer) = probe_info.buffer() else {
|
||||
return gst::PadProbeReturn::Ok;
|
||||
};
|
||||
|
||||
// We know what format the data in the memory region has, since we requested
|
||||
// it by setting the appsink's caps. So what we do here is interpret the
|
||||
// memory region we mapped as an array of signed 16 bit integers.
|
||||
let samples = if let Ok(samples) = map.as_slice_of::<i16>() {
|
||||
samples
|
||||
} else {
|
||||
return gst::PadProbeReturn::Ok;
|
||||
};
|
||||
// At this point, buffer is only a reference to an existing memory region somewhere.
|
||||
// When we want to access its content, we have to map it while requesting the required
|
||||
// mode of access (read, read/write).
|
||||
// This type of abstraction is necessary, because the buffer in question might not be
|
||||
// on the machine's main memory itself, but rather in the GPU's memory.
|
||||
// So mapping the buffer makes the underlying memory region accessible to us.
|
||||
// See: https://gstreamer.freedesktop.org/documentation/plugin-development/advanced/allocation.html
|
||||
let map = buffer.map_readable().unwrap();
|
||||
|
||||
// For buffer (= chunk of samples), we calculate the root mean square:
|
||||
let sum: f64 = samples
|
||||
.iter()
|
||||
.map(|sample| {
|
||||
let f = f64::from(*sample) / f64::from(i16::MAX);
|
||||
f * f
|
||||
})
|
||||
.sum();
|
||||
let rms = (sum / (samples.len() as f64)).sqrt();
|
||||
println!("rms: {rms}");
|
||||
}
|
||||
// We know what format the data in the memory region has, since we requested
|
||||
// it by setting the appsink's caps. So what we do here is interpret the
|
||||
// memory region we mapped as an array of signed 16 bit integers.
|
||||
let samples = if let Ok(samples) = map.as_slice_of::<i16>() {
|
||||
samples
|
||||
} else {
|
||||
return gst::PadProbeReturn::Ok;
|
||||
};
|
||||
|
||||
// For buffer (= chunk of samples), we calculate the root mean square:
|
||||
let sum: f64 = samples
|
||||
.iter()
|
||||
.map(|sample| {
|
||||
let f = f64::from(*sample) / f64::from(i16::MAX);
|
||||
f * f
|
||||
})
|
||||
.sum();
|
||||
let rms = (sum / (samples.len() as f64)).sqrt();
|
||||
println!("rms: {rms}");
|
||||
|
||||
gst::PadProbeReturn::Ok
|
||||
});
|
||||
|
|
|
@ -37,6 +37,11 @@ fn main_loop(uri: &str) -> Result<(), Error> {
|
|||
Err(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Set the message bus to flushing to ensure that all pending messages are dropped and there
|
||||
// are no further references to the play instance.
|
||||
play.message_bus().set_flushing(true);
|
||||
|
||||
result.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
|
|
|
@ -50,9 +50,8 @@ fn example_main() {
|
|||
let timeout_id = glib::timeout_add_seconds(1, move || {
|
||||
// Here we temporarily retrieve a strong reference on the pipeline from the weak one
|
||||
// we moved into this callback.
|
||||
let pipeline = match pipeline_weak.upgrade() {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return glib::ControlFlow::Continue,
|
||||
let Some(pipeline) = pipeline_weak.upgrade() else {
|
||||
return glib::ControlFlow::Break;
|
||||
};
|
||||
|
||||
//let pos = pipeline.query_position(gst::Format::Time).unwrap_or(-1);
|
||||
|
|
|
@ -203,9 +203,8 @@ fn example_main() -> Result<(), Error> {
|
|||
|
||||
let depay_weak = depay.downgrade();
|
||||
rtpbin.connect_pad_added(move |rtpbin, src_pad| {
|
||||
let depay = match depay_weak.upgrade() {
|
||||
Some(depay) => depay,
|
||||
None => return,
|
||||
let Some(depay) = depay_weak.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
match connect_rtpbin_srcpad(src_pad, &depay) {
|
||||
|
|
188
examples/src/bin/subclass_vfuncs/iirfilter/imp.rs
Normal file
188
examples/src/bin/subclass_vfuncs/iirfilter/imp.rs
Normal file
|
@ -0,0 +1,188 @@
|
|||
// In the imp submodule we include the actual implementation
|
||||
|
||||
use std::{collections::VecDeque, sync::Mutex};
|
||||
|
||||
use glib::{once_cell::sync::Lazy, prelude::*};
|
||||
use gst_audio::subclass::prelude::*;
|
||||
|
||||
use byte_slice_cast::*;
|
||||
|
||||
use atomic_refcell::AtomicRefCell;
|
||||
|
||||
// The debug category we use below for our filter
|
||||
pub static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"rsiirfilter",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Rust IIR Filter"),
|
||||
)
|
||||
});
|
||||
|
||||
#[derive(Default)]
|
||||
// This is the state of our filter
|
||||
struct State {
|
||||
a: Vec<f64>,
|
||||
b: Vec<f64>,
|
||||
x: VecDeque<f64>,
|
||||
y: VecDeque<f64>,
|
||||
}
|
||||
|
||||
// This is the private data of our filter
|
||||
#[derive(Default)]
|
||||
pub struct IirFilter {
|
||||
coeffs: Mutex<Option<(Vec<f64>, Vec<f64>)>>,
|
||||
state: AtomicRefCell<State>,
|
||||
}
|
||||
|
||||
// This trait registers our type with the GObject object system and
|
||||
// provides the entry points for creating a new instance and setting
|
||||
// up the class data
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for IirFilter {
|
||||
const NAME: &'static str = "RsIirFilter";
|
||||
const ABSTRACT: bool = true;
|
||||
type Type = super::IirFilter;
|
||||
type ParentType = gst_audio::AudioFilter;
|
||||
type Class = super::Class;
|
||||
|
||||
// Here we set default implementations for all the virtual methods.
|
||||
// This is mandatory for all virtual methods that are not `Option`s.
|
||||
fn class_init(class: &mut Self::Class) {
|
||||
class.set_rate = |obj, rate| obj.imp().set_rate_default(rate);
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of glib::Object virtual methods
|
||||
impl ObjectImpl for IirFilter {}
|
||||
|
||||
impl GstObjectImpl for IirFilter {}
|
||||
|
||||
// Implementation of gst::Element virtual methods
|
||||
impl ElementImpl for IirFilter {}
|
||||
|
||||
// Implementation of gst_base::BaseTransform virtual methods
|
||||
impl BaseTransformImpl for IirFilter {
|
||||
// Configure basetransform so that we are always running in-place,
|
||||
// don't passthrough on same caps and also never call transform_ip
|
||||
// in passthrough mode (which does not matter for us here).
|
||||
//
|
||||
// The way how our processing is implemented, in-place transformation
|
||||
// is simpler.
|
||||
const MODE: gst_base::subclass::BaseTransformMode =
|
||||
gst_base::subclass::BaseTransformMode::AlwaysInPlace;
|
||||
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
self.parent_start()?;
|
||||
|
||||
*self.state.borrow_mut() = State::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn stop(&self) -> Result<(), gst::ErrorMessage> {
|
||||
self.parent_stop()?;
|
||||
|
||||
*self.state.borrow_mut() = State::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn transform_ip(&self, buf: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
// Update coefficients if new coefficients were set
|
||||
{
|
||||
let mut coeffs = self.coeffs.lock().unwrap();
|
||||
|
||||
if let Some((a, b)) = coeffs.take() {
|
||||
state.x.clear();
|
||||
state.y.clear();
|
||||
if !a.is_empty() {
|
||||
state.y.resize(a.len() - 1, 0.0);
|
||||
}
|
||||
if !b.is_empty() {
|
||||
state.x.resize(b.len() - 1, 0.0);
|
||||
}
|
||||
state.a = a;
|
||||
state.b = b;
|
||||
}
|
||||
}
|
||||
|
||||
if state.a.is_empty() | state.b.is_empty() {
|
||||
return Ok(gst::FlowSuccess::Ok);
|
||||
}
|
||||
|
||||
let mut map = buf.map_writable().map_err(|_| {
|
||||
gst::error!(CAT, imp: self, "Failed to map buffer writable");
|
||||
gst::FlowError::Error
|
||||
})?;
|
||||
|
||||
let samples = map.as_mut_slice_of::<f32>().unwrap();
|
||||
|
||||
assert!(state.b.len() - 1 == state.x.len());
|
||||
assert!(state.a.len() - 1 == state.y.len());
|
||||
|
||||
for sample in samples.iter_mut() {
|
||||
let mut val = state.b[0] * *sample as f64;
|
||||
|
||||
for (b, x) in Iterator::zip(state.b.iter().skip(1), state.x.iter()) {
|
||||
val += b * x;
|
||||
}
|
||||
|
||||
for (a, y) in Iterator::zip(state.a.iter().skip(1), state.y.iter()) {
|
||||
val -= a * y;
|
||||
}
|
||||
|
||||
val /= state.a[0];
|
||||
|
||||
let _ = state.x.pop_back().unwrap();
|
||||
state.x.push_front(*sample as f64);
|
||||
|
||||
let _ = state.y.pop_back().unwrap();
|
||||
state.y.push_front(val);
|
||||
|
||||
*sample = val as f32;
|
||||
}
|
||||
|
||||
Ok(gst::FlowSuccess::Ok)
|
||||
}
|
||||
}
|
||||
|
||||
impl AudioFilterImpl for IirFilter {
|
||||
fn allowed_caps() -> &'static gst::Caps {
|
||||
static CAPS: Lazy<gst::Caps> = Lazy::new(|| {
|
||||
// On both of pads we can only handle F32 mono at any sample rate.
|
||||
gst_audio::AudioCapsBuilder::new_interleaved()
|
||||
.format(gst_audio::AUDIO_FORMAT_F32)
|
||||
.channels(1)
|
||||
.build()
|
||||
});
|
||||
|
||||
&CAPS
|
||||
}
|
||||
|
||||
fn setup(&self, info: &gst_audio::AudioInfo) -> Result<(), gst::LoggableError> {
|
||||
self.parent_setup(info)?;
|
||||
|
||||
gst::debug!(CAT, imp: self, "Rate changed to {}", info.rate());
|
||||
let obj = self.obj();
|
||||
(obj.class().as_ref().set_rate)(&obj, info.rate());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrappers for public methods and associated helper functions.
|
||||
impl IirFilter {
|
||||
pub(super) fn set_coeffs(&self, a: Vec<f64>, b: Vec<f64>) {
|
||||
gst::debug!(CAT, imp: self, "Setting coefficients a: {a:?}, b: {b:?}");
|
||||
*self.coeffs.lock().unwrap() = Some((a, b));
|
||||
}
|
||||
}
|
||||
|
||||
/// Default virtual method implementations.
|
||||
impl IirFilter {
|
||||
fn set_rate_default(&self, _rate: u32) {}
|
||||
}
|
86
examples/src/bin/subclass_vfuncs/iirfilter/mod.rs
Normal file
86
examples/src/bin/subclass_vfuncs/iirfilter/mod.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
use gst::{prelude::*, subclass::prelude::*};
|
||||
use gst_audio::subclass::prelude::*;
|
||||
|
||||
mod imp;
|
||||
|
||||
// This here defines the public interface of our element and implements
|
||||
// the corresponding traits so that it behaves like any other gst::Element
|
||||
//
|
||||
// GObject
|
||||
// ╰──GstObject
|
||||
// ╰──GstElement
|
||||
// ╰──GstBaseTransform
|
||||
// ╰──GstAudioFilter
|
||||
// ╰──IirFilter
|
||||
glib::wrapper! {
|
||||
pub struct IirFilter(ObjectSubclass<imp::IirFilter>) @extends gst_audio::AudioFilter, gst_base::BaseTransform, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
/// Trait containing extension methods for `IirFilter`.
|
||||
pub trait IirFilterExt: IsA<IirFilter> {
|
||||
// Sets the coefficients by getting access to the private struct and simply setting them
|
||||
fn set_coeffs(&self, a: Vec<f64>, b: Vec<f64>) {
|
||||
self.upcast_ref::<IirFilter>().imp().set_coeffs(a, b)
|
||||
}
|
||||
}
|
||||
|
||||
impl<O: IsA<IirFilter>> IirFilterExt for O {}
|
||||
|
||||
/// Trait to implement in `IirFilter` subclasses.
|
||||
pub trait IirFilterImpl: AudioFilterImpl {
|
||||
/// Called whenever the sample rate is changing.
|
||||
fn set_rate(&self, rate: u32) {
|
||||
self.parent_set_rate(rate);
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait containing extension methods for `IirFilterImpl`, specifically methods for chaining
|
||||
/// up to the parent implementation of virtual methods.
|
||||
pub trait IirFilterImplExt: IirFilterImpl {
|
||||
fn parent_set_rate(&self, rate: u32) {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = &*(data.as_ref().parent_class() as *mut Class);
|
||||
(parent_class.set_rate)(self.obj().unsafe_cast_ref(), rate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: IirFilterImpl> IirFilterImplExt for T {}
|
||||
|
||||
/// Class struct for `IirFilter`.
|
||||
#[repr(C)]
|
||||
pub struct Class {
|
||||
parent: <<imp::IirFilter as ObjectSubclass>::ParentType as glib::ObjectType>::GlibClassType,
|
||||
|
||||
set_rate: fn(&IirFilter, rate: u32),
|
||||
}
|
||||
|
||||
unsafe impl ClassStruct for Class {
|
||||
type Type = imp::IirFilter;
|
||||
}
|
||||
|
||||
/// This allows directly using `Class` as e.g. `gst_audio::AudioFilterClass` or
|
||||
/// `gst_base::BaseTransformClass` without having to cast.
|
||||
impl std::ops::Deref for Class {
|
||||
type Target = glib::Class<<<Self as ClassStruct>::Type as ObjectSubclass>::ParentType>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
unsafe { &*(&self.parent as *const _ as *const _) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Overrides the virtual methods with the actual implementation of the subclass as is provided by
|
||||
/// the subclass' implementation of the `Impl` trait.
|
||||
unsafe impl<T: IirFilterImpl> IsSubclassable<T> for IirFilter {
|
||||
fn class_init(class: &mut glib::Class<Self>) {
|
||||
Self::parent_class_init::<T>(class);
|
||||
|
||||
let class = class.as_mut();
|
||||
|
||||
class.set_rate = |obj, rate| unsafe {
|
||||
let imp = obj.unsafe_cast_ref::<T::Type>().imp();
|
||||
imp.set_rate(rate);
|
||||
};
|
||||
}
|
||||
}
|
170
examples/src/bin/subclass_vfuncs/lowpass/imp.rs
Normal file
170
examples/src/bin/subclass_vfuncs/lowpass/imp.rs
Normal file
|
@ -0,0 +1,170 @@
|
|||
// In the imp submodule we include the actual implementation
|
||||
|
||||
use std::sync::Mutex;
|
||||
|
||||
use glib::{once_cell::sync::Lazy, prelude::*};
|
||||
use gst::prelude::*;
|
||||
use gst_audio::subclass::prelude::*;
|
||||
|
||||
use crate::iirfilter::{IirFilterExt, IirFilterImpl};
|
||||
|
||||
// These are the property values of our filter
|
||||
pub struct Settings {
|
||||
cutoff: f32,
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings { cutoff: 0.0 }
|
||||
}
|
||||
}
|
||||
|
||||
// This is the state of our filter
|
||||
#[derive(Default)]
|
||||
pub struct State {
|
||||
rate: Option<u32>,
|
||||
}
|
||||
|
||||
// This is the private data of our filter
|
||||
#[derive(Default)]
|
||||
pub struct Lowpass {
|
||||
settings: Mutex<Settings>,
|
||||
state: Mutex<State>,
|
||||
}
|
||||
|
||||
// This trait registers our type with the GObject object system and
|
||||
// provides the entry points for creating a new instance and setting
|
||||
// up the class data
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for Lowpass {
|
||||
const NAME: &'static str = "RsLowpass";
|
||||
type Type = super::Lowpass;
|
||||
type ParentType = crate::iirfilter::IirFilter;
|
||||
}
|
||||
|
||||
// Implementation of glib::Object virtual methods
|
||||
impl ObjectImpl for Lowpass {
|
||||
fn properties() -> &'static [glib::ParamSpec] {
|
||||
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
||||
vec![glib::ParamSpecFloat::builder("cutoff")
|
||||
.nick("Cutoff")
|
||||
.blurb("Cutoff frequency in Hz")
|
||||
.default_value(Settings::default().cutoff)
|
||||
.minimum(0.0)
|
||||
.mutable_playing()
|
||||
.build()]
|
||||
});
|
||||
|
||||
PROPERTIES.as_ref()
|
||||
}
|
||||
|
||||
fn set_property(&self, _id: usize, value: &glib::Value, pspec: &glib::ParamSpec) {
|
||||
match pspec.name() {
|
||||
"cutoff" => {
|
||||
self.settings.lock().unwrap().cutoff = value.get().unwrap();
|
||||
self.calculate_coeffs();
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
}
|
||||
|
||||
fn property(&self, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
||||
match pspec.name() {
|
||||
"cutoff" => self.settings.lock().unwrap().cutoff.to_value(),
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GstObjectImpl for Lowpass {}
|
||||
|
||||
// Implementation of gst::Element virtual methods
|
||||
impl ElementImpl for Lowpass {
|
||||
// The element specific metadata. This information is what is visible from
|
||||
// gst-inspect-1.0 and can also be programmatically retrieved from the gst::Registry
|
||||
// after initial registration without having to load the plugin in memory.
|
||||
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
||||
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
||||
gst::subclass::ElementMetadata::new(
|
||||
"Lowpass Filter",
|
||||
"Filter/Effect/Audio",
|
||||
"A Lowpass audio filter",
|
||||
"Sebastian Dröge <sebastian@centricular.com>",
|
||||
)
|
||||
});
|
||||
|
||||
Some(&*ELEMENT_METADATA)
|
||||
}
|
||||
}
|
||||
|
||||
// Implementation of gst_base::BaseTransform virtual methods
|
||||
impl BaseTransformImpl for Lowpass {
|
||||
const MODE: gst_base::subclass::BaseTransformMode =
|
||||
<<crate::iirfilter::IirFilter as glib::object::ObjectSubclassIs>::Subclass>::MODE;
|
||||
const PASSTHROUGH_ON_SAME_CAPS: bool =
|
||||
<<crate::iirfilter::IirFilter as glib::object::ObjectSubclassIs>::Subclass>::PASSTHROUGH_ON_SAME_CAPS;
|
||||
const TRANSFORM_IP_ON_PASSTHROUGH: bool =
|
||||
<<crate::iirfilter::IirFilter as glib::object::ObjectSubclassIs>::Subclass>::TRANSFORM_IP_ON_PASSTHROUGH;
|
||||
|
||||
fn start(&self) -> Result<(), gst::ErrorMessage> {
|
||||
self.parent_start()?;
|
||||
|
||||
*self.state.lock().unwrap() = State::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Implement of gst_audio::AudioFilter virtual methods
|
||||
impl AudioFilterImpl for Lowpass {}
|
||||
|
||||
// Implement of IirFilter virtual methods
|
||||
impl IirFilterImpl for Lowpass {
|
||||
fn set_rate(&self, rate: u32) {
|
||||
// Could call
|
||||
// self.parent_set_rate(rate);
|
||||
// here but chaining up is not necessary if the base class doesn't require that
|
||||
// or if the behaviour of the parent class should be completely overridden.
|
||||
|
||||
self.state.lock().unwrap().rate = Some(rate);
|
||||
self.calculate_coeffs();
|
||||
}
|
||||
}
|
||||
|
||||
impl Lowpass {
|
||||
fn calculate_coeffs(&self) {
|
||||
use std::f64;
|
||||
|
||||
let Some(rate) = self.state.lock().unwrap().rate else {
|
||||
return;
|
||||
};
|
||||
let cutoff = self.settings.lock().unwrap().cutoff;
|
||||
|
||||
// See Audio EQ Cookbook
|
||||
// https://www.w3.org/TR/audio-eq-cookbook
|
||||
let cutoff = cutoff as f64 / rate as f64;
|
||||
|
||||
let omega = 2.0 * f64::consts::PI * cutoff;
|
||||
let q = 1.0;
|
||||
|
||||
let alpha = f64::sin(omega) / (2.0 * q);
|
||||
|
||||
let mut b = vec![
|
||||
(1.0 - f64::cos(omega)) / 2.0,
|
||||
1.0 - f64::cos(omega),
|
||||
(1.0 - f64::cos(omega) / 2.0),
|
||||
];
|
||||
|
||||
let mut a = vec![1.0 + alpha, -2.0 * f64::cos(omega), 1.0 - alpha];
|
||||
|
||||
let a0 = a[0];
|
||||
for a in &mut a {
|
||||
*a /= a0;
|
||||
}
|
||||
for b in &mut b {
|
||||
*b /= a0;
|
||||
}
|
||||
|
||||
self.obj().set_coeffs(a, b);
|
||||
}
|
||||
}
|
15
examples/src/bin/subclass_vfuncs/lowpass/mod.rs
Normal file
15
examples/src/bin/subclass_vfuncs/lowpass/mod.rs
Normal file
|
@ -0,0 +1,15 @@
|
|||
mod imp;
|
||||
|
||||
// This here defines the public interface of our element and implements
|
||||
// the corresponding traits so that it behaves like any other gst::Element
|
||||
//
|
||||
// GObject
|
||||
// ╰──GstObject
|
||||
// ╰──GstElement
|
||||
// ╰──GstBaseTransform
|
||||
// ╰──GstAudioFilter
|
||||
// ╰──IirFilter
|
||||
// ╰──Lowpass
|
||||
glib::wrapper! {
|
||||
pub struct Lowpass(ObjectSubclass<imp::Lowpass>) @extends crate::iirfilter::IirFilter, gst_audio::AudioFilter, gst_base::BaseTransform, gst::Element, gst::Object;
|
||||
}
|
66
examples/src/bin/subclass_vfuncs/main.rs
Normal file
66
examples/src/bin/subclass_vfuncs/main.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
// This example implements a baseclass IirFilter, and a subclass Lowpass of that.
|
||||
//
|
||||
// The example shows how to provide and implement virtual methods, and how to provide non-virtual
|
||||
// methods on the base class.
|
||||
|
||||
use gst::prelude::*;
|
||||
|
||||
mod iirfilter;
|
||||
mod lowpass;
|
||||
|
||||
#[path = "../../examples-common.rs"]
|
||||
mod examples_common;
|
||||
|
||||
fn example_main() {
|
||||
gst::init().unwrap();
|
||||
|
||||
let pipeline = gst::Pipeline::new();
|
||||
let src = gst::ElementFactory::make("audiotestsrc")
|
||||
.property_from_str("wave", "white-noise")
|
||||
.build()
|
||||
.unwrap();
|
||||
let filter = glib::Object::builder::<lowpass::Lowpass>()
|
||||
.property("cutoff", 4000.0f32)
|
||||
.build();
|
||||
let conv = gst::ElementFactory::make("audioconvert").build().unwrap();
|
||||
let sink = gst::ElementFactory::make("autoaudiosink").build().unwrap();
|
||||
|
||||
pipeline
|
||||
.add_many([&src, filter.as_ref(), &conv, &sink])
|
||||
.unwrap();
|
||||
gst::Element::link_many([&src, filter.as_ref(), &conv, &sink]).unwrap();
|
||||
|
||||
let bus = pipeline.bus().unwrap();
|
||||
|
||||
pipeline
|
||||
.set_state(gst::State::Playing)
|
||||
.expect("Unable to set the pipeline to the `Playing` state");
|
||||
|
||||
for msg in bus.iter_timed(gst::ClockTime::NONE) {
|
||||
use gst::MessageView;
|
||||
|
||||
match msg.view() {
|
||||
MessageView::Eos(..) => break,
|
||||
MessageView::Error(err) => {
|
||||
println!(
|
||||
"Error from {:?}: {} ({:?})",
|
||||
err.src().map(|s| s.path_string()),
|
||||
err.error(),
|
||||
err.debug()
|
||||
);
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
pipeline
|
||||
.set_state(gst::State::Null)
|
||||
.expect("Unable to set the pipeline to the `Null` state");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// tutorials_common::run is only required to set up the application environment on macOS
|
||||
// (but not necessary in normal Cocoa applications where this is set up automatically)
|
||||
examples_common::run(example_main);
|
||||
}
|
|
@ -52,9 +52,8 @@ fn example_main() {
|
|||
decodebin.connect_pad_added(move |_, src_pad| {
|
||||
// Here we temporarily retrieve a strong reference on the pipeline from the weak one
|
||||
// we moved into this callback.
|
||||
let pipeline = match pipeline_weak.upgrade() {
|
||||
Some(pipeline) => pipeline,
|
||||
None => return,
|
||||
let Some(pipeline) = pipeline_weak.upgrade() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// In this example, we are only interested about parsing the ToC, so
|
||||
|
|
|
@ -96,7 +96,7 @@ fn example_main() {
|
|||
mixer_src_pad.add_probe(gst::PadProbeType::EVENT_UPSTREAM, move |_, probe_info| {
|
||||
let mixer_sink_pad = mixer_sink_pad_weak.upgrade().unwrap();
|
||||
|
||||
let Some(gst::PadProbeData::Event(ref ev)) = probe_info.data else {
|
||||
let Some(ev) = probe_info.event() else {
|
||||
return gst::PadProbeReturn::Ok;
|
||||
};
|
||||
|
||||
|
|
|
@ -1,15 +1,30 @@
|
|||
// This example demonstrates how to output GL textures, within an
|
||||
// EGL/X11 context provided by the application, and render those
|
||||
// textures in the GL application.
|
||||
//! This example demonstrates how to output GL textures, within an EGL/X11 context provided by the
|
||||
//! application, and render those textures in the GL application.
|
||||
//!
|
||||
//! This example follow common patterns from `glutin`:
|
||||
//! <https://github.com/rust-windowing/glutin/blob/master/glutin_examples/src/lib.rs>
|
||||
|
||||
// {videotestsrc} - { glsinkbin }
|
||||
|
||||
use std::{ffi::CStr, mem, ptr, sync};
|
||||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
mem,
|
||||
num::NonZeroU32,
|
||||
ptr,
|
||||
};
|
||||
|
||||
use anyhow::Error;
|
||||
use anyhow::{Context, Result};
|
||||
use derive_more::{Display, Error};
|
||||
use glutin::{
|
||||
config::GetGlConfig as _,
|
||||
context::AsRawContext as _,
|
||||
display::{AsRawDisplay as _, GetGlDisplay as _},
|
||||
prelude::*,
|
||||
};
|
||||
use glutin_winit::GlWindow as _;
|
||||
use gst::element_error;
|
||||
use gst_gl::prelude::*;
|
||||
use raw_window_handle::HasRawWindowHandle as _;
|
||||
|
||||
#[derive(Debug, Display, Error)]
|
||||
#[display(fmt = "Received error from {src}: {error} (debug: {debug:?})")]
|
||||
|
@ -171,7 +186,7 @@ impl Gl {
|
|||
}
|
||||
}
|
||||
|
||||
fn resize(&self, size: glutin::dpi::PhysicalSize<u32>) {
|
||||
fn resize(&self, size: winit::dpi::PhysicalSize<u32>) {
|
||||
unsafe {
|
||||
self.gl
|
||||
.Viewport(0, 0, size.width as i32, size.height as i32);
|
||||
|
@ -179,14 +194,17 @@ impl Gl {
|
|||
}
|
||||
}
|
||||
|
||||
fn load(gl_context: &glutin::WindowedContext<glutin::PossiblyCurrent>) -> Gl {
|
||||
let gl = gl::Gl::load_with(|ptr| gl_context.get_proc_address(ptr) as *const _);
|
||||
fn load(gl_display: &impl glutin::display::GlDisplay) -> Gl {
|
||||
let gl = gl::Gl::load_with(|symbol| {
|
||||
let symbol = CString::new(symbol).unwrap();
|
||||
gl_display.get_proc_address(&symbol).cast()
|
||||
});
|
||||
|
||||
let version = unsafe {
|
||||
let data = CStr::from_ptr(gl.GetString(gl::VERSION) as *const _)
|
||||
.to_bytes()
|
||||
.to_vec();
|
||||
String::from_utf8(data).unwrap()
|
||||
let version = gl.GetString(gl::VERSION);
|
||||
assert!(!version.is_null());
|
||||
let version = CStr::from_ptr(version.cast());
|
||||
version.to_string_lossy()
|
||||
};
|
||||
|
||||
println!("OpenGL version {version}");
|
||||
|
@ -206,9 +224,10 @@ fn load(gl_context: &glutin::WindowedContext<glutin::PossiblyCurrent>) -> Gl {
|
|||
gl.LinkProgram(program);
|
||||
|
||||
{
|
||||
let mut success: gl::types::GLint = 1;
|
||||
gl.GetProgramiv(fs, gl::LINK_STATUS, &mut success);
|
||||
assert!(success != 0);
|
||||
let mut success = 1;
|
||||
gl.GetProgramiv(program, gl::LINK_STATUS, &mut success);
|
||||
assert_ne!(success, 0);
|
||||
assert_eq!(gl.GetError(), 0);
|
||||
}
|
||||
|
||||
let attr_position = gl.GetAttribLocation(program, b"a_position\0".as_ptr() as *const _);
|
||||
|
@ -279,6 +298,8 @@ fn load(gl_context: &glutin::WindowedContext<glutin::PossiblyCurrent>) -> Gl {
|
|||
gl.BindBuffer(gl::ELEMENT_ARRAY_BUFFER, 0);
|
||||
gl.BindBuffer(gl::ARRAY_BUFFER, 0);
|
||||
|
||||
assert_eq!(gl.GetError(), 0);
|
||||
|
||||
(
|
||||
program,
|
||||
attr_position,
|
||||
|
@ -310,154 +331,199 @@ pub(crate) struct App {
|
|||
pipeline: gst::Pipeline,
|
||||
appsink: gst_app::AppSink,
|
||||
bus: gst::Bus,
|
||||
event_loop: glutin::event_loop::EventLoop<Message>,
|
||||
windowed_context: glutin::WindowedContext<glutin::PossiblyCurrent>,
|
||||
event_loop: winit::event_loop::EventLoop<Message>,
|
||||
window: Option<winit::window::Window>,
|
||||
not_current_gl_context: Option<glutin::context::NotCurrentContext>,
|
||||
shared_context: gst_gl::GLContext,
|
||||
}
|
||||
|
||||
impl App {
|
||||
pub(crate) fn new(gl_element: Option<&gst::Element>) -> Result<App, Error> {
|
||||
pub(crate) fn new(gl_element: Option<&gst::Element>) -> Result<App> {
|
||||
gst::init()?;
|
||||
|
||||
let (pipeline, appsink) = App::create_pipeline(gl_element)?;
|
||||
let bus = pipeline
|
||||
.bus()
|
||||
.expect("Pipeline without bus. Shouldn't happen!");
|
||||
.context("Pipeline without bus. Shouldn't happen!")?;
|
||||
|
||||
let event_loop = glutin::event_loop::EventLoopBuilder::with_user_event().build();
|
||||
let window = glutin::window::WindowBuilder::new().with_title("GL rendering");
|
||||
let windowed_context = glutin::ContextBuilder::new()
|
||||
.with_vsync(true)
|
||||
.build_windowed(window, &event_loop)?;
|
||||
let event_loop = winit::event_loop::EventLoopBuilder::with_user_event().build()?;
|
||||
|
||||
let windowed_context = unsafe { windowed_context.make_current().map_err(|(_, err)| err)? };
|
||||
// Only Windows requires the window to be present before creating a `glutin::Display`. Other
|
||||
// platforms don't really need one (and on Android, none exists until `Event::Resumed`).
|
||||
let window_builder = cfg!(windows).then(|| {
|
||||
winit::window::WindowBuilder::new()
|
||||
.with_transparent(true)
|
||||
.with_title("GL rendering")
|
||||
});
|
||||
|
||||
#[cfg(any(feature = "gst-gl-x11", feature = "gst-gl-wayland"))]
|
||||
let inner_window = windowed_context.window();
|
||||
let display_builder =
|
||||
glutin_winit::DisplayBuilder::new().with_window_builder(window_builder);
|
||||
// XXX on macOS/cgl only one config can be queried at a time. If transparency is needed,
|
||||
// add .with_transparency(true) to ConfigTemplateBuilder. EGL on X11 doesn't support
|
||||
// transparency at all.
|
||||
let template = glutin::config::ConfigTemplateBuilder::new().with_alpha_size(8);
|
||||
let (window, gl_config) = display_builder
|
||||
.build(&event_loop, template, |configs| {
|
||||
configs
|
||||
.reduce(|current, new_config| {
|
||||
let prefer_transparency =
|
||||
new_config.supports_transparency().unwrap_or(false)
|
||||
& !current.supports_transparency().unwrap_or(false);
|
||||
|
||||
let shared_context: gst_gl::GLContext;
|
||||
if cfg!(target_os = "linux") {
|
||||
#[cfg(any(feature = "gst-gl-x11", feature = "gst-gl-wayland"))]
|
||||
use glutin::platform::unix::WindowExtUnix;
|
||||
use glutin::platform::{unix::RawHandle, ContextTraitExt};
|
||||
|
||||
let api = App::map_gl_api(windowed_context.get_api());
|
||||
|
||||
let (gl_context, gl_display, platform) = match unsafe { windowed_context.raw_handle() }
|
||||
{
|
||||
#[cfg(any(feature = "gst-gl-egl", feature = "gst-gl-wayland"))]
|
||||
RawHandle::Egl(egl_context) => {
|
||||
let mut gl_display = None;
|
||||
|
||||
#[cfg(feature = "gst-gl-egl")]
|
||||
if let Some(display) = unsafe { windowed_context.get_egl_display() } {
|
||||
gl_display = Some(
|
||||
unsafe { gst_gl_egl::GLDisplayEGL::with_egl_display(display as usize) }
|
||||
.unwrap()
|
||||
.upcast::<gst_gl::GLDisplay>(),
|
||||
)
|
||||
};
|
||||
|
||||
#[cfg(feature = "gst-gl-wayland")]
|
||||
if let Some(display) = inner_window.wayland_display() {
|
||||
gl_display = Some(
|
||||
unsafe {
|
||||
gst_gl_wayland::GLDisplayWayland::with_display(display as usize)
|
||||
}
|
||||
.unwrap()
|
||||
.upcast::<gst_gl::GLDisplay>(),
|
||||
)
|
||||
};
|
||||
|
||||
(
|
||||
egl_context as usize,
|
||||
gl_display.expect("Could not retrieve GLDisplay through EGL context and/or Wayland display"),
|
||||
gst_gl::GLPlatform::EGL,
|
||||
)
|
||||
}
|
||||
#[cfg(feature = "gst-gl-x11")]
|
||||
RawHandle::Glx(glx_context) => {
|
||||
let gl_display = if let Some(display) = inner_window.xlib_display() {
|
||||
unsafe { gst_gl_x11::GLDisplayX11::with_display(display as usize) }.unwrap()
|
||||
} else {
|
||||
panic!("X11 window without X Display");
|
||||
};
|
||||
|
||||
(
|
||||
glx_context as usize,
|
||||
gl_display.upcast::<gst_gl::GLDisplay>(),
|
||||
gst_gl::GLPlatform::GLX,
|
||||
)
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
handler => panic!("Unsupported platform: {handler:?}."),
|
||||
};
|
||||
|
||||
shared_context =
|
||||
unsafe { gst_gl::GLContext::new_wrapped(&gl_display, gl_context, platform, api) }
|
||||
.unwrap();
|
||||
|
||||
shared_context
|
||||
.activate(true)
|
||||
.expect("Couldn't activate wrapped GL context");
|
||||
|
||||
shared_context.fill_info()?;
|
||||
|
||||
let gl_context = shared_context.clone();
|
||||
let event_proxy = sync::Mutex::new(event_loop.create_proxy());
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
bus.set_sync_handler(move |_, msg| {
|
||||
match msg.view() {
|
||||
gst::MessageView::NeedContext(ctxt) => {
|
||||
let context_type = ctxt.context_type();
|
||||
if context_type == *gst_gl::GL_DISPLAY_CONTEXT_TYPE {
|
||||
if let Some(el) =
|
||||
msg.src().map(|s| s.downcast_ref::<gst::Element>().unwrap())
|
||||
{
|
||||
let context = gst::Context::new(context_type, true);
|
||||
context.set_gl_display(&gl_display);
|
||||
el.set_context(&context);
|
||||
}
|
||||
if prefer_transparency || new_config.num_samples() > current.num_samples() {
|
||||
new_config
|
||||
} else {
|
||||
current
|
||||
}
|
||||
if context_type == "gst.gl.app_context" {
|
||||
if let Some(el) =
|
||||
msg.src().map(|s| s.downcast_ref::<gst::Element>().unwrap())
|
||||
{
|
||||
let mut context = gst::Context::new(context_type, true);
|
||||
{
|
||||
let context = context.get_mut().unwrap();
|
||||
let s = context.structure_mut();
|
||||
s.set("context", &gl_context);
|
||||
}
|
||||
el.set_context(&context);
|
||||
}
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
.expect("Failed to build display");
|
||||
println!(
|
||||
"Picked a config with {} samples and transparency {}. Pixel format: {:?}",
|
||||
gl_config.num_samples(),
|
||||
gl_config.supports_transparency().unwrap_or(false),
|
||||
gl_config.color_buffer_type()
|
||||
);
|
||||
println!("Config supports GL API(s) {:?}", gl_config.api());
|
||||
|
||||
// XXX The display could be obtained from any object created by it, so we can query it from
|
||||
// the config.
|
||||
let gl_display = gl_config.display();
|
||||
let raw_gl_display = gl_display.raw_display();
|
||||
|
||||
println!("Using raw display connection {:?}", raw_gl_display);
|
||||
|
||||
let raw_window_handle = window.as_ref().map(|window| window.raw_window_handle());
|
||||
|
||||
// The context creation part. It can be created before surface and that's how
|
||||
// it's expected in multithreaded + multiwindow operation mode, since you
|
||||
// can send NotCurrentContext, but not Surface.
|
||||
let context_attributes =
|
||||
glutin::context::ContextAttributesBuilder::new().build(raw_window_handle);
|
||||
|
||||
// Since glutin by default tries to create OpenGL core context, which may not be
|
||||
// present we should try gles.
|
||||
let fallback_context_attributes = glutin::context::ContextAttributesBuilder::new()
|
||||
.with_context_api(glutin::context::ContextApi::Gles(None))
|
||||
.build(raw_window_handle);
|
||||
|
||||
// There are also some old devices that support neither modern OpenGL nor GLES.
|
||||
// To support these we can try and create a 2.1 context.
|
||||
let legacy_context_attributes = glutin::context::ContextAttributesBuilder::new()
|
||||
.with_context_api(glutin::context::ContextApi::OpenGl(Some(
|
||||
glutin::context::Version::new(2, 1),
|
||||
)))
|
||||
.build(raw_window_handle);
|
||||
|
||||
let not_current_gl_context = unsafe {
|
||||
gl_display
|
||||
.create_context(&gl_config, &context_attributes)
|
||||
.or_else(|_| {
|
||||
gl_display
|
||||
.create_context(&gl_config, &fallback_context_attributes)
|
||||
.or_else(|_| {
|
||||
gl_display.create_context(&gl_config, &legacy_context_attributes)
|
||||
})
|
||||
})
|
||||
}
|
||||
.context("failed to create context")?;
|
||||
|
||||
let raw_gl_context = not_current_gl_context.raw_context();
|
||||
|
||||
println!("Using raw GL context {:?}", raw_gl_context);
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
compile_error!("This example only has Linux support");
|
||||
|
||||
let api = App::map_gl_api(gl_config.api());
|
||||
|
||||
let (raw_gl_context, gst_gl_display, platform) = match (raw_gl_display, raw_gl_context) {
|
||||
#[cfg(feature = "gst-gl-egl")]
|
||||
(
|
||||
glutin::display::RawDisplay::Egl(egl_display),
|
||||
glutin::context::RawContext::Egl(egl_context),
|
||||
) => {
|
||||
let gl_display =
|
||||
unsafe { gst_gl_egl::GLDisplayEGL::with_egl_display(egl_display as usize) }
|
||||
.context("Failed to create GLDisplayEGL from raw `EGLDisplay`")?
|
||||
.upcast::<gst_gl::GLDisplay>();
|
||||
|
||||
(egl_context as usize, gl_display, gst_gl::GLPlatform::EGL)
|
||||
}
|
||||
#[cfg(feature = "gst-gl-x11")]
|
||||
(
|
||||
glutin::display::RawDisplay::Glx(glx_display),
|
||||
glutin::context::RawContext::Glx(glx_context),
|
||||
) => {
|
||||
let gl_display =
|
||||
unsafe { gst_gl_x11::GLDisplayX11::with_display(glx_display as usize) }
|
||||
.context("Failed to create GLDisplayX11 from raw X11 `Display`")?
|
||||
.upcast::<gst_gl::GLDisplay>();
|
||||
(glx_context as usize, gl_display, gst_gl::GLPlatform::GLX)
|
||||
}
|
||||
#[allow(unreachable_patterns)]
|
||||
handler => anyhow::bail!("Unsupported platform: {handler:?}."),
|
||||
};
|
||||
|
||||
let shared_context = unsafe {
|
||||
gst_gl::GLContext::new_wrapped(&gst_gl_display, raw_gl_context, platform, api)
|
||||
}
|
||||
.context("Couldn't wrap GL context")?;
|
||||
|
||||
let gl_context = shared_context.clone();
|
||||
let event_proxy = event_loop.create_proxy();
|
||||
|
||||
#[allow(clippy::single_match)]
|
||||
bus.set_sync_handler(move |_, msg| {
|
||||
match msg.view() {
|
||||
gst::MessageView::NeedContext(ctxt) => {
|
||||
let context_type = ctxt.context_type();
|
||||
if context_type == *gst_gl::GL_DISPLAY_CONTEXT_TYPE {
|
||||
if let Some(el) =
|
||||
msg.src().map(|s| s.downcast_ref::<gst::Element>().unwrap())
|
||||
{
|
||||
let context = gst::Context::new(context_type, true);
|
||||
context.set_gl_display(&gst_gl_display);
|
||||
el.set_context(&context);
|
||||
}
|
||||
}
|
||||
if context_type == "gst.gl.app_context" {
|
||||
if let Some(el) =
|
||||
msg.src().map(|s| s.downcast_ref::<gst::Element>().unwrap())
|
||||
{
|
||||
let mut context = gst::Context::new(context_type, true);
|
||||
{
|
||||
let context = context.get_mut().unwrap();
|
||||
let s = context.structure_mut();
|
||||
s.set("context", &gl_context);
|
||||
}
|
||||
el.set_context(&context);
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if let Err(e) = event_proxy.lock().unwrap().send_event(Message::BusEvent) {
|
||||
eprintln!("Failed to send BusEvent to event proxy: {e}")
|
||||
}
|
||||
if let Err(e) = event_proxy.send_event(Message::BusEvent) {
|
||||
eprintln!("Failed to send BusEvent to event proxy: {e}")
|
||||
}
|
||||
|
||||
gst::BusSyncReply::Pass
|
||||
});
|
||||
} else {
|
||||
panic!("This example only has Linux support");
|
||||
}
|
||||
gst::BusSyncReply::Pass
|
||||
});
|
||||
|
||||
Ok(App {
|
||||
pipeline,
|
||||
appsink,
|
||||
bus,
|
||||
event_loop,
|
||||
windowed_context,
|
||||
window,
|
||||
not_current_gl_context: Some(not_current_gl_context),
|
||||
shared_context,
|
||||
})
|
||||
}
|
||||
|
||||
fn setup(&self, event_loop: &glutin::event_loop::EventLoop<Message>) -> Result<(), Error> {
|
||||
fn setup(&self, event_loop: &winit::event_loop::EventLoop<Message>) -> Result<()> {
|
||||
let event_proxy = event_loop.create_proxy();
|
||||
self.appsink.set_callbacks(
|
||||
gst_app::AppSinkCallbacks::builder()
|
||||
|
@ -521,22 +587,33 @@ impl App {
|
|||
.build(),
|
||||
);
|
||||
|
||||
self.pipeline.set_state(gst::State::Playing)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn map_gl_api(api: glutin::Api) -> gst_gl::GLAPI {
|
||||
match api {
|
||||
glutin::Api::OpenGl => gst_gl::GLAPI::OPENGL3,
|
||||
glutin::Api::OpenGlEs => gst_gl::GLAPI::GLES2,
|
||||
_ => gst_gl::GLAPI::empty(),
|
||||
}
|
||||
/// Converts from <https://docs.rs/glutin/latest/glutin/config/struct.Api.html> to
|
||||
/// <https://gstreamer.freedesktop.org/documentation/gl/gstglapi.html?gi-language=c#GstGLAPI>.
|
||||
fn map_gl_api(api: glutin::config::Api) -> gst_gl::GLAPI {
|
||||
use glutin::config::Api;
|
||||
use gst_gl::GLAPI;
|
||||
|
||||
let mut gst_gl_api = GLAPI::empty();
|
||||
// In gstreamer:
|
||||
// GLAPI::OPENGL: Desktop OpenGL up to and including 3.1. The compatibility profile when the OpenGL version is >= 3.2
|
||||
// GLAPI::OPENGL3: Desktop OpenGL >= 3.2 core profile
|
||||
// In glutin, API::OPENGL is set for every context API, except EGL where it is set based on
|
||||
// EGL_RENDERABLE_TYPE containing EGL_OPENGL_BIT:
|
||||
// https://registry.khronos.org/EGL/sdk/docs/man/html/eglChooseConfig.xhtml
|
||||
gst_gl_api.set(GLAPI::OPENGL | GLAPI::OPENGL3, api.contains(Api::OPENGL));
|
||||
gst_gl_api.set(GLAPI::GLES1, api.contains(Api::GLES1));
|
||||
// OpenGL ES 2.x and 3.x
|
||||
gst_gl_api.set(GLAPI::GLES2, api.intersects(Api::GLES2 | Api::GLES3));
|
||||
|
||||
gst_gl_api
|
||||
}
|
||||
|
||||
fn create_pipeline(
|
||||
gl_element: Option<&gst::Element>,
|
||||
) -> Result<(gst::Pipeline, gst_app::AppSink), Error> {
|
||||
) -> Result<(gst::Pipeline, gst_app::AppSink)> {
|
||||
let pipeline = gst::Pipeline::default();
|
||||
let src = gst::ElementFactory::make("videotestsrc").build()?;
|
||||
|
||||
|
@ -576,7 +653,7 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn handle_messages(bus: &gst::Bus) -> Result<(), Error> {
|
||||
fn handle_messages(bus: &gst::Bus) -> Result<()> {
|
||||
use gst::MessageView;
|
||||
|
||||
for msg in bus.iter() {
|
||||
|
@ -601,77 +678,143 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn main_loop(app: App) -> Result<(), Error> {
|
||||
pub(crate) fn main_loop(app: App) -> Result<()> {
|
||||
app.setup(&app.event_loop)?;
|
||||
|
||||
println!(
|
||||
"Pixel format of the window's GL context {:?}",
|
||||
app.windowed_context.get_pixel_format()
|
||||
);
|
||||
|
||||
let gl = load(&app.windowed_context);
|
||||
|
||||
let mut curr_frame: Option<gst_video::VideoFrame<gst_video::video_frame::Readable>> = None;
|
||||
|
||||
let App {
|
||||
pipeline,
|
||||
bus,
|
||||
event_loop,
|
||||
pipeline,
|
||||
mut window,
|
||||
mut not_current_gl_context,
|
||||
shared_context,
|
||||
windowed_context,
|
||||
..
|
||||
} = app;
|
||||
|
||||
event_loop.run(move |event, _, cf| {
|
||||
*cf = glutin::event_loop::ControlFlow::Wait;
|
||||
let mut curr_frame: Option<gst_video::VideoFrame<gst_video::video_frame::Readable>> = None;
|
||||
|
||||
let mut running_state = None::<(
|
||||
Gl,
|
||||
glutin::context::PossiblyCurrentContext,
|
||||
glutin::surface::Surface<glutin::surface::WindowSurface>,
|
||||
)>;
|
||||
|
||||
Ok(event_loop.run(move |event, window_target| {
|
||||
window_target.set_control_flow(winit::event_loop::ControlFlow::Wait);
|
||||
|
||||
let mut needs_redraw = false;
|
||||
match event {
|
||||
glutin::event::Event::LoopDestroyed => {
|
||||
winit::event::Event::LoopExiting => {
|
||||
pipeline.send_event(gst::event::Eos::new());
|
||||
pipeline.set_state(gst::State::Null).unwrap();
|
||||
}
|
||||
glutin::event::Event::WindowEvent { event, .. } => match event {
|
||||
glutin::event::WindowEvent::CloseRequested
|
||||
| glutin::event::WindowEvent::KeyboardInput {
|
||||
input:
|
||||
glutin::event::KeyboardInput {
|
||||
state: glutin::event::ElementState::Released,
|
||||
virtual_keycode: Some(glutin::event::VirtualKeyCode::Escape),
|
||||
winit::event::Event::WindowEvent { event, .. } => match event {
|
||||
winit::event::WindowEvent::CloseRequested
|
||||
| winit::event::WindowEvent::KeyboardInput {
|
||||
event:
|
||||
winit::event::KeyEvent {
|
||||
state: winit::event::ElementState::Released,
|
||||
logical_key:
|
||||
winit::keyboard::Key::Named(winit::keyboard::NamedKey::Escape),
|
||||
..
|
||||
},
|
||||
..
|
||||
} => *cf = glutin::event_loop::ControlFlow::Exit,
|
||||
glutin::event::WindowEvent::Resized(physical_size) => {
|
||||
windowed_context.resize(physical_size);
|
||||
gl.resize(physical_size);
|
||||
} => window_target.exit(),
|
||||
winit::event::WindowEvent::Resized(size) => {
|
||||
// Some platforms like EGL require resizing GL surface to update the size
|
||||
// Notable platforms here are Wayland and macOS, other don't require it
|
||||
// and the function is no-op, but it's wise to resize it for portability
|
||||
// reasons.
|
||||
if let Some((gl, gl_context, gl_surface)) = &running_state {
|
||||
gl_surface.resize(
|
||||
gl_context,
|
||||
// XXX Ignore minimizing
|
||||
NonZeroU32::new(size.width).unwrap(),
|
||||
NonZeroU32::new(size.height).unwrap(),
|
||||
);
|
||||
gl.resize(size);
|
||||
}
|
||||
}
|
||||
winit::event::WindowEvent::RedrawRequested => needs_redraw = true,
|
||||
_ => (),
|
||||
},
|
||||
glutin::event::Event::RedrawRequested(_) => needs_redraw = true,
|
||||
// Receive a frame
|
||||
glutin::event::Event::UserEvent(Message::Frame(info, buffer)) => {
|
||||
winit::event::Event::UserEvent(Message::Frame(info, buffer)) => {
|
||||
if let Ok(frame) = gst_video::VideoFrame::from_buffer_readable_gl(buffer, &info) {
|
||||
curr_frame = Some(frame);
|
||||
needs_redraw = true;
|
||||
}
|
||||
}
|
||||
// Handle all pending messages when we are awaken by set_sync_handler
|
||||
glutin::event::Event::UserEvent(Message::BusEvent) => {
|
||||
winit::event::Event::UserEvent(Message::BusEvent) => {
|
||||
App::handle_messages(&bus).unwrap();
|
||||
}
|
||||
winit::event::Event::Resumed => {
|
||||
let not_current_gl_context = not_current_gl_context
|
||||
.take()
|
||||
.expect("There must be a NotCurrentContext prior to Event::Resumed");
|
||||
|
||||
let gl_config = not_current_gl_context.config();
|
||||
let gl_display = gl_config.display();
|
||||
|
||||
let window = window.get_or_insert_with(|| {
|
||||
let window_builder = winit::window::WindowBuilder::new().with_transparent(true);
|
||||
glutin_winit::finalize_window(window_target, window_builder, &gl_config)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let attrs = window.build_surface_attributes(<_>::default());
|
||||
let gl_surface = unsafe {
|
||||
gl_config
|
||||
.display()
|
||||
.create_window_surface(&gl_config, &attrs)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
// Make it current.
|
||||
let gl_context = not_current_gl_context.make_current(&gl_surface).unwrap();
|
||||
|
||||
// Tell GStreamer that the context has been made current (for borrowed contexts,
|
||||
// this does not try to make it current again)
|
||||
shared_context.activate(true).unwrap();
|
||||
|
||||
shared_context
|
||||
.fill_info()
|
||||
.expect("Couldn't fill context info");
|
||||
|
||||
// The context needs to be current for the Renderer to set up shaders and buffers.
|
||||
// It also performs function loading, which needs a current context on WGL.
|
||||
let gl = load(&gl_display);
|
||||
|
||||
// Try setting vsync.
|
||||
if let Err(res) = gl_surface.set_swap_interval(
|
||||
&gl_context,
|
||||
glutin::surface::SwapInterval::Wait(std::num::NonZeroU32::new(1).unwrap()),
|
||||
) {
|
||||
eprintln!("Error setting vsync: {res:?}");
|
||||
}
|
||||
|
||||
pipeline.set_state(gst::State::Playing).unwrap();
|
||||
|
||||
assert!(running_state
|
||||
.replace((gl, gl_context, gl_surface))
|
||||
.is_none());
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if needs_redraw {
|
||||
if let Some(frame) = curr_frame.as_ref() {
|
||||
let sync_meta = frame.buffer().meta::<gst_gl::GLSyncMeta>().unwrap();
|
||||
sync_meta.wait(&shared_context);
|
||||
if let Some(texture) = frame.texture_id(0) {
|
||||
gl.draw_frame(texture as gl::types::GLuint);
|
||||
if let Some((gl, gl_context, gl_surface)) = &running_state {
|
||||
if let Some(frame) = curr_frame.as_ref() {
|
||||
let sync_meta = frame.buffer().meta::<gst_gl::GLSyncMeta>().unwrap();
|
||||
sync_meta.wait(&shared_context);
|
||||
if let Some(texture) = frame.texture_id(0) {
|
||||
gl.draw_frame(texture as gl::types::GLuint);
|
||||
}
|
||||
}
|
||||
|
||||
gl_surface.swap_buffers(gl_context).unwrap();
|
||||
}
|
||||
windowed_context.swap_buffers().unwrap();
|
||||
}
|
||||
})
|
||||
})?)
|
||||
}
|
||||
|
|
2
gir
2
gir
|
@ -1 +1 @@
|
|||
Subproject commit 1d1ce102e130a70684f90f411ee75b4e73f90beb
|
||||
Subproject commit 23d7c100187cb4767ef77e0e4b3a511cbfadf035
|
|
@ -1 +1 @@
|
|||
Subproject commit 060b114d8edb20c4041abb1e95acccb2cb2302be
|
||||
Subproject commit 6415239ef4354633334410cfae108e64ae7ff708
|
|
@ -1 +1 @@
|
|||
Subproject commit 8ea27b3f0b3316bdc3df48581820805b18473413
|
||||
Subproject commit 62054dc7234dfe1499245ce19ac9454dcb5dd594
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-allocators"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
description = "Rust bindings for GStreamer Allocators library"
|
||||
|
@ -15,9 +15,9 @@ rust-version = "1.70"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
ffi = { package = "gstreamer-allocators-sys", path = "sys" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
ffi = { package = "gstreamer-allocators-sys", path = "sys", version = "0.21" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
|
||||
[dev-dependencies]
|
||||
gir-format-check = "0.1"
|
||||
|
|
|
@ -94,3 +94,15 @@ status = "generate"
|
|||
[[object.function]]
|
||||
name = "alloc"
|
||||
manual = true
|
||||
|
||||
[[object]]
|
||||
name = "GstAllocators.ShmAllocator"
|
||||
status = "generate"
|
||||
cfg_condition = "unix"
|
||||
[[object.function]]
|
||||
name = "get"
|
||||
manual = true
|
||||
[[object.function]]
|
||||
name = "init_once"
|
||||
manual = true
|
||||
|
||||
|
|
|
@ -11,6 +11,11 @@ pub static ALLOCATOR_DMABUF: &GStr =
|
|||
#[doc(alias = "GST_ALLOCATOR_FD")]
|
||||
pub static ALLOCATOR_FD: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::GST_ALLOCATOR_FD) };
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "GST_ALLOCATOR_SHM")]
|
||||
pub static ALLOCATOR_SHM: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::GST_ALLOCATOR_SHM) };
|
||||
#[doc(alias = "GST_CAPS_FEATURE_MEMORY_DMABUF")]
|
||||
pub static CAPS_FEATURE_MEMORY_DMABUF: &GStr =
|
||||
unsafe { GStr::from_utf8_with_nul_unchecked(ffi::GST_CAPS_FEATURE_MEMORY_DMABUF) };
|
||||
|
|
|
@ -27,6 +27,17 @@ pub use self::fd_allocator::FdAllocator;
|
|||
mod phys_memory_allocator;
|
||||
pub use self::phys_memory_allocator::PhysMemoryAllocator;
|
||||
|
||||
#[cfg(unix)]
|
||||
#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
mod shm_allocator;
|
||||
#[cfg(unix)]
|
||||
#[cfg_attr(docsrs, doc(cfg(unix)))]
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub use self::shm_allocator::ShmAllocator;
|
||||
|
||||
mod flags;
|
||||
pub use self::flags::FdMemoryFlags;
|
||||
|
||||
|
@ -35,6 +46,9 @@ pub mod functions;
|
|||
mod constants;
|
||||
pub use self::constants::ALLOCATOR_DMABUF;
|
||||
pub use self::constants::ALLOCATOR_FD;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub use self::constants::ALLOCATOR_SHM;
|
||||
pub use self::constants::CAPS_FEATURE_MEMORY_DMABUF;
|
||||
|
||||
#[doc(hidden)]
|
||||
|
|
20
gstreamer-allocators/src/auto/shm_allocator.rs
Normal file
20
gstreamer-allocators/src/auto/shm_allocator.rs
Normal file
|
@ -0,0 +1,20 @@
|
|||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git)
|
||||
// DO NOT EDIT
|
||||
|
||||
use crate::FdAllocator;
|
||||
|
||||
glib::wrapper! {
|
||||
#[doc(alias = "GstShmAllocator")]
|
||||
pub struct ShmAllocator(Object<ffi::GstShmAllocator, ffi::GstShmAllocatorClass>) @extends FdAllocator, gst::Allocator;
|
||||
|
||||
match fn {
|
||||
type_ => || ffi::gst_shm_allocator_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
impl ShmAllocator {}
|
||||
|
||||
unsafe impl Send for ShmAllocator {}
|
||||
unsafe impl Sync for ShmAllocator {}
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -44,6 +44,10 @@ mod drm_dumb_allocator;
|
|||
#[cfg_attr(docsrs, doc(cfg(all(feature = "v1_24", target_os = "linux"))))]
|
||||
pub use drm_dumb_allocator::*;
|
||||
|
||||
#[cfg(any(all(feature = "v1_24", unix), docsrs))]
|
||||
#[cfg_attr(docsrs, doc(cfg(all(feature = "v1_24", unix))))]
|
||||
mod shm_allocator;
|
||||
|
||||
mod phys_memory;
|
||||
pub use phys_memory::*;
|
||||
|
||||
|
|
14
gstreamer-allocators/src/shm_allocator.rs
Normal file
14
gstreamer-allocators/src/shm_allocator.rs
Normal file
|
@ -0,0 +1,14 @@
|
|||
use glib::translate::*;
|
||||
|
||||
use crate::ShmAllocator;
|
||||
|
||||
impl ShmAllocator {
|
||||
#[doc(alias = "gst_shm_allocator_get")]
|
||||
pub fn get() -> Option<gst::Allocator> {
|
||||
assert_initialized_main_thread!();
|
||||
unsafe {
|
||||
ffi::gst_shm_allocator_init_once();
|
||||
from_glib_full(ffi::gst_shm_allocator_get())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,14 +7,19 @@ libc = "0.2"
|
|||
[dependencies.glib]
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
package = "glib-sys"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gobject]
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
package = "gobject-sys"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gst]
|
||||
package = "gstreamer-sys"
|
||||
path = "../../gstreamer/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
shell-words = "1.0.0"
|
||||
|
@ -43,7 +48,7 @@ name = "gstreamer-allocators-sys"
|
|||
readme = "README.md"
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
rust-version = "1.70"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -24,6 +24,7 @@ use glib::{gboolean, gconstpointer, gpointer, GType};
|
|||
// Constants
|
||||
pub const GST_ALLOCATOR_DMABUF: &[u8] = b"dmabuf\0";
|
||||
pub const GST_ALLOCATOR_FD: &[u8] = b"fd\0";
|
||||
pub const GST_ALLOCATOR_SHM: &[u8] = b"shm\0";
|
||||
pub const GST_CAPS_FEATURE_MEMORY_DMABUF: &[u8] = b"memory:DMABuf\0";
|
||||
|
||||
// Flags
|
||||
|
@ -93,6 +94,20 @@ impl ::std::fmt::Debug for GstPhysMemoryAllocatorInterface {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct GstShmAllocatorClass {
|
||||
pub parent_class: GstFdAllocatorClass,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for GstShmAllocatorClass {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(&format!("GstShmAllocatorClass @ {self:p}"))
|
||||
.field("parent_class", &self.parent_class)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// Classes
|
||||
#[repr(C)]
|
||||
pub struct GstDRMDumbAllocator {
|
||||
|
@ -136,6 +151,19 @@ impl ::std::fmt::Debug for GstFdAllocator {
|
|||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct GstShmAllocator {
|
||||
_data: [u8; 0],
|
||||
_marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for GstShmAllocator {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(&format!("GstShmAllocator @ {self:p}"))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
// Interfaces
|
||||
#[repr(C)]
|
||||
pub struct GstPhysMemoryAllocator {
|
||||
|
@ -211,6 +239,19 @@ extern "C" {
|
|||
flags: GstFdMemoryFlags,
|
||||
) -> *mut gst::GstMemory;
|
||||
|
||||
//=========================================================================
|
||||
// GstShmAllocator
|
||||
//=========================================================================
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_shm_allocator_get_type() -> GType;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_shm_allocator_get() -> *mut gst::GstAllocator;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_shm_allocator_init_once();
|
||||
|
||||
//=========================================================================
|
||||
// GstPhysMemoryAllocator
|
||||
//=========================================================================
|
||||
|
|
|
@ -247,11 +247,19 @@ const RUST_LAYOUTS: &[(&str, Layout)] = &[
|
|||
alignment: align_of::<GstPhysMemoryAllocatorInterface>(),
|
||||
},
|
||||
),
|
||||
(
|
||||
"GstShmAllocatorClass",
|
||||
Layout {
|
||||
size: size_of::<GstShmAllocatorClass>(),
|
||||
alignment: align_of::<GstShmAllocatorClass>(),
|
||||
},
|
||||
),
|
||||
];
|
||||
|
||||
const RUST_CONSTANTS: &[(&str, &str)] = &[
|
||||
("GST_ALLOCATOR_DMABUF", "dmabuf"),
|
||||
("GST_ALLOCATOR_FD", "fd"),
|
||||
("GST_ALLOCATOR_SHM", "shm"),
|
||||
("GST_CAPS_FEATURE_MEMORY_DMABUF", "memory:DMABuf"),
|
||||
("(guint) GST_FD_MEMORY_FLAG_DONT_CLOSE", "4"),
|
||||
("(guint) GST_FD_MEMORY_FLAG_KEEP_MAPPED", "1"),
|
||||
|
|
|
@ -31,6 +31,7 @@
|
|||
int main() {
|
||||
PRINT_CONSTANT(GST_ALLOCATOR_DMABUF);
|
||||
PRINT_CONSTANT(GST_ALLOCATOR_FD);
|
||||
PRINT_CONSTANT(GST_ALLOCATOR_SHM);
|
||||
PRINT_CONSTANT(GST_CAPS_FEATURE_MEMORY_DMABUF);
|
||||
PRINT_CONSTANT((guint) GST_FD_MEMORY_FLAG_DONT_CLOSE);
|
||||
PRINT_CONSTANT((guint) GST_FD_MEMORY_FLAG_KEEP_MAPPED);
|
||||
|
|
|
@ -15,5 +15,6 @@ int main() {
|
|||
printf("%s;%zu;%zu\n", "GstFdAllocatorClass", sizeof(GstFdAllocatorClass), alignof(GstFdAllocatorClass));
|
||||
printf("%s;%zu;%zu\n", "GstFdMemoryFlags", sizeof(GstFdMemoryFlags), alignof(GstFdMemoryFlags));
|
||||
printf("%s;%zu;%zu\n", "GstPhysMemoryAllocatorInterface", sizeof(GstPhysMemoryAllocatorInterface), alignof(GstPhysMemoryAllocatorInterface));
|
||||
printf("%s;%zu;%zu\n", "GstShmAllocatorClass", sizeof(GstShmAllocatorClass), alignof(GstShmAllocatorClass));
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-app"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
description = "Rust bindings for GStreamer App library"
|
||||
|
@ -17,10 +17,10 @@ rust-version = "1.70"
|
|||
futures-core = "0.3"
|
||||
futures-sink = "0.3"
|
||||
libc = "0.2"
|
||||
ffi = { package = "gstreamer-app-sys", path = "sys" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base" }
|
||||
ffi = { package = "gstreamer-app-sys", path = "sys", version = "0.21.1" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base", version = "0.21" }
|
||||
|
||||
[dev-dependencies]
|
||||
futures-util = { version = "0.3", features = ["sink"] }
|
||||
|
|
|
@ -1163,6 +1163,24 @@ impl AppSinkBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn max_time(self, max_time: Option<gst::ClockTime>) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("max-time", max_time),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn max_bytes(self, max_bytes: u64) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("max-bytes", max_bytes),
|
||||
..self
|
||||
}
|
||||
}
|
||||
|
||||
pub fn name(self, name: impl Into<glib::GString>) -> Self {
|
||||
Self {
|
||||
builder: self.builder.property("name", name.into()),
|
||||
|
@ -1236,9 +1254,8 @@ impl Stream for AppSinkStream {
|
|||
fn poll_next(self: Pin<&mut Self>, context: &mut Context) -> Poll<Option<Self::Item>> {
|
||||
let mut waker = self.waker_reference.lock().unwrap();
|
||||
|
||||
let app_sink = match self.app_sink.upgrade() {
|
||||
Some(app_sink) => app_sink,
|
||||
None => return Poll::Ready(None),
|
||||
let Some(app_sink) = self.app_sink.upgrade() else {
|
||||
return Poll::Ready(None);
|
||||
};
|
||||
|
||||
app_sink
|
||||
|
|
|
@ -588,9 +588,8 @@ impl Sink<gst::Sample> for AppSrcSink {
|
|||
fn poll_ready(self: Pin<&mut Self>, context: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
let mut waker = self.waker_reference.lock().unwrap();
|
||||
|
||||
let app_src = match self.app_src.upgrade() {
|
||||
Some(app_src) => app_src,
|
||||
None => return Poll::Ready(Err(gst::FlowError::Eos)),
|
||||
let Some(app_src) = self.app_src.upgrade() else {
|
||||
return Poll::Ready(Err(gst::FlowError::Eos));
|
||||
};
|
||||
|
||||
let current_level_bytes = app_src.current_level_bytes();
|
||||
|
@ -606,9 +605,8 @@ impl Sink<gst::Sample> for AppSrcSink {
|
|||
}
|
||||
|
||||
fn start_send(self: Pin<&mut Self>, sample: gst::Sample) -> Result<(), Self::Error> {
|
||||
let app_src = match self.app_src.upgrade() {
|
||||
Some(app_src) => app_src,
|
||||
None => return Err(gst::FlowError::Eos),
|
||||
let Some(app_src) = self.app_src.upgrade() else {
|
||||
return Err(gst::FlowError::Eos);
|
||||
};
|
||||
|
||||
app_src.push_sample(&sample)?;
|
||||
|
@ -621,9 +619,8 @@ impl Sink<gst::Sample> for AppSrcSink {
|
|||
}
|
||||
|
||||
fn poll_close(self: Pin<&mut Self>, _: &mut Context) -> Poll<Result<(), Self::Error>> {
|
||||
let app_src = match self.app_src.upgrade() {
|
||||
Some(app_src) => app_src,
|
||||
None => return Poll::Ready(Ok(())),
|
||||
let Some(app_src) = self.app_src.upgrade() else {
|
||||
return Poll::Ready(Ok(()));
|
||||
};
|
||||
|
||||
app_src.end_of_stream()?;
|
||||
|
|
|
@ -48,6 +48,22 @@ impl AppSink {
|
|||
unsafe { ffi::gst_app_sink_get_max_buffers(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "gst_app_sink_get_max_bytes")]
|
||||
#[doc(alias = "get_max_bytes")]
|
||||
pub fn max_bytes(&self) -> u64 {
|
||||
unsafe { ffi::gst_app_sink_get_max_bytes(self.to_glib_none().0) }
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "gst_app_sink_get_max_time")]
|
||||
#[doc(alias = "get_max_time")]
|
||||
pub fn max_time(&self) -> Option<gst::ClockTime> {
|
||||
unsafe { from_glib(ffi::gst_app_sink_get_max_time(self.to_glib_none().0)) }
|
||||
}
|
||||
|
||||
#[doc(alias = "gst_app_sink_get_wait_on_eos")]
|
||||
#[doc(alias = "get_wait_on_eos")]
|
||||
pub fn is_wait_on_eos(&self) -> bool {
|
||||
|
@ -121,6 +137,24 @@ impl AppSink {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "gst_app_sink_set_max_bytes")]
|
||||
pub fn set_max_bytes(&self, max: u64) {
|
||||
unsafe {
|
||||
ffi::gst_app_sink_set_max_bytes(self.to_glib_none().0, max);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "gst_app_sink_set_max_time")]
|
||||
pub fn set_max_time(&self, max: impl Into<Option<gst::ClockTime>>) {
|
||||
unsafe {
|
||||
ffi::gst_app_sink_set_max_time(self.to_glib_none().0, max.into().into_glib());
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "gst_app_sink_set_wait_on_eos")]
|
||||
pub fn set_wait_on_eos(&self, wait: bool) {
|
||||
unsafe {
|
||||
|
@ -313,6 +347,64 @@ impl AppSink {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "max-bytes")]
|
||||
pub fn connect_max_bytes_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn notify_max_bytes_trampoline<
|
||||
F: Fn(&AppSink) + Send + Sync + 'static,
|
||||
>(
|
||||
this: *mut ffi::GstAppSink,
|
||||
_param_spec: glib::ffi::gpointer,
|
||||
f: glib::ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this))
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"notify::max-bytes\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
notify_max_bytes_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "max-time")]
|
||||
pub fn connect_max_time_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn notify_max_time_trampoline<F: Fn(&AppSink) + Send + Sync + 'static>(
|
||||
this: *mut ffi::GstAppSink,
|
||||
_param_spec: glib::ffi::gpointer,
|
||||
f: glib::ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this))
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"notify::max-time\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
notify_max_time_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "wait-on-eos")]
|
||||
pub fn connect_wait_on_eos_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
|
|
|
@ -59,6 +59,7 @@ impl FromGlib<ffi::GstAppLeakyType> for AppLeakyType {
|
|||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
|
||||
impl StaticType for AppLeakyType {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_app_leaky_type_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_app_leaky_type_get_type()) }
|
||||
}
|
||||
|
@ -72,7 +73,7 @@ impl glib::HasParamSpec for AppLeakyType {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -168,6 +169,7 @@ impl FromGlib<ffi::GstAppStreamType> for AppStreamType {
|
|||
|
||||
impl StaticType for AppStreamType {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_app_stream_type_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_app_stream_type_get_type()) }
|
||||
}
|
||||
|
@ -179,7 +181,7 @@ impl glib::HasParamSpec for AppStreamType {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -7,14 +7,18 @@ libc = "0.2"
|
|||
[dependencies.glib]
|
||||
package = "glib-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gst_base]
|
||||
package = "gstreamer-base-sys"
|
||||
path = "../../gstreamer-base/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dependencies.gst]
|
||||
package = "gstreamer-sys"
|
||||
path = "../../gstreamer/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
shell-words = "1.0.0"
|
||||
|
@ -41,7 +45,7 @@ license = "MIT"
|
|||
name = "gstreamer-app-sys"
|
||||
readme = "README.md"
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -215,6 +215,12 @@ extern "C" {
|
|||
pub fn gst_app_sink_get_drop(appsink: *mut GstAppSink) -> gboolean;
|
||||
pub fn gst_app_sink_get_emit_signals(appsink: *mut GstAppSink) -> gboolean;
|
||||
pub fn gst_app_sink_get_max_buffers(appsink: *mut GstAppSink) -> c_uint;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_app_sink_get_max_bytes(appsink: *mut GstAppSink) -> u64;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_app_sink_get_max_time(appsink: *mut GstAppSink) -> gst::GstClockTime;
|
||||
pub fn gst_app_sink_get_wait_on_eos(appsink: *mut GstAppSink) -> gboolean;
|
||||
pub fn gst_app_sink_is_eos(appsink: *mut GstAppSink) -> gboolean;
|
||||
#[cfg(feature = "v1_20")]
|
||||
|
@ -233,6 +239,12 @@ extern "C" {
|
|||
pub fn gst_app_sink_set_drop(appsink: *mut GstAppSink, drop: gboolean);
|
||||
pub fn gst_app_sink_set_emit_signals(appsink: *mut GstAppSink, emit: gboolean);
|
||||
pub fn gst_app_sink_set_max_buffers(appsink: *mut GstAppSink, max: c_uint);
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_app_sink_set_max_bytes(appsink: *mut GstAppSink, max: u64);
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_app_sink_set_max_time(appsink: *mut GstAppSink, max: gst::GstClockTime);
|
||||
pub fn gst_app_sink_set_wait_on_eos(appsink: *mut GstAppSink, wait: gboolean);
|
||||
#[cfg(feature = "v1_20")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-audio"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
description = "Rust bindings for GStreamer Audio library"
|
||||
|
@ -16,14 +16,14 @@ rust-version = "1.70"
|
|||
[dependencies]
|
||||
libc = "0.2"
|
||||
cfg-if = "1.0"
|
||||
ffi = { package = "gstreamer-audio-sys", path = "sys" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base" }
|
||||
ffi = { package = "gstreamer-audio-sys", path = "sys", version = "0.21" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base", version = "0.21" }
|
||||
serde = { version = "1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
itertools = "0.11"
|
||||
itertools = "0.12"
|
||||
serde_json = "1.0"
|
||||
gir-format-check = "0.1"
|
||||
|
||||
|
|
|
@ -160,7 +160,7 @@ impl AudioConverterConfig {
|
|||
})
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.unwrap_or_else(Vec::new)
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_22")]
|
||||
|
|
|
@ -148,13 +148,14 @@ impl str::FromStr for crate::AudioFormat {
|
|||
}
|
||||
|
||||
impl PartialOrd for crate::AudioFormat {
|
||||
#[inline]
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
crate::AudioFormatInfo::from_format(*self)
|
||||
.partial_cmp(&crate::AudioFormatInfo::from_format(*other))
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for crate::AudioFormat {
|
||||
#[inline]
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
crate::AudioFormatInfo::from_format(*self).cmp(&crate::AudioFormatInfo::from_format(*other))
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ impl FromGlib<ffi::GstAudioDitherMethod> for AudioDitherMethod {
|
|||
|
||||
impl StaticType for AudioDitherMethod {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_dither_method_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_dither_method_get_type()) }
|
||||
}
|
||||
|
@ -67,7 +68,7 @@ impl glib::HasParamSpec for AudioDitherMethod {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -284,6 +285,7 @@ impl FromGlib<ffi::GstAudioFormat> for AudioFormat {
|
|||
|
||||
impl StaticType for AudioFormat {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_format_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_format_get_type()) }
|
||||
}
|
||||
|
@ -295,7 +297,7 @@ impl glib::HasParamSpec for AudioFormat {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,6 +381,7 @@ impl FromGlib<ffi::GstAudioLayout> for AudioLayout {
|
|||
|
||||
impl StaticType for AudioLayout {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_layout_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_layout_get_type()) }
|
||||
}
|
||||
|
@ -390,7 +393,7 @@ impl glib::HasParamSpec for AudioLayout {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -486,6 +489,7 @@ impl FromGlib<ffi::GstAudioNoiseShapingMethod> for AudioNoiseShapingMethod {
|
|||
|
||||
impl StaticType for AudioNoiseShapingMethod {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_noise_shaping_method_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_noise_shaping_method_get_type()) }
|
||||
}
|
||||
|
@ -497,7 +501,7 @@ impl glib::HasParamSpec for AudioNoiseShapingMethod {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,6 +597,7 @@ impl FromGlib<ffi::GstAudioResamplerMethod> for AudioResamplerMethod {
|
|||
|
||||
impl StaticType for AudioResamplerMethod {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_resampler_method_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_resampler_method_get_type()) }
|
||||
}
|
||||
|
@ -604,7 +609,7 @@ impl glib::HasParamSpec for AudioResamplerMethod {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -746,6 +751,7 @@ impl FromGlib<ffi::GstAudioRingBufferFormatType> for AudioRingBufferFormatType {
|
|||
|
||||
impl StaticType for AudioRingBufferFormatType {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_ring_buffer_format_type_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_ring_buffer_format_type_get_type()) }
|
||||
}
|
||||
|
@ -757,7 +763,7 @@ impl glib::HasParamSpec for AudioRingBufferFormatType {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ impl FromGlib<ffi::GstAudioFlags> for AudioFlags {
|
|||
|
||||
impl StaticType for AudioFlags {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_flags_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_flags_get_type()) }
|
||||
}
|
||||
|
@ -46,7 +47,7 @@ impl glib::HasParamSpec for AudioFlags {
|
|||
type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -126,6 +127,7 @@ impl FromGlib<ffi::GstAudioFormatFlags> for AudioFormatFlags {
|
|||
|
||||
impl StaticType for AudioFormatFlags {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_format_flags_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_format_flags_get_type()) }
|
||||
}
|
||||
|
@ -137,7 +139,7 @@ impl glib::HasParamSpec for AudioFormatFlags {
|
|||
type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -209,6 +211,7 @@ impl FromGlib<ffi::GstAudioPackFlags> for AudioPackFlags {
|
|||
|
||||
impl StaticType for AudioPackFlags {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_audio_pack_flags_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_audio_pack_flags_get_type()) }
|
||||
}
|
||||
|
@ -220,7 +223,7 @@ impl glib::HasParamSpec for AudioPackFlags {
|
|||
type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use glib::translate::*;
|
||||
use glib::{once_cell::sync::Lazy, translate::*};
|
||||
use gst_base::{prelude::*, subclass::prelude::*};
|
||||
|
||||
use crate::{AudioFilter, AudioInfo};
|
||||
|
||||
pub trait AudioFilterImpl: AudioFilterImplExt + BaseTransformImpl {
|
||||
fn allowed_caps() -> &'static gst::Caps;
|
||||
fn allowed_caps() -> &'static gst::Caps {
|
||||
Self::parent_allowed_caps()
|
||||
}
|
||||
|
||||
fn setup(&self, info: &AudioInfo) -> Result<(), gst::LoggableError> {
|
||||
self.parent_setup(info)
|
||||
|
@ -38,6 +40,27 @@ pub trait AudioFilterImplExt: sealed::Sealed + ObjectSubclass {
|
|||
.unwrap_or(Ok(()))
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_allowed_caps() -> &'static gst::Caps {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut gst::ffi::GstElementClass;
|
||||
|
||||
let templ = gst::ffi::gst_element_class_get_pad_template(
|
||||
parent_class,
|
||||
glib::gstr!("sink").to_glib_none().0,
|
||||
);
|
||||
|
||||
if templ.is_null() {
|
||||
static ANY_AUDIO_CAPS: Lazy<gst::Caps> =
|
||||
Lazy::new(|| crate::AudioCapsBuilder::new().build());
|
||||
|
||||
return &ANY_AUDIO_CAPS;
|
||||
}
|
||||
|
||||
&*(&(*templ).caps as *const *mut gst::ffi::GstCaps as *const gst::Caps)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AudioFilterImpl> AudioFilterImplExt for T {}
|
||||
|
|
|
@ -7,18 +7,24 @@ libc = "0.2"
|
|||
[dependencies.glib]
|
||||
package = "glib-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gobject]
|
||||
package = "gobject-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gst_base]
|
||||
package = "gstreamer-base-sys"
|
||||
path = "../../gstreamer-base/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dependencies.gst]
|
||||
package = "gstreamer-sys"
|
||||
path = "../../gstreamer/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
shell-words = "1.0.0"
|
||||
|
@ -45,7 +51,7 @@ license = "MIT"
|
|||
name = "gstreamer-audio-sys"
|
||||
readme = "README.md"
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -2317,6 +2317,9 @@ extern "C" {
|
|||
buf: *mut GstAudioRingBuffer,
|
||||
position: *const GstAudioChannelPosition,
|
||||
);
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_audio_ring_buffer_set_errored(buf: *mut GstAudioRingBuffer);
|
||||
pub fn gst_audio_ring_buffer_set_flushing(buf: *mut GstAudioRingBuffer, flushing: gboolean);
|
||||
pub fn gst_audio_ring_buffer_set_sample(buf: *mut GstAudioRingBuffer, sample: u64);
|
||||
pub fn gst_audio_ring_buffer_set_timestamp(
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-base"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
description = "Rust bindings for GStreamer Base library"
|
||||
|
@ -16,9 +16,9 @@ rust-version = "1.70"
|
|||
[dependencies]
|
||||
cfg-if = "1.0"
|
||||
libc = "0.2"
|
||||
ffi = { package = "gstreamer-base-sys", path = "sys" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
ffi = { package = "gstreamer-base-sys", path = "sys", version = "0.21" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
atomic_refcell = "0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -61,6 +61,7 @@ impl FromGlib<ffi::GstAggregatorStartTimeSelection> for AggregatorStartTimeSelec
|
|||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
|
||||
impl StaticType for AggregatorStartTimeSelection {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_aggregator_start_time_selection_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_aggregator_start_time_selection_get_type()) }
|
||||
}
|
||||
|
@ -74,7 +75,7 @@ impl glib::HasParamSpec for AggregatorStartTimeSelection {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -7,14 +7,19 @@ libc = "0.2"
|
|||
[dependencies.glib]
|
||||
package = "glib-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gobject]
|
||||
package = "gobject-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gst]
|
||||
package = "gstreamer-sys"
|
||||
path = "../../gstreamer/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
shell-words = "1.0.0"
|
||||
|
@ -43,7 +48,7 @@ license = "MIT"
|
|||
name = "gstreamer-base-sys"
|
||||
readme = "README.md"
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -1706,6 +1706,22 @@ extern "C" {
|
|||
pub fn gst_queue_array_pop_head_struct(array: *mut GstQueueArray) -> gpointer;
|
||||
pub fn gst_queue_array_pop_tail(array: *mut GstQueueArray) -> gpointer;
|
||||
pub fn gst_queue_array_pop_tail_struct(array: *mut GstQueueArray) -> gpointer;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_queue_array_push_sorted(
|
||||
array: *mut GstQueueArray,
|
||||
data: gpointer,
|
||||
func: glib::GCompareDataFunc,
|
||||
user_data: gpointer,
|
||||
);
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_queue_array_push_sorted_struct(
|
||||
array: *mut GstQueueArray,
|
||||
p_struct: gpointer,
|
||||
func: glib::GCompareDataFunc,
|
||||
user_data: gpointer,
|
||||
);
|
||||
pub fn gst_queue_array_push_tail(array: *mut GstQueueArray, data: gpointer);
|
||||
pub fn gst_queue_array_push_tail_struct(array: *mut GstQueueArray, p_struct: gpointer);
|
||||
#[cfg(feature = "v1_16")]
|
||||
|
@ -1714,6 +1730,13 @@ extern "C" {
|
|||
array: *mut GstQueueArray,
|
||||
clear_func: glib::GDestroyNotify,
|
||||
);
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn gst_queue_array_sort(
|
||||
array: *mut GstQueueArray,
|
||||
compare_func: glib::GCompareDataFunc,
|
||||
user_data: gpointer,
|
||||
);
|
||||
pub fn gst_queue_array_new(initial_size: c_uint) -> *mut GstQueueArray;
|
||||
pub fn gst_queue_array_new_for_struct(
|
||||
struct_size: size_t,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-check"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
description = "Rust bindings for GStreamer Check library"
|
||||
|
@ -14,9 +14,9 @@ edition = "2021"
|
|||
rust-version = "1.70"
|
||||
|
||||
[dependencies]
|
||||
ffi = { package = "gstreamer-check-sys", path = "sys" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
ffi = { package = "gstreamer-check-sys", path = "sys", version = "0.21" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
|
||||
[dev-dependencies]
|
||||
gir-format-check = "0.1"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -7,14 +7,19 @@ libc = "0.2"
|
|||
[dependencies.glib]
|
||||
package = "glib-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gobject]
|
||||
package = "gobject-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gst]
|
||||
package = "gstreamer-sys"
|
||||
path = "../../gstreamer/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
shell-words = "1.0.0"
|
||||
|
@ -41,7 +46,7 @@ license = "MIT"
|
|||
name = "gstreamer-check-sys"
|
||||
readme = "README.md"
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-controller"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = ["Alexey Galakhov <agalakhov@gmail.com>", "Sebastian Dröge <sebastian@centricular.com>"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
description = "Rust bindings for GStreamer Controller library"
|
||||
|
@ -14,9 +14,9 @@ edition = "2021"
|
|||
rust-version = "1.70"
|
||||
|
||||
[dependencies]
|
||||
ffi = { package = "gstreamer-controller-sys", path = "sys" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
ffi = { package = "gstreamer-controller-sys", path = "sys", version = "0.21" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
|
||||
[dev-dependencies]
|
||||
gir-format-check = "0.1"
|
||||
|
|
|
@ -55,6 +55,7 @@ impl FromGlib<ffi::GstInterpolationMode> for InterpolationMode {
|
|||
|
||||
impl StaticType for InterpolationMode {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_interpolation_mode_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_interpolation_mode_get_type()) }
|
||||
}
|
||||
|
@ -66,7 +67,7 @@ impl glib::HasParamSpec for InterpolationMode {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,6 +163,7 @@ impl FromGlib<ffi::GstLFOWaveform> for LFOWaveform {
|
|||
|
||||
impl StaticType for LFOWaveform {
|
||||
#[inline]
|
||||
#[doc(alias = "gst_lfo_waveform_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::gst_lfo_waveform_get_type()) }
|
||||
}
|
||||
|
@ -173,7 +175,7 @@ impl glib::HasParamSpec for LFOWaveform {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -8,14 +8,19 @@ libc = "0.2"
|
|||
[dependencies.glib]
|
||||
package = "glib-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gobject]
|
||||
package = "gobject-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gst]
|
||||
package = "gstreamer-sys"
|
||||
path = "../../gstreamer/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
shell-words = "1.0.0"
|
||||
|
@ -42,7 +47,7 @@ license = "MIT"
|
|||
name = "gstreamer-controller-sys"
|
||||
readme = "README.md"
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-editing-services"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = ["Thibault Saunier <tsaunier@igalia.com>", "Sebastian Dröge <sebastian@centricular.com>"]
|
||||
categories = ["api-bindings", "multimedia"]
|
||||
description = "Rust bindings for GStreamer Editing Services"
|
||||
|
@ -15,12 +15,12 @@ rust-version = "1.70"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
ffi = { package = "gstreamer-editing-services-sys", path = "sys"}
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gio = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base" }
|
||||
gst-pbutils = { package = "gstreamer-pbutils", path = "../gstreamer-pbutils" }
|
||||
ffi = { package = "gstreamer-editing-services-sys", path = "sys", version = "0.21"}
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gio = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base", version = "0.21" }
|
||||
gst-pbutils = { package = "gstreamer-pbutils", path = "../gstreamer-pbutils", version = "0.21" }
|
||||
serde = { version = "1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -18,6 +18,7 @@ external_libraries = [
|
|||
]
|
||||
|
||||
manual = [
|
||||
"GES.FrameCompositionMeta",
|
||||
"Gio.AsyncReadyCallback",
|
||||
"Gio.Cancellable",
|
||||
"GLib.Date",
|
||||
|
|
|
@ -6,4 +6,5 @@
|
|||
#[allow(unused_imports)]
|
||||
use crate::auto::*;
|
||||
|
||||
#[doc(alias = "GESFrameNumber")]
|
||||
pub type FrameNumber = i64;
|
||||
|
|
|
@ -150,6 +150,36 @@ impl DiscovererManager {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "source-setup")]
|
||||
pub fn connect_source_setup<F: Fn(&Self, &gst::Element) + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn source_setup_trampoline<
|
||||
F: Fn(&DiscovererManager, &gst::Element) + 'static,
|
||||
>(
|
||||
this: *mut ffi::GESDiscovererManager,
|
||||
source: *mut gst::ffi::GstElement,
|
||||
f: glib::ffi::gpointer,
|
||||
) {
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&from_glib_borrow(this), &from_glib_borrow(source))
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"source-setup\0".as_ptr() as *const _,
|
||||
Some(transmute::<_, unsafe extern "C" fn()>(
|
||||
source_setup_trampoline::<F> as *const (),
|
||||
)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
#[doc(alias = "timeout")]
|
||||
|
|
|
@ -176,6 +176,7 @@ impl FromGlib<ffi::GESEdge> for Edge {
|
|||
|
||||
impl StaticType for Edge {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_edge_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_edge_get_type()) }
|
||||
}
|
||||
|
@ -187,7 +188,7 @@ impl glib::HasParamSpec for Edge {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,6 +307,7 @@ impl FromGlib<ffi::GESEditMode> for EditMode {
|
|||
|
||||
impl StaticType for EditMode {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_edit_mode_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_edit_mode_get_type()) }
|
||||
}
|
||||
|
@ -317,7 +319,7 @@ impl glib::HasParamSpec for EditMode {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -481,6 +483,7 @@ impl FromGlib<ffi::GESTextHAlign> for TextHAlign {
|
|||
|
||||
impl StaticType for TextHAlign {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_text_halign_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_text_halign_get_type()) }
|
||||
}
|
||||
|
@ -492,7 +495,7 @@ impl glib::HasParamSpec for TextHAlign {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -592,6 +595,7 @@ impl FromGlib<ffi::GESTextVAlign> for TextVAlign {
|
|||
|
||||
impl StaticType for TextVAlign {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_text_valign_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_text_valign_get_type()) }
|
||||
}
|
||||
|
@ -603,7 +607,7 @@ impl glib::HasParamSpec for TextVAlign {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -973,6 +977,7 @@ impl FromGlib<ffi::GESVideoStandardTransitionType> for VideoStandardTransitionTy
|
|||
|
||||
impl StaticType for VideoStandardTransitionType {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_video_standard_transition_type_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_video_standard_transition_type_get_type()) }
|
||||
}
|
||||
|
@ -984,7 +989,7 @@ impl glib::HasParamSpec for VideoStandardTransitionType {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1130,6 +1135,7 @@ impl FromGlib<ffi::GESVideoTestPattern> for VideoTestPattern {
|
|||
|
||||
impl StaticType for VideoTestPattern {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_video_test_pattern_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_video_test_pattern_get_type()) }
|
||||
}
|
||||
|
@ -1141,7 +1147,7 @@ impl glib::HasParamSpec for VideoTestPattern {
|
|||
type BuilderFn = fn(&str, Self) -> glib::ParamSpecEnumBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name, default_value| Self::ParamSpec::builder_with_default(name, default_value)
|
||||
Self::ParamSpec::builder_with_default
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ impl FromGlib<ffi::GESMarkerFlags> for MarkerFlags {
|
|||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_20")))]
|
||||
impl StaticType for MarkerFlags {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_marker_flags_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_marker_flags_get_type()) }
|
||||
}
|
||||
|
@ -59,7 +60,7 @@ impl glib::HasParamSpec for MarkerFlags {
|
|||
type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,6 +144,7 @@ impl FromGlib<ffi::GESMetaFlag> for MetaFlag {
|
|||
|
||||
impl StaticType for MetaFlag {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_meta_flag_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_meta_flag_get_type()) }
|
||||
}
|
||||
|
@ -154,7 +156,7 @@ impl glib::HasParamSpec for MetaFlag {
|
|||
type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,6 +236,7 @@ impl FromGlib<ffi::GESPipelineFlags> for PipelineFlags {
|
|||
|
||||
impl StaticType for PipelineFlags {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_pipeline_flags_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_pipeline_flags_get_type()) }
|
||||
}
|
||||
|
@ -245,7 +248,7 @@ impl glib::HasParamSpec for PipelineFlags {
|
|||
type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -344,6 +347,7 @@ impl FromGlib<ffi::GESTrackType> for TrackType {
|
|||
|
||||
impl StaticType for TrackType {
|
||||
#[inline]
|
||||
#[doc(alias = "ges_track_type_get_type")]
|
||||
fn static_type() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_track_type_get_type()) }
|
||||
}
|
||||
|
@ -355,7 +359,7 @@ impl glib::HasParamSpec for TrackType {
|
|||
type BuilderFn = fn(&str) -> glib::ParamSpecFlagsBuilder<Self>;
|
||||
|
||||
fn param_spec_builder() -> Self::BuilderFn {
|
||||
|name| Self::ParamSpec::builder(name)
|
||||
Self::ParamSpec::builder
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
141
gstreamer-editing-services/src/composition_meta.rs
Normal file
141
gstreamer-editing-services/src/composition_meta.rs
Normal file
|
@ -0,0 +1,141 @@
|
|||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use std::fmt;
|
||||
|
||||
use glib::translate::from_glib;
|
||||
use gst::prelude::*;
|
||||
|
||||
#[repr(transparent)]
|
||||
#[doc(alias = "GESFrameCompositionMeta")]
|
||||
pub struct FrameCompositionMeta(ffi::GESFrameCompositionMeta);
|
||||
|
||||
unsafe impl Send for FrameCompositionMeta {}
|
||||
|
||||
unsafe impl Sync for FrameCompositionMeta {}
|
||||
|
||||
impl FrameCompositionMeta {
|
||||
#[inline]
|
||||
pub fn alpha(&self) -> f64 {
|
||||
self.0.alpha
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn position(&self) -> (i32, i32) {
|
||||
(self.0.posx, self.0.posy)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pos_x(&self) -> i32 {
|
||||
self.0.posx
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn pos_y(&self) -> i32 {
|
||||
self.0.posy
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn size(&self) -> (i32, i32) {
|
||||
(self.0.width, self.0.height)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn width(&self) -> i32 {
|
||||
self.0.width
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn height(&self) -> i32 {
|
||||
self.0.height
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn zorder(&self) -> u32 {
|
||||
self.0.zorder
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn operator(&self) -> i32 {
|
||||
self.0.operator
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl MetaAPI for FrameCompositionMeta {
|
||||
type GstType = ffi::GESFrameCompositionMeta;
|
||||
|
||||
#[doc(alias = "ges_frame_composition_meta_api_get_type")]
|
||||
#[inline]
|
||||
fn meta_api() -> glib::Type {
|
||||
unsafe { from_glib(ffi::ges_frame_composition_meta_api_get_type()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for FrameCompositionMeta {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
f.debug_struct("FrameCompositionMeta")
|
||||
.field("pos-x", &self.pos_x())
|
||||
.field("pos-y", &self.pos_y())
|
||||
.field("width", &self.width())
|
||||
.field("height", &self.height())
|
||||
.field("zorder", &self.zorder())
|
||||
.field("alpha", &self.alpha())
|
||||
.field("operator", &self.operator())
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
fn add_composition_meta(
|
||||
buffer: &mut gst::BufferRef,
|
||||
position: (i32, i32),
|
||||
size: (i32, i32),
|
||||
alpha: f64,
|
||||
zorder: u32,
|
||||
operator: i32,
|
||||
) -> Result<gst::MetaRefMut<FrameCompositionMeta, gst::meta::Standalone>, glib::BoolError> {
|
||||
assert_initialized_main_thread!();
|
||||
|
||||
unsafe {
|
||||
let meta = ffi::ges_buffer_add_frame_composition_meta(buffer.as_mut_ptr());
|
||||
|
||||
if meta.is_null() {
|
||||
return Err(glib::bool_error!("Failed to add frame composition meta"));
|
||||
}
|
||||
|
||||
let mut result = FrameCompositionMeta::from_mut_ptr(buffer, meta);
|
||||
result.0.posx = position.0;
|
||||
result.0.posy = position.1;
|
||||
result.0.width = size.0;
|
||||
result.0.height = size.1;
|
||||
result.0.alpha = alpha;
|
||||
result.0.zorder = zorder;
|
||||
result.0.operator = operator;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_add_get_meta() {
|
||||
gst::init().unwrap();
|
||||
crate::init().unwrap();
|
||||
|
||||
let mut buffer = gst::Buffer::with_size(320 * 240 * 4).unwrap();
|
||||
{
|
||||
let _meta =
|
||||
add_composition_meta(buffer.get_mut().unwrap(), (42, 42), (20, 22), 0.42, 2, 42)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
{
|
||||
let meta = buffer.meta::<FrameCompositionMeta>().unwrap();
|
||||
assert_eq!(meta.position(), (42, 42));
|
||||
assert_eq!(meta.size(), (20, 22));
|
||||
assert_eq!(meta.alpha(), 0.42);
|
||||
assert_eq!(meta.zorder(), 2);
|
||||
assert_eq!(meta.operator(), 42);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -56,6 +56,9 @@ macro_rules! skip_assert_initialized {
|
|||
mod auto;
|
||||
mod formatter;
|
||||
pub use crate::auto::*;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
mod composition_meta;
|
||||
pub mod subclass;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
|
@ -74,5 +77,8 @@ pub mod prelude {
|
|||
pub use gst_pbutils::prelude::*;
|
||||
|
||||
pub use crate::auto::traits::*;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub use crate::composition_meta::FrameCompositionMeta;
|
||||
pub use crate::formatter::FormatterExtManual;
|
||||
}
|
||||
|
|
|
@ -7,22 +7,30 @@ libc = "0.2"
|
|||
[dependencies.gio]
|
||||
package = "gio-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.glib]
|
||||
package = "glib-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gobject]
|
||||
package = "gobject-sys"
|
||||
git = "https://github.com/gtk-rs/gtk-rs-core"
|
||||
branch = "0.18"
|
||||
version = "0.18"
|
||||
|
||||
[dependencies.gst_pbutils]
|
||||
package = "gstreamer-pbutils-sys"
|
||||
path = "../../gstreamer-pbutils/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dependencies.gst]
|
||||
package = "gstreamer-sys"
|
||||
path = "../../gstreamer/sys"
|
||||
version = "0.21"
|
||||
|
||||
[dev-dependencies]
|
||||
shell-words = "1.0.0"
|
||||
|
@ -49,7 +57,7 @@ license = "MIT"
|
|||
name = "gstreamer-editing-services-sys"
|
||||
readme = "README.md"
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
edition = "2021"
|
||||
rust-version = "1.70"
|
||||
|
||||
|
|
|
@ -30,3 +30,10 @@ ignore = [
|
|||
[external_libraries]
|
||||
gstreamer="Gst"
|
||||
gstreamer_pbutils="GstPbutils"
|
||||
|
||||
[[object]]
|
||||
name = "GES.*"
|
||||
status = "generate"
|
||||
[[object.function]]
|
||||
name = "frame_composition_meta_api_get_type"
|
||||
version = "1.24"
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
Generated by gir (https://github.com/gtk-rs/gir @ 1d1ce102e130)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 060b114d8edb)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 8ea27b3f0b33)
|
||||
Generated by gir (https://github.com/gtk-rs/gir @ 23d7c100187c)
|
||||
from gir-files (https://github.com/gtk-rs/gir-files @ 6415239ef435)
|
||||
from gst-gir-files (https://gitlab.freedesktop.org/gstreamer/gir-files-rs.git @ 62054dc7234d)
|
||||
|
|
|
@ -868,6 +868,34 @@ pub struct _GESFormatterPrivate {
|
|||
|
||||
pub type GESFormatterPrivate = *mut _GESFormatterPrivate;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct GESFrameCompositionMeta {
|
||||
pub meta: gst::GstMeta,
|
||||
pub alpha: c_double,
|
||||
pub posx: c_int,
|
||||
pub posy: c_int,
|
||||
pub height: c_int,
|
||||
pub width: c_int,
|
||||
pub zorder: c_uint,
|
||||
pub operator: c_int,
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for GESFrameCompositionMeta {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(&format!("GESFrameCompositionMeta @ {self:p}"))
|
||||
.field("meta", &self.meta)
|
||||
.field("alpha", &self.alpha)
|
||||
.field("posx", &self.posx)
|
||||
.field("posy", &self.posy)
|
||||
.field("height", &self.height)
|
||||
.field("width", &self.width)
|
||||
.field("zorder", &self.zorder)
|
||||
.field("operator", &self.operator)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct GESGroupClass {
|
||||
|
@ -4395,10 +4423,18 @@ extern "C" {
|
|||
// Other functions
|
||||
//=========================================================================
|
||||
pub fn ges_add_missing_uri_relocation_uri(uri: *const c_char, recurse: gboolean) -> gboolean;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn ges_buffer_add_frame_composition_meta(
|
||||
buffer: *mut gst::GstBuffer,
|
||||
) -> *mut GESFrameCompositionMeta;
|
||||
pub fn ges_deinit();
|
||||
#[cfg(feature = "v1_18")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_18")))]
|
||||
pub fn ges_find_formatter_for_uri(uri: *const c_char) -> *mut GESAsset;
|
||||
#[cfg(feature = "v1_24")]
|
||||
#[cfg_attr(docsrs, doc(cfg(feature = "v1_24")))]
|
||||
pub fn ges_frame_composition_meta_api_get_type() -> GType;
|
||||
pub fn ges_init() -> gboolean;
|
||||
pub fn ges_init_check(
|
||||
argc: *mut c_int,
|
||||
|
|
|
@ -499,6 +499,13 @@ const RUST_LAYOUTS: &[(&str, Layout)] = &[
|
|||
alignment: align_of::<GESFormatterClass>(),
|
||||
},
|
||||
),
|
||||
(
|
||||
"GESFrameCompositionMeta",
|
||||
Layout {
|
||||
size: size_of::<GESFrameCompositionMeta>(),
|
||||
alignment: align_of::<GESFrameCompositionMeta>(),
|
||||
},
|
||||
),
|
||||
(
|
||||
"GESFrameNumber",
|
||||
Layout {
|
||||
|
|
|
@ -51,6 +51,7 @@ int main() {
|
|||
printf("%s;%zu;%zu\n", "GESExtractableInterface", sizeof(GESExtractableInterface), alignof(GESExtractableInterface));
|
||||
printf("%s;%zu;%zu\n", "GESFormatter", sizeof(GESFormatter), alignof(GESFormatter));
|
||||
printf("%s;%zu;%zu\n", "GESFormatterClass", sizeof(GESFormatterClass), alignof(GESFormatterClass));
|
||||
printf("%s;%zu;%zu\n", "GESFrameCompositionMeta", sizeof(GESFrameCompositionMeta), alignof(GESFrameCompositionMeta));
|
||||
printf("%s;%zu;%zu\n", "GESFrameNumber", sizeof(GESFrameNumber), alignof(GESFrameNumber));
|
||||
printf("%s;%zu;%zu\n", "GESGroup", sizeof(GESGroup), alignof(GESGroup));
|
||||
printf("%s;%zu;%zu\n", "GESGroupClass", sizeof(GESGroupClass), alignof(GESGroupClass));
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-gl"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = [
|
||||
"Sebastian Dröge <sebastian@centricular.com>",
|
||||
"Víctor M. Jáquez L. <vjaquez@igalia.com>"
|
||||
|
@ -18,11 +18,11 @@ rust-version = "1.70"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
ffi = { package = "gstreamer-gl-sys", path = "sys" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base" }
|
||||
gst-video = { package = "gstreamer-video", path = "../gstreamer-video" }
|
||||
ffi = { package = "gstreamer-gl-sys", path = "sys", version = "0.21" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../gstreamer", version = "0.21" }
|
||||
gst-base = { package = "gstreamer-base", path = "../gstreamer-base", version = "0.21" }
|
||||
gst-video = { package = "gstreamer-video", path = "../gstreamer-video", version = "0.21" }
|
||||
serde = { version = "1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
|
|
|
@ -120,6 +120,12 @@ status = "generate"
|
|||
[[object.function]]
|
||||
name = "buffer_pool_config_set_gl_allocation_params"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "buffer_pool_config_get_gl_min_free_queue_size"
|
||||
ignore = true
|
||||
[[object.function]]
|
||||
name = "buffer_pool_config_set_gl_min_free_queue_size"
|
||||
ignore = true
|
||||
|
||||
# Needs manual binding to be an extension on gst_video::VideoAffineTransformationMeta
|
||||
[[object.function]]
|
||||
|
@ -340,7 +346,7 @@ status = "generate"
|
|||
[[object.function]]
|
||||
name = "get_handle"
|
||||
# return handle
|
||||
ignore = true
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "get_gl_context_for_thread"
|
||||
|
|
1
gstreamer-gl/egl/COPYRIGHT
Symbolic link
1
gstreamer-gl/egl/COPYRIGHT
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../COPYRIGHT
|
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "gstreamer-gl-egl"
|
||||
version = "0.21.0"
|
||||
version = "0.21.3"
|
||||
authors = [
|
||||
"Sebastian Dröge <sebastian@centricular.com>",
|
||||
"Víctor M. Jáquez L. <vjaquez@igalia.com>"
|
||||
|
@ -18,11 +18,11 @@ rust-version = "1.70"
|
|||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
ffi = { package = "gstreamer-gl-egl-sys", path = "sys" }
|
||||
ffi = { package = "gstreamer-gl-egl-sys", path = "sys", version = "0.21" }
|
||||
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core" }
|
||||
gst = { package = "gstreamer", path = "../../gstreamer" }
|
||||
gst-gl = { package = "gstreamer-gl", path = "../" }
|
||||
glib = { git = "https://github.com/gtk-rs/gtk-rs-core", branch = "0.18", version = "0.18" }
|
||||
gst = { package = "gstreamer", path = "../../gstreamer", version = "0.21" }
|
||||
gst-gl = { package = "gstreamer-gl", path = "../", version = "0.21" }
|
||||
|
||||
[dev-dependencies]
|
||||
gir-format-check = "0.1"
|
||||
|
|
|
@ -59,3 +59,16 @@ status = "generate"
|
|||
name = "new"
|
||||
[object.function.return]
|
||||
nullable_return_is_error = "Failed to create EGL display"
|
||||
|
||||
[[object.function]]
|
||||
name = "new_with_egl_display"
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "get_from_native"
|
||||
manual = true
|
||||
|
||||
[[object.function]]
|
||||
name = "new_surfaceless"
|
||||
[object.function.return]
|
||||
nullable_return_is_error = "Failed to create surfaceless EGL display"
|
||||
|
|
1
gstreamer-gl/egl/LICENSE-APACHE
Symbolic link
1
gstreamer-gl/egl/LICENSE-APACHE
Symbolic link
|
@ -0,0 +1 @@
|
|||
../../LICENSE-APACHE
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue