2020-01-06 12:19:11 +00:00
|
|
|
// Copyright (C) 2019-2020 François Laignel <fengalin@free.fr>
|
2020-03-05 15:59:13 +00:00
|
|
|
// Copyright (C) 2020 Sebastian Dröge <sebastian@centricular.com>
|
2019-12-02 09:30:07 +00:00
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
|
|
|
//! An implementation of `Pad`s to run asynchronous processings.
|
|
|
|
//!
|
|
|
|
//! [`PadSink`] & [`PadSrc`] provide an asynchronous API to ease the development of `Element`s in
|
|
|
|
//! the `threadshare` GStreamer plugins framework.
|
|
|
|
//!
|
|
|
|
//! The diagram below shows how the [`PadSrc`] & [`PadSink`] and the related `struct`s integrate in
|
|
|
|
//! `ts` `Element`s.
|
|
|
|
//!
|
|
|
|
//! Note: [`PadSrc`] & [`PadSink`] only support `gst::PadMode::Push` at the moment.
|
|
|
|
//!
|
|
|
|
//! ```text
|
|
|
|
//! ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓ ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
|
|
//! Element A ┃ ┃ Element B
|
|
|
|
//! ┃ ┃
|
|
|
|
//! ╭─────────────────╮ ┃ ┃ ╭──────────────────╮
|
|
|
|
//! │ PadSrc │ ┃ ┃ │ PadSink │
|
|
|
|
//! │ Handler │ ┃ ┃ │ Handler │
|
|
|
|
//! │─────────────────│ ┃ ┃ │──────────────────│
|
|
|
|
//! │ - src_activate* │ ╭──┸──╮ ╭──┸──╮ │ - sink_activate* │
|
|
|
|
//! │ - src_event* │<────│ │<╌╌╌│ │───>│ - sink_chain* │
|
|
|
|
//! │ - src_query │<────│ gst │ │ gst │───>│ - sink_event* │
|
|
|
|
//! │─────────────────│ │ │ │ │───>│ - sink_query │
|
|
|
|
//! │ - task fn │ │ Pad │ │ Pad │ ╰──────────────────╯
|
|
|
|
//! ╰─────────────────╯ ╭─>│ │╌╌╌>│ │─╮ │
|
|
|
|
//! ╭───────╯ │ │ ╰──┰──╯ ╰──┰──╯ ╰───────╮ │
|
|
|
|
//! ╭────────────╮ ╭────────╮ push* │ ┃ ┃ ╭─────────╮
|
|
|
|
//! │ Pad Task ↺ │<──│ PadSrc │───────╯ ┃ ┃ │ PadSink │
|
|
|
|
//! ╰────────────╯ ╰────────╯ ┃ ┃ ╰─────────╯
|
|
|
|
//! ━━━━━━━━━━━━━━━━━━━━━━│━━━━━━━━━━━━━━━━━┛ ┗━━━━━━━━━━━━━━━│━━━━━━━━━━━━
|
|
|
|
//! ╰───────────────────╮ ╭─────────────────╯
|
|
|
|
//! ╭──────────────╮
|
|
|
|
//! │ PadContext │
|
|
|
|
//! │╭────────────╮│
|
|
|
|
//! ││ Context ↺ ││
|
|
|
|
//! ╰╰────────────╯╯
|
|
|
|
//! ```
|
|
|
|
//!
|
|
|
|
//! Asynchronous operations for both [`PadSrc`] in `Element A` and [`PadSink`] in `Element B` run on
|
|
|
|
//! the same [`Context`], which can also be shared by other `Element`s or instances of the same
|
|
|
|
//! `Element`s in multiple `Pipeline`s.
|
|
|
|
//!
|
|
|
|
//! `Element A` & `Element B` can also be linked to non-threadshare `Element`s in which case, they
|
|
|
|
//! operate in a regular synchronous way.
|
|
|
|
//!
|
2020-03-05 15:59:13 +00:00
|
|
|
//! Note that only operations on the streaming thread (serialized events, buffers, serialized
|
|
|
|
//! queries) are handled from the `PadContext` and asynchronously, everything else operates
|
|
|
|
//! blocking.
|
|
|
|
//!
|
2019-12-02 09:30:07 +00:00
|
|
|
//! [`PadSink`]: struct.PadSink.html
|
|
|
|
//! [`PadSrc`]: struct.PadSrc.html
|
|
|
|
//! [`Context`]: ../executor/struct.Context.html
|
|
|
|
|
|
|
|
use futures::future;
|
|
|
|
use futures::future::BoxFuture;
|
|
|
|
use futures::prelude::*;
|
|
|
|
|
|
|
|
use gst;
|
|
|
|
use gst::prelude::*;
|
|
|
|
use gst::subclass::prelude::*;
|
2020-03-05 15:59:13 +00:00
|
|
|
use gst::{gst_debug, gst_error, gst_fixme, gst_log, gst_loggable_error};
|
2019-12-02 09:30:07 +00:00
|
|
|
use gst::{FlowError, FlowSuccess};
|
|
|
|
|
|
|
|
use std::fmt;
|
|
|
|
use std::marker::PhantomData;
|
|
|
|
use std::sync;
|
|
|
|
use std::sync::{Arc, Weak};
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
use super::executor::Context;
|
2019-12-02 09:30:07 +00:00
|
|
|
use super::task::Task;
|
|
|
|
use super::RUNTIME_CAT;
|
|
|
|
|
|
|
|
/// Errors related to [`PadSrc`] `Context` handling.
|
|
|
|
///
|
|
|
|
/// [`PadSrc`]: struct.PadSrc.html
|
|
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
|
|
pub enum PadContextError {
|
|
|
|
ActiveContext,
|
|
|
|
ActiveTask,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for PadContextError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
|
|
match self {
|
|
|
|
PadContextError::ActiveContext => {
|
|
|
|
write!(f, "The PadSrc is already operating on a Context")
|
|
|
|
}
|
|
|
|
PadContextError::ActiveTask => write!(f, "A task is still active"),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for PadContextError {}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-05 15:59:13 +00:00
|
|
|
fn event_ret_to_event_full_res(
|
|
|
|
ret: bool,
|
|
|
|
event_type: gst::EventType,
|
|
|
|
) -> Result<FlowSuccess, FlowError> {
|
2019-12-02 09:30:07 +00:00
|
|
|
if ret {
|
|
|
|
Ok(FlowSuccess::Ok)
|
2020-03-05 15:59:13 +00:00
|
|
|
} else if event_type == gst::EventType::Caps {
|
2019-12-02 09:30:07 +00:00
|
|
|
Err(FlowError::NotNegotiated)
|
|
|
|
} else {
|
|
|
|
Err(FlowError::Error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-05 15:59:13 +00:00
|
|
|
fn event_to_event_full(ret: bool, event_type: gst::EventType) -> Result<FlowSuccess, FlowError> {
|
|
|
|
event_ret_to_event_full_res(ret, event_type)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn event_to_event_full_serialized(
|
|
|
|
ret: BoxFuture<'static, bool>,
|
|
|
|
event_type: gst::EventType,
|
|
|
|
) -> BoxFuture<'static, Result<FlowSuccess, FlowError>> {
|
|
|
|
ret.map(move |ret| event_ret_to_event_full_res(ret, event_type))
|
|
|
|
.boxed()
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A trait to define `handler`s for [`PadSrc`] callbacks.
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`PadSrc`]: struct.PadSrc.html
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
pub trait PadSrcHandler: Clone + Send + Sync + 'static {
|
2020-03-05 15:59:13 +00:00
|
|
|
type ElementImpl: ElementImpl + ObjectSubclass;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
fn src_activate(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSrcRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
_element: &gst::Element,
|
|
|
|
) -> Result<(), gst::LoggableError> {
|
|
|
|
let gst_pad = pad.gst_pad();
|
|
|
|
if gst_pad.is_active() {
|
|
|
|
gst_debug!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
obj: gst_pad,
|
|
|
|
"Already activated in {:?} mode ",
|
|
|
|
gst_pad.get_mode()
|
|
|
|
);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_pad
|
|
|
|
.activate_mode(gst::PadMode::Push, true)
|
|
|
|
.map_err(|err| {
|
|
|
|
gst_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
obj: gst_pad,
|
|
|
|
"Error in PadSink activate: {:?}",
|
|
|
|
err
|
|
|
|
);
|
|
|
|
gst_loggable_error!(RUNTIME_CAT, "Error in PadSink activate: {:?}", err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn src_activatemode(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
_pad: &PadSrcRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
_element: &gst::Element,
|
|
|
|
_mode: gst::PadMode,
|
|
|
|
_active: bool,
|
|
|
|
) -> Result<(), gst::LoggableError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn src_event(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSrcRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
event: gst::Event,
|
2020-03-05 15:59:13 +00:00
|
|
|
) -> bool {
|
|
|
|
gst_log!(RUNTIME_CAT, obj: pad.gst_pad(), "Handling {:?}", event);
|
|
|
|
pad.gst_pad().event_default(Some(element), event)
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn src_event_full(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSrcRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
event: gst::Event,
|
2020-03-05 15:59:13 +00:00
|
|
|
) -> Result<FlowSuccess, FlowError> {
|
2019-12-02 09:30:07 +00:00
|
|
|
// default is to dispatch to `src_event`
|
|
|
|
// (as implemented in `gst_pad_send_event_unchecked`)
|
2020-03-05 15:59:13 +00:00
|
|
|
let event_type = event.get_type();
|
|
|
|
event_to_event_full(self.src_event(pad, imp, element, event), event_type)
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn src_query(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSrcRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
query: &mut gst::QueryRef,
|
|
|
|
) -> bool {
|
2020-01-06 12:19:11 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: pad.gst_pad(), "Handling {:?}", query);
|
2019-12-02 09:30:07 +00:00
|
|
|
if query.is_serialized() {
|
|
|
|
// FIXME serialized queries should be handled with the dataflow
|
|
|
|
// but we can't return a `Future` because we couldn't honor QueryRef's lifetime
|
|
|
|
false
|
|
|
|
} else {
|
|
|
|
pad.gst_pad().query_default(Some(element), query)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Default, Debug)]
|
2020-03-05 15:59:13 +00:00
|
|
|
struct PadSrcState;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct PadSrcInner {
|
2020-03-05 15:59:13 +00:00
|
|
|
state: sync::Mutex<PadSrcState>,
|
2019-12-02 09:30:07 +00:00
|
|
|
gst_pad: gst::Pad,
|
|
|
|
task: Task,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PadSrcInner {
|
|
|
|
fn new(gst_pad: gst::Pad) -> Self {
|
|
|
|
if gst_pad.get_direction() != gst::PadDirection::Src {
|
|
|
|
panic!("Wrong pad direction for PadSrc");
|
|
|
|
}
|
|
|
|
|
|
|
|
PadSrcInner {
|
2020-03-05 15:59:13 +00:00
|
|
|
state: sync::Mutex::new(PadSrcState::default()),
|
2019-12-02 09:30:07 +00:00
|
|
|
gst_pad,
|
|
|
|
task: Task::default(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A [`PadSrc`] which can be moved in [`handler`]s functions and `Future`s.
|
|
|
|
///
|
|
|
|
/// Call [`upgrade`] to use the [`PadSrc`].
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`PadSrc`]: struct.PadSrc.html
|
|
|
|
/// [`handler`]: trait.PadSrcHandler.html
|
|
|
|
/// [`upgrade`]: struct.PadSrcWeak.html#method.upgrade
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct PadSrcWeak(Weak<PadSrcInner>);
|
|
|
|
|
|
|
|
impl PadSrcWeak {
|
|
|
|
pub fn upgrade(&self) -> Option<PadSrcRef<'_>> {
|
|
|
|
self.0.upgrade().map(PadSrcRef::new)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A [`PadSrc`] to be used in `Handler`s functions and `Future`s.
|
|
|
|
///
|
|
|
|
/// Call [`downgrade`] if you need to `clone` the [`PadSrc`].
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`PadSrc`]: struct.PadSrc.html
|
|
|
|
/// [`PadSrcWeak`]: struct.PadSrcWeak.html
|
|
|
|
/// [`downgrade`]: struct.PadSrcRef.html#method.downgrade
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PadSrcRef<'a> {
|
|
|
|
strong: PadSrcStrong,
|
|
|
|
phantom: PhantomData<&'a PadSrcStrong>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> PadSrcRef<'a> {
|
|
|
|
fn new(inner_arc: Arc<PadSrcInner>) -> Self {
|
|
|
|
PadSrcRef {
|
|
|
|
strong: PadSrcStrong(inner_arc),
|
|
|
|
phantom: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gst_pad(&self) -> &gst::Pad {
|
|
|
|
self.strong.gst_pad()
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
///// Spawns `future` using current [`PadContext`].
|
|
|
|
/////
|
|
|
|
///// # Panics
|
|
|
|
/////
|
|
|
|
///// This function panics if the `PadSrc` is not prepared.
|
|
|
|
/////
|
|
|
|
///// [`PadContext`]: ../struct.PadContext.html
|
|
|
|
//pub fn spawn<Fut>(&self, future: Fut) -> JoinHandle<Fut::Output>
|
|
|
|
//where
|
|
|
|
// Fut: Future + Send + 'static,
|
|
|
|
// Fut::Output: Send + 'static,
|
|
|
|
//{
|
|
|
|
// self.strong.spawn(future)
|
|
|
|
//}
|
2020-01-06 12:19:11 +00:00
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
pub fn downgrade(&self) -> PadSrcWeak {
|
|
|
|
self.strong.downgrade()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn push(&self, buffer: gst::Buffer) -> Result<FlowSuccess, FlowError> {
|
|
|
|
self.strong.push(buffer).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn push_list(&self, list: gst::BufferList) -> Result<FlowSuccess, FlowError> {
|
|
|
|
self.strong.push_list(list).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn push_event(&self, event: gst::Event) -> bool {
|
|
|
|
self.strong.push_event(event).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `Start` the `Pad` `task`.
|
|
|
|
///
|
|
|
|
/// The `Task` will loop on the provided `func`.
|
|
|
|
/// The execution occurs on the `Task`'s context.
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn start_task<F, Fut>(&self, func: F)
|
2019-12-02 09:30:07 +00:00
|
|
|
where
|
|
|
|
F: (FnMut() -> Fut) + Send + 'static,
|
2020-03-05 15:59:13 +00:00
|
|
|
Fut: Future<Output = glib::Continue> + Send + 'static,
|
2019-12-02 09:30:07 +00:00
|
|
|
{
|
2020-03-05 15:59:13 +00:00
|
|
|
self.strong.start_task(func);
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
/// Cancels the `Started` `Pad` `Task`.
|
|
|
|
pub fn cancel_task(&self) {
|
|
|
|
self.strong.cancel_task();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
/// Stops the `Started` `Pad` `Task`.
|
|
|
|
pub fn stop_task(&self) {
|
|
|
|
self.strong.stop_task();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn activate_mode_hook(
|
|
|
|
&self,
|
|
|
|
mode: gst::PadMode,
|
|
|
|
active: bool,
|
|
|
|
) -> Result<(), gst::LoggableError> {
|
|
|
|
// Important: don't panic here as the hook is used without `catch_panic_pad_function`
|
|
|
|
// in the default `activatemode` handling
|
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "ActivateMode {:?}, {}", mode, active);
|
|
|
|
|
|
|
|
if mode == gst::PadMode::Pull {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: self.gst_pad(), "Pull mode not supported by PadSrc");
|
|
|
|
return Err(gst_loggable_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Pull mode not supported by PadSrc"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct PadSrcStrong(Arc<PadSrcInner>);
|
|
|
|
|
|
|
|
impl PadSrcStrong {
|
|
|
|
fn new(gst_pad: gst::Pad) -> Self {
|
|
|
|
PadSrcStrong(Arc::new(PadSrcInner::new(gst_pad)))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
fn gst_pad(&self) -> &gst::Pad {
|
|
|
|
&self.0.gst_pad
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
//#[inline]
|
|
|
|
//fn spawn<Fut>(&self, future: Fut) -> JoinHandle<Fut::Output>
|
|
|
|
//where
|
|
|
|
// Fut: Future + Send + 'static,
|
|
|
|
// Fut::Output: Send + 'static,
|
|
|
|
//{
|
|
|
|
// let pad_ctx = self.pad_context_priv();
|
|
|
|
// pad_ctx
|
|
|
|
// .as_ref()
|
|
|
|
// .expect("PadContext not initialized")
|
|
|
|
// .spawn(future)
|
|
|
|
//}
|
2020-01-06 12:19:11 +00:00
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
#[inline]
|
|
|
|
fn downgrade(&self) -> PadSrcWeak {
|
|
|
|
PadSrcWeak(Arc::downgrade(&self.0))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
async fn push(&self, buffer: gst::Buffer) -> Result<FlowSuccess, FlowError> {
|
2020-01-06 12:19:11 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Pushing {:?}", buffer);
|
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
let success = self.gst_pad().push(buffer).map_err(|err| {
|
|
|
|
gst_error!(RUNTIME_CAT,
|
|
|
|
obj: self.gst_pad(),
|
|
|
|
"Failed to push Buffer to PadSrc: {:?}",
|
|
|
|
err,
|
|
|
|
);
|
|
|
|
err
|
|
|
|
})?;
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Processing any pending sub tasks");
|
|
|
|
Context::drain_sub_tasks().await?;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
Ok(success)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
async fn push_list(&self, list: gst::BufferList) -> Result<FlowSuccess, FlowError> {
|
2020-01-06 12:19:11 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Pushing {:?}", list);
|
|
|
|
|
2019-12-02 09:30:07 +00:00
|
|
|
let success = self.gst_pad().push_list(list).map_err(|err| {
|
|
|
|
gst_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
obj: self.gst_pad(),
|
2020-03-05 15:59:13 +00:00
|
|
|
"Failed to push BufferList to PadSrc: {:?}",
|
2019-12-02 09:30:07 +00:00
|
|
|
err,
|
|
|
|
);
|
|
|
|
err
|
|
|
|
})?;
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Processing any pending sub tasks");
|
|
|
|
Context::drain_sub_tasks().await?;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
Ok(success)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
async fn push_event(&self, event: gst::Event) -> bool {
|
2020-03-05 15:59:13 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Pushing {:?}", event);
|
2020-01-06 12:19:11 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
let was_handled = self.gst_pad().push_event(event);
|
2020-01-06 12:19:11 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Processing any pending sub tasks");
|
|
|
|
if Context::drain_sub_tasks().await.is_err() {
|
|
|
|
return false;
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
was_handled
|
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-05 15:59:13 +00:00
|
|
|
fn start_task<F, Fut>(&self, func: F)
|
2019-12-02 09:30:07 +00:00
|
|
|
where
|
|
|
|
F: (FnMut() -> Fut) + Send + 'static,
|
2020-03-05 15:59:13 +00:00
|
|
|
Fut: Future<Output = glib::Continue> + Send + 'static,
|
2019-12-02 09:30:07 +00:00
|
|
|
{
|
2020-03-05 15:59:13 +00:00
|
|
|
self.0.task.start(func);
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-05 15:59:13 +00:00
|
|
|
fn cancel_task(&self) {
|
|
|
|
self.0.task.cancel();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[inline]
|
2020-03-05 15:59:13 +00:00
|
|
|
fn stop_task(&self) {
|
|
|
|
self.0.task.stop();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The `PadSrc` which `Element`s must own.
|
|
|
|
///
|
|
|
|
/// Call [`downgrade`] if you need to `clone` the `PadSrc`.
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`downgrade`]: struct.PadSrc.html#method.downgrade
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PadSrc(PadSrcStrong);
|
|
|
|
|
|
|
|
impl PadSrc {
|
|
|
|
pub fn new(gst_pad: gst::Pad) -> Self {
|
|
|
|
let pad = gst_pad.clone();
|
|
|
|
let this = PadSrc(PadSrcStrong::new(gst_pad));
|
|
|
|
|
|
|
|
let this_weak = this.downgrade();
|
|
|
|
pad.set_activatemode_function(move |gst_pad, _parent, mode, active| {
|
|
|
|
// Important: don't panic here as we operate without `catch_panic_pad_function`
|
|
|
|
// because we may not know which element the PadSrc is associated to yet
|
|
|
|
this_weak
|
|
|
|
.upgrade()
|
|
|
|
.ok_or_else(|| {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: gst_pad, "PadSrc no longer exists");
|
|
|
|
gst_loggable_error!(RUNTIME_CAT, "PadSrc no longer exists")
|
|
|
|
})?
|
|
|
|
.activate_mode_hook(mode, active)
|
|
|
|
});
|
|
|
|
|
|
|
|
this
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_from_template(templ: &gst::PadTemplate, name: Option<&str>) -> Self {
|
|
|
|
Self::new(gst::Pad::new_from_template(templ, name))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn as_ref(&self) -> PadSrcRef<'_> {
|
|
|
|
PadSrcRef::new(Arc::clone(&(self.0).0))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gst_pad(&self) -> &gst::Pad {
|
|
|
|
self.0.gst_pad()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn downgrade(&self) -> PadSrcWeak {
|
|
|
|
self.0.downgrade()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn check_reconfigure(&self) -> bool {
|
|
|
|
self.gst_pad().check_reconfigure()
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
///// Spawns `future` using current [`PadContext`].
|
|
|
|
/////
|
|
|
|
///// # Panics
|
|
|
|
/////
|
|
|
|
///// This function panics if the `PadSrc` is not prepared.
|
|
|
|
/////
|
|
|
|
///// [`PadContext`]: ../struct.PadContext.html
|
|
|
|
//pub fn spawn<Fut>(&self, future: Fut) -> JoinHandle<Fut::Output>
|
|
|
|
//where
|
|
|
|
// Fut: Future + Send + 'static,
|
|
|
|
// Fut::Output: Send + 'static,
|
|
|
|
//{
|
|
|
|
// self.0.spawn(future)
|
|
|
|
//}
|
2020-01-06 12:19:11 +00:00
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
fn init_pad_functions<H: PadSrcHandler>(&self, handler: &H) {
|
2019-12-02 09:30:07 +00:00
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_activate_function(move |gst_pad, parent| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: gst_pad, "Panic in PadSrc activate");
|
|
|
|
Err(gst_loggable_error!(RUNTIME_CAT, "Panic in PadSrc activate"))
|
|
|
|
},
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSrc no longer exists");
|
2020-01-06 12:19:11 +00:00
|
|
|
handler.src_activate(&this_ref, imp, element)
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_activatemode_function(move |gst_pad, parent, mode, active| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: gst_pad, "Panic in PadSrc activatemode");
|
|
|
|
Err(gst_loggable_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Panic in PadSrc activatemode"
|
|
|
|
))
|
|
|
|
},
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSrc no longer exists");
|
|
|
|
this_ref.activate_mode_hook(mode, active)?;
|
2020-01-06 12:19:11 +00:00
|
|
|
handler.src_activatemode(&this_ref, imp, element, mode, active)
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
// No need to `set_event_function` since `set_event_full_function`
|
|
|
|
// overrides it and dispatches to `src_event` when necessary
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_event_full_function(move |_gst_pad, parent, event| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| Err(FlowError::Error),
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSrc no longer exists");
|
2020-03-05 15:59:13 +00:00
|
|
|
handler.src_event_full(&this_ref, imp, &element, event)
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_query_function(move |_gst_pad, parent, query| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| false,
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSrc no longer exists");
|
2020-01-02 21:32:52 +00:00
|
|
|
if !query.is_serialized() {
|
2020-01-06 12:19:11 +00:00
|
|
|
handler.src_query(&this_ref, imp, &element, query)
|
2020-01-02 21:32:52 +00:00
|
|
|
} else {
|
|
|
|
gst_fixme!(RUNTIME_CAT, obj: this_ref.gst_pad(), "Serialized Query not supported");
|
|
|
|
false
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn prepare<H: PadSrcHandler>(
|
2019-12-02 09:30:07 +00:00
|
|
|
&self,
|
|
|
|
context: Context,
|
|
|
|
handler: &H,
|
2020-03-05 15:59:13 +00:00
|
|
|
) -> Result<(), super::task::TaskError> {
|
|
|
|
let _state = (self.0).0.state.lock().unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Preparing");
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
(self.0).0.task.prepare(context)?;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
self.init_pad_functions(handler);
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn prepare_with_func<H: PadSrcHandler, F, Fut>(
|
|
|
|
&self,
|
|
|
|
context: Context,
|
|
|
|
handler: &H,
|
|
|
|
prepare_func: F,
|
|
|
|
) -> Result<(), super::task::TaskError>
|
|
|
|
where
|
|
|
|
F: (FnOnce() -> Fut) + Send + 'static,
|
|
|
|
Fut: Future<Output = ()> + Send + 'static,
|
|
|
|
{
|
|
|
|
let _state = (self.0).0.state.lock().unwrap();
|
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Preparing");
|
|
|
|
|
|
|
|
(self.0).0.task.prepare_with_func(context, prepare_func)?;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
2020-01-02 21:32:52 +00:00
|
|
|
self.init_pad_functions(handler);
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Releases the resources held by this `PadSrc`.
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn unprepare(&self) -> Result<(), PadContextError> {
|
|
|
|
let _state = (self.0).0.state.lock().unwrap();
|
2019-12-02 09:30:07 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Unpreparing");
|
|
|
|
|
|
|
|
(self.0)
|
|
|
|
.0
|
|
|
|
.task
|
|
|
|
.unprepare()
|
|
|
|
.map_err(|_| PadContextError::ActiveTask)?;
|
|
|
|
|
|
|
|
self.gst_pad()
|
|
|
|
.set_activatemode_function(move |_gst_pad, _parent, _mode, _active| {
|
|
|
|
Err(gst_loggable_error!(RUNTIME_CAT, "PadSrc unprepared"))
|
|
|
|
});
|
|
|
|
self.gst_pad()
|
|
|
|
.set_event_function(move |_gst_pad, _parent, _event| false);
|
|
|
|
self.gst_pad()
|
2020-03-05 15:59:13 +00:00
|
|
|
.set_event_full_function(move |_gst_pad, _parent, _event| Err(FlowError::Flushing));
|
2019-12-02 09:30:07 +00:00
|
|
|
self.gst_pad()
|
|
|
|
.set_query_function(move |_gst_pad, _parent, _query| false);
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn push(&self, buffer: gst::Buffer) -> Result<FlowSuccess, FlowError> {
|
|
|
|
self.0.push(buffer).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn push_list(&self, list: gst::BufferList) -> Result<FlowSuccess, FlowError> {
|
|
|
|
self.0.push_list(list).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn push_event(&self, event: gst::Event) -> bool {
|
|
|
|
self.0.push_event(event).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `Start` the `Pad` `task`.
|
|
|
|
///
|
|
|
|
/// The `Task` will loop on the provided `func`.
|
|
|
|
/// The execution occurs on the `Task`'s context.
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn start_task<F, Fut>(&self, func: F)
|
2019-12-02 09:30:07 +00:00
|
|
|
where
|
|
|
|
F: (FnMut() -> Fut) + Send + 'static,
|
2020-03-05 15:59:13 +00:00
|
|
|
Fut: Future<Output = glib::Continue> + Send + 'static,
|
2019-12-02 09:30:07 +00:00
|
|
|
{
|
2020-03-05 15:59:13 +00:00
|
|
|
self.0.start_task(func);
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn cancel_task(&self) {
|
|
|
|
self.0.cancel_task();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn stop_task(&self) {
|
|
|
|
self.0.stop_task();
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A trait to define `handler`s for [`PadSink`] callbacks.
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`PadSink`]: struct.PadSink.html
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
pub trait PadSinkHandler: Clone + Send + Sync + 'static {
|
2020-03-05 15:59:13 +00:00
|
|
|
type ElementImpl: ElementImpl + ObjectSubclass;
|
2019-12-02 09:30:07 +00:00
|
|
|
|
|
|
|
fn sink_activate(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSinkRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
_element: &gst::Element,
|
|
|
|
) -> Result<(), gst::LoggableError> {
|
|
|
|
let gst_pad = pad.gst_pad();
|
|
|
|
if gst_pad.is_active() {
|
|
|
|
gst_debug!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
obj: gst_pad,
|
|
|
|
"Already activated in {:?} mode ",
|
|
|
|
gst_pad.get_mode()
|
|
|
|
);
|
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_pad
|
|
|
|
.activate_mode(gst::PadMode::Push, true)
|
|
|
|
.map_err(|err| {
|
|
|
|
gst_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
obj: gst_pad,
|
|
|
|
"Error in PadSink activate: {:?}",
|
|
|
|
err
|
|
|
|
);
|
|
|
|
gst_loggable_error!(RUNTIME_CAT, "Error in PadSink activate: {:?}", err)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_activatemode(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
_pad: &PadSinkRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
_element: &gst::Element,
|
|
|
|
_mode: gst::PadMode,
|
|
|
|
_active: bool,
|
|
|
|
) -> Result<(), gst::LoggableError> {
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_chain(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
_pad: &PadSinkRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
_element: &gst::Element,
|
|
|
|
_buffer: gst::Buffer,
|
|
|
|
) -> BoxFuture<'static, Result<FlowSuccess, FlowError>> {
|
|
|
|
future::err(FlowError::NotSupported).boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_chain_list(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
_pad: &PadSinkRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
_element: &gst::Element,
|
|
|
|
_buffer_list: gst::BufferList,
|
|
|
|
) -> BoxFuture<'static, Result<FlowSuccess, FlowError>> {
|
|
|
|
future::err(FlowError::NotSupported).boxed()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_event(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSinkRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
event: gst::Event,
|
2020-03-05 15:59:13 +00:00
|
|
|
) -> bool {
|
|
|
|
assert!(!event.is_serialized());
|
|
|
|
gst_log!(RUNTIME_CAT, obj: pad.gst_pad(), "Handling {:?}", event);
|
|
|
|
pad.gst_pad().event_default(Some(element), event)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_event_serialized(
|
|
|
|
&self,
|
|
|
|
pad: &PadSinkRef,
|
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
event: gst::Event,
|
|
|
|
) -> BoxFuture<'static, bool> {
|
|
|
|
assert!(event.is_serialized());
|
|
|
|
let pad_weak = pad.downgrade();
|
|
|
|
let element = element.clone();
|
|
|
|
|
|
|
|
async move {
|
|
|
|
let pad = pad_weak.upgrade().expect("PadSink no longer exists");
|
2020-01-06 12:19:11 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: pad.gst_pad(), "Handling {:?}", event);
|
2020-03-05 15:59:13 +00:00
|
|
|
|
|
|
|
pad.gst_pad().event_default(Some(&element), event)
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
2020-03-05 15:59:13 +00:00
|
|
|
.boxed()
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_event_full(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSinkRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
event: gst::Event,
|
2020-03-05 15:59:13 +00:00
|
|
|
) -> Result<FlowSuccess, FlowError> {
|
|
|
|
assert!(!event.is_serialized());
|
2019-12-02 09:30:07 +00:00
|
|
|
// default is to dispatch to `sink_event`
|
|
|
|
// (as implemented in `gst_pad_send_event_unchecked`)
|
2020-03-05 15:59:13 +00:00
|
|
|
let event_type = event.get_type();
|
|
|
|
event_to_event_full(self.sink_event(pad, imp, element, event), event_type)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_event_full_serialized(
|
|
|
|
&self,
|
|
|
|
pad: &PadSinkRef,
|
|
|
|
imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
event: gst::Event,
|
|
|
|
) -> BoxFuture<'static, Result<FlowSuccess, FlowError>> {
|
|
|
|
assert!(event.is_serialized());
|
|
|
|
// default is to dispatch to `sink_event`
|
|
|
|
// (as implemented in `gst_pad_send_event_unchecked`)
|
|
|
|
let event_type = event.get_type();
|
|
|
|
event_to_event_full_serialized(
|
|
|
|
self.sink_event_serialized(pad, imp, element, event),
|
|
|
|
event_type,
|
|
|
|
)
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_query(
|
|
|
|
&self,
|
2020-01-06 12:19:11 +00:00
|
|
|
pad: &PadSinkRef,
|
2019-12-02 09:30:07 +00:00
|
|
|
_imp: &Self::ElementImpl,
|
|
|
|
element: &gst::Element,
|
|
|
|
query: &mut gst::QueryRef,
|
|
|
|
) -> bool {
|
|
|
|
if query.is_serialized() {
|
2020-03-05 15:59:13 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: pad.gst_pad(), "Dropping {:?}", query);
|
2019-12-02 09:30:07 +00:00
|
|
|
// FIXME serialized queries should be handled with the dataflow
|
|
|
|
// but we can't return a `Future` because we couldn't honor QueryRef's lifetime
|
|
|
|
false
|
|
|
|
} else {
|
2020-03-05 15:59:13 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: pad.gst_pad(), "Handling {:?}", query);
|
2019-12-02 09:30:07 +00:00
|
|
|
pad.gst_pad().query_default(Some(element), query)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct PadSinkInner {
|
|
|
|
gst_pad: gst::Pad,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl PadSinkInner {
|
|
|
|
fn new(gst_pad: gst::Pad) -> Self {
|
|
|
|
if gst_pad.get_direction() != gst::PadDirection::Sink {
|
|
|
|
panic!("Wrong pad direction for PadSink");
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
PadSinkInner { gst_pad }
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A [`PadSink`] which can be moved in `Handler`s functions and `Future`s.
|
|
|
|
///
|
|
|
|
/// Call [`upgrade`] to use the [`PadSink`].
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`PadSink`]: struct.PadSink.html
|
|
|
|
/// [`upgrade`]: struct.PadSinkWeak.html#method.upgrade
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct PadSinkWeak(Weak<PadSinkInner>);
|
|
|
|
|
|
|
|
impl PadSinkWeak {
|
|
|
|
pub fn upgrade(&self) -> Option<PadSinkRef<'_>> {
|
|
|
|
self.0.upgrade().map(PadSinkRef::new)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A [`PadSink`] to be used in [`handler`]s functions and `Future`s.
|
|
|
|
///
|
|
|
|
/// Call [`downgrade`] if you need to `clone` the [`PadSink`].
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`PadSink`]: struct.PadSink.html
|
|
|
|
/// [`handler`]: trait.PadSinkHandler.html
|
|
|
|
/// [`downgrade`]: struct.PadSinkRef.html#method.downgrade
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PadSinkRef<'a> {
|
|
|
|
strong: PadSinkStrong,
|
|
|
|
phantom: PhantomData<&'a PadSrcStrong>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> PadSinkRef<'a> {
|
|
|
|
fn new(inner_arc: Arc<PadSinkInner>) -> Self {
|
|
|
|
PadSinkRef {
|
|
|
|
strong: PadSinkStrong(inner_arc),
|
|
|
|
phantom: PhantomData,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gst_pad(&self) -> &gst::Pad {
|
|
|
|
self.strong.gst_pad()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn downgrade(&self) -> PadSinkWeak {
|
|
|
|
self.strong.downgrade()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn activate_mode_hook(
|
|
|
|
&self,
|
|
|
|
mode: gst::PadMode,
|
|
|
|
active: bool,
|
|
|
|
) -> Result<(), gst::LoggableError> {
|
|
|
|
// Important: don't panic here as the hook is used without `catch_panic_pad_function`
|
|
|
|
// in the default `activatemode` handling
|
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "ActivateMode {:?}, {}", mode, active);
|
|
|
|
|
|
|
|
if mode == gst::PadMode::Pull {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: self.gst_pad(), "Pull mode not supported by PadSink");
|
|
|
|
return Err(gst_loggable_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Pull mode not supported by PadSink"
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-01-06 12:19:11 +00:00
|
|
|
|
|
|
|
fn handle_future(
|
|
|
|
&self,
|
|
|
|
fut: impl Future<Output = Result<FlowSuccess, FlowError>> + Send + 'static,
|
|
|
|
) -> Result<FlowSuccess, FlowError> {
|
2020-03-05 15:59:13 +00:00
|
|
|
// First try to add it as a sub task to the current task, if any
|
|
|
|
if let Err(fut) = Context::add_sub_task(fut.map(|res| res.map(drop))) {
|
|
|
|
// FIXME: update comments below
|
2020-01-06 12:19:11 +00:00
|
|
|
// Not on a context thread: execute the Future immediately.
|
|
|
|
//
|
|
|
|
// - If there is no PadContext, we don't have any other options.
|
|
|
|
// - If there is a PadContext, it means that we received it from
|
|
|
|
// an upstream element, but there is at least one non-ts element
|
|
|
|
// operating on another thread in between, so we can't take
|
|
|
|
// advantage of the task queue.
|
|
|
|
//
|
|
|
|
// Note: we don't use `crate::runtime::executor::block_on` here
|
|
|
|
// because `Context::is_context_thread()` is checked in the `if`
|
|
|
|
// statement above.
|
2020-03-05 15:59:13 +00:00
|
|
|
futures::executor::block_on(fut.map(|res| res.map(|_| gst::FlowSuccess::Ok)))
|
|
|
|
} else {
|
|
|
|
Ok(gst::FlowSuccess::Ok)
|
2020-01-06 12:19:11 +00:00
|
|
|
}
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct PadSinkStrong(Arc<PadSinkInner>);
|
|
|
|
|
|
|
|
impl PadSinkStrong {
|
|
|
|
fn new(gst_pad: gst::Pad) -> Self {
|
|
|
|
PadSinkStrong(Arc::new(PadSinkInner::new(gst_pad)))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn gst_pad(&self) -> &gst::Pad {
|
|
|
|
&self.0.gst_pad
|
|
|
|
}
|
|
|
|
|
|
|
|
fn downgrade(&self) -> PadSinkWeak {
|
|
|
|
PadSinkWeak(Arc::downgrade(&self.0))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The `PadSink` which `Element`s must own.
|
|
|
|
///
|
|
|
|
/// Call [`downgrade`] if you need to `clone` the `PadSink`.
|
|
|
|
///
|
|
|
|
/// *See the [`pad` module] documentation for a description of the model.*
|
|
|
|
///
|
|
|
|
/// [`downgrade`]: struct.PadSink.html#method.downgrade
|
|
|
|
/// [`pad` module]: index.html
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct PadSink(PadSinkStrong);
|
|
|
|
|
|
|
|
impl PadSink {
|
|
|
|
pub fn new(gst_pad: gst::Pad) -> Self {
|
|
|
|
let pad = gst_pad.clone();
|
|
|
|
let this = PadSink(PadSinkStrong::new(gst_pad));
|
|
|
|
|
|
|
|
let this_weak = this.downgrade();
|
|
|
|
pad.set_activatemode_function(move |gst_pad, _parent, mode, active| {
|
|
|
|
// Important: don't panic here as we operate without `catch_panic_pad_function`
|
|
|
|
// because we may not know which element the PadSrc is associated to yet
|
|
|
|
this_weak
|
|
|
|
.upgrade()
|
|
|
|
.ok_or_else(|| {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: gst_pad, "PadSink no longer exists");
|
|
|
|
gst_loggable_error!(RUNTIME_CAT, "PadSink no longer exists")
|
|
|
|
})?
|
|
|
|
.activate_mode_hook(mode, active)
|
|
|
|
});
|
|
|
|
|
|
|
|
this
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn new_from_template(templ: &gst::PadTemplate, name: Option<&str>) -> Self {
|
|
|
|
Self::new(gst::Pad::new_from_template(templ, name))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn gst_pad(&self) -> &gst::Pad {
|
|
|
|
self.0.gst_pad()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn downgrade(&self) -> PadSinkWeak {
|
|
|
|
self.0.downgrade()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn init_pad_functions<H: PadSinkHandler>(&self, handler: &H) {
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_activate_function(move |gst_pad, parent| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: gst_pad, "Panic in PadSink activate");
|
|
|
|
Err(gst_loggable_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Panic in PadSink activate"
|
|
|
|
))
|
|
|
|
},
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSink no longer exists");
|
2020-01-06 12:19:11 +00:00
|
|
|
handler.sink_activate(&this_ref, imp, element)
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_activatemode_function(move |gst_pad, parent, mode, active| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| {
|
|
|
|
gst_error!(RUNTIME_CAT, obj: gst_pad, "Panic in PadSink activatemode");
|
|
|
|
Err(gst_loggable_error!(
|
|
|
|
RUNTIME_CAT,
|
|
|
|
"Panic in PadSink activatemode"
|
|
|
|
))
|
|
|
|
},
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSink no longer exists");
|
|
|
|
this_ref.activate_mode_hook(mode, active)?;
|
|
|
|
|
2020-01-06 12:19:11 +00:00
|
|
|
handler.sink_activatemode(&this_ref, imp, element, mode, active)
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_chain_function(move |_gst_pad, parent, buffer| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| Err(FlowError::Error),
|
|
|
|
move |imp, element| {
|
2020-03-05 15:59:13 +00:00
|
|
|
if Context::current_has_sub_tasks() {
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
let handler = handler.clone();
|
|
|
|
let element = element.clone();
|
|
|
|
let delayed_fut = async move {
|
|
|
|
let imp =
|
|
|
|
<H::ElementImpl as ObjectSubclass>::from_instance(&element);
|
|
|
|
let this_ref =
|
|
|
|
this_weak.upgrade().ok_or(gst::FlowError::Flushing)?;
|
|
|
|
handler.sink_chain(&this_ref, imp, &element, buffer).await
|
|
|
|
};
|
|
|
|
let _ = Context::add_sub_task(delayed_fut.map(|res| res.map(drop)));
|
|
|
|
|
|
|
|
Ok(gst::FlowSuccess::Ok)
|
|
|
|
} else {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSink no longer exists");
|
|
|
|
let chain_fut = handler.sink_chain(&this_ref, imp, &element, buffer);
|
|
|
|
this_ref.handle_future(chain_fut)
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_chain_list_function(move |_gst_pad, parent, list| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| Err(FlowError::Error),
|
|
|
|
move |imp, element| {
|
2020-03-05 15:59:13 +00:00
|
|
|
if Context::current_has_sub_tasks() {
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
let handler = handler.clone();
|
|
|
|
let element = element.clone();
|
|
|
|
let delayed_fut = async move {
|
|
|
|
let imp =
|
|
|
|
<H::ElementImpl as ObjectSubclass>::from_instance(&element);
|
|
|
|
let this_ref =
|
|
|
|
this_weak.upgrade().ok_or(gst::FlowError::Flushing)?;
|
|
|
|
handler
|
|
|
|
.sink_chain_list(&this_ref, imp, &element, list)
|
|
|
|
.await
|
|
|
|
};
|
|
|
|
let _ = Context::add_sub_task(delayed_fut.map(|res| res.map(drop)));
|
|
|
|
|
|
|
|
Ok(gst::FlowSuccess::Ok)
|
|
|
|
} else {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSink no longer exists");
|
|
|
|
let chain_list_fut =
|
|
|
|
handler.sink_chain_list(&this_ref, imp, &element, list);
|
|
|
|
this_ref.handle_future(chain_list_fut)
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
// No need to `set_event_function` since `set_event_full_function`
|
|
|
|
// overrides it and dispatches to `sink_event` when necessary
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
2020-03-05 15:59:13 +00:00
|
|
|
.set_event_full_function(move |_gst_pad, parent, event| {
|
2019-12-02 09:30:07 +00:00
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| Err(FlowError::Error),
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSink no longer exists");
|
2020-03-05 15:59:13 +00:00
|
|
|
if event.is_serialized() {
|
|
|
|
if Context::current_has_sub_tasks() {
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
let handler = handler.clone();
|
|
|
|
let element = element.clone();
|
|
|
|
let delayed_fut = async move {
|
|
|
|
let imp =
|
|
|
|
<H::ElementImpl as ObjectSubclass>::from_instance(&element);
|
|
|
|
let this_ref =
|
|
|
|
this_weak.upgrade().ok_or(gst::FlowError::Flushing)?;
|
|
|
|
|
|
|
|
handler
|
|
|
|
.sink_event_full_serialized(&this_ref, imp, &element, event)
|
|
|
|
.await
|
|
|
|
};
|
|
|
|
let _ = Context::add_sub_task(delayed_fut.map(|res| res.map(drop)));
|
|
|
|
|
|
|
|
Ok(gst::FlowSuccess::Ok)
|
|
|
|
} else {
|
|
|
|
let event_fut = handler
|
|
|
|
.sink_event_full_serialized(&this_ref, imp, &element, event);
|
|
|
|
this_ref.handle_future(event_fut)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
handler.sink_event_full(&this_ref, imp, &element, event)
|
2019-12-02 09:30:07 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
let handler_clone = handler.clone();
|
|
|
|
let this_weak = self.downgrade();
|
|
|
|
self.gst_pad()
|
|
|
|
.set_query_function(move |_gst_pad, parent, query| {
|
|
|
|
let handler = handler_clone.clone();
|
|
|
|
let this_weak = this_weak.clone();
|
|
|
|
H::ElementImpl::catch_panic_pad_function(
|
|
|
|
parent,
|
|
|
|
|| false,
|
|
|
|
move |imp, element| {
|
|
|
|
let this_ref = this_weak.upgrade().expect("PadSink no longer exists");
|
2020-01-02 21:32:52 +00:00
|
|
|
if !query.is_serialized() {
|
2020-01-06 12:19:11 +00:00
|
|
|
handler.sink_query(&this_ref, imp, &element, query)
|
2020-01-02 21:32:52 +00:00
|
|
|
} else {
|
|
|
|
gst_fixme!(RUNTIME_CAT, obj: this_ref.gst_pad(), "Serialized Query not supported");
|
|
|
|
false
|
|
|
|
}
|
2019-12-02 09:30:07 +00:00
|
|
|
},
|
|
|
|
)
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn prepare<H: PadSinkHandler>(&self, handler: &H) {
|
2019-12-02 09:30:07 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Preparing");
|
|
|
|
self.init_pad_functions(handler);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Releases the resources held by this `PadSink`.
|
2020-03-05 15:59:13 +00:00
|
|
|
pub fn unprepare(&self) {
|
2019-12-02 09:30:07 +00:00
|
|
|
gst_log!(RUNTIME_CAT, obj: self.gst_pad(), "Unpreparing");
|
|
|
|
|
|
|
|
self.gst_pad()
|
2020-03-05 15:59:13 +00:00
|
|
|
.set_chain_function(move |_gst_pad, _parent, _buffer| Err(FlowError::Flushing));
|
2019-12-02 09:30:07 +00:00
|
|
|
self.gst_pad()
|
2020-03-05 15:59:13 +00:00
|
|
|
.set_chain_list_function(move |_gst_pad, _parent, _list| Err(FlowError::Flushing));
|
2019-12-02 09:30:07 +00:00
|
|
|
self.gst_pad()
|
|
|
|
.set_event_function(move |_gst_pad, _parent, _event| false);
|
|
|
|
self.gst_pad()
|
2020-03-05 15:59:13 +00:00
|
|
|
.set_event_full_function(move |_gst_pad, _parent, _event| Err(FlowError::Flushing));
|
2019-12-02 09:30:07 +00:00
|
|
|
self.gst_pad()
|
|
|
|
.set_query_function(move |_gst_pad, _parent, _query| false);
|
|
|
|
}
|
|
|
|
}
|