mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-12-22 18:16:28 +00:00
fallbackswitch: New plugin providing a fallbackswitch element
Allows having a live input stream and falling back to another input stream after a configurable timeout without any buffers received on the main input.
This commit is contained in:
parent
064cb52d0b
commit
b401b2f243
18 changed files with 6771 additions and 0 deletions
|
@ -14,6 +14,7 @@ members = [
|
|||
"gst-plugin-cdg",
|
||||
"gst-plugin-rav1e",
|
||||
"gst-plugin-s3",
|
||||
"gst-plugin-fallbackswitch",
|
||||
]
|
||||
|
||||
[profile.release]
|
||||
|
|
33
gst-plugin-fallbackswitch/Cargo.toml
Normal file
33
gst-plugin-fallbackswitch/Cargo.toml
Normal file
|
@ -0,0 +1,33 @@
|
|||
[package]
|
||||
name = "gst-plugin-fallbackswitch"
|
||||
version = "0.6.0"
|
||||
authors = ["Sebastian Dröge <sebastian@centricular.com>"]
|
||||
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
|
||||
license = "LGPL-2.1+"
|
||||
edition = "2018"
|
||||
description = "Fallback Switcher Plugin"
|
||||
|
||||
[dependencies]
|
||||
libc = "0.2"
|
||||
glib = { git = "https://github.com/gtk-rs/glib", features = ["subclassing"] }
|
||||
glib-sys = { git = "https://github.com/gtk-rs/sys" }
|
||||
gobject-sys = { git = "https://github.com/gtk-rs/sys" }
|
||||
gstreamer = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_14", "subclassing"] }
|
||||
gstreamer-base = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_16", "subclassing"], optional = true }
|
||||
gstreamer-sys = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys" }
|
||||
gstreamer-audio = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_14"] }
|
||||
gstreamer-video = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs", features = ["v1_14"] }
|
||||
|
||||
[lib]
|
||||
name = "gstfallbackswitch"
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
path = "src/lib.rs"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = { path="../gst-plugin-version-helper" }
|
||||
cc = "1.0"
|
||||
pkg-config = "0.3"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
#v1_18 = ["gstreamer/v1_18", "gstreamer-base"]
|
41
gst-plugin-fallbackswitch/build.rs
Normal file
41
gst-plugin-fallbackswitch/build.rs
Normal file
|
@ -0,0 +1,41 @@
|
|||
extern crate cc;
|
||||
extern crate gst_plugin_version_helper;
|
||||
extern crate pkg_config;
|
||||
|
||||
fn main() {
|
||||
gst_plugin_version_helper::get_info();
|
||||
|
||||
if cfg!(feature = "v1_18") {
|
||||
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().flat_map(|i| i) {
|
||||
build.include(p);
|
||||
}
|
||||
|
||||
build.define(
|
||||
"PACKAGE_BUGREPORT",
|
||||
"\"https://gitlab.freedesktop.org/gstreamer/gstreamer/issues/new\"",
|
||||
);
|
||||
build.define("GstAggregator", "GstAggregatorFallback");
|
||||
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");
|
||||
}
|
95
gst-plugin-fallbackswitch/src/base/aggregator.rs
Normal file
95
gst-plugin-fallbackswitch/src/base/aggregator.rs
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::gst_base_sys;
|
||||
use super::Aggregator;
|
||||
use glib::prelude::*;
|
||||
use glib::signal::{connect_raw, SignalHandlerId};
|
||||
use glib::translate::*;
|
||||
use glib::IsA;
|
||||
use glib::Value;
|
||||
use gst;
|
||||
use std::boxed::Box as Box_;
|
||||
use std::mem::transmute;
|
||||
|
||||
pub trait AggregatorExtManual: 'static {
|
||||
fn finish_buffer(&self, buffer: gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError>;
|
||||
fn get_property_min_upstream_latency(&self) -> gst::ClockTime;
|
||||
|
||||
fn set_property_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime);
|
||||
|
||||
fn connect_property_min_upstream_latency_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId;
|
||||
}
|
||||
|
||||
impl<O: IsA<Aggregator>> AggregatorExtManual for O {
|
||||
fn finish_buffer(&self, buffer: gst::Buffer) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
let ret: gst::FlowReturn = unsafe {
|
||||
from_glib(gst_base_sys::gst_aggregator_finish_buffer(
|
||||
self.as_ref().to_glib_none().0,
|
||||
buffer.into_ptr(),
|
||||
))
|
||||
};
|
||||
ret.into_result()
|
||||
}
|
||||
|
||||
fn get_property_min_upstream_latency(&self) -> gst::ClockTime {
|
||||
unsafe {
|
||||
let mut value = Value::from_type(<gst::ClockTime as StaticType>::static_type());
|
||||
gobject_sys::g_object_get_property(
|
||||
self.to_glib_none().0 as *mut gobject_sys::GObject,
|
||||
b"min-upstream-latency\0".as_ptr() as *const _,
|
||||
value.to_glib_none_mut().0,
|
||||
);
|
||||
value
|
||||
.get()
|
||||
.expect("AggregatorExtManual::get_property_min_upstream_latency")
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property_min_upstream_latency(&self, min_upstream_latency: gst::ClockTime) {
|
||||
unsafe {
|
||||
gobject_sys::g_object_set_property(
|
||||
self.to_glib_none().0 as *mut gobject_sys::GObject,
|
||||
b"min-upstream-latency\0".as_ptr() as *const _,
|
||||
Value::from(&min_upstream_latency).to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_property_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(transmute(
|
||||
notify_min_upstream_latency_trampoline::<Self, F> as usize,
|
||||
)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn notify_min_upstream_latency_trampoline<P, F: Fn(&P) + Send + Sync + 'static>(
|
||||
this: *mut gst_base_sys::GstAggregator,
|
||||
_param_spec: glib_sys::gpointer,
|
||||
f: glib_sys::gpointer,
|
||||
) where
|
||||
P: IsA<Aggregator>,
|
||||
{
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&Aggregator::from_glib_borrow(this).unsafe_cast())
|
||||
}
|
28
gst-plugin-fallbackswitch/src/base/aggregator_pad.rs
Normal file
28
gst-plugin-fallbackswitch/src/base/aggregator_pad.rs
Normal file
|
@ -0,0 +1,28 @@
|
|||
// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::gst_base_sys;
|
||||
use super::AggregatorPad;
|
||||
use glib::object::IsA;
|
||||
use glib::translate::*;
|
||||
use gst;
|
||||
use gst_sys;
|
||||
|
||||
pub trait AggregatorPadExtManual: 'static {
|
||||
fn get_segment(&self) -> gst::Segment;
|
||||
}
|
||||
|
||||
impl<O: IsA<AggregatorPad>> AggregatorPadExtManual for O {
|
||||
fn get_segment(&self) -> gst::Segment {
|
||||
unsafe {
|
||||
let ptr: &gst_base_sys::GstAggregatorPad = &*(self.as_ptr() as *const _);
|
||||
super::utils::MutexGuard::lock(&ptr.parent.object.lock);
|
||||
from_glib_none(&ptr.segment as *const gst_sys::GstSegment)
|
||||
}
|
||||
}
|
||||
}
|
190
gst-plugin-fallbackswitch/src/base/auto/aggregator.rs
Normal file
190
gst-plugin-fallbackswitch/src/base/auto/aggregator.rs
Normal file
|
@ -0,0 +1,190 @@
|
|||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use super::super::gst_base_sys;
|
||||
use glib::object::Cast;
|
||||
use glib::object::IsA;
|
||||
use glib::signal::connect_raw;
|
||||
use glib::signal::SignalHandlerId;
|
||||
use glib::translate::*;
|
||||
use glib::StaticType;
|
||||
use glib::Value;
|
||||
use glib_sys;
|
||||
use gobject_sys;
|
||||
use gst;
|
||||
use std::boxed::Box as Box_;
|
||||
use std::mem::transmute;
|
||||
|
||||
glib_wrapper! {
|
||||
pub struct Aggregator(Object<gst_base_sys::GstAggregator, gst_base_sys::GstAggregatorClass, AggregatorClass>) @extends gst::Element, gst::Object;
|
||||
|
||||
match fn {
|
||||
get_type => || gst_base_sys::gst_aggregator_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Aggregator {}
|
||||
unsafe impl Sync for Aggregator {}
|
||||
|
||||
pub const NONE_AGGREGATOR: Option<&Aggregator> = None;
|
||||
|
||||
pub trait AggregatorExt: 'static {
|
||||
fn get_buffer_pool(&self) -> Option<gst::BufferPool>;
|
||||
|
||||
fn get_latency(&self) -> gst::ClockTime;
|
||||
|
||||
fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime);
|
||||
|
||||
fn set_src_caps(&self, caps: &gst::Caps);
|
||||
|
||||
fn simple_get_next_time(&self) -> gst::ClockTime;
|
||||
|
||||
fn get_property_start_time(&self) -> u64;
|
||||
|
||||
fn set_property_start_time(&self, start_time: u64);
|
||||
|
||||
fn connect_property_latency_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId;
|
||||
|
||||
fn connect_property_start_time_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId;
|
||||
|
||||
fn negotiate(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<O: IsA<Aggregator>> AggregatorExt for O {
|
||||
//fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams) {
|
||||
// unsafe { TODO: call gst_base_sys:gst_aggregator_get_allocator() }
|
||||
//}
|
||||
|
||||
fn get_buffer_pool(&self) -> Option<gst::BufferPool> {
|
||||
unsafe {
|
||||
from_glib_full(gst_base_sys::gst_aggregator_get_buffer_pool(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_latency(&self) -> gst::ClockTime {
|
||||
unsafe {
|
||||
from_glib(gst_base_sys::gst_aggregator_get_latency(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime) {
|
||||
unsafe {
|
||||
gst_base_sys::gst_aggregator_set_latency(
|
||||
self.as_ref().to_glib_none().0,
|
||||
min_latency.to_glib(),
|
||||
max_latency.to_glib(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_src_caps(&self, caps: &gst::Caps) {
|
||||
unsafe {
|
||||
gst_base_sys::gst_aggregator_set_src_caps(
|
||||
self.as_ref().to_glib_none().0,
|
||||
caps.to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn simple_get_next_time(&self) -> gst::ClockTime {
|
||||
unsafe {
|
||||
from_glib(gst_base_sys::gst_aggregator_simple_get_next_time(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property_start_time(&self) -> u64 {
|
||||
unsafe {
|
||||
let mut value = Value::from_type(<u64 as StaticType>::static_type());
|
||||
gobject_sys::g_object_get_property(
|
||||
self.to_glib_none().0 as *mut gobject_sys::GObject,
|
||||
b"start-time\0".as_ptr() as *const _,
|
||||
value.to_glib_none_mut().0,
|
||||
);
|
||||
value
|
||||
.get()
|
||||
.expect("Return Value for property `start-time` getter")
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property_start_time(&self, start_time: u64) {
|
||||
unsafe {
|
||||
gobject_sys::g_object_set_property(
|
||||
self.to_glib_none().0 as *mut gobject_sys::GObject,
|
||||
b"start-time\0".as_ptr() as *const _,
|
||||
Value::from(&start_time).to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_property_latency_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn notify_latency_trampoline<P, F: Fn(&P) + Send + Sync + 'static>(
|
||||
this: *mut gst_base_sys::GstAggregator,
|
||||
_param_spec: glib_sys::gpointer,
|
||||
f: glib_sys::gpointer,
|
||||
) where
|
||||
P: IsA<Aggregator>,
|
||||
{
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&Aggregator::from_glib_borrow(this).unsafe_cast())
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"notify::latency\0".as_ptr() as *const _,
|
||||
Some(transmute(notify_latency_trampoline::<Self, F> as usize)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_property_start_time_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn notify_start_time_trampoline<P, F: Fn(&P) + Send + Sync + 'static>(
|
||||
this: *mut gst_base_sys::GstAggregator,
|
||||
_param_spec: glib_sys::gpointer,
|
||||
f: glib_sys::gpointer,
|
||||
) where
|
||||
P: IsA<Aggregator>,
|
||||
{
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&Aggregator::from_glib_borrow(this).unsafe_cast())
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"notify::start-time\0".as_ptr() as *const _,
|
||||
Some(transmute(notify_start_time_trampoline::<Self, F> as usize)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn negotiate(&self) -> bool {
|
||||
unsafe {
|
||||
from_glib(gst_base_sys::gst_aggregator_negotiate(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
182
gst-plugin-fallbackswitch/src/base/auto/aggregator_pad.rs
Normal file
182
gst-plugin-fallbackswitch/src/base/auto/aggregator_pad.rs
Normal file
|
@ -0,0 +1,182 @@
|
|||
// This file was generated by gir (https://github.com/gtk-rs/gir)
|
||||
// from gir-files (https://github.com/gtk-rs/gir-files)
|
||||
// DO NOT EDIT
|
||||
|
||||
use super::super::gst_base_sys;
|
||||
use glib::object::Cast;
|
||||
use glib::object::IsA;
|
||||
use glib::signal::connect_raw;
|
||||
use glib::signal::SignalHandlerId;
|
||||
use glib::translate::*;
|
||||
use glib::StaticType;
|
||||
use glib::Value;
|
||||
use glib_sys;
|
||||
use gobject_sys;
|
||||
use gst;
|
||||
use gst_sys;
|
||||
use std::boxed::Box as Box_;
|
||||
use std::mem::transmute;
|
||||
|
||||
glib_wrapper! {
|
||||
pub struct AggregatorPad(Object<gst_base_sys::GstAggregatorPad, gst_base_sys::GstAggregatorPadClass, AggregatorPadClass>) @extends gst::Pad, gst::Object;
|
||||
|
||||
match fn {
|
||||
get_type => || gst_base_sys::gst_aggregator_pad_get_type(),
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for AggregatorPad {}
|
||||
unsafe impl Sync for AggregatorPad {}
|
||||
|
||||
pub const NONE_AGGREGATOR_PAD: Option<&AggregatorPad> = None;
|
||||
|
||||
pub trait AggregatorPadExt: 'static {
|
||||
fn drop_buffer(&self) -> bool;
|
||||
|
||||
fn has_buffer(&self) -> bool;
|
||||
|
||||
fn is_eos(&self) -> bool;
|
||||
|
||||
fn peek_buffer(&self) -> Option<gst::Buffer>;
|
||||
|
||||
fn pop_buffer(&self) -> Option<gst::Buffer>;
|
||||
|
||||
fn get_property_emit_signals(&self) -> bool;
|
||||
|
||||
fn set_property_emit_signals(&self, emit_signals: bool);
|
||||
|
||||
fn connect_buffer_consumed<F: Fn(&Self, &gst::Buffer) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId;
|
||||
|
||||
fn connect_property_emit_signals_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId;
|
||||
}
|
||||
|
||||
impl<O: IsA<AggregatorPad>> AggregatorPadExt for O {
|
||||
fn drop_buffer(&self) -> bool {
|
||||
unsafe {
|
||||
from_glib(gst_base_sys::gst_aggregator_pad_drop_buffer(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn has_buffer(&self) -> bool {
|
||||
unsafe {
|
||||
from_glib(gst_base_sys::gst_aggregator_pad_has_buffer(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn is_eos(&self) -> bool {
|
||||
unsafe {
|
||||
from_glib(gst_base_sys::gst_aggregator_pad_is_eos(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn peek_buffer(&self) -> Option<gst::Buffer> {
|
||||
unsafe {
|
||||
from_glib_full(gst_base_sys::gst_aggregator_pad_peek_buffer(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn pop_buffer(&self) -> Option<gst::Buffer> {
|
||||
unsafe {
|
||||
from_glib_full(gst_base_sys::gst_aggregator_pad_pop_buffer(
|
||||
self.as_ref().to_glib_none().0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property_emit_signals(&self) -> bool {
|
||||
unsafe {
|
||||
let mut value = Value::from_type(<bool as StaticType>::static_type());
|
||||
gobject_sys::g_object_get_property(
|
||||
self.to_glib_none().0 as *mut gobject_sys::GObject,
|
||||
b"emit-signals\0".as_ptr() as *const _,
|
||||
value.to_glib_none_mut().0,
|
||||
);
|
||||
value
|
||||
.get()
|
||||
.expect("Return Value for property `emit-signals` getter")
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn set_property_emit_signals(&self, emit_signals: bool) {
|
||||
unsafe {
|
||||
gobject_sys::g_object_set_property(
|
||||
self.to_glib_none().0 as *mut gobject_sys::GObject,
|
||||
b"emit-signals\0".as_ptr() as *const _,
|
||||
Value::from(&emit_signals).to_glib_none().0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_buffer_consumed<F: Fn(&Self, &gst::Buffer) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn buffer_consumed_trampoline<
|
||||
P,
|
||||
F: Fn(&P, &gst::Buffer) + Send + Sync + 'static,
|
||||
>(
|
||||
this: *mut gst_base_sys::GstAggregatorPad,
|
||||
object: *mut gst_sys::GstBuffer,
|
||||
f: glib_sys::gpointer,
|
||||
) where
|
||||
P: IsA<AggregatorPad>,
|
||||
{
|
||||
let f: &F = &*(f as *const F);
|
||||
f(
|
||||
&AggregatorPad::from_glib_borrow(this).unsafe_cast(),
|
||||
&from_glib_borrow(object),
|
||||
)
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"buffer-consumed\0".as_ptr() as *const _,
|
||||
Some(transmute(buffer_consumed_trampoline::<Self, F> as usize)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn connect_property_emit_signals_notify<F: Fn(&Self) + Send + Sync + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SignalHandlerId {
|
||||
unsafe extern "C" fn notify_emit_signals_trampoline<P, F: Fn(&P) + Send + Sync + 'static>(
|
||||
this: *mut gst_base_sys::GstAggregatorPad,
|
||||
_param_spec: glib_sys::gpointer,
|
||||
f: glib_sys::gpointer,
|
||||
) where
|
||||
P: IsA<AggregatorPad>,
|
||||
{
|
||||
let f: &F = &*(f as *const F);
|
||||
f(&AggregatorPad::from_glib_borrow(this).unsafe_cast())
|
||||
}
|
||||
unsafe {
|
||||
let f: Box_<F> = Box_::new(f);
|
||||
connect_raw(
|
||||
self.as_ptr() as *mut _,
|
||||
b"notify::emit-signals\0".as_ptr() as *const _,
|
||||
Some(transmute(
|
||||
notify_emit_signals_trampoline::<Self, F> as usize,
|
||||
)),
|
||||
Box_::into_raw(f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
13
gst-plugin-fallbackswitch/src/base/auto/mod.rs
Normal file
13
gst-plugin-fallbackswitch/src/base/auto/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
mod aggregator;
|
||||
pub use self::aggregator::AggregatorExt;
|
||||
pub use self::aggregator::{Aggregator, AggregatorClass, NONE_AGGREGATOR};
|
||||
|
||||
mod aggregator_pad;
|
||||
pub use self::aggregator_pad::AggregatorPadExt;
|
||||
pub use self::aggregator_pad::{AggregatorPad, AggregatorPadClass, NONE_AGGREGATOR_PAD};
|
||||
|
||||
#[doc(hidden)]
|
||||
pub mod traits {
|
||||
pub use super::AggregatorExt;
|
||||
pub use super::AggregatorPadExt;
|
||||
}
|
3459
gst-plugin-fallbackswitch/src/base/gstaggregator.c
Normal file
3459
gst-plugin-fallbackswitch/src/base/gstaggregator.c
Normal file
File diff suppressed because it is too large
Load diff
394
gst-plugin-fallbackswitch/src/base/gstaggregator.h
Normal file
394
gst-plugin-fallbackswitch/src/base/gstaggregator.h
Normal file
|
@ -0,0 +1,394 @@
|
|||
/* GStreamer aggregator base class
|
||||
* Copyright (C) 2014 Mathieu Duponchelle <mathieu.duponchelle@oencreed.com>
|
||||
* Copyright (C) 2014 Thibault Saunier <tsaunier@gnome.org>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#ifndef __GST_AGGREGATOR_H__
|
||||
#define __GST_AGGREGATOR_H__
|
||||
|
||||
#include <gst/gst.h>
|
||||
#include <gst/base/base-prelude.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/**************************
|
||||
* GstAggregator Structs *
|
||||
*************************/
|
||||
|
||||
typedef struct _GstAggregator GstAggregator;
|
||||
typedef struct _GstAggregatorPrivate GstAggregatorPrivate;
|
||||
typedef struct _GstAggregatorClass GstAggregatorClass;
|
||||
|
||||
/************************
|
||||
* GstAggregatorPad API *
|
||||
***********************/
|
||||
|
||||
#define GST_TYPE_AGGREGATOR_PAD (gst_aggregator_pad_get_type())
|
||||
#define GST_AGGREGATOR_PAD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AGGREGATOR_PAD, GstAggregatorPad))
|
||||
#define GST_AGGREGATOR_PAD_CAST(obj) ((GstAggregatorPad *)(obj))
|
||||
#define GST_AGGREGATOR_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AGGREGATOR_PAD, GstAggregatorPadClass))
|
||||
#define GST_AGGREGATOR_PAD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AGGREGATOR_PAD, GstAggregatorPadClass))
|
||||
#define GST_IS_AGGREGATOR_PAD(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AGGREGATOR_PAD))
|
||||
#define GST_IS_AGGREGATOR_PAD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AGGREGATOR_PAD))
|
||||
|
||||
/****************************
|
||||
* GstAggregatorPad Structs *
|
||||
***************************/
|
||||
|
||||
typedef struct _GstAggregatorPad GstAggregatorPad;
|
||||
typedef struct _GstAggregatorPadClass GstAggregatorPadClass;
|
||||
typedef struct _GstAggregatorPadPrivate GstAggregatorPadPrivate;
|
||||
|
||||
/**
|
||||
* GstAggregatorPad:
|
||||
* @segment: last segment received.
|
||||
*
|
||||
* The implementation the GstPad to use with #GstAggregator
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct _GstAggregatorPad
|
||||
{
|
||||
GstPad parent;
|
||||
|
||||
/*< public >*/
|
||||
/* Protected by the OBJECT_LOCK */
|
||||
GstSegment segment;
|
||||
|
||||
/* < private > */
|
||||
GstAggregatorPadPrivate * priv;
|
||||
|
||||
gpointer _gst_reserved[GST_PADDING];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstAggregatorPadClass:
|
||||
* @flush: Optional
|
||||
* Called when the pad has received a flush stop, this is the place
|
||||
* to flush any information specific to the pad, it allows for individual
|
||||
* pads to be flushed while others might not be.
|
||||
* @skip_buffer: Optional
|
||||
* Called before input buffers are queued in the pad, return %TRUE
|
||||
* if the buffer should be skipped.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct _GstAggregatorPadClass
|
||||
{
|
||||
GstPadClass parent_class;
|
||||
|
||||
GstFlowReturn (*flush) (GstAggregatorPad * aggpad, GstAggregator * aggregator);
|
||||
gboolean (*skip_buffer) (GstAggregatorPad * aggpad, GstAggregator * aggregator, GstBuffer * buffer);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
GST_BASE_API
|
||||
GType gst_aggregator_pad_get_type (void);
|
||||
|
||||
/****************************
|
||||
* GstAggregatorPad methods *
|
||||
***************************/
|
||||
|
||||
GST_BASE_API
|
||||
GstBuffer * gst_aggregator_pad_pop_buffer (GstAggregatorPad * pad);
|
||||
|
||||
GST_BASE_API
|
||||
GstBuffer * gst_aggregator_pad_peek_buffer (GstAggregatorPad * pad);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_aggregator_pad_drop_buffer (GstAggregatorPad * pad);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_aggregator_pad_has_buffer (GstAggregatorPad * pad);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_aggregator_pad_is_eos (GstAggregatorPad * pad);
|
||||
|
||||
/*********************
|
||||
* GstAggregator API *
|
||||
********************/
|
||||
|
||||
#define GST_TYPE_AGGREGATOR (gst_aggregator_get_type())
|
||||
#define GST_AGGREGATOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_AGGREGATOR,GstAggregator))
|
||||
#define GST_AGGREGATOR_CAST(obj) ((GstAggregator *)(obj))
|
||||
#define GST_AGGREGATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_AGGREGATOR,GstAggregatorClass))
|
||||
#define GST_AGGREGATOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),GST_TYPE_AGGREGATOR,GstAggregatorClass))
|
||||
#define GST_IS_AGGREGATOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_AGGREGATOR))
|
||||
#define GST_IS_AGGREGATOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_AGGREGATOR))
|
||||
|
||||
#define GST_AGGREGATOR_FLOW_NEED_DATA GST_FLOW_CUSTOM_ERROR
|
||||
|
||||
/**
|
||||
* GstAggregator:
|
||||
* @srcpad: the aggregator's source pad
|
||||
*
|
||||
* Aggregator base class object structure.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct _GstAggregator
|
||||
{
|
||||
GstElement parent;
|
||||
|
||||
/*< public >*/
|
||||
GstPad * srcpad;
|
||||
|
||||
/*< private >*/
|
||||
GstAggregatorPrivate * priv;
|
||||
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE];
|
||||
};
|
||||
|
||||
/**
|
||||
* GstAggregatorClass:
|
||||
* @flush: Optional.
|
||||
* Called after a successful flushing seek, once all the flush
|
||||
* stops have been received. Flush pad-specific data in
|
||||
* #GstAggregatorPad->flush.
|
||||
* @clip: Optional.
|
||||
* Called when a buffer is received on a sink pad, the task of
|
||||
* clipping it and translating it to the current segment falls
|
||||
* on the subclass. The function should use the segment of data
|
||||
* and the negotiated media type on the pad to perform
|
||||
* clipping of input buffer. This function takes ownership of
|
||||
* buf and should output a buffer or return NULL in
|
||||
* if the buffer should be dropped.
|
||||
* @finish_buffer: Optional.
|
||||
* Called when a subclass calls gst_aggregator_finish_buffer()
|
||||
* from their aggregate function to push out a buffer.
|
||||
* Subclasses can override this to modify or decorate buffers
|
||||
* before they get pushed out. This function takes ownership
|
||||
* of the buffer passed. Subclasses that override this method
|
||||
* should always chain up to the parent class virtual method.
|
||||
* @sink_event: Optional.
|
||||
* Called when an event is received on a sink pad, the subclass
|
||||
* should always chain up.
|
||||
* @sink_query: Optional.
|
||||
* Called when a query is received on a sink pad, the subclass
|
||||
* should always chain up.
|
||||
* @src_event: Optional.
|
||||
* Called when an event is received on the src pad, the subclass
|
||||
* should always chain up.
|
||||
* @src_query: Optional.
|
||||
* Called when a query is received on the src pad, the subclass
|
||||
* should always chain up.
|
||||
* @src_activate: Optional.
|
||||
* Called when the src pad is activated, it will start/stop its
|
||||
* pad task right after that call.
|
||||
* @aggregate: Mandatory.
|
||||
* Called when buffers are queued on all sinkpads. Classes
|
||||
* should iterate the GstElement->sinkpads and peek or steal
|
||||
* buffers from the #GstAggregatorPads. If the subclass returns
|
||||
* GST_FLOW_EOS, sending of the eos event will be taken care
|
||||
* of. Once / if a buffer has been constructed from the
|
||||
* aggregated buffers, the subclass should call _finish_buffer.
|
||||
* @stop: Optional.
|
||||
* Called when the element goes from PAUSED to READY.
|
||||
* The subclass should free all resources and reset its state.
|
||||
* @start: Optional.
|
||||
* Called when the element goes from READY to PAUSED.
|
||||
* The subclass should get ready to process
|
||||
* aggregated buffers.
|
||||
* @get_next_time: Optional.
|
||||
* Called when the element needs to know the running time of the next
|
||||
* rendered buffer for live pipelines. This causes deadline
|
||||
* based aggregation to occur. Defaults to returning
|
||||
* GST_CLOCK_TIME_NONE causing the element to wait for buffers
|
||||
* on all sink pads before aggregating.
|
||||
* @create_new_pad: Optional.
|
||||
* Called when a new pad needs to be created. Allows subclass that
|
||||
* don't have a single sink pad template to provide a pad based
|
||||
* on the provided information.
|
||||
* @update_src_caps: Lets subclasses update the #GstCaps representing
|
||||
* the src pad caps before usage. The result should end up
|
||||
* in @ret. Return %GST_AGGREGATOR_FLOW_NEED_DATA to indicate that the
|
||||
* element needs more information (caps, a buffer, etc) to
|
||||
* choose the correct caps. Should return ANY caps if the
|
||||
* stream has not caps at all.
|
||||
* @fixate_src_caps: Optional.
|
||||
* Fixate and return the src pad caps provided. The function takes
|
||||
* ownership of @caps and returns a fixated version of
|
||||
* @caps. @caps is not guaranteed to be writable.
|
||||
* @negotiated_src_caps: Optional.
|
||||
* Notifies subclasses what caps format has been negotiated
|
||||
* @decide_allocation: Optional.
|
||||
* Allows the subclass to influence the allocation choices.
|
||||
* Setup the allocation parameters for allocating output
|
||||
* buffers. The passed in query contains the result of the
|
||||
* downstream allocation query.
|
||||
* @propose_allocation: Optional.
|
||||
* Allows the subclass to handle the allocation query from upstream.
|
||||
* @negotiate: Optional.
|
||||
* Negotiate the caps with the peer (Since: 1.18).
|
||||
* @sink_event_pre_queue: Optional.
|
||||
* Called when an event is received on a sink pad before queueing up
|
||||
* serialized events. The subclass should always chain up (Since: 1.18).
|
||||
* @sink_query_pre_queue: Optional.
|
||||
* Called when a query is received on a sink pad before queueing up
|
||||
* serialized queries. The subclass should always chain up (Since: 1.18).
|
||||
*
|
||||
* The aggregator base class will handle in a thread-safe way all manners of
|
||||
* concurrent flushes, seeks, pad additions and removals, leaving to the
|
||||
* subclass the responsibility of clipping buffers, and aggregating buffers in
|
||||
* the way the implementor sees fit.
|
||||
*
|
||||
* It will also take care of event ordering (stream-start, segment, eos).
|
||||
*
|
||||
* Basically, a simple implementation will override @aggregate, and call
|
||||
* _finish_buffer from inside that function.
|
||||
*
|
||||
* Since: 1.14
|
||||
*/
|
||||
struct _GstAggregatorClass {
|
||||
GstElementClass parent_class;
|
||||
|
||||
GstFlowReturn (*flush) (GstAggregator * aggregator);
|
||||
|
||||
GstBuffer * (*clip) (GstAggregator * aggregator,
|
||||
GstAggregatorPad * aggregator_pad,
|
||||
GstBuffer * buf);
|
||||
|
||||
GstFlowReturn (*finish_buffer) (GstAggregator * aggregator,
|
||||
GstBuffer * buffer);
|
||||
|
||||
/* sinkpads virtual methods */
|
||||
gboolean (*sink_event) (GstAggregator * aggregator,
|
||||
GstAggregatorPad * aggregator_pad,
|
||||
GstEvent * event);
|
||||
|
||||
gboolean (*sink_query) (GstAggregator * aggregator,
|
||||
GstAggregatorPad * aggregator_pad,
|
||||
GstQuery * query);
|
||||
|
||||
/* srcpad virtual methods */
|
||||
gboolean (*src_event) (GstAggregator * aggregator,
|
||||
GstEvent * event);
|
||||
|
||||
gboolean (*src_query) (GstAggregator * aggregator,
|
||||
GstQuery * query);
|
||||
|
||||
gboolean (*src_activate) (GstAggregator * aggregator,
|
||||
GstPadMode mode,
|
||||
gboolean active);
|
||||
|
||||
GstFlowReturn (*aggregate) (GstAggregator * aggregator,
|
||||
gboolean timeout);
|
||||
|
||||
gboolean (*stop) (GstAggregator * aggregator);
|
||||
|
||||
gboolean (*start) (GstAggregator * aggregator);
|
||||
|
||||
GstClockTime (*get_next_time) (GstAggregator * aggregator);
|
||||
|
||||
GstAggregatorPad * (*create_new_pad) (GstAggregator * self,
|
||||
GstPadTemplate * templ,
|
||||
const gchar * req_name,
|
||||
const GstCaps * caps);
|
||||
|
||||
/**
|
||||
* GstAggregatorClass::update_src_caps:
|
||||
* @ret: (out) (allow-none):
|
||||
*/
|
||||
GstFlowReturn (*update_src_caps) (GstAggregator * self,
|
||||
GstCaps * caps,
|
||||
GstCaps ** ret);
|
||||
GstCaps * (*fixate_src_caps) (GstAggregator * self,
|
||||
GstCaps * caps);
|
||||
gboolean (*negotiated_src_caps) (GstAggregator * self,
|
||||
GstCaps * caps);
|
||||
gboolean (*decide_allocation) (GstAggregator * self,
|
||||
GstQuery * query);
|
||||
gboolean (*propose_allocation) (GstAggregator * self,
|
||||
GstAggregatorPad * pad,
|
||||
GstQuery * decide_query,
|
||||
GstQuery * query);
|
||||
|
||||
gboolean (*negotiate) (GstAggregator * self);
|
||||
|
||||
gboolean (*sink_event_pre_queue) (GstAggregator * aggregator,
|
||||
GstAggregatorPad * aggregator_pad,
|
||||
GstEvent * event);
|
||||
|
||||
gboolean (*sink_query_pre_queue) (GstAggregator * aggregator,
|
||||
GstAggregatorPad * aggregator_pad,
|
||||
GstQuery * query);
|
||||
|
||||
/*< private >*/
|
||||
gpointer _gst_reserved[GST_PADDING_LARGE-3];
|
||||
};
|
||||
|
||||
/************************************
|
||||
* GstAggregator convenience macros *
|
||||
***********************************/
|
||||
|
||||
/**
|
||||
* GST_AGGREGATOR_SRC_PAD:
|
||||
* @agg: a #GstAggregator
|
||||
*
|
||||
* Convenience macro to access the source pad of #GstAggregator
|
||||
*
|
||||
* Since: 1.6
|
||||
*/
|
||||
#define GST_AGGREGATOR_SRC_PAD(agg) (((GstAggregator *)(agg))->srcpad)
|
||||
|
||||
/*************************
|
||||
* GstAggregator methods *
|
||||
************************/
|
||||
|
||||
GST_BASE_API
|
||||
GstFlowReturn gst_aggregator_finish_buffer (GstAggregator * aggregator,
|
||||
GstBuffer * buffer);
|
||||
|
||||
GST_BASE_API
|
||||
void gst_aggregator_set_src_caps (GstAggregator * self,
|
||||
GstCaps * caps);
|
||||
|
||||
GST_BASE_API
|
||||
gboolean gst_aggregator_negotiate (GstAggregator * self);
|
||||
|
||||
GST_BASE_API
|
||||
void gst_aggregator_set_latency (GstAggregator * self,
|
||||
GstClockTime min_latency,
|
||||
GstClockTime max_latency);
|
||||
|
||||
GST_BASE_API
|
||||
GType gst_aggregator_get_type(void);
|
||||
|
||||
GST_BASE_API
|
||||
GstClockTime gst_aggregator_get_latency (GstAggregator * self);
|
||||
|
||||
GST_BASE_API
|
||||
GstBufferPool * gst_aggregator_get_buffer_pool (GstAggregator * self);
|
||||
|
||||
GST_BASE_API
|
||||
void gst_aggregator_get_allocator (GstAggregator * self,
|
||||
GstAllocator ** allocator,
|
||||
GstAllocationParams * params);
|
||||
|
||||
GST_BASE_API
|
||||
GstClockTime gst_aggregator_simple_get_next_time (GstAggregator * self);
|
||||
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstAggregator, gst_object_unref)
|
||||
G_DEFINE_AUTOPTR_CLEANUP_FUNC(GstAggregatorPad, gst_object_unref)
|
||||
|
||||
#endif /* __GST_AGGREGATOR_H__ */
|
27
gst-plugin-fallbackswitch/src/base/mod.rs
Normal file
27
gst-plugin-fallbackswitch/src/base/mod.rs
Normal file
|
@ -0,0 +1,27 @@
|
|||
#[allow(clippy::unreadable_literal)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::match_same_arms)]
|
||||
#[allow(clippy::type_complexity)]
|
||||
mod auto;
|
||||
pub use auto::*;
|
||||
|
||||
mod utils;
|
||||
|
||||
mod aggregator;
|
||||
mod aggregator_pad;
|
||||
|
||||
pub mod prelude {
|
||||
pub use glib::prelude::*;
|
||||
pub use gst::prelude::*;
|
||||
|
||||
pub use super::aggregator::AggregatorExtManual;
|
||||
pub use super::aggregator_pad::AggregatorPadExtManual;
|
||||
pub use super::auto::traits::*;
|
||||
}
|
||||
|
||||
pub mod subclass;
|
||||
|
||||
mod sys;
|
||||
use sys as gst_base_sys;
|
||||
|
||||
pub const AGGREGATOR_FLOW_NEED_DATA: gst::FlowError = gst::FlowError::CustomError;
|
1059
gst-plugin-fallbackswitch/src/base/subclass/aggregator.rs
Normal file
1059
gst-plugin-fallbackswitch/src/base/subclass/aggregator.rs
Normal file
File diff suppressed because it is too large
Load diff
149
gst-plugin-fallbackswitch/src/base/subclass/aggregator_pad.rs
Normal file
149
gst-plugin-fallbackswitch/src/base/subclass/aggregator_pad.rs
Normal file
|
@ -0,0 +1,149 @@
|
|||
// Copyright (C) 2018 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use super::super::gst_base_sys;
|
||||
use glib_sys;
|
||||
use gst_sys;
|
||||
|
||||
use glib::translate::*;
|
||||
use gst;
|
||||
|
||||
use glib::subclass::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
|
||||
use super::super::Aggregator;
|
||||
use super::super::AggregatorPad;
|
||||
use super::super::AggregatorPadClass;
|
||||
|
||||
pub trait AggregatorPadImpl: AggregatorPadImplExt + PadImpl + Send + Sync + 'static {
|
||||
fn flush(
|
||||
&self,
|
||||
aggregator_pad: &AggregatorPad,
|
||||
aggregator: &Aggregator,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
self.parent_flush(aggregator_pad, aggregator)
|
||||
}
|
||||
|
||||
fn skip_buffer(
|
||||
&self,
|
||||
aggregator_pad: &AggregatorPad,
|
||||
aggregator: &Aggregator,
|
||||
buffer: &gst::Buffer,
|
||||
) -> bool {
|
||||
self.parent_skip_buffer(aggregator_pad, aggregator, buffer)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait AggregatorPadImplExt {
|
||||
fn parent_flush(
|
||||
&self,
|
||||
aggregator_pad: &AggregatorPad,
|
||||
aggregator: &Aggregator,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError>;
|
||||
|
||||
fn parent_skip_buffer(
|
||||
&self,
|
||||
aggregator_pad: &AggregatorPad,
|
||||
aggregator: &Aggregator,
|
||||
buffer: &gst::Buffer,
|
||||
) -> bool;
|
||||
}
|
||||
|
||||
impl<T: AggregatorPadImpl + ObjectImpl> AggregatorPadImplExt for T {
|
||||
fn parent_flush(
|
||||
&self,
|
||||
aggregator_pad: &AggregatorPad,
|
||||
aggregator: &Aggregator,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
unsafe {
|
||||
let data = self.get_type_data();
|
||||
let parent_class =
|
||||
data.as_ref().get_parent_class() as *mut gst_base_sys::GstAggregatorPadClass;
|
||||
(*parent_class)
|
||||
.flush
|
||||
.map(|f| {
|
||||
from_glib(f(
|
||||
aggregator_pad.to_glib_none().0,
|
||||
aggregator.to_glib_none().0,
|
||||
))
|
||||
})
|
||||
.unwrap_or(gst::FlowReturn::Ok)
|
||||
.into_result()
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_skip_buffer(
|
||||
&self,
|
||||
aggregator_pad: &AggregatorPad,
|
||||
aggregator: &Aggregator,
|
||||
buffer: &gst::Buffer,
|
||||
) -> bool {
|
||||
unsafe {
|
||||
let data = self.get_type_data();
|
||||
let parent_class =
|
||||
data.as_ref().get_parent_class() as *mut gst_base_sys::GstAggregatorPadClass;
|
||||
(*parent_class)
|
||||
.skip_buffer
|
||||
.map(|f| {
|
||||
from_glib(f(
|
||||
aggregator_pad.to_glib_none().0,
|
||||
aggregator.to_glib_none().0,
|
||||
buffer.to_glib_none().0,
|
||||
))
|
||||
})
|
||||
.unwrap_or(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe impl<T: ObjectSubclass + AggregatorPadImpl> IsSubclassable<T> for AggregatorPadClass {
|
||||
fn override_vfuncs(&mut self) {
|
||||
<gst::PadClass as IsSubclassable<T>>::override_vfuncs(self);
|
||||
unsafe {
|
||||
let klass = &mut *(self as *mut Self as *mut gst_base_sys::GstAggregatorPadClass);
|
||||
klass.flush = Some(aggregator_pad_flush::<T>);
|
||||
klass.skip_buffer = Some(aggregator_pad_skip_buffer::<T>);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn aggregator_pad_flush<T: ObjectSubclass>(
|
||||
ptr: *mut gst_base_sys::GstAggregatorPad,
|
||||
aggregator: *mut gst_base_sys::GstAggregator,
|
||||
) -> gst_sys::GstFlowReturn
|
||||
where
|
||||
T: AggregatorPadImpl,
|
||||
{
|
||||
glib_floating_reference_guard!(ptr);
|
||||
let instance = &*(ptr as *mut T::Instance);
|
||||
let imp = instance.get_impl();
|
||||
let wrap: AggregatorPad = from_glib_borrow(ptr);
|
||||
|
||||
let res: gst::FlowReturn = imp.flush(&wrap, &from_glib_borrow(aggregator)).into();
|
||||
res.to_glib()
|
||||
}
|
||||
|
||||
unsafe extern "C" fn aggregator_pad_skip_buffer<T: ObjectSubclass>(
|
||||
ptr: *mut gst_base_sys::GstAggregatorPad,
|
||||
aggregator: *mut gst_base_sys::GstAggregator,
|
||||
buffer: *mut gst_sys::GstBuffer,
|
||||
) -> glib_sys::gboolean
|
||||
where
|
||||
T: AggregatorPadImpl,
|
||||
{
|
||||
glib_floating_reference_guard!(ptr);
|
||||
let instance = &*(ptr as *mut T::Instance);
|
||||
let imp = instance.get_impl();
|
||||
let wrap: AggregatorPad = from_glib_borrow(ptr);
|
||||
|
||||
imp.skip_buffer(
|
||||
&wrap,
|
||||
&from_glib_borrow(aggregator),
|
||||
&from_glib_borrow(buffer),
|
||||
)
|
||||
.to_glib()
|
||||
}
|
17
gst-plugin-fallbackswitch/src/base/subclass/mod.rs
Normal file
17
gst-plugin-fallbackswitch/src/base/subclass/mod.rs
Normal file
|
@ -0,0 +1,17 @@
|
|||
// Copyright (C) 2016-2018 Sebastian Dröge <sebastian@centricular.com>
|
||||
// 2016 Luis de Bethencourt <luisbg@osg.samsung.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
#![allow(clippy::cast_ptr_alignment)]
|
||||
|
||||
pub mod aggregator;
|
||||
pub mod aggregator_pad;
|
||||
|
||||
pub mod prelude {
|
||||
pub use super::aggregator::{AggregatorImpl, AggregatorImplExt};
|
||||
pub use super::aggregator_pad::{AggregatorPadImpl, AggregatorPadImplExt};
|
||||
}
|
235
gst-plugin-fallbackswitch/src/base/sys.rs
Normal file
235
gst-plugin-fallbackswitch/src/base/sys.rs
Normal file
|
@ -0,0 +1,235 @@
|
|||
#![allow(non_camel_case_types, non_upper_case_globals, non_snake_case)]
|
||||
#![allow(
|
||||
clippy::approx_constant,
|
||||
clippy::type_complexity,
|
||||
clippy::unreadable_literal
|
||||
)]
|
||||
|
||||
extern crate gstreamer_sys as gst;
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use libc::{
|
||||
c_char, c_double, c_float, c_int, c_long, c_short, c_uchar, c_uint, c_ulong, c_ushort, c_void,
|
||||
intptr_t, size_t, ssize_t, time_t, uintptr_t, FILE,
|
||||
};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use glib_sys::{gboolean, gconstpointer, gpointer, GType};
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct GstAggregatorClass {
|
||||
pub parent_class: gst::GstElementClass,
|
||||
pub flush: Option<unsafe extern "C" fn(*mut GstAggregator) -> gst::GstFlowReturn>,
|
||||
pub clip: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut GstAggregatorPad,
|
||||
*mut gst::GstBuffer,
|
||||
) -> *mut gst::GstBuffer,
|
||||
>,
|
||||
pub finish_buffer:
|
||||
Option<unsafe extern "C" fn(*mut GstAggregator, *mut gst::GstBuffer) -> gst::GstFlowReturn>,
|
||||
pub sink_event: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut GstAggregatorPad,
|
||||
*mut gst::GstEvent,
|
||||
) -> gboolean,
|
||||
>,
|
||||
pub sink_query: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut GstAggregatorPad,
|
||||
*mut gst::GstQuery,
|
||||
) -> gboolean,
|
||||
>,
|
||||
pub src_event: Option<unsafe extern "C" fn(*mut GstAggregator, *mut gst::GstEvent) -> gboolean>,
|
||||
pub src_query: Option<unsafe extern "C" fn(*mut GstAggregator, *mut gst::GstQuery) -> gboolean>,
|
||||
pub src_activate:
|
||||
Option<unsafe extern "C" fn(*mut GstAggregator, gst::GstPadMode, gboolean) -> gboolean>,
|
||||
pub aggregate: Option<unsafe extern "C" fn(*mut GstAggregator, gboolean) -> gst::GstFlowReturn>,
|
||||
pub stop: Option<unsafe extern "C" fn(*mut GstAggregator) -> gboolean>,
|
||||
pub start: Option<unsafe extern "C" fn(*mut GstAggregator) -> gboolean>,
|
||||
pub get_next_time: Option<unsafe extern "C" fn(*mut GstAggregator) -> gst::GstClockTime>,
|
||||
pub create_new_pad: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut gst::GstPadTemplate,
|
||||
*const c_char,
|
||||
*const gst::GstCaps,
|
||||
) -> *mut GstAggregatorPad,
|
||||
>,
|
||||
pub update_src_caps: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut gst::GstCaps,
|
||||
*mut *mut gst::GstCaps,
|
||||
) -> gst::GstFlowReturn,
|
||||
>,
|
||||
pub fixate_src_caps:
|
||||
Option<unsafe extern "C" fn(*mut GstAggregator, *mut gst::GstCaps) -> *mut gst::GstCaps>,
|
||||
pub negotiated_src_caps:
|
||||
Option<unsafe extern "C" fn(*mut GstAggregator, *mut gst::GstCaps) -> gboolean>,
|
||||
pub decide_allocation:
|
||||
Option<unsafe extern "C" fn(*mut GstAggregator, *mut gst::GstQuery) -> gboolean>,
|
||||
pub propose_allocation: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut GstAggregatorPad,
|
||||
*mut gst::GstQuery,
|
||||
*mut gst::GstQuery,
|
||||
) -> gboolean,
|
||||
>,
|
||||
pub negotiate: Option<unsafe extern "C" fn(*mut GstAggregator) -> gboolean>,
|
||||
pub sink_event_pre_queue: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut GstAggregatorPad,
|
||||
*mut gst::GstEvent,
|
||||
) -> gboolean,
|
||||
>,
|
||||
pub sink_query_pre_queue: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregator,
|
||||
*mut GstAggregatorPad,
|
||||
*mut gst::GstQuery,
|
||||
) -> gboolean,
|
||||
>,
|
||||
pub _gst_reserved: [gpointer; 17],
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for GstAggregatorClass {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(&format!("GstAggregatorClass @ {:?}", self as *const _))
|
||||
.field("parent_class", &self.parent_class)
|
||||
.field("flush", &self.flush)
|
||||
.field("clip", &self.clip)
|
||||
.field("finish_buffer", &self.finish_buffer)
|
||||
.field("sink_event", &self.sink_event)
|
||||
.field("sink_query", &self.sink_query)
|
||||
.field("src_event", &self.src_event)
|
||||
.field("src_query", &self.src_query)
|
||||
.field("src_activate", &self.src_activate)
|
||||
.field("aggregate", &self.aggregate)
|
||||
.field("stop", &self.stop)
|
||||
.field("start", &self.start)
|
||||
.field("get_next_time", &self.get_next_time)
|
||||
.field("create_new_pad", &self.create_new_pad)
|
||||
.field("update_src_caps", &self.update_src_caps)
|
||||
.field("fixate_src_caps", &self.fixate_src_caps)
|
||||
.field("negotiated_src_caps", &self.negotiated_src_caps)
|
||||
.field("decide_allocation", &self.decide_allocation)
|
||||
.field("propose_allocation", &self.propose_allocation)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct GstAggregatorPadClass {
|
||||
pub parent_class: gst::GstPadClass,
|
||||
pub flush: Option<
|
||||
unsafe extern "C" fn(*mut GstAggregatorPad, *mut GstAggregator) -> gst::GstFlowReturn,
|
||||
>,
|
||||
pub skip_buffer: Option<
|
||||
unsafe extern "C" fn(
|
||||
*mut GstAggregatorPad,
|
||||
*mut GstAggregator,
|
||||
*mut gst::GstBuffer,
|
||||
) -> gboolean,
|
||||
>,
|
||||
pub _gst_reserved: [gpointer; 20],
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for GstAggregatorPadClass {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(&format!("GstAggregatorPadClass @ {:?}", self as *const _))
|
||||
.field("parent_class", &self.parent_class)
|
||||
.field("flush", &self.flush)
|
||||
.field("skip_buffer", &self.skip_buffer)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct _GstAggregatorPadPrivate(c_void);
|
||||
|
||||
pub type GstAggregatorPadPrivate = *mut _GstAggregatorPadPrivate;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct _GstAggregatorPrivate(c_void);
|
||||
|
||||
pub type GstAggregatorPrivate = *mut _GstAggregatorPrivate;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct GstAggregator {
|
||||
pub parent: gst::GstElement,
|
||||
pub srcpad: *mut gst::GstPad,
|
||||
pub priv_: *mut GstAggregatorPrivate,
|
||||
pub _gst_reserved: [gpointer; 20],
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for GstAggregator {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(&format!("GstAggregator @ {:?}", self as *const _))
|
||||
.field("parent", &self.parent)
|
||||
.field("srcpad", &self.srcpad)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct GstAggregatorPad {
|
||||
pub parent: gst::GstPad,
|
||||
pub segment: gst::GstSegment,
|
||||
pub priv_: *mut GstAggregatorPadPrivate,
|
||||
pub _gst_reserved: [gpointer; 4],
|
||||
}
|
||||
|
||||
impl ::std::fmt::Debug for GstAggregatorPad {
|
||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
||||
f.debug_struct(&format!("GstAggregatorPad @ {:?}", self as *const _))
|
||||
.field("parent", &self.parent)
|
||||
.field("segment", &self.segment)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
//=========================================================================
|
||||
// GstAggregator
|
||||
//=========================================================================
|
||||
pub fn gst_aggregator_get_type() -> GType;
|
||||
pub fn gst_aggregator_finish_buffer(
|
||||
aggregator: *mut GstAggregator,
|
||||
buffer: *mut gst::GstBuffer,
|
||||
) -> gst::GstFlowReturn;
|
||||
pub fn gst_aggregator_negotiate(aggregator: *mut GstAggregator) -> gboolean;
|
||||
pub fn gst_aggregator_get_allocator(
|
||||
self_: *mut GstAggregator,
|
||||
allocator: *mut *mut gst::GstAllocator,
|
||||
params: *mut gst::GstAllocationParams,
|
||||
);
|
||||
pub fn gst_aggregator_get_buffer_pool(self_: *mut GstAggregator) -> *mut gst::GstBufferPool;
|
||||
pub fn gst_aggregator_get_latency(self_: *mut GstAggregator) -> gst::GstClockTime;
|
||||
pub fn gst_aggregator_set_latency(
|
||||
self_: *mut GstAggregator,
|
||||
min_latency: gst::GstClockTime,
|
||||
max_latency: gst::GstClockTime,
|
||||
);
|
||||
pub fn gst_aggregator_set_src_caps(self_: *mut GstAggregator, caps: *mut gst::GstCaps);
|
||||
pub fn gst_aggregator_simple_get_next_time(self_: *mut GstAggregator) -> gst::GstClockTime;
|
||||
|
||||
//=========================================================================
|
||||
// GstAggregatorPad
|
||||
//=========================================================================
|
||||
pub fn gst_aggregator_pad_get_type() -> GType;
|
||||
pub fn gst_aggregator_pad_drop_buffer(pad: *mut GstAggregatorPad) -> gboolean;
|
||||
pub fn gst_aggregator_pad_has_buffer(pad: *mut GstAggregatorPad) -> gboolean;
|
||||
pub fn gst_aggregator_pad_is_eos(pad: *mut GstAggregatorPad) -> gboolean;
|
||||
pub fn gst_aggregator_pad_peek_buffer(pad: *mut GstAggregatorPad) -> *mut gst::GstBuffer;
|
||||
pub fn gst_aggregator_pad_pop_buffer(pad: *mut GstAggregatorPad) -> *mut gst::GstBuffer;
|
||||
}
|
30
gst-plugin-fallbackswitch/src/base/utils.rs
Normal file
30
gst-plugin-fallbackswitch/src/base/utils.rs
Normal file
|
@ -0,0 +1,30 @@
|
|||
// Copyright (C) 2017 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use glib::translate::mut_override;
|
||||
use glib_sys;
|
||||
|
||||
pub struct MutexGuard<'a>(&'a glib_sys::GMutex);
|
||||
|
||||
impl<'a> MutexGuard<'a> {
|
||||
#[allow(clippy::trivially_copy_pass_by_ref)]
|
||||
pub fn lock(mutex: &'a glib_sys::GMutex) -> Self {
|
||||
unsafe {
|
||||
glib_sys::g_mutex_lock(mut_override(mutex));
|
||||
}
|
||||
MutexGuard(mutex)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for MutexGuard<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
glib_sys::g_mutex_unlock(mut_override(self.0));
|
||||
}
|
||||
}
|
||||
}
|
759
gst-plugin-fallbackswitch/src/fallbackswitch.rs
Normal file
759
gst-plugin-fallbackswitch/src/fallbackswitch.rs
Normal file
|
@ -0,0 +1,759 @@
|
|||
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the
|
||||
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||
// Boston, MA 02110-1335, USA.
|
||||
|
||||
#[cfg(not(feature = "v1_18"))]
|
||||
use super::base as gst_base;
|
||||
use glib;
|
||||
use glib::prelude::*;
|
||||
use glib::subclass;
|
||||
use glib::subclass::prelude::*;
|
||||
use gst;
|
||||
use gst::prelude::*;
|
||||
use gst::subclass::prelude::*;
|
||||
use gst_audio;
|
||||
#[cfg(feature = "v1_18")]
|
||||
use gst_base;
|
||||
use gst_base::prelude::*;
|
||||
use gst_base::subclass::prelude::*;
|
||||
use gst_video;
|
||||
|
||||
use std::sync::{Mutex, RwLock};
|
||||
|
||||
struct FallbackSwitch {
|
||||
cat: gst::DebugCategory,
|
||||
sinkpad: gst_base::AggregatorPad,
|
||||
fallback_sinkpad: RwLock<Option<gst_base::AggregatorPad>>,
|
||||
active_sinkpad: Mutex<gst::Pad>,
|
||||
output_state: Mutex<OutputState>,
|
||||
pad_states: RwLock<PadStates>,
|
||||
settings: Mutex<Settings>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct OutputState {
|
||||
last_sinkpad_time: gst::ClockTime,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PadStates {
|
||||
sinkpad: PadState,
|
||||
fallback_sinkpad: Option<PadState>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct PadState {
|
||||
caps: Option<gst::Caps>,
|
||||
audio_info: Option<gst_audio::AudioInfo>,
|
||||
video_info: Option<gst_video::VideoInfo>,
|
||||
}
|
||||
|
||||
const DEFAULT_TIMEOUT: u64 = 5 * gst::SECOND_VAL;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct Settings {
|
||||
timeout: gst::ClockTime,
|
||||
}
|
||||
|
||||
impl Default for OutputState {
|
||||
fn default() -> Self {
|
||||
OutputState {
|
||||
last_sinkpad_time: gst::CLOCK_TIME_NONE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Settings {
|
||||
fn default() -> Self {
|
||||
Settings {
|
||||
timeout: DEFAULT_TIMEOUT.into(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static PROPERTIES: [subclass::Property; 2] = [
|
||||
subclass::Property("timeout", |name| {
|
||||
glib::ParamSpec::uint64(
|
||||
name,
|
||||
"Timeout",
|
||||
"Timeout in nanoseconds",
|
||||
0,
|
||||
std::u64::MAX,
|
||||
DEFAULT_TIMEOUT,
|
||||
glib::ParamFlags::READWRITE,
|
||||
)
|
||||
}),
|
||||
subclass::Property("active-pad", |name| {
|
||||
glib::ParamSpec::object(
|
||||
name,
|
||||
"Active Pad",
|
||||
"Currently active pad",
|
||||
gst::Pad::static_type(),
|
||||
glib::ParamFlags::READABLE,
|
||||
)
|
||||
}),
|
||||
];
|
||||
|
||||
impl FallbackSwitch {
|
||||
fn handle_main_buffer(
|
||||
&self,
|
||||
agg: &gst_base::Aggregator,
|
||||
state: &mut OutputState,
|
||||
buffer: gst::Buffer,
|
||||
fallback_sinkpad: Option<&gst_base::AggregatorPad>,
|
||||
) -> Result<(gst::Buffer, gst::Caps, bool), gst::FlowError> {
|
||||
// If we got a buffer on the sinkpad just handle it
|
||||
gst_debug!(self.cat, obj: agg, "Got buffer on sinkpad {:?}", buffer);
|
||||
|
||||
if buffer.get_pts().is_none() {
|
||||
gst_error!(self.cat, obj: agg, "Only buffers with PTS supported");
|
||||
return Err(gst::FlowError::Error);
|
||||
}
|
||||
|
||||
let segment = match self.sinkpad.get_segment().downcast::<gst::ClockTime>() {
|
||||
Ok(segment) => segment,
|
||||
Err(_) => {
|
||||
gst_error!(self.cat, obj: agg, "Only TIME segments supported");
|
||||
return Err(gst::FlowError::Error);
|
||||
}
|
||||
};
|
||||
|
||||
let mut active_sinkpad = self.active_sinkpad.lock().unwrap();
|
||||
let pad_change = &*active_sinkpad != self.sinkpad.upcast_ref::<gst::Pad>();
|
||||
if pad_change {
|
||||
gst_info!(self.cat, obj: agg, "Active pad changed to sinkpad");
|
||||
*active_sinkpad = self.sinkpad.clone().upcast();
|
||||
}
|
||||
drop(active_sinkpad);
|
||||
|
||||
state.last_sinkpad_time = segment.to_running_time(buffer.get_pts());
|
||||
|
||||
// Drop all older buffers from the fallback sinkpad
|
||||
if let Some(fallback_sinkpad) = fallback_sinkpad {
|
||||
let fallback_segment = match self.sinkpad.get_segment().downcast::<gst::ClockTime>() {
|
||||
Ok(segment) => segment,
|
||||
Err(_) => {
|
||||
gst_error!(self.cat, obj: agg, "Only TIME segments supported");
|
||||
return Err(gst::FlowError::Error);
|
||||
}
|
||||
};
|
||||
|
||||
while let Some(fallback_buffer) = fallback_sinkpad.peek_buffer() {
|
||||
let fallback_pts = fallback_buffer.get_pts();
|
||||
if fallback_pts.is_none()
|
||||
|| fallback_segment.to_running_time(fallback_pts) <= state.last_sinkpad_time
|
||||
{
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Dropping fallback buffer {:?}",
|
||||
fallback_buffer
|
||||
);
|
||||
fallback_sinkpad.drop_buffer();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let pad_states = self.pad_states.read().unwrap();
|
||||
let active_caps = pad_states.sinkpad.caps.as_ref().unwrap().clone();
|
||||
drop(pad_states);
|
||||
|
||||
Ok((buffer, active_caps, pad_change))
|
||||
}
|
||||
|
||||
fn get_fallback_buffer(
|
||||
&self,
|
||||
agg: &gst_base::Aggregator,
|
||||
state: &mut OutputState,
|
||||
settings: &Settings,
|
||||
fallback_sinkpad: &gst_base::AggregatorPad,
|
||||
) -> Result<(gst::Buffer, gst::Caps, bool), gst::FlowError> {
|
||||
// If we have a fallback sinkpad and timeout, try to get a fallback buffer from here
|
||||
// and drop all too old buffers in the process
|
||||
loop {
|
||||
let buffer = if let Some(buffer) = fallback_sinkpad.pop_buffer() {
|
||||
buffer
|
||||
} else {
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Got no buffer on sinkpad and fallback sinkpad"
|
||||
);
|
||||
return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA);
|
||||
};
|
||||
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Got buffer on fallback sinkpad {:?}",
|
||||
buffer
|
||||
);
|
||||
|
||||
if buffer.get_pts().is_none() {
|
||||
gst_error!(self.cat, obj: agg, "Only buffers with PTS supported");
|
||||
return Err(gst::FlowError::Error);
|
||||
}
|
||||
|
||||
let fallback_segment = match fallback_sinkpad.get_segment().downcast::<gst::ClockTime>()
|
||||
{
|
||||
Ok(segment) => segment,
|
||||
Err(_) => {
|
||||
gst_error!(self.cat, obj: agg, "Only TIME segments supported");
|
||||
return Err(gst::FlowError::Error);
|
||||
}
|
||||
};
|
||||
let running_time = fallback_segment.to_running_time(buffer.get_pts());
|
||||
|
||||
// If we never had a real buffer, initialize with the running time of the fallback
|
||||
// sinkpad so that we still output fallback buffers after the timeout
|
||||
if state.last_sinkpad_time.is_none() {
|
||||
state.last_sinkpad_time = running_time;
|
||||
}
|
||||
|
||||
// Get the next one if this one is before the timeout
|
||||
if state.last_sinkpad_time + settings.timeout > running_time {
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Timeout not reached yet: {} + {} > {}",
|
||||
state.last_sinkpad_time,
|
||||
settings.timeout,
|
||||
running_time
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Timeout reached: {} + {} <= {}",
|
||||
state.last_sinkpad_time,
|
||||
settings.timeout,
|
||||
running_time
|
||||
);
|
||||
|
||||
let mut active_sinkpad = self.active_sinkpad.lock().unwrap();
|
||||
let pad_change = &*active_sinkpad != fallback_sinkpad.upcast_ref::<gst::Pad>();
|
||||
if pad_change {
|
||||
gst_info!(self.cat, obj: agg, "Active pad changed to fallback sinkpad");
|
||||
*active_sinkpad = fallback_sinkpad.clone().upcast();
|
||||
}
|
||||
drop(active_sinkpad);
|
||||
|
||||
let pad_states = self.pad_states.read().unwrap();
|
||||
let active_caps = match pad_states.fallback_sinkpad {
|
||||
None => {
|
||||
// This can happen if the pad is removed in the meantime,
|
||||
// not a problem really
|
||||
return Err(gst_base::AGGREGATOR_FLOW_NEED_DATA);
|
||||
}
|
||||
Some(ref fallback_sinkpad) => fallback_sinkpad.caps.as_ref().unwrap().clone(),
|
||||
};
|
||||
drop(pad_states);
|
||||
|
||||
break Ok((buffer, active_caps, pad_change));
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_buffer(
|
||||
&self,
|
||||
agg: &gst_base::Aggregator,
|
||||
timeout: bool,
|
||||
) -> Result<(gst::Buffer, gst::Caps, bool), gst::FlowError> {
|
||||
let settings = self.settings.lock().unwrap().clone();
|
||||
let mut state = self.output_state.lock().unwrap();
|
||||
let fallback_sinkpad = self.fallback_sinkpad.read().unwrap();
|
||||
|
||||
gst_debug!(self.cat, obj: agg, "Aggregate called: timeout {}", timeout);
|
||||
|
||||
if let Some(buffer) = self.sinkpad.pop_buffer() {
|
||||
self.handle_main_buffer(agg, &mut *state, buffer, fallback_sinkpad.as_ref())
|
||||
} else if self.sinkpad.is_eos() {
|
||||
gst_log!(self.cat, obj: agg, "Sinkpad is EOS");
|
||||
Err(gst::FlowError::Eos)
|
||||
} else if let (false, Some(_)) = (timeout, &*fallback_sinkpad) {
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Have fallback sinkpad but no timeout yet"
|
||||
);
|
||||
|
||||
Err(gst_base::AGGREGATOR_FLOW_NEED_DATA)
|
||||
} else if let (true, Some(fallback_sinkpad)) = (timeout, &*fallback_sinkpad) {
|
||||
self.get_fallback_buffer(agg, &mut *state, &settings, fallback_sinkpad)
|
||||
} else {
|
||||
// Otherwise there's not much we can do at this point
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Got no buffer on sinkpad and have no fallback sinkpad"
|
||||
);
|
||||
Err(gst_base::AGGREGATOR_FLOW_NEED_DATA)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectSubclass for FallbackSwitch {
|
||||
const NAME: &'static str = "FallbackSwitch";
|
||||
type ParentType = gst_base::Aggregator;
|
||||
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
||||
type Class = subclass::simple::ClassStruct<Self>;
|
||||
|
||||
glib_object_subclass!();
|
||||
|
||||
fn new_with_class(klass: &subclass::simple::ClassStruct<Self>) -> Self {
|
||||
let templ = klass.get_pad_template("sink").unwrap();
|
||||
let sinkpad: gst_base::AggregatorPad = glib::Object::new(
|
||||
gst_base::AggregatorPad::static_type(),
|
||||
&[
|
||||
("name", &"sink"),
|
||||
("direction", &gst::PadDirection::Sink),
|
||||
("template", &templ),
|
||||
],
|
||||
)
|
||||
.unwrap()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
|
||||
Self {
|
||||
cat: gst::DebugCategory::new(
|
||||
"fallbackswitch",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Fallback switch Element"),
|
||||
),
|
||||
sinkpad: sinkpad.clone(),
|
||||
fallback_sinkpad: RwLock::new(None),
|
||||
active_sinkpad: Mutex::new(sinkpad.upcast()),
|
||||
output_state: Mutex::new(OutputState::default()),
|
||||
pad_states: RwLock::new(PadStates::default()),
|
||||
settings: Mutex::new(Settings::default()),
|
||||
}
|
||||
}
|
||||
|
||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||
klass.set_metadata(
|
||||
"Fallback Switch",
|
||||
"Generic",
|
||||
"Allows switching to a fallback input after a given timeout",
|
||||
"Sebastian Dröge <sebastian@centricular.com>",
|
||||
);
|
||||
|
||||
let caps = {
|
||||
let mut caps = gst::Caps::new_empty();
|
||||
{
|
||||
let caps = caps.get_mut().unwrap();
|
||||
caps.append_structure(gst::Structure::new_empty("video/x-raw"));
|
||||
caps.append_structure(gst::Structure::new_empty("audio/x-raw"));
|
||||
}
|
||||
caps
|
||||
};
|
||||
let src_pad_template = gst::PadTemplate::new_with_gtype(
|
||||
"src",
|
||||
gst::PadDirection::Src,
|
||||
gst::PadPresence::Always,
|
||||
&caps,
|
||||
gst_base::AggregatorPad::static_type(),
|
||||
)
|
||||
.unwrap();
|
||||
klass.add_pad_template(src_pad_template);
|
||||
|
||||
let sink_pad_template = gst::PadTemplate::new_with_gtype(
|
||||
"sink",
|
||||
gst::PadDirection::Sink,
|
||||
gst::PadPresence::Always,
|
||||
&caps,
|
||||
gst_base::AggregatorPad::static_type(),
|
||||
)
|
||||
.unwrap();
|
||||
klass.add_pad_template(sink_pad_template);
|
||||
|
||||
let fallbacksink_pad_template = gst::PadTemplate::new_with_gtype(
|
||||
"fallback_sink",
|
||||
gst::PadDirection::Sink,
|
||||
gst::PadPresence::Request,
|
||||
&caps,
|
||||
gst_base::AggregatorPad::static_type(),
|
||||
)
|
||||
.unwrap();
|
||||
klass.add_pad_template(fallbacksink_pad_template);
|
||||
|
||||
klass.install_properties(&PROPERTIES);
|
||||
}
|
||||
}
|
||||
|
||||
impl ObjectImpl for FallbackSwitch {
|
||||
glib_object_impl!();
|
||||
|
||||
fn constructed(&self, obj: &glib::Object) {
|
||||
self.parent_constructed(obj);
|
||||
|
||||
let agg = obj.downcast_ref::<gst_base::Aggregator>().unwrap();
|
||||
agg.add_pad(&self.sinkpad).unwrap();
|
||||
}
|
||||
|
||||
fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
|
||||
let prop = &PROPERTIES[id];
|
||||
let agg = obj.downcast_ref::<gst_base::Aggregator>().unwrap();
|
||||
|
||||
match *prop {
|
||||
subclass::Property("timeout", ..) => {
|
||||
let mut settings = self.settings.lock().unwrap();
|
||||
let timeout = value.get_some().expect("type checked upstream");
|
||||
gst_info!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Changing timeout from {} to {}",
|
||||
settings.timeout,
|
||||
timeout
|
||||
);
|
||||
settings.timeout = timeout;
|
||||
drop(settings);
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
||||
let prop = &PROPERTIES[id];
|
||||
|
||||
match *prop {
|
||||
subclass::Property("timeout", ..) => {
|
||||
let settings = self.settings.lock().unwrap();
|
||||
Ok(settings.timeout.to_value())
|
||||
}
|
||||
subclass::Property("active-pad", ..) => {
|
||||
let active_pad = self.active_sinkpad.lock().unwrap().clone();
|
||||
Ok(active_pad.to_value())
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ElementImpl for FallbackSwitch {
|
||||
fn request_new_pad(
|
||||
&self,
|
||||
element: &gst::Element,
|
||||
templ: &gst::PadTemplate,
|
||||
name: Option<String>,
|
||||
_caps: Option<&gst::Caps>,
|
||||
) -> Option<gst::Pad> {
|
||||
let agg = element.downcast_ref::<gst_base::Aggregator>().unwrap();
|
||||
let fallback_sink_templ = agg.get_pad_template("fallback_sink").unwrap();
|
||||
if templ != &fallback_sink_templ
|
||||
|| (name.is_some() && name.as_ref().map(String::as_str) != Some("fallback_sink"))
|
||||
{
|
||||
gst_error!(self.cat, obj: agg, "Wrong pad template or name");
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut fallback_sinkpad = self.fallback_sinkpad.write().unwrap();
|
||||
if fallback_sinkpad.is_some() {
|
||||
gst_error!(self.cat, obj: agg, "Already have a fallback sinkpad");
|
||||
return None;
|
||||
}
|
||||
|
||||
let sinkpad: gst_base::AggregatorPad = glib::Object::new(
|
||||
gst_base::AggregatorPad::static_type(),
|
||||
&[
|
||||
("name", &"fallback_sink"),
|
||||
("direction", &gst::PadDirection::Sink),
|
||||
("template", templ),
|
||||
],
|
||||
)
|
||||
.unwrap()
|
||||
.downcast()
|
||||
.unwrap();
|
||||
*fallback_sinkpad = Some(sinkpad.clone());
|
||||
drop(fallback_sinkpad);
|
||||
|
||||
agg.add_pad(&sinkpad).unwrap();
|
||||
|
||||
Some(sinkpad.upcast())
|
||||
}
|
||||
|
||||
fn release_pad(&self, element: &gst::Element, pad: &gst::Pad) {
|
||||
let agg = element.downcast_ref::<gst_base::Aggregator>().unwrap();
|
||||
let mut fallback_sinkpad = self.fallback_sinkpad.write().unwrap();
|
||||
let mut pad_states = self.pad_states.write().unwrap();
|
||||
|
||||
if fallback_sinkpad.as_ref().map(|p| p.upcast_ref()) == Some(pad) {
|
||||
*fallback_sinkpad = None;
|
||||
pad_states.fallback_sinkpad = None;
|
||||
drop(pad_states);
|
||||
drop(fallback_sinkpad);
|
||||
agg.remove_pad(pad).unwrap();
|
||||
gst_debug!(self.cat, obj: agg, "Removed fallback sinkpad {:?}", pad);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AggregatorImpl for FallbackSwitch {
|
||||
fn start(&self, _agg: &gst_base::Aggregator) -> Result<(), gst::ErrorMessage> {
|
||||
*self.active_sinkpad.lock().unwrap() = self.sinkpad.clone().upcast();
|
||||
*self.output_state.lock().unwrap() = OutputState::default();
|
||||
*self.pad_states.write().unwrap() = PadStates::default();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn sink_event_pre_queue(
|
||||
&self,
|
||||
agg: &gst_base::Aggregator,
|
||||
agg_pad: &gst_base::AggregatorPad,
|
||||
event: gst::Event,
|
||||
) -> bool {
|
||||
use gst::EventView;
|
||||
|
||||
match event.view() {
|
||||
EventView::Gap(_) => {
|
||||
gst_debug!(self.cat, obj: agg_pad, "Dropping gap event");
|
||||
true
|
||||
}
|
||||
_ => self.parent_sink_event_pre_queue(agg, agg_pad, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn sink_event(
|
||||
&self,
|
||||
agg: &gst_base::Aggregator,
|
||||
agg_pad: &gst_base::AggregatorPad,
|
||||
event: gst::Event,
|
||||
) -> bool {
|
||||
use gst::EventView;
|
||||
|
||||
match event.view() {
|
||||
EventView::Caps(caps) => {
|
||||
let caps = caps.get_caps_owned();
|
||||
gst_debug!(self.cat, obj: agg_pad, "Received caps {}", caps);
|
||||
|
||||
let audio_info;
|
||||
let video_info;
|
||||
if caps.get_structure(0).unwrap().get_name() == "audio/x-raw" {
|
||||
audio_info = gst_audio::AudioInfo::from_caps(&caps);
|
||||
video_info = None;
|
||||
} else if caps.get_structure(0).unwrap().get_name() == "video/x-raw" {
|
||||
audio_info = None;
|
||||
video_info = gst_video::VideoInfo::from_caps(&caps);
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
let new_pad_state = PadState {
|
||||
caps: Some(caps),
|
||||
audio_info,
|
||||
video_info,
|
||||
};
|
||||
|
||||
let mut pad_states = self.pad_states.write().unwrap();
|
||||
if agg_pad == &self.sinkpad {
|
||||
pad_states.sinkpad = new_pad_state;
|
||||
} else if Some(agg_pad) == self.fallback_sinkpad.read().unwrap().as_ref() {
|
||||
pad_states.fallback_sinkpad = Some(new_pad_state);
|
||||
}
|
||||
drop(pad_states);
|
||||
|
||||
self.parent_sink_event(agg, agg_pad, event)
|
||||
}
|
||||
_ => self.parent_sink_event(agg, agg_pad, event),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_next_time(&self, agg: &gst_base::Aggregator) -> gst::ClockTime {
|
||||
// If we have a buffer on the sinkpad then the timeout is always going to be immediately,
|
||||
// i.e. 0. We want to output that buffer immediately, no matter what.
|
||||
//
|
||||
// Otherwise if we have a fallback sinkpad and it has a buffer, then the timeout is going
|
||||
// to be its running time. We will then either output the buffer or drop it, depending on
|
||||
// its distance from the last sinkpad time
|
||||
if let Some(_) = self.sinkpad.peek_buffer() {
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Have buffer on sinkpad, immediate timeout"
|
||||
);
|
||||
0.into()
|
||||
} else if self.sinkpad.is_eos() {
|
||||
gst_debug!(self.cat, obj: agg, "Sinkpad is EOS, immediate timeout");
|
||||
0.into()
|
||||
} else if let Some((buffer, fallback_sinkpad)) = self
|
||||
.fallback_sinkpad
|
||||
.read()
|
||||
.unwrap()
|
||||
.as_ref()
|
||||
.and_then(|p| p.peek_buffer().map(|buffer| (buffer, p)))
|
||||
{
|
||||
if buffer.get_pts().is_none() {
|
||||
gst_error!(self.cat, obj: agg, "Only buffers with PTS supported");
|
||||
// Trigger aggregate immediately to error out immediately
|
||||
return 0.into();
|
||||
}
|
||||
|
||||
let segment = match fallback_sinkpad.get_segment().downcast::<gst::ClockTime>() {
|
||||
Ok(segment) => segment,
|
||||
Err(_) => {
|
||||
gst_error!(self.cat, obj: agg, "Only TIME segments supported");
|
||||
// Trigger aggregate immediately to error out immediately
|
||||
return 0.into();
|
||||
}
|
||||
};
|
||||
|
||||
let running_time = segment.to_running_time(buffer.get_pts());
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Have buffer on fallback sinkpad, timeout at {}",
|
||||
running_time
|
||||
);
|
||||
running_time
|
||||
} else {
|
||||
gst_debug!(self.cat, obj: agg, "Have no buffer at all yet");
|
||||
gst::CLOCK_TIME_NONE
|
||||
}
|
||||
}
|
||||
|
||||
// Clip the raw audio/video buffers we have to the segment boundaries to ensure that
|
||||
// calculating the running times later works correctly
|
||||
fn clip(
|
||||
&self,
|
||||
agg: &gst_base::Aggregator,
|
||||
agg_pad: &gst_base::AggregatorPad,
|
||||
mut buffer: gst::Buffer,
|
||||
) -> Option<gst::Buffer> {
|
||||
let segment = match agg_pad.get_segment().downcast::<gst::ClockTime>() {
|
||||
Ok(segment) => segment,
|
||||
Err(_) => {
|
||||
gst_error!(self.cat, obj: agg, "Only TIME segments supported");
|
||||
return Some(buffer);
|
||||
}
|
||||
};
|
||||
|
||||
let pts = buffer.get_pts();
|
||||
if pts.is_none() {
|
||||
gst_error!(self.cat, obj: agg, "Only buffers with PTS supported");
|
||||
return Some(buffer);
|
||||
}
|
||||
|
||||
let pad_states = self.pad_states.read().unwrap();
|
||||
let pad_state = if agg_pad == &self.sinkpad {
|
||||
&pad_states.sinkpad
|
||||
} else if Some(agg_pad) == self.fallback_sinkpad.read().unwrap().as_ref() {
|
||||
if let Some(ref pad_state) = pad_states.fallback_sinkpad {
|
||||
pad_state
|
||||
} else {
|
||||
return Some(buffer);
|
||||
}
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
let duration = if buffer.get_duration().is_some() {
|
||||
buffer.get_duration()
|
||||
} else if let Some(ref audio_info) = pad_state.audio_info {
|
||||
gst::SECOND
|
||||
.mul_div_floor(
|
||||
buffer.get_size() as u64,
|
||||
audio_info.rate() as u64 * audio_info.bpf() as u64,
|
||||
)
|
||||
.unwrap()
|
||||
} else if let Some(ref video_info) = pad_state.video_info {
|
||||
gst::SECOND
|
||||
.mul_div_floor(
|
||||
*video_info.fps().denom() as u64,
|
||||
*video_info.fps().numer() as u64,
|
||||
)
|
||||
.unwrap()
|
||||
} else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
gst_debug!(
|
||||
self.cat,
|
||||
obj: agg_pad,
|
||||
"Clipping buffer {:?} with PTS {} and duration {}",
|
||||
buffer,
|
||||
pts,
|
||||
duration
|
||||
);
|
||||
if let Some(ref audio_info) = pad_state.audio_info {
|
||||
gst_audio::audio_buffer_clip(
|
||||
buffer,
|
||||
segment.upcast_ref(),
|
||||
audio_info.rate(),
|
||||
audio_info.bpf(),
|
||||
)
|
||||
} else if let Some(_) = pad_state.video_info {
|
||||
segment.clip(pts, pts + duration).map(|(start, stop)| {
|
||||
{
|
||||
let buffer = buffer.make_mut();
|
||||
buffer.set_pts(start);
|
||||
buffer.set_dts(start);
|
||||
buffer.set_duration(stop - start);
|
||||
}
|
||||
|
||||
buffer
|
||||
})
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
|
||||
fn aggregate(
|
||||
&self,
|
||||
agg: &gst_base::Aggregator,
|
||||
timeout: bool,
|
||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||
gst_debug!(self.cat, obj: agg, "Aggregate called: timeout {}", timeout);
|
||||
|
||||
let (mut buffer, active_caps, pad_change) = self.get_next_buffer(agg, timeout)?;
|
||||
|
||||
let current_src_caps = agg.get_static_pad("src").unwrap().get_current_caps();
|
||||
if Some(&active_caps) != current_src_caps.as_ref() {
|
||||
gst_info!(
|
||||
self.cat,
|
||||
obj: agg,
|
||||
"Caps change from {:?} to {:?}",
|
||||
current_src_caps,
|
||||
active_caps
|
||||
);
|
||||
agg.set_src_caps(&active_caps);
|
||||
}
|
||||
|
||||
if pad_change {
|
||||
agg.notify("active-pad");
|
||||
buffer.make_mut().set_flags(gst::BufferFlags::DISCONT);
|
||||
}
|
||||
|
||||
gst_debug!(self.cat, obj: agg, "Finishing buffer {:?}", buffer);
|
||||
agg.finish_buffer(buffer)
|
||||
}
|
||||
|
||||
fn negotiate(&self, _agg: &gst_base::Aggregator) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
gst::Element::register(
|
||||
Some(plugin),
|
||||
"fallbackswitch",
|
||||
gst::Rank::None,
|
||||
FallbackSwitch::get_type(),
|
||||
)
|
||||
}
|
59
gst-plugin-fallbackswitch/src/lib.rs
Normal file
59
gst-plugin-fallbackswitch/src/lib.rs
Normal file
|
@ -0,0 +1,59 @@
|
|||
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.com>
|
||||
//
|
||||
// This library is free software; you can redistribute it and/or
|
||||
// modify it under the terms of the GNU Library General Public
|
||||
// License as published by the Free Software Foundation; either
|
||||
// version 2 of the License, or (at your option) any later version.
|
||||
//
|
||||
// This library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
// Library General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Library General Public
|
||||
// License along with this library; if not, write to the
|
||||
// Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
|
||||
// Boston, MA 02110-1335, USA.
|
||||
|
||||
#[macro_use]
|
||||
extern crate glib;
|
||||
#[macro_use]
|
||||
extern crate gstreamer as gst;
|
||||
|
||||
extern crate gstreamer_audio as gst_audio;
|
||||
extern crate gstreamer_video as gst_video;
|
||||
|
||||
#[cfg(not(feature = "v1_18"))]
|
||||
extern crate glib_sys;
|
||||
#[cfg(not(feature = "v1_18"))]
|
||||
extern crate gobject_sys;
|
||||
#[cfg(feature = "v1_18")]
|
||||
extern crate gstreamer_base as gst_base;
|
||||
#[cfg(not(feature = "v1_18"))]
|
||||
extern crate gstreamer_sys as gst_sys;
|
||||
#[cfg(not(feature = "v1_18"))]
|
||||
#[allow(dead_code)]
|
||||
mod base;
|
||||
#[cfg(not(feature = "v1_18"))]
|
||||
mod gst_base {
|
||||
pub use super::base::*;
|
||||
}
|
||||
|
||||
mod fallbackswitch;
|
||||
|
||||
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||
fallbackswitch::register(plugin)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
gst_plugin_define!(
|
||||
fallbackswitch,
|
||||
env!("CARGO_PKG_DESCRIPTION"),
|
||||
plugin_init,
|
||||
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
|
||||
"MIT/X11",
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_NAME"),
|
||||
env!("CARGO_PKG_REPOSITORY"),
|
||||
env!("BUILD_REL_DATE")
|
||||
);
|
Loading…
Reference in a new issue