Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport sink to gst 1.14 #78

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ description = "NewTek NDI Plugin"
edition = "2018"

[dependencies]
libc = { version = "0.2", optional = true }
glib = "0.14"
gst = { package = "gstreamer", version = "0.17.4", features = ["v1_12"] }
gst-base = { package = "gstreamer-base", version = "0.17" }
Expand All @@ -19,12 +20,15 @@ byteorder = "1.0"

[build-dependencies]
gst-plugin-version-helper = "0.7"
cc = "1.0"
pkg-config = "0.3"

[features]
default = ["interlaced-fields", "reference-timestamps", "sink"]
interlaced-fields = ["gst/v1_16", "gst-video/v1_16"]
reference-timestamps = ["gst/v1_14"]
sink = ["gst/v1_18", "gst-base/v1_18"]
sink-v1_14 = ["libc", "gst/v1_14", "gst-base/v1_14"]
advanced-sdk = []

[lib]
Expand Down
20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,25 @@ export GST_PLUGIN_PATH=`pwd`/target/debug
gst-inspect-1.0 ndi
```

By defult GStreamer 1.16 is required, to use only GStreamer 1.12 instead of 1.16, pass `--disable-default-features` to cargo. Only a subset of video formats is supported with this GStreamer version.
By default, GStreamer 1.18 is required to build the project entirely. Some elements support lower GStreamer versions, as shown in the following table.

| Element | Required GStreamer Version |
|----------------------|----------------------------|
| src | v1.12 |
| interlaced-fields | v1.16 |
| reference-timestamps | v1.14 |
| sink | v1.18, v1.14* |

To build the `src` element, pass the `--no-default-features` flag. Only a subset of video formats is supported with this GStreamer version. To build any of the other elements independently, specify them as features. For example, to build the sink element, pass it as:
```
cargo build --no-default-features --features="sink"
```
Multiple elements may be built if separated by commas (`--features="sink,reference-timestamps"`).

There is a backport of the sink element for GStreamer 1.14. In order to build the backport, use the following special feature:
```
cargo build --no-default-features --features="sink-v1_14"
```

If all went ok, you should see info related to the NDI element. To make the plugin available without using `GST_PLUGIN_PATH` it's necessary to copy the plugin to the gstreamer plugins folder.

Expand Down
37 changes: 36 additions & 1 deletion build.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,38 @@
fn main() {
gst_plugin_version_helper::info()
gst_plugin_version_helper::info();

if !cfg!(feature = "sink-v1_14") {
return;
}

let gstreamer = pkg_config::probe_library("gstreamer-1.0").unwrap();
let includes = [gstreamer.include_paths];

let files = ["src/base/gstaggregator.c"];

let mut build = cc::Build::new();
build.include("src/base");

for f in files.iter() {
build.file(f);
}

for p in includes.iter().flatten() {
build.include(p);
}

build.define(
"PACKAGE_BUGREPORT",
"\"https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/new\"",
);
build.extra_warnings(false);
build.define("GstAggregator", "GstAggregatorFallback");
michaelgruner marked this conversation as resolved.
Show resolved Hide resolved
build.define("GstAggregatorClass", "GstAggregatorFallbackClass");
build.define("GstAggregatorPrivate", "GstAggregatorFallbackPrivate");
build.define("GstAggregatorPad", "GstAggregatorFallbackPad");
build.define("GstAggregatorPadClass", "GstAggregatorFallbackPadClass");
build.define("GstAggregatorPadPrivate", "GstAggregatorFallbackPadPrivate");
build.define("GST_BASE_API", "G_GNUC_INTERNAL");

build.compile("libgstaggregator-c.a");
}
106 changes: 106 additions & 0 deletions src/base/aggregator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Take a look at the license at the top of the repository in the LICENSE file.

use super::ffi;
use super::Aggregator;

use glib::signal::{connect_raw, SignalHandlerId};
use glib::translate::*;
use glib::IsA;
use glib::Value;
use gst::glib;
use gst::prelude::*;

use std::boxed::Box as Box_;
use std::mem;
use std::ptr;

pub trait AggregatorExtManual: 'static {
fn allocator(&self) -> (Option<gst::Allocator>, gst::AllocationParams);

fn finish_buffer(&self, buffer: gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError>;
fn min_upstream_latency(&self) -> gst::ClockTime;

fn set_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime);

#[doc(alias = "min-upstream-latency")]
fn connect_min_upstream_latency_notify<F: Fn(&Self) + Send + Sync + 'static>(
&self,
f: F,
) -> SignalHandlerId;
}

impl<O: IsA<Aggregator>> AggregatorExtManual for O {
fn allocator(&self) -> (Option<gst::Allocator>, gst::AllocationParams) {
unsafe {
let mut allocator = ptr::null_mut();
let mut params = mem::zeroed();
ffi::gst_aggregator_get_allocator(
self.as_ref().to_glib_none().0,
&mut allocator,
&mut params,
);
(from_glib_full(allocator), params.into())
}
}

fn finish_buffer(&self, buffer: gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
unsafe {
try_from_glib(ffi::gst_aggregator_finish_buffer(
self.as_ref().to_glib_none().0,
buffer.into_ptr(),
))
}
}

fn min_upstream_latency(&self) -> gst::ClockTime {
unsafe {
let mut value = Value::from_type(<gst::ClockTime as StaticType>::static_type());
glib::gobject_ffi::g_object_get_property(
self.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
b"min-upstream-latency\0".as_ptr() as *const _,
value.to_glib_none_mut().0,
);
value
.get()
.expect("AggregatorExtManual::min_upstream_latency")
}
}

fn set_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime) {
unsafe {
glib::gobject_ffi::g_object_set_property(
self.to_glib_none().0 as *mut glib::gobject_ffi::GObject,
b"min-upstream-latency\0".as_ptr() as *const _,
Value::from(&min_upstream_latency).to_glib_none().0,
);
}
}

fn connect_min_upstream_latency_notify<F: Fn(&Self) + Send + Sync + 'static>(
&self,
f: F,
) -> SignalHandlerId {
unsafe {
let f: Box_<F> = Box_::new(f);
connect_raw(
self.as_ptr() as *mut _,
b"notify::min-upstream-latency\0".as_ptr() as *const _,
Some(mem::transmute::<_, unsafe extern "C" fn()>(
notify_min_upstream_latency_trampoline::<Self, F> as *const (),
)),
Box_::into_raw(f),
)
}
}
}

unsafe extern "C" fn notify_min_upstream_latency_trampoline<P, F: Fn(&P) + Send + Sync + 'static>(
this: *mut ffi::GstAggregator,
_param_spec: glib::ffi::gpointer,
f: glib::ffi::gpointer,
) where
P: IsA<Aggregator>,
{
let f: &F = &*(f as *const F);
f(&Aggregator::from_glib_borrow(this).unsafe_cast_ref())
}
23 changes: 23 additions & 0 deletions src/base/aggregator_pad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Take a look at the license at the top of the repository in the LICENSE file.

use super::ffi;
use super::AggregatorPad;

use glib::object::IsA;
use glib::translate::*;
use gst::glib;

pub trait AggregatorPadExtManual: 'static {
#[doc(alias = "get_segment")]
fn segment(&self) -> gst::Segment;
}

impl<O: IsA<AggregatorPad>> AggregatorPadExtManual for O {
fn segment(&self) -> gst::Segment {
unsafe {
let ptr: &ffi::GstAggregatorPad = &*(self.as_ptr() as *const _);
let _guard = super::utils::MutexGuard::lock(&ptr.parent.object.lock);
from_glib_none(&ptr.segment as *const gst::ffi::GstSegment)
}
}
}
Loading