2019-08-14 18:02:28 +00:00
|
|
|
// 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.
|
|
|
|
|
2021-06-03 18:20:54 +00:00
|
|
|
use gst::glib;
|
|
|
|
|
2020-04-24 11:33:21 +00:00
|
|
|
use gst_base::prelude::*;
|
|
|
|
use gst_base::subclass::prelude::*;
|
|
|
|
|
2020-11-25 13:52:21 +00:00
|
|
|
use gst::{gst_debug, gst_error, gst_info, gst_log, gst_warning};
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2020-04-07 20:47:15 +00:00
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
use std::sync::{Mutex, RwLock};
|
|
|
|
|
2020-11-25 13:52:21 +00:00
|
|
|
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone, Copy, glib::GEnum)]
|
2020-09-24 20:09:01 +00:00
|
|
|
#[repr(u32)]
|
|
|
|
#[genum(type_name = "GstFallbackSwitchStreamHealth")]
|
|
|
|
pub(crate) enum StreamHealth {
|
|
|
|
#[genum(name = "Data flow is inactive or late", nick = "inactive")]
|
|
|
|
Inactive = 0,
|
|
|
|
#[genum(name = "Data is currently flowing in the stream", nick = "present")]
|
|
|
|
Present = 1,
|
|
|
|
}
|
|
|
|
|
2020-11-15 12:08:54 +00:00
|
|
|
pub struct FallbackSwitch {
|
2020-09-24 20:09:01 +00:00
|
|
|
primary_sinkpad: gst_base::AggregatorPad,
|
|
|
|
primary_state: RwLock<PadInputState>,
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
fallback_sinkpad: RwLock<Option<gst_base::AggregatorPad>>,
|
2020-09-24 20:09:01 +00:00
|
|
|
fallback_state: RwLock<PadInputState>,
|
|
|
|
|
2020-07-02 10:50:42 +00:00
|
|
|
active_sinkpad: Mutex<Option<gst::Pad>>,
|
2019-08-14 18:02:28 +00:00
|
|
|
output_state: Mutex<OutputState>,
|
|
|
|
settings: Mutex<Settings>,
|
|
|
|
}
|
|
|
|
|
2020-04-07 20:47:15 +00:00
|
|
|
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
|
|
|
gst::DebugCategory::new(
|
2019-10-31 22:34:21 +00:00
|
|
|
"fallbackswitch",
|
|
|
|
gst::DebugColorFlags::empty(),
|
|
|
|
Some("Fallback switch Element"),
|
2020-04-07 20:47:15 +00:00
|
|
|
)
|
|
|
|
});
|
2019-10-31 22:34:21 +00:00
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
struct PadOutputState {
|
2021-05-28 16:35:28 +00:00
|
|
|
last_sinkpad_time: Option<gst::ClockTime>,
|
2020-09-24 20:09:01 +00:00
|
|
|
stream_health: StreamHealth,
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct OutputState {
|
2021-05-28 16:35:28 +00:00
|
|
|
last_output_time: Option<gst::ClockTime>,
|
2020-09-24 20:09:01 +00:00
|
|
|
primary: PadOutputState,
|
|
|
|
fallback: PadOutputState,
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Default)]
|
2020-09-24 20:09:01 +00:00
|
|
|
struct PadInputState {
|
2019-08-14 18:02:28 +00:00
|
|
|
caps: Option<gst::Caps>,
|
|
|
|
audio_info: Option<gst_audio::AudioInfo>,
|
|
|
|
video_info: Option<gst_video::VideoInfo>,
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
const DEFAULT_TIMEOUT: gst::ClockTime = gst::ClockTime::from_seconds(5);
|
2020-09-24 20:09:01 +00:00
|
|
|
const DEFAULT_AUTO_SWITCH: bool = true;
|
|
|
|
const DEFAULT_STREAM_HEALTH: StreamHealth = StreamHealth::Inactive;
|
2021-05-21 20:28:06 +00:00
|
|
|
const DEFAULT_IMMEDIATE_FALLBACK: bool = false;
|
2019-08-14 18:02:28 +00:00
|
|
|
|
|
|
|
#[derive(Debug, Clone)]
|
|
|
|
struct Settings {
|
|
|
|
timeout: gst::ClockTime,
|
2020-09-24 20:09:01 +00:00
|
|
|
auto_switch: bool,
|
2021-05-21 20:28:06 +00:00
|
|
|
immediate_fallback: bool,
|
2020-09-24 20:09:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for StreamHealth {
|
|
|
|
fn default() -> Self {
|
|
|
|
DEFAULT_STREAM_HEALTH
|
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for OutputState {
|
|
|
|
fn default() -> Self {
|
|
|
|
OutputState {
|
2021-05-28 16:35:28 +00:00
|
|
|
last_output_time: gst::ClockTime::NONE,
|
2020-09-24 20:09:01 +00:00
|
|
|
primary: PadOutputState::default(),
|
|
|
|
fallback: PadOutputState::default(),
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Settings {
|
|
|
|
fn default() -> Self {
|
|
|
|
Settings {
|
2021-05-28 16:35:28 +00:00
|
|
|
timeout: DEFAULT_TIMEOUT,
|
2020-09-24 20:09:01 +00:00
|
|
|
auto_switch: DEFAULT_AUTO_SWITCH,
|
2021-05-21 20:28:06 +00:00
|
|
|
immediate_fallback: DEFAULT_IMMEDIATE_FALLBACK,
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-11 12:19:56 +00:00
|
|
|
impl OutputState {
|
2021-09-08 12:33:31 +00:00
|
|
|
#[allow(clippy::blocks_in_if_conditions)]
|
2021-04-20 12:57:40 +00:00
|
|
|
fn health(
|
2020-12-11 12:19:56 +00:00
|
|
|
&self,
|
|
|
|
settings: &Settings,
|
|
|
|
check_primary_pad: bool,
|
2021-05-28 16:35:28 +00:00
|
|
|
cur_running_time: impl Into<Option<gst::ClockTime>>,
|
2020-12-11 12:19:56 +00:00
|
|
|
) -> StreamHealth {
|
|
|
|
let last_sinkpad_time = if check_primary_pad {
|
|
|
|
self.primary.last_sinkpad_time
|
|
|
|
} else {
|
|
|
|
self.fallback.last_sinkpad_time
|
|
|
|
};
|
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
if last_sinkpad_time.is_none() {
|
2020-12-11 12:19:56 +00:00
|
|
|
StreamHealth::Inactive
|
2021-05-28 16:35:28 +00:00
|
|
|
} else if cur_running_time.into().map_or(false, |cur_running_time| {
|
|
|
|
cur_running_time < last_sinkpad_time.expect("checked above") + settings.timeout
|
|
|
|
}) {
|
2020-12-11 12:19:56 +00:00
|
|
|
StreamHealth::Present
|
|
|
|
} else {
|
|
|
|
StreamHealth::Inactive
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn check_health_changes(
|
|
|
|
&mut self,
|
|
|
|
settings: &Settings,
|
|
|
|
backup_pad: &Option<&gst_base::AggregatorPad>,
|
|
|
|
preferred_is_primary: bool,
|
2021-05-28 16:35:28 +00:00
|
|
|
cur_running_time: impl Into<Option<gst::ClockTime>> + Copy,
|
2020-12-11 12:19:56 +00:00
|
|
|
) -> (bool, bool) {
|
2021-04-20 12:58:11 +00:00
|
|
|
let preferred_health = self.health(settings, preferred_is_primary, cur_running_time);
|
2020-12-11 12:19:56 +00:00
|
|
|
let backup_health = if backup_pad.is_some() {
|
2021-04-20 12:58:11 +00:00
|
|
|
self.health(settings, !preferred_is_primary, cur_running_time)
|
2020-12-11 12:19:56 +00:00
|
|
|
} else {
|
|
|
|
StreamHealth::Inactive
|
|
|
|
};
|
|
|
|
|
|
|
|
if preferred_is_primary {
|
|
|
|
let primary_changed = preferred_health != self.primary.stream_health;
|
|
|
|
let fallback_changed = backup_health != self.fallback.stream_health;
|
|
|
|
|
|
|
|
self.primary.stream_health = preferred_health;
|
|
|
|
self.fallback.stream_health = backup_health;
|
|
|
|
|
|
|
|
(primary_changed, fallback_changed)
|
|
|
|
} else {
|
|
|
|
let primary_changed = backup_health != self.primary.stream_health;
|
|
|
|
let fallback_changed = preferred_health != self.fallback.stream_health;
|
|
|
|
|
|
|
|
self.primary.stream_health = backup_health;
|
|
|
|
self.fallback.stream_health = preferred_health;
|
|
|
|
|
|
|
|
(primary_changed, fallback_changed)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
impl FallbackSwitch {
|
2020-09-24 20:09:01 +00:00
|
|
|
fn drain_pad_to_time(
|
|
|
|
&self,
|
|
|
|
state: &mut OutputState,
|
|
|
|
pad: &gst_base::AggregatorPad,
|
2021-05-28 16:35:28 +00:00
|
|
|
target_running_time: impl Into<Option<gst::ClockTime>> + Copy,
|
2020-09-24 20:09:01 +00:00
|
|
|
) -> Result<(), gst::FlowError> {
|
2021-04-12 12:49:54 +00:00
|
|
|
let segment = pad.segment();
|
2020-09-24 20:09:01 +00:00
|
|
|
|
|
|
|
/* No segment yet - no data */
|
2021-04-12 12:49:54 +00:00
|
|
|
if segment.format() == gst::Format::Undefined {
|
2020-09-24 20:09:01 +00:00
|
|
|
return Ok(());
|
|
|
|
}
|
|
|
|
|
|
|
|
let segment = segment.downcast::<gst::ClockTime>().map_err(|_| {
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_error!(CAT, obj: pad, "Only TIME segments supported");
|
2020-09-24 20:09:01 +00:00
|
|
|
gst::FlowError::Error
|
|
|
|
})?;
|
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
let mut running_time = gst::ClockTime::NONE;
|
2020-09-24 20:09:01 +00:00
|
|
|
|
|
|
|
while let Some(buffer) = pad.peek_buffer() {
|
2021-04-12 12:49:54 +00:00
|
|
|
let pts = buffer.dts_or_pts();
|
2020-09-24 20:09:01 +00:00
|
|
|
let new_running_time = segment.to_running_time(pts);
|
2021-02-04 16:06:19 +00:00
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
if pts.is_none()
|
|
|
|
|| new_running_time
|
2021-10-09 10:17:05 +00:00
|
|
|
.opt_le(target_running_time.into())
|
|
|
|
.unwrap_or(false)
|
2021-05-28 16:35:28 +00:00
|
|
|
{
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_debug!(CAT, obj: pad, "Dropping trailing buffer {:?}", buffer);
|
2020-09-24 20:09:01 +00:00
|
|
|
pad.drop_buffer();
|
|
|
|
running_time = new_running_time;
|
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2021-05-28 16:35:28 +00:00
|
|
|
if running_time.is_some() {
|
2020-09-24 20:09:01 +00:00
|
|
|
if pad == &self.primary_sinkpad {
|
|
|
|
state.primary.last_sinkpad_time = running_time;
|
|
|
|
} else {
|
|
|
|
state.fallback.last_sinkpad_time = running_time;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(clippy::too_many_arguments)]
|
2019-08-14 18:02:28 +00:00
|
|
|
fn handle_main_buffer(
|
|
|
|
&self,
|
2020-11-15 12:08:54 +00:00
|
|
|
agg: &super::FallbackSwitch,
|
2019-08-14 18:02:28 +00:00
|
|
|
state: &mut OutputState,
|
2020-07-03 13:52:21 +00:00
|
|
|
settings: &Settings,
|
2019-09-18 11:29:55 +00:00
|
|
|
mut buffer: gst::Buffer,
|
2020-09-24 20:09:01 +00:00
|
|
|
preferred_pad: &gst_base::AggregatorPad,
|
|
|
|
backup_pad: &Option<&gst_base::AggregatorPad>,
|
2021-05-28 16:35:28 +00:00
|
|
|
cur_running_time: impl Into<Option<gst::ClockTime>>,
|
2019-09-18 11:24:23 +00:00
|
|
|
) -> Result<Option<(gst::Buffer, gst::Caps, bool)>, gst::FlowError> {
|
2019-08-14 18:02:28 +00:00
|
|
|
// If we got a buffer on the sinkpad just handle it
|
2020-09-24 20:09:01 +00:00
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
2020-12-11 12:33:56 +00:00
|
|
|
obj: preferred_pad,
|
2020-09-24 20:09:01 +00:00
|
|
|
"Got buffer on pad {} - {:?}",
|
2021-04-12 12:49:54 +00:00
|
|
|
preferred_pad.name(),
|
2020-09-24 20:09:01 +00:00
|
|
|
buffer
|
|
|
|
);
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
if buffer.pts().is_none() {
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_error!(CAT, obj: preferred_pad, "Only buffers with PTS supported");
|
2019-08-14 18:02:28 +00:00
|
|
|
return Err(gst::FlowError::Error);
|
|
|
|
}
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
let segment = preferred_pad
|
2021-04-12 12:49:54 +00:00
|
|
|
.segment()
|
2019-09-18 10:50:48 +00:00
|
|
|
.downcast::<gst::ClockTime>()
|
|
|
|
.map_err(|_| {
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_error!(CAT, obj: preferred_pad, "Only TIME segments supported");
|
2019-09-18 10:50:48 +00:00
|
|
|
gst::FlowError::Error
|
|
|
|
})?;
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
let running_time = segment.to_running_time(buffer.dts_or_pts());
|
2020-09-24 20:09:01 +00:00
|
|
|
|
2019-09-18 11:29:55 +00:00
|
|
|
{
|
|
|
|
// FIXME: This will not work correctly for negative DTS
|
|
|
|
let buffer = buffer.make_mut();
|
2021-04-12 12:49:54 +00:00
|
|
|
buffer.set_pts(segment.to_running_time(buffer.pts()));
|
|
|
|
buffer.set_dts(segment.to_running_time(buffer.dts()));
|
2019-09-18 11:29:55 +00:00
|
|
|
}
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
if preferred_pad == &self.primary_sinkpad {
|
|
|
|
state.primary.last_sinkpad_time = running_time;
|
|
|
|
} else {
|
|
|
|
state.fallback.last_sinkpad_time = running_time;
|
|
|
|
}
|
2020-07-03 13:52:21 +00:00
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
let cur_running_time = cur_running_time.into();
|
2021-10-09 10:17:05 +00:00
|
|
|
let (is_late, deadline) = match (cur_running_time, agg.latency(), running_time) {
|
|
|
|
(Some(cur_running_time), Some(latency), Some(running_time)) => {
|
|
|
|
let deadline = running_time + latency + 40 * gst::ClockTime::MSECOND;
|
|
|
|
(cur_running_time > deadline, Some(deadline))
|
|
|
|
}
|
|
|
|
_ => (false, None),
|
|
|
|
};
|
2020-07-03 13:52:21 +00:00
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
if is_late {
|
2020-07-03 13:52:21 +00:00
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
2020-12-11 12:33:56 +00:00
|
|
|
obj: preferred_pad,
|
2021-05-28 16:35:28 +00:00
|
|
|
"Buffer is too late: {} > {}",
|
|
|
|
cur_running_time.display(),
|
|
|
|
deadline.display(),
|
2020-07-03 13:52:21 +00:00
|
|
|
);
|
|
|
|
|
2021-10-09 10:17:05 +00:00
|
|
|
let is_late = state
|
|
|
|
.last_output_time
|
|
|
|
.opt_add(settings.timeout)
|
|
|
|
.opt_le(running_time);
|
|
|
|
|
|
|
|
if let Some(true) = is_late {
|
2021-05-28 16:35:28 +00:00
|
|
|
/* This buffer arrived too late - we either already switched
|
|
|
|
* to the other pad or there's no point outputting this anyway */
|
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: preferred_pad,
|
|
|
|
"Buffer is too late and timeout reached: {} + {} <= {}",
|
|
|
|
state.last_output_time.display(),
|
|
|
|
settings.timeout,
|
|
|
|
running_time.display(),
|
|
|
|
);
|
|
|
|
|
|
|
|
return Ok(None);
|
|
|
|
}
|
2020-07-03 13:52:21 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
let mut active_sinkpad = self.active_sinkpad.lock().unwrap();
|
2020-09-24 20:09:01 +00:00
|
|
|
let pad_change = settings.auto_switch
|
|
|
|
&& active_sinkpad.as_ref() != Some(preferred_pad.upcast_ref::<gst::Pad>());
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
if pad_change {
|
2021-04-12 12:49:54 +00:00
|
|
|
if buffer.flags().contains(gst::BufferFlags::DELTA_UNIT) {
|
2019-09-18 11:24:23 +00:00
|
|
|
gst_info!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2020-12-11 12:33:56 +00:00
|
|
|
obj: preferred_pad,
|
2020-09-24 20:09:01 +00:00
|
|
|
"Can't change back to sinkpad {}, waiting for keyframe",
|
2021-04-12 12:49:54 +00:00
|
|
|
preferred_pad.name()
|
2019-09-18 11:24:23 +00:00
|
|
|
);
|
2020-09-24 20:09:01 +00:00
|
|
|
preferred_pad.push_event(
|
2020-06-25 16:49:07 +00:00
|
|
|
gst_video::UpstreamForceKeyUnitEvent::builder()
|
2019-09-18 11:24:23 +00:00
|
|
|
.all_headers(true)
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
return Ok(None);
|
|
|
|
}
|
|
|
|
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_info!(CAT, obj: preferred_pad, "Active pad changed to sinkpad");
|
2020-09-24 20:09:01 +00:00
|
|
|
*active_sinkpad = Some(preferred_pad.clone().upcast());
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
drop(active_sinkpad);
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
if !is_late || state.last_output_time.is_none() {
|
|
|
|
state.last_output_time = running_time;
|
2020-07-03 13:52:21 +00:00
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
let active_caps = if preferred_pad == &self.primary_sinkpad {
|
|
|
|
let pad_state = self.primary_state.read().unwrap();
|
|
|
|
pad_state.caps.as_ref().unwrap().clone()
|
|
|
|
} else {
|
|
|
|
let pad_state = self.fallback_state.read().unwrap();
|
|
|
|
pad_state.caps.as_ref().unwrap().clone()
|
|
|
|
};
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
// Drop all older buffers from the fallback sinkpad
|
2020-09-24 20:09:01 +00:00
|
|
|
if let Some(backup_pad) = backup_pad {
|
2021-07-30 10:53:35 +00:00
|
|
|
self.drain_pad_to_time(state, backup_pad, state.last_output_time)?;
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
2019-09-18 11:24:23 +00:00
|
|
|
Ok(Some((buffer, active_caps, pad_change)))
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
2021-04-20 12:57:40 +00:00
|
|
|
fn backup_buffer(
|
2019-08-14 18:02:28 +00:00
|
|
|
&self,
|
|
|
|
state: &mut OutputState,
|
|
|
|
settings: &Settings,
|
2020-09-24 20:09:01 +00:00
|
|
|
backup_pad: &gst_base::AggregatorPad,
|
2019-08-14 18:02:28 +00:00
|
|
|
) -> 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 {
|
2020-09-24 20:09:01 +00:00
|
|
|
let mut buffer = backup_pad
|
2019-09-18 10:50:48 +00:00
|
|
|
.pop_buffer()
|
|
|
|
.ok_or(gst_base::AGGREGATOR_FLOW_NEED_DATA)?;
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: backup_pad,
|
|
|
|
"Got buffer on fallback sinkpad {:?}",
|
|
|
|
buffer
|
|
|
|
);
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
if buffer.pts().is_none() {
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_error!(CAT, obj: backup_pad, "Only buffers with PTS supported");
|
2019-08-14 18:02:28 +00:00
|
|
|
return Err(gst::FlowError::Error);
|
|
|
|
}
|
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
let backup_segment =
|
|
|
|
backup_pad
|
|
|
|
.segment()
|
|
|
|
.downcast::<gst::ClockTime>()
|
|
|
|
.map_err(|_| {
|
|
|
|
gst_error!(CAT, obj: backup_pad, "Only TIME segments supported");
|
|
|
|
gst::FlowError::Error
|
|
|
|
})?;
|
|
|
|
let running_time = backup_segment.to_running_time(buffer.dts_or_pts());
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2019-09-18 11:29:55 +00:00
|
|
|
{
|
|
|
|
// FIXME: This will not work correctly for negative DTS
|
|
|
|
let buffer = buffer.make_mut();
|
2021-04-12 12:49:54 +00:00
|
|
|
buffer.set_pts(backup_segment.to_running_time(buffer.pts()));
|
|
|
|
buffer.set_dts(backup_segment.to_running_time(buffer.dts()));
|
2019-09-18 11:29:55 +00:00
|
|
|
}
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
// 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
|
2020-09-24 20:09:01 +00:00
|
|
|
if state.last_output_time.is_none() {
|
|
|
|
state.last_output_time = running_time;
|
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-05-21 20:28:06 +00:00
|
|
|
// If the other pad never received a buffer, we want to start consuming
|
|
|
|
// buffers on this pad in order to provide an output at start up
|
|
|
|
// (for example with a slow primary)
|
|
|
|
let ignore_timeout = settings.immediate_fallback && {
|
|
|
|
if backup_pad == &self.primary_sinkpad {
|
|
|
|
state.primary.last_sinkpad_time = running_time;
|
|
|
|
state.fallback.last_sinkpad_time.is_none()
|
|
|
|
} else {
|
|
|
|
state.fallback.last_sinkpad_time = running_time;
|
|
|
|
state.primary.last_sinkpad_time.is_none()
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if !ignore_timeout {
|
2021-10-09 10:17:05 +00:00
|
|
|
let timed_out = state
|
|
|
|
.last_output_time
|
|
|
|
.opt_add(settings.timeout)
|
|
|
|
.opt_le(running_time)
|
|
|
|
.unwrap_or(true);
|
2021-09-08 12:33:31 +00:00
|
|
|
|
|
|
|
// Get the next one if this one is before the timeout
|
|
|
|
if !timed_out {
|
2021-05-21 20:28:06 +00:00
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: backup_pad,
|
|
|
|
"Timeout not reached yet: {} + {} > {}",
|
|
|
|
state.last_output_time.display(),
|
|
|
|
settings.timeout,
|
|
|
|
running_time.display(),
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
gst_debug!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2020-12-11 12:33:56 +00:00
|
|
|
obj: backup_pad,
|
2021-05-21 20:28:06 +00:00
|
|
|
"Timeout reached: {} + {} <= {}",
|
2021-05-28 16:35:28 +00:00
|
|
|
state.last_output_time.display(),
|
2019-08-14 18:02:28 +00:00
|
|
|
settings.timeout,
|
2021-05-28 16:35:28 +00:00
|
|
|
running_time.display(),
|
2019-08-14 18:02:28 +00:00
|
|
|
);
|
2021-05-21 20:28:06 +00:00
|
|
|
} else {
|
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: backup_pad,
|
|
|
|
"Consuming buffer as we haven't yet received a buffer on the other pad",
|
|
|
|
);
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let mut active_sinkpad = self.active_sinkpad.lock().unwrap();
|
2020-09-24 20:09:01 +00:00
|
|
|
let pad_change = settings.auto_switch
|
|
|
|
&& active_sinkpad.as_ref() != Some(backup_pad.upcast_ref::<gst::Pad>());
|
2019-08-14 18:02:28 +00:00
|
|
|
if pad_change {
|
2021-04-12 12:49:54 +00:00
|
|
|
if buffer.flags().contains(gst::BufferFlags::DELTA_UNIT) {
|
2019-09-18 11:24:23 +00:00
|
|
|
gst_info!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2020-12-11 12:33:56 +00:00
|
|
|
obj: backup_pad,
|
2020-09-24 20:09:01 +00:00
|
|
|
"Can't change to sinkpad {} yet, waiting for keyframe",
|
2021-04-12 12:49:54 +00:00
|
|
|
backup_pad.name()
|
2019-09-18 11:24:23 +00:00
|
|
|
);
|
2020-09-24 20:09:01 +00:00
|
|
|
backup_pad.push_event(
|
2020-06-25 16:49:07 +00:00
|
|
|
gst_video::UpstreamForceKeyUnitEvent::builder()
|
2019-09-18 11:24:23 +00:00
|
|
|
.all_headers(true)
|
|
|
|
.build(),
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2020-12-11 12:33:56 +00:00
|
|
|
gst_info!(
|
|
|
|
CAT,
|
|
|
|
obj: backup_pad,
|
|
|
|
"Active pad changed to fallback sinkpad"
|
|
|
|
);
|
2020-09-24 20:09:01 +00:00
|
|
|
*active_sinkpad = Some(backup_pad.clone().upcast());
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
drop(active_sinkpad);
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
let active_caps = if backup_pad == &self.primary_sinkpad {
|
|
|
|
let pad_state = self.primary_state.read().unwrap();
|
|
|
|
pad_state.caps.as_ref().unwrap().clone()
|
|
|
|
} else {
|
|
|
|
let pad_state = self.fallback_state.read().unwrap();
|
|
|
|
pad_state.caps.as_ref().unwrap().clone()
|
2019-08-14 18:02:28 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
break Ok((buffer, active_caps, pad_change));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
#[allow(clippy::type_complexity)]
|
2021-04-20 12:57:40 +00:00
|
|
|
fn next_buffer(
|
2019-08-14 18:02:28 +00:00
|
|
|
&self,
|
2020-11-15 12:08:54 +00:00
|
|
|
agg: &super::FallbackSwitch,
|
2019-08-14 18:02:28 +00:00
|
|
|
timeout: bool,
|
2020-09-24 20:09:01 +00:00
|
|
|
) -> (
|
2020-12-11 12:44:53 +00:00
|
|
|
Result<
|
|
|
|
(
|
|
|
|
gst::Buffer, // Next buffer from the chosen pad
|
|
|
|
gst::Caps, // Caps for the buffer
|
|
|
|
bool, // If the input pad changed to/from primary<->fallback
|
|
|
|
),
|
|
|
|
gst::FlowError,
|
|
|
|
>,
|
|
|
|
(
|
|
|
|
bool, // If the health of the primary pad changed
|
|
|
|
bool, // If the health of the fallback pad changed
|
|
|
|
),
|
2020-09-24 20:09:01 +00:00
|
|
|
) {
|
2019-08-14 18:02:28 +00:00
|
|
|
let settings = self.settings.lock().unwrap().clone();
|
|
|
|
let mut state = self.output_state.lock().unwrap();
|
|
|
|
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_debug!(CAT, obj: agg, "Aggregate called: timeout {}", timeout);
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
if self.primary_sinkpad.is_eos() {
|
|
|
|
gst_log!(CAT, obj: agg, "Sinkpad is EOS");
|
|
|
|
return (Err(gst::FlowError::Eos), (false, false));
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Choose which pad we check first */
|
|
|
|
let active_sinkpad = self.active_sinkpad.lock().unwrap();
|
|
|
|
let prefer_primary = settings.auto_switch
|
|
|
|
|| active_sinkpad.is_none()
|
|
|
|
|| active_sinkpad.as_ref() == Some(self.primary_sinkpad.upcast_ref::<gst::Pad>());
|
|
|
|
drop(active_sinkpad);
|
|
|
|
|
|
|
|
let fallback_sinkpad = self.fallback_sinkpad.read().unwrap();
|
|
|
|
|
|
|
|
let (preferred_pad, backup_pad) = if prefer_primary {
|
|
|
|
(&self.primary_sinkpad, fallback_sinkpad.as_ref())
|
|
|
|
} else {
|
|
|
|
(
|
|
|
|
fallback_sinkpad.as_ref().unwrap(),
|
|
|
|
Some(&self.primary_sinkpad),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
let clock = agg.clock();
|
|
|
|
let base_time = agg.base_time();
|
2020-09-24 20:09:01 +00:00
|
|
|
|
|
|
|
let cur_running_time = if let Some(clock) = clock {
|
2021-10-09 10:17:05 +00:00
|
|
|
clock.time().opt_checked_sub(base_time).ok().flatten()
|
2020-09-24 20:09:01 +00:00
|
|
|
} else {
|
2021-05-28 16:35:28 +00:00
|
|
|
gst::ClockTime::NONE
|
2020-09-24 20:09:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/* See if there's a buffer on the preferred pad and output that */
|
|
|
|
if let Some(buffer) = preferred_pad.pop_buffer() {
|
|
|
|
match self.handle_main_buffer(
|
2020-07-03 13:52:21 +00:00
|
|
|
agg,
|
|
|
|
&mut *state,
|
|
|
|
&settings,
|
|
|
|
buffer,
|
2020-09-24 20:09:01 +00:00
|
|
|
preferred_pad,
|
|
|
|
&backup_pad,
|
|
|
|
cur_running_time,
|
|
|
|
) {
|
|
|
|
Ok(Some(res)) => {
|
|
|
|
return (
|
|
|
|
Ok(res),
|
2020-12-11 12:19:56 +00:00
|
|
|
state.check_health_changes(
|
2020-09-24 20:09:01 +00:00
|
|
|
&settings,
|
|
|
|
&backup_pad,
|
2020-12-11 12:19:56 +00:00
|
|
|
prefer_primary,
|
2020-09-24 20:09:01 +00:00
|
|
|
cur_running_time,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
return (
|
|
|
|
Err(e),
|
2020-12-11 12:19:56 +00:00
|
|
|
state.check_health_changes(
|
2020-09-24 20:09:01 +00:00
|
|
|
&settings,
|
|
|
|
&backup_pad,
|
2020-12-11 12:19:56 +00:00
|
|
|
prefer_primary,
|
2020-09-24 20:09:01 +00:00
|
|
|
cur_running_time,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
_ => (),
|
2019-09-18 11:24:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
/* If we can't auto-switch, then can't fetch anything from the backup pad */
|
|
|
|
if !settings.auto_switch {
|
2021-02-04 16:06:19 +00:00
|
|
|
/* Not switching, but backup pad needs draining of late buffers still */
|
|
|
|
gst_log!(
|
|
|
|
CAT,
|
|
|
|
obj: agg,
|
|
|
|
"No primary buffer, but can't autoswitch - draining backup pad"
|
|
|
|
);
|
2020-09-24 20:09:01 +00:00
|
|
|
if let Some(backup_pad) = &backup_pad {
|
2021-07-30 10:53:35 +00:00
|
|
|
if let Err(e) = self.drain_pad_to_time(&mut *state, backup_pad, cur_running_time) {
|
2020-09-24 20:09:01 +00:00
|
|
|
return (
|
|
|
|
Err(e),
|
2020-12-11 12:19:56 +00:00
|
|
|
state.check_health_changes(
|
2020-09-24 20:09:01 +00:00
|
|
|
&settings,
|
|
|
|
&Some(backup_pad),
|
2020-12-11 12:19:56 +00:00
|
|
|
prefer_primary,
|
2020-09-24 20:09:01 +00:00
|
|
|
cur_running_time,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
return (
|
|
|
|
Err(gst_base::AGGREGATOR_FLOW_NEED_DATA),
|
2020-12-11 12:19:56 +00:00
|
|
|
state.check_health_changes(
|
2020-09-24 20:09:01 +00:00
|
|
|
&settings,
|
|
|
|
&backup_pad,
|
2020-12-11 12:19:56 +00:00
|
|
|
prefer_primary,
|
2020-09-24 20:09:01 +00:00
|
|
|
cur_running_time,
|
|
|
|
),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let (false, Some(backup_pad)) = (timeout, &backup_pad) {
|
|
|
|
gst_debug!(CAT, obj: agg, "Have fallback sinkpad but no timeout yet");
|
|
|
|
(
|
|
|
|
Err(gst_base::AGGREGATOR_FLOW_NEED_DATA),
|
2020-12-11 12:19:56 +00:00
|
|
|
state.check_health_changes(
|
2020-09-24 20:09:01 +00:00
|
|
|
&settings,
|
|
|
|
&Some(backup_pad),
|
2020-12-11 12:19:56 +00:00
|
|
|
prefer_primary,
|
2020-09-24 20:09:01 +00:00
|
|
|
cur_running_time,
|
|
|
|
),
|
|
|
|
)
|
|
|
|
} else if let (true, Some(backup_pad)) = (timeout, &backup_pad) {
|
|
|
|
(
|
2021-04-20 12:58:11 +00:00
|
|
|
self.backup_buffer(&mut *state, &settings, backup_pad),
|
2020-12-11 12:19:56 +00:00
|
|
|
state.check_health_changes(
|
2020-09-24 20:09:01 +00:00
|
|
|
&settings,
|
|
|
|
&Some(backup_pad),
|
2020-12-11 12:19:56 +00:00
|
|
|
prefer_primary,
|
2020-09-24 20:09:01 +00:00
|
|
|
cur_running_time,
|
|
|
|
),
|
|
|
|
)
|
2019-08-14 18:02:28 +00:00
|
|
|
} else {
|
|
|
|
// Otherwise there's not much we can do at this point
|
|
|
|
gst_debug!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2019-08-14 18:02:28 +00:00
|
|
|
obj: agg,
|
|
|
|
"Got no buffer on sinkpad and have no fallback sinkpad"
|
|
|
|
);
|
2020-09-24 20:09:01 +00:00
|
|
|
(
|
|
|
|
Err(gst_base::AGGREGATOR_FLOW_NEED_DATA),
|
2020-12-11 12:19:56 +00:00
|
|
|
state.check_health_changes(
|
2020-09-24 20:09:01 +00:00
|
|
|
&settings,
|
|
|
|
&backup_pad,
|
2020-12-11 12:19:56 +00:00
|
|
|
prefer_primary,
|
2020-09-24 20:09:01 +00:00
|
|
|
cur_running_time,
|
|
|
|
),
|
|
|
|
)
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-07 16:22:24 +00:00
|
|
|
#[glib::object_subclass]
|
2019-08-14 18:02:28 +00:00
|
|
|
impl ObjectSubclass for FallbackSwitch {
|
|
|
|
const NAME: &'static str = "FallbackSwitch";
|
2020-11-15 12:08:54 +00:00
|
|
|
type Type = super::FallbackSwitch;
|
2019-08-14 18:02:28 +00:00
|
|
|
type ParentType = gst_base::Aggregator;
|
|
|
|
|
2020-11-15 12:08:54 +00:00
|
|
|
fn with_class(klass: &Self::Class) -> Self {
|
2021-04-20 12:58:11 +00:00
|
|
|
let templ = klass.pad_template("sink").unwrap();
|
2020-11-15 12:08:54 +00:00
|
|
|
let sinkpad =
|
|
|
|
gst::PadBuilder::<gst_base::AggregatorPad>::from_template(&templ, Some("sink")).build();
|
2019-08-14 18:02:28 +00:00
|
|
|
|
|
|
|
Self {
|
2020-09-24 20:09:01 +00:00
|
|
|
primary_sinkpad: sinkpad,
|
|
|
|
primary_state: RwLock::new(PadInputState::default()),
|
2019-08-14 18:02:28 +00:00
|
|
|
fallback_sinkpad: RwLock::new(None),
|
2020-09-24 20:09:01 +00:00
|
|
|
fallback_state: RwLock::new(PadInputState::default()),
|
2020-07-02 10:50:42 +00:00
|
|
|
active_sinkpad: Mutex::new(None),
|
2019-08-14 18:02:28 +00:00
|
|
|
output_state: Mutex::new(OutputState::default()),
|
|
|
|
settings: Mutex::new(Settings::default()),
|
|
|
|
}
|
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-01-21 18:21:29 +00:00
|
|
|
impl ObjectImpl for FallbackSwitch {
|
|
|
|
fn properties() -> &'static [glib::ParamSpec] {
|
|
|
|
static PROPERTIES: Lazy<Vec<glib::ParamSpec>> = Lazy::new(|| {
|
|
|
|
vec![
|
2021-11-20 10:25:14 +00:00
|
|
|
glib::ParamSpecUInt64::new(
|
2021-01-21 18:21:29 +00:00
|
|
|
"timeout",
|
|
|
|
"Timeout",
|
|
|
|
"Timeout in nanoseconds",
|
|
|
|
0,
|
2021-05-28 16:35:28 +00:00
|
|
|
std::u64::MAX - 1,
|
|
|
|
DEFAULT_TIMEOUT.nseconds() as u64,
|
2021-01-31 12:44:45 +00:00
|
|
|
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_READY,
|
2021-01-21 18:21:29 +00:00
|
|
|
),
|
2021-11-20 10:25:14 +00:00
|
|
|
glib::ParamSpecObject::new(
|
2021-01-21 18:21:29 +00:00
|
|
|
"active-pad",
|
|
|
|
"Active Pad",
|
|
|
|
"Currently active pad. Writes are ignored if auto-switch=true",
|
|
|
|
gst::Pad::static_type(),
|
2021-01-31 12:44:45 +00:00
|
|
|
glib::ParamFlags::READWRITE | gst::PARAM_FLAG_MUTABLE_PLAYING,
|
2021-01-21 18:21:29 +00:00
|
|
|
),
|
2021-11-20 10:25:14 +00:00
|
|
|
glib::ParamSpecBoolean::new(
|
2021-01-21 18:21:29 +00:00
|
|
|
"auto-switch",
|
|
|
|
"Automatically switch pads",
|
|
|
|
"Automatically switch pads (If true, prefer primary sink, otherwise manual selection via the active-pad property)",
|
|
|
|
DEFAULT_AUTO_SWITCH,
|
2021-01-31 12:44:45 +00:00
|
|
|
glib::ParamFlags::READWRITE| gst::PARAM_FLAG_MUTABLE_READY,
|
2021-01-21 18:21:29 +00:00
|
|
|
),
|
2021-11-20 10:25:14 +00:00
|
|
|
glib::ParamSpecEnum::new(
|
2021-01-21 18:21:29 +00:00
|
|
|
"primary-health",
|
|
|
|
"Primary stream state",
|
|
|
|
"Reports the health of the primary stream on the sink pad",
|
|
|
|
StreamHealth::static_type(),
|
|
|
|
DEFAULT_STREAM_HEALTH as i32,
|
|
|
|
glib::ParamFlags::READABLE,
|
|
|
|
),
|
2021-11-20 10:25:14 +00:00
|
|
|
glib::ParamSpecEnum::new(
|
2021-01-21 18:21:29 +00:00
|
|
|
"fallback-health",
|
|
|
|
"Fallback stream state",
|
|
|
|
"Reports the health of the fallback stream on the fallback_sink pad",
|
|
|
|
StreamHealth::static_type(),
|
|
|
|
DEFAULT_STREAM_HEALTH as i32,
|
|
|
|
glib::ParamFlags::READABLE,
|
|
|
|
),
|
2021-11-20 10:25:14 +00:00
|
|
|
glib::ParamSpecBoolean::new(
|
2021-05-21 20:28:06 +00:00
|
|
|
"immediate-fallback",
|
|
|
|
"Immediate fallback",
|
|
|
|
"Forward the fallback stream immediately at startup, when the primary stream is slow to start up and immediate output is required",
|
2021-11-18 18:02:07 +00:00
|
|
|
DEFAULT_IMMEDIATE_FALLBACK,
|
2021-05-21 20:28:06 +00:00
|
|
|
glib::ParamFlags::READWRITE| gst::PARAM_FLAG_MUTABLE_READY,
|
|
|
|
),
|
2021-01-21 18:21:29 +00:00
|
|
|
]
|
|
|
|
});
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-01-21 18:21:29 +00:00
|
|
|
PROPERTIES.as_ref()
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
2020-11-15 12:08:54 +00:00
|
|
|
fn constructed(&self, obj: &Self::Type) {
|
2019-08-14 18:02:28 +00:00
|
|
|
self.parent_constructed(obj);
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
obj.add_pad(&self.primary_sinkpad).unwrap();
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
2021-01-21 18:21:29 +00:00
|
|
|
fn set_property(
|
|
|
|
&self,
|
|
|
|
obj: &Self::Type,
|
|
|
|
_id: usize,
|
|
|
|
value: &glib::Value,
|
|
|
|
pspec: &glib::ParamSpec,
|
|
|
|
) {
|
2021-04-12 12:49:54 +00:00
|
|
|
match pspec.name() {
|
2021-01-21 18:21:29 +00:00
|
|
|
"timeout" => {
|
2019-08-14 18:02:28 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2021-04-25 12:41:22 +00:00
|
|
|
let timeout = value.get().expect("type checked upstream");
|
2019-08-14 18:02:28 +00:00
|
|
|
gst_info!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2020-11-15 12:08:54 +00:00
|
|
|
obj: obj,
|
2019-08-14 18:02:28 +00:00
|
|
|
"Changing timeout from {} to {}",
|
|
|
|
settings.timeout,
|
|
|
|
timeout
|
|
|
|
);
|
|
|
|
settings.timeout = timeout;
|
|
|
|
drop(settings);
|
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"active-pad" => {
|
2020-09-24 20:09:01 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
if settings.auto_switch {
|
|
|
|
gst_warning!(
|
|
|
|
CAT,
|
|
|
|
obj: obj,
|
|
|
|
"active-pad property setting ignored, because auto-switch=true"
|
|
|
|
);
|
|
|
|
} else {
|
2021-04-25 12:41:22 +00:00
|
|
|
let active_pad = value
|
|
|
|
.get::<Option<gst::Pad>>()
|
|
|
|
.expect("type checked upstream");
|
2020-09-24 20:09:01 +00:00
|
|
|
/* Trigger a pad switch if needed */
|
|
|
|
let mut cur_active_pad = self.active_sinkpad.lock().unwrap();
|
|
|
|
if *cur_active_pad != active_pad {
|
|
|
|
*cur_active_pad = active_pad;
|
|
|
|
}
|
|
|
|
drop(cur_active_pad);
|
|
|
|
}
|
|
|
|
drop(settings);
|
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"auto-switch" => {
|
2020-09-24 20:09:01 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2021-04-25 12:41:22 +00:00
|
|
|
settings.auto_switch = value.get().expect("type checked upstream");
|
2020-09-24 20:09:01 +00:00
|
|
|
}
|
2021-05-21 20:28:06 +00:00
|
|
|
"immediate-fallback" => {
|
|
|
|
let mut settings = self.settings.lock().unwrap();
|
|
|
|
settings.immediate_fallback = value.get().expect("type checked upstream");
|
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-20 12:57:40 +00:00
|
|
|
fn property(&self, _obj: &Self::Type, _id: usize, pspec: &glib::ParamSpec) -> glib::Value {
|
2021-04-12 12:49:54 +00:00
|
|
|
match pspec.name() {
|
2021-01-21 18:21:29 +00:00
|
|
|
"timeout" => {
|
2019-08-14 18:02:28 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2020-11-19 15:55:57 +00:00
|
|
|
settings.timeout.to_value()
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"active-pad" => {
|
2019-08-14 18:02:28 +00:00
|
|
|
let active_pad = self.active_sinkpad.lock().unwrap().clone();
|
2020-11-19 15:55:57 +00:00
|
|
|
active_pad.to_value()
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"auto-switch" => {
|
2020-09-24 20:09:01 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2020-11-25 13:52:21 +00:00
|
|
|
settings.auto_switch.to_value()
|
2020-09-24 20:09:01 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"primary-health" => {
|
2020-09-24 20:09:01 +00:00
|
|
|
let state = self.output_state.lock().unwrap();
|
2020-11-25 13:52:21 +00:00
|
|
|
state.primary.stream_health.to_value()
|
2020-09-24 20:09:01 +00:00
|
|
|
}
|
2021-01-21 18:21:29 +00:00
|
|
|
"fallback-health" => {
|
2020-09-24 20:09:01 +00:00
|
|
|
let state = self.output_state.lock().unwrap();
|
2020-11-25 13:52:21 +00:00
|
|
|
state.fallback.stream_health.to_value()
|
2020-09-24 20:09:01 +00:00
|
|
|
}
|
2021-05-21 20:28:06 +00:00
|
|
|
"immediate-fallback" => {
|
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
settings.immediate_fallback.to_value()
|
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
_ => unimplemented!(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-23 08:57:31 +00:00
|
|
|
impl GstObjectImpl for FallbackSwitch {}
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
impl ElementImpl for FallbackSwitch {
|
2021-01-21 18:21:29 +00:00
|
|
|
fn metadata() -> Option<&'static gst::subclass::ElementMetadata> {
|
|
|
|
static ELEMENT_METADATA: Lazy<gst::subclass::ElementMetadata> = Lazy::new(|| {
|
|
|
|
gst::subclass::ElementMetadata::new(
|
|
|
|
"Fallback Switch",
|
|
|
|
"Generic",
|
|
|
|
"Allows switching to a fallback input after a given timeout",
|
|
|
|
"Sebastian Dröge <sebastian@centricular.com>",
|
|
|
|
)
|
|
|
|
});
|
|
|
|
|
|
|
|
Some(&*ELEMENT_METADATA)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn pad_templates() -> &'static [gst::PadTemplate] {
|
|
|
|
static PAD_TEMPLATES: Lazy<Vec<gst::PadTemplate>> = Lazy::new(|| {
|
|
|
|
let caps = gst::Caps::new_any();
|
|
|
|
let src_pad_template = gst::PadTemplate::with_gtype(
|
|
|
|
"src",
|
|
|
|
gst::PadDirection::Src,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
|
|
|
gst_base::AggregatorPad::static_type(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let sink_pad_template = gst::PadTemplate::with_gtype(
|
|
|
|
"sink",
|
|
|
|
gst::PadDirection::Sink,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
|
|
|
gst_base::AggregatorPad::static_type(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
let fallbacksink_pad_template = gst::PadTemplate::with_gtype(
|
|
|
|
"fallback_sink",
|
|
|
|
gst::PadDirection::Sink,
|
|
|
|
gst::PadPresence::Request,
|
|
|
|
&caps,
|
|
|
|
gst_base::AggregatorPad::static_type(),
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
vec![
|
|
|
|
src_pad_template,
|
|
|
|
sink_pad_template,
|
|
|
|
fallbacksink_pad_template,
|
|
|
|
]
|
|
|
|
});
|
|
|
|
|
|
|
|
PAD_TEMPLATES.as_ref()
|
|
|
|
}
|
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
fn request_new_pad(
|
|
|
|
&self,
|
2020-11-15 12:08:54 +00:00
|
|
|
element: &Self::Type,
|
2019-08-14 18:02:28 +00:00
|
|
|
templ: &gst::PadTemplate,
|
|
|
|
name: Option<String>,
|
|
|
|
_caps: Option<&gst::Caps>,
|
|
|
|
) -> Option<gst::Pad> {
|
2021-04-20 12:58:11 +00:00
|
|
|
let fallback_sink_templ = element.pad_template("fallback_sink").unwrap();
|
2019-08-14 18:02:28 +00:00
|
|
|
if templ != &fallback_sink_templ
|
2020-03-19 10:55:29 +00:00
|
|
|
|| (name.is_some() && name.as_deref() != Some("fallback_sink"))
|
2019-08-14 18:02:28 +00:00
|
|
|
{
|
2020-11-15 12:08:54 +00:00
|
|
|
gst_error!(CAT, obj: element, "Wrong pad template or name");
|
2019-08-14 18:02:28 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut fallback_sinkpad = self.fallback_sinkpad.write().unwrap();
|
|
|
|
if fallback_sinkpad.is_some() {
|
2020-11-15 12:08:54 +00:00
|
|
|
gst_error!(CAT, obj: element, "Already have a fallback sinkpad");
|
2019-08-14 18:02:28 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
|
2021-07-30 10:53:35 +00:00
|
|
|
let sinkpad =
|
|
|
|
gst::PadBuilder::<gst_base::AggregatorPad>::from_template(templ, Some("fallback_sink"))
|
|
|
|
.build();
|
2020-09-24 20:09:01 +00:00
|
|
|
|
2019-08-14 18:02:28 +00:00
|
|
|
*fallback_sinkpad = Some(sinkpad.clone());
|
|
|
|
drop(fallback_sinkpad);
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
let mut state = self.output_state.lock().unwrap();
|
|
|
|
state.fallback = PadOutputState::default();
|
|
|
|
drop(state);
|
|
|
|
|
2020-11-15 12:08:54 +00:00
|
|
|
element.add_pad(&sinkpad).unwrap();
|
2019-08-14 18:02:28 +00:00
|
|
|
|
|
|
|
Some(sinkpad.upcast())
|
|
|
|
}
|
|
|
|
|
2020-11-15 12:08:54 +00:00
|
|
|
fn release_pad(&self, element: &Self::Type, pad: &gst::Pad) {
|
2019-08-14 18:02:28 +00:00
|
|
|
let mut fallback_sinkpad = self.fallback_sinkpad.write().unwrap();
|
|
|
|
|
|
|
|
if fallback_sinkpad.as_ref().map(|p| p.upcast_ref()) == Some(pad) {
|
|
|
|
*fallback_sinkpad = None;
|
|
|
|
drop(fallback_sinkpad);
|
2020-11-15 12:08:54 +00:00
|
|
|
element.remove_pad(pad).unwrap();
|
|
|
|
gst_debug!(CAT, obj: element, "Removed fallback sinkpad {:?}", pad);
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
2020-12-11 12:53:11 +00:00
|
|
|
*self.fallback_state.write().unwrap() = PadInputState::default();
|
|
|
|
*self.active_sinkpad.lock().unwrap() = None;
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AggregatorImpl for FallbackSwitch {
|
2020-11-15 12:08:54 +00:00
|
|
|
fn start(&self, _agg: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
2019-08-14 18:02:28 +00:00
|
|
|
*self.output_state.lock().unwrap() = OutputState::default();
|
2020-09-24 20:09:01 +00:00
|
|
|
|
|
|
|
*self.primary_state.write().unwrap() = PadInputState::default();
|
|
|
|
*self.fallback_state.write().unwrap() = PadInputState::default();
|
2019-08-14 18:02:28 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-11-15 12:08:54 +00:00
|
|
|
fn stop(&self, _agg: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
2020-07-02 10:50:42 +00:00
|
|
|
*self.active_sinkpad.lock().unwrap() = None;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-08-01 16:15:19 +00:00
|
|
|
#[cfg(feature = "v1_20")]
|
2019-08-14 18:02:28 +00:00
|
|
|
fn sink_event_pre_queue(
|
|
|
|
&self,
|
2020-11-15 12:08:54 +00:00
|
|
|
agg: &Self::Type,
|
2019-08-14 18:02:28 +00:00
|
|
|
agg_pad: &gst_base::AggregatorPad,
|
|
|
|
event: gst::Event,
|
2020-04-08 12:34:19 +00:00
|
|
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
2019-08-14 18:02:28 +00:00
|
|
|
use gst::EventView;
|
|
|
|
|
|
|
|
match event.view() {
|
2021-08-01 16:15:19 +00:00
|
|
|
EventView::Gap(gap) => {
|
|
|
|
if gap.gap_flags().contains(gst::GapFlags::DATA) {
|
|
|
|
gst_debug!(CAT, obj: agg_pad, "Dropping gap event");
|
|
|
|
Ok(gst::FlowSuccess::Ok)
|
|
|
|
} else {
|
|
|
|
self.parent_sink_event_pre_queue(agg, agg_pad, event)
|
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
_ => self.parent_sink_event_pre_queue(agg, agg_pad, event),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn sink_event(
|
|
|
|
&self,
|
2020-11-15 12:08:54 +00:00
|
|
|
agg: &Self::Type,
|
2019-08-14 18:02:28 +00:00
|
|
|
agg_pad: &gst_base::AggregatorPad,
|
|
|
|
event: gst::Event,
|
|
|
|
) -> bool {
|
|
|
|
use gst::EventView;
|
|
|
|
|
|
|
|
match event.view() {
|
|
|
|
EventView::Caps(caps) => {
|
2021-04-12 12:49:54 +00:00
|
|
|
let caps = caps.caps_owned();
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_debug!(CAT, obj: agg_pad, "Received caps {}", caps);
|
2019-08-14 18:02:28 +00:00
|
|
|
|
|
|
|
let audio_info;
|
|
|
|
let video_info;
|
2021-04-20 12:58:11 +00:00
|
|
|
if caps.structure(0).unwrap().name() == "audio/x-raw" {
|
2019-12-14 15:26:20 +00:00
|
|
|
audio_info = gst_audio::AudioInfo::from_caps(&caps).ok();
|
2019-08-14 18:02:28 +00:00
|
|
|
video_info = None;
|
2021-04-20 12:58:11 +00:00
|
|
|
} else if caps.structure(0).unwrap().name() == "video/x-raw" {
|
2019-08-14 18:02:28 +00:00
|
|
|
audio_info = None;
|
2019-12-15 08:51:12 +00:00
|
|
|
video_info = gst_video::VideoInfo::from_caps(&caps).ok();
|
2019-08-14 18:02:28 +00:00
|
|
|
} else {
|
2019-09-18 11:24:23 +00:00
|
|
|
audio_info = None;
|
|
|
|
video_info = None;
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
let new_pad_state = PadInputState {
|
2019-08-14 18:02:28 +00:00
|
|
|
caps: Some(caps),
|
|
|
|
audio_info,
|
|
|
|
video_info,
|
|
|
|
};
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
if agg_pad == &self.primary_sinkpad {
|
|
|
|
*self.primary_state.write().unwrap() = new_pad_state;
|
2019-08-14 18:02:28 +00:00
|
|
|
} else if Some(agg_pad) == self.fallback_sinkpad.read().unwrap().as_ref() {
|
2020-09-24 20:09:01 +00:00
|
|
|
*self.fallback_state.write().unwrap() = new_pad_state;
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
self.parent_sink_event(agg, agg_pad, event)
|
|
|
|
}
|
|
|
|
_ => self.parent_sink_event(agg, agg_pad, event),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
fn next_time(&self, agg: &Self::Type) -> Option<gst::ClockTime> {
|
2020-09-24 20:09:01 +00:00
|
|
|
/* At each iteration, we have a preferred pad and a backup pad. If autoswitch is true,
|
|
|
|
* the sinkpad is always preferred, otherwise it's the active sinkpad as set by the app.
|
|
|
|
* The backup pad is the other one (may be None if there's no fallback pad yet).
|
|
|
|
*
|
|
|
|
* If we have a buffer on the preferred pad 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 backup sinkpad and it has a buffer, then the timeout is going
|
|
|
|
* to be that buffer's running time. We will then either output the buffer or drop it, depending on
|
|
|
|
* its distance from the last output time
|
|
|
|
*/
|
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
let active_sinkpad = self.active_sinkpad.lock().unwrap();
|
|
|
|
let fallback_sinkpad = self.fallback_sinkpad.read().unwrap();
|
|
|
|
|
|
|
|
let prefer_primary = settings.auto_switch
|
|
|
|
|| active_sinkpad.is_none()
|
|
|
|
|| active_sinkpad.as_ref() == Some(self.primary_sinkpad.upcast_ref::<gst::Pad>());
|
|
|
|
|
|
|
|
let (preferred_pad, backup_pad) = if prefer_primary {
|
|
|
|
(&self.primary_sinkpad, fallback_sinkpad.as_ref())
|
|
|
|
} else {
|
|
|
|
(
|
|
|
|
fallback_sinkpad.as_ref().unwrap(),
|
|
|
|
Some(&self.primary_sinkpad),
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
if preferred_pad.peek_buffer().is_some() {
|
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: agg,
|
|
|
|
"Have buffer on sinkpad {}, immediate timeout",
|
2021-04-12 12:49:54 +00:00
|
|
|
preferred_pad.name()
|
2020-09-24 20:09:01 +00:00
|
|
|
);
|
2021-05-28 16:35:28 +00:00
|
|
|
Some(gst::ClockTime::ZERO)
|
2020-09-24 20:09:01 +00:00
|
|
|
} else if self.primary_sinkpad.is_eos() {
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_debug!(CAT, obj: agg, "Sinkpad is EOS, immediate timeout");
|
2021-05-28 16:35:28 +00:00
|
|
|
Some(gst::ClockTime::ZERO)
|
2020-09-24 20:09:01 +00:00
|
|
|
} else if let Some((buffer, backup_sinkpad)) = backup_pad
|
2019-08-14 18:02:28 +00:00
|
|
|
.as_ref()
|
|
|
|
.and_then(|p| p.peek_buffer().map(|buffer| (buffer, p)))
|
|
|
|
{
|
2021-04-12 12:49:54 +00:00
|
|
|
if buffer.pts().is_none() {
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_error!(CAT, obj: agg, "Only buffers with PTS supported");
|
2019-08-14 18:02:28 +00:00
|
|
|
// Trigger aggregate immediately to error out immediately
|
2021-05-28 16:35:28 +00:00
|
|
|
return Some(gst::ClockTime::ZERO);
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
let segment = match backup_sinkpad.segment().downcast::<gst::ClockTime>() {
|
2019-08-14 18:02:28 +00:00
|
|
|
Ok(segment) => segment,
|
|
|
|
Err(_) => {
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_error!(CAT, obj: agg, "Only TIME segments supported");
|
2019-08-14 18:02:28 +00:00
|
|
|
// Trigger aggregate immediately to error out immediately
|
2021-05-28 16:35:28 +00:00
|
|
|
return Some(gst::ClockTime::ZERO);
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
let running_time = segment.to_running_time(buffer.dts_or_pts());
|
2019-08-14 18:02:28 +00:00
|
|
|
gst_debug!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2019-08-14 18:02:28 +00:00
|
|
|
obj: agg,
|
2020-09-24 20:09:01 +00:00
|
|
|
"Have buffer on {} pad, timeout at {}",
|
2021-04-12 12:49:54 +00:00
|
|
|
backup_sinkpad.name(),
|
2021-05-28 16:35:28 +00:00
|
|
|
running_time.display(),
|
2019-08-14 18:02:28 +00:00
|
|
|
);
|
|
|
|
running_time
|
|
|
|
} else {
|
2020-09-24 20:09:01 +00:00
|
|
|
gst_debug!(CAT, obj: agg, "No buffer available on either input");
|
2021-05-28 16:35:28 +00:00
|
|
|
gst::ClockTime::NONE
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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,
|
2020-11-15 12:08:54 +00:00
|
|
|
agg: &Self::Type,
|
2019-08-14 18:02:28 +00:00
|
|
|
agg_pad: &gst_base::AggregatorPad,
|
|
|
|
mut buffer: gst::Buffer,
|
|
|
|
) -> Option<gst::Buffer> {
|
2021-04-12 12:49:54 +00:00
|
|
|
let segment = match agg_pad.segment().downcast::<gst::ClockTime>() {
|
2019-08-14 18:02:28 +00:00
|
|
|
Ok(segment) => segment,
|
|
|
|
Err(_) => {
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_error!(CAT, obj: agg, "Only TIME segments supported");
|
2019-08-14 18:02:28 +00:00
|
|
|
return Some(buffer);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2021-04-12 12:49:54 +00:00
|
|
|
let pts = buffer.pts();
|
2019-08-14 18:02:28 +00:00
|
|
|
if pts.is_none() {
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_error!(CAT, obj: agg, "Only buffers with PTS supported");
|
2019-08-14 18:02:28 +00:00
|
|
|
return Some(buffer);
|
|
|
|
}
|
|
|
|
|
2020-09-24 20:09:01 +00:00
|
|
|
let primary_state = self.primary_state.read().unwrap();
|
|
|
|
let fallback_state = self.fallback_state.read().unwrap();
|
|
|
|
|
|
|
|
let pad_state = if agg_pad == &self.primary_sinkpad {
|
|
|
|
&primary_state
|
2019-08-14 18:02:28 +00:00
|
|
|
} else if Some(agg_pad) == self.fallback_sinkpad.read().unwrap().as_ref() {
|
2020-09-24 20:09:01 +00:00
|
|
|
&fallback_state
|
2019-08-14 18:02:28 +00:00
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
|
|
|
|
2019-09-18 11:24:23 +00:00
|
|
|
if pad_state.audio_info.is_none() && pad_state.video_info.is_none() {
|
|
|
|
// No clipping possible for non-raw formats
|
|
|
|
return Some(buffer);
|
|
|
|
}
|
|
|
|
|
2021-05-28 16:35:28 +00:00
|
|
|
let duration = if let Some(duration) = buffer.duration() {
|
|
|
|
Some(duration)
|
2019-08-14 18:02:28 +00:00
|
|
|
} else if let Some(ref audio_info) = pad_state.audio_info {
|
2021-05-28 16:35:28 +00:00
|
|
|
gst::ClockTime::SECOND.mul_div_floor(
|
|
|
|
buffer.size() as u64,
|
|
|
|
audio_info.rate() as u64 * audio_info.bpf() as u64,
|
|
|
|
)
|
2019-08-14 18:02:28 +00:00
|
|
|
} else if let Some(ref video_info) = pad_state.video_info {
|
2021-11-06 07:34:10 +00:00
|
|
|
if video_info.fps().numer() > 0 {
|
2021-05-28 16:35:28 +00:00
|
|
|
gst::ClockTime::SECOND.mul_div_floor(
|
2021-11-06 07:34:10 +00:00
|
|
|
video_info.fps().denom() as u64,
|
|
|
|
video_info.fps().numer() as u64,
|
2021-05-28 16:35:28 +00:00
|
|
|
)
|
2019-09-11 19:04:46 +00:00
|
|
|
} else {
|
2021-05-28 16:35:28 +00:00
|
|
|
gst::ClockTime::NONE
|
2019-09-11 19:04:46 +00:00
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
} else {
|
|
|
|
unreachable!()
|
|
|
|
};
|
|
|
|
|
|
|
|
gst_debug!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2019-08-14 18:02:28 +00:00
|
|
|
obj: agg_pad,
|
|
|
|
"Clipping buffer {:?} with PTS {} and duration {}",
|
|
|
|
buffer,
|
2021-05-28 16:35:28 +00:00
|
|
|
pts.display(),
|
|
|
|
duration.display(),
|
2019-08-14 18:02:28 +00:00
|
|
|
);
|
|
|
|
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(),
|
|
|
|
)
|
2019-11-24 22:00:27 +00:00
|
|
|
} else if pad_state.video_info.is_some() {
|
2021-10-09 10:17:05 +00:00
|
|
|
let stop = pts.opt_add(duration);
|
2021-05-28 16:35:28 +00:00
|
|
|
segment.clip(pts, stop).map(|(start, stop)| {
|
2019-08-14 18:02:28 +00:00
|
|
|
{
|
|
|
|
let buffer = buffer.make_mut();
|
|
|
|
buffer.set_pts(start);
|
2019-09-11 19:04:46 +00:00
|
|
|
if duration.is_some() {
|
2021-10-09 10:17:05 +00:00
|
|
|
buffer.set_duration(stop.opt_checked_sub(start).ok().flatten());
|
2019-09-11 19:04:46 +00:00
|
|
|
}
|
2019-08-14 18:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
buffer
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
unreachable!();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn aggregate(
|
|
|
|
&self,
|
2020-11-15 12:08:54 +00:00
|
|
|
agg: &Self::Type,
|
2019-08-14 18:02:28 +00:00
|
|
|
timeout: bool,
|
|
|
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_debug!(CAT, obj: agg, "Aggregate called: timeout {}", timeout);
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-04-20 12:58:11 +00:00
|
|
|
let (res, (primary_health_change, fallback_health_change)) = self.next_buffer(agg, timeout);
|
2020-09-24 20:09:01 +00:00
|
|
|
|
|
|
|
if primary_health_change {
|
2021-02-04 16:06:19 +00:00
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: agg,
|
|
|
|
"Primary pad health now {}",
|
|
|
|
&primary_health_change
|
|
|
|
);
|
2020-09-24 20:09:01 +00:00
|
|
|
agg.notify("primary-health");
|
|
|
|
}
|
|
|
|
if fallback_health_change {
|
2021-02-04 16:06:19 +00:00
|
|
|
gst_debug!(
|
|
|
|
CAT,
|
|
|
|
obj: agg,
|
|
|
|
"Fallback pad health now {}",
|
|
|
|
&fallback_health_change
|
|
|
|
);
|
2020-09-24 20:09:01 +00:00
|
|
|
agg.notify("fallback-health");
|
|
|
|
}
|
|
|
|
|
|
|
|
let (mut buffer, active_caps, pad_change) = res?;
|
2019-08-14 18:02:28 +00:00
|
|
|
|
2021-04-20 12:58:11 +00:00
|
|
|
let current_src_caps = agg.static_pad("src").unwrap().current_caps();
|
2019-08-14 18:02:28 +00:00
|
|
|
if Some(&active_caps) != current_src_caps.as_ref() {
|
|
|
|
gst_info!(
|
2019-10-31 22:34:21 +00:00
|
|
|
CAT,
|
2019-08-14 18:02:28 +00:00
|
|
|
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);
|
|
|
|
}
|
2019-10-31 22:34:21 +00:00
|
|
|
gst_debug!(CAT, obj: agg, "Finishing buffer {:?}", buffer);
|
2019-08-14 18:02:28 +00:00
|
|
|
agg.finish_buffer(buffer)
|
|
|
|
}
|
|
|
|
|
2020-11-15 12:08:54 +00:00
|
|
|
fn negotiate(&self, _agg: &Self::Type) -> bool {
|
2019-08-14 18:02:28 +00:00
|
|
|
true
|
|
|
|
}
|
|
|
|
}
|