2018-04-09 05:53:04 +00:00
|
|
|
use glib;
|
2018-12-11 16:47:03 +00:00
|
|
|
use glib::subclass;
|
2018-04-09 05:53:04 +00:00
|
|
|
use gst;
|
|
|
|
use gst::prelude::*;
|
2018-12-11 16:47:03 +00:00
|
|
|
use gst::subclass::prelude::*;
|
2021-02-23 10:37:24 +00:00
|
|
|
use gst::{gst_debug, gst_element_error, gst_error, gst_error_msg};
|
2018-06-15 13:16:25 +00:00
|
|
|
use gst_audio;
|
2018-12-11 16:47:03 +00:00
|
|
|
use gst_base;
|
2018-04-09 05:53:04 +00:00
|
|
|
use gst_base::prelude::*;
|
2020-07-27 13:08:56 +00:00
|
|
|
use gst_base::subclass::base_src::CreateSuccess;
|
2018-12-11 16:47:03 +00:00
|
|
|
use gst_base::subclass::prelude::*;
|
2018-04-09 05:53:04 +00:00
|
|
|
|
|
|
|
use std::sync::Mutex;
|
|
|
|
use std::{i32, u32};
|
|
|
|
|
2019-07-23 11:20:56 +00:00
|
|
|
use crate::connect_ndi;
|
|
|
|
use crate::ndisys;
|
2019-07-11 18:56:30 +00:00
|
|
|
|
2019-07-23 11:20:56 +00:00
|
|
|
use crate::AudioReceiver;
|
|
|
|
use crate::Receiver;
|
|
|
|
use crate::ReceiverControlHandle;
|
|
|
|
use crate::ReceiverItem;
|
|
|
|
use crate::TimestampMode;
|
|
|
|
use crate::DEFAULT_RECEIVER_NDI_NAME;
|
2018-08-14 13:45:13 +00:00
|
|
|
|
2018-04-12 15:17:59 +00:00
|
|
|
#[derive(Debug, Clone)]
|
2018-04-09 05:53:04 +00:00
|
|
|
struct Settings {
|
2019-07-17 07:40:26 +00:00
|
|
|
ndi_name: Option<String>,
|
2020-01-16 09:14:10 +00:00
|
|
|
url_address: Option<String>,
|
2019-07-17 07:40:26 +00:00
|
|
|
connect_timeout: u32,
|
|
|
|
timeout: u32,
|
|
|
|
receiver_ndi_name: String,
|
|
|
|
bandwidth: ndisys::NDIlib_recv_bandwidth_e,
|
2019-07-16 14:43:03 +00:00
|
|
|
timestamp_mode: TimestampMode,
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for Settings {
|
|
|
|
fn default() -> Self {
|
|
|
|
Settings {
|
2019-07-17 07:40:26 +00:00
|
|
|
ndi_name: None,
|
2020-01-16 09:14:10 +00:00
|
|
|
url_address: None,
|
2019-07-17 07:40:26 +00:00
|
|
|
receiver_ndi_name: DEFAULT_RECEIVER_NDI_NAME.clone(),
|
|
|
|
connect_timeout: 10000,
|
|
|
|
timeout: 5000,
|
|
|
|
bandwidth: ndisys::NDIlib_recv_bandwidth_highest,
|
2019-07-16 14:43:03 +00:00
|
|
|
timestamp_mode: TimestampMode::ReceiveTime,
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-16 09:14:10 +00:00
|
|
|
static PROPERTIES: [subclass::Property; 7] = [
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("ndi-name", |name| {
|
2019-03-26 16:41:28 +00:00
|
|
|
glib::ParamSpec::string(
|
2019-07-16 14:43:03 +00:00
|
|
|
name,
|
2019-07-17 07:40:26 +00:00
|
|
|
"NDI Name",
|
|
|
|
"NDI stream name of the sender",
|
2019-03-26 16:41:28 +00:00
|
|
|
None,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2020-01-16 09:14:10 +00:00
|
|
|
subclass::Property("url-address", |name| {
|
|
|
|
glib::ParamSpec::string(
|
|
|
|
name,
|
|
|
|
"URL/Address",
|
2020-01-16 10:18:57 +00:00
|
|
|
"URL/address and port of the sender, e.g. 127.0.0.1:5961",
|
2020-01-16 09:14:10 +00:00
|
|
|
None,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("receiver-ndi-name", |name| {
|
|
|
|
glib::ParamSpec::string(
|
|
|
|
name,
|
|
|
|
"Receiver NDI Name",
|
|
|
|
"NDI stream name of this receiver",
|
|
|
|
Some(&*DEFAULT_RECEIVER_NDI_NAME),
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
subclass::Property("connect-timeout", |name| {
|
|
|
|
glib::ParamSpec::uint(
|
|
|
|
name,
|
|
|
|
"Connect Timeout",
|
|
|
|
"Connection timeout in ms",
|
|
|
|
0,
|
|
|
|
u32::MAX,
|
|
|
|
10000,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
subclass::Property("timeout", |name| {
|
2019-03-26 16:41:28 +00:00
|
|
|
glib::ParamSpec::uint(
|
2019-07-16 14:43:03 +00:00
|
|
|
name,
|
2019-07-17 07:40:26 +00:00
|
|
|
"Timeout",
|
|
|
|
"Receive timeout in ms",
|
2019-03-26 16:41:28 +00:00
|
|
|
0,
|
2019-07-17 07:40:26 +00:00
|
|
|
u32::MAX,
|
|
|
|
5000,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
|
|
|
subclass::Property("bandwidth", |name| {
|
|
|
|
glib::ParamSpec::int(
|
|
|
|
name,
|
|
|
|
"Bandwidth",
|
|
|
|
"Bandwidth, -10 metadata-only, 10 audio-only, 100 highest",
|
|
|
|
-10,
|
|
|
|
100,
|
|
|
|
100,
|
2019-03-26 16:41:28 +00:00
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2019-07-16 14:43:03 +00:00
|
|
|
subclass::Property("timestamp-mode", |name| {
|
|
|
|
glib::ParamSpec::enum_(
|
|
|
|
name,
|
|
|
|
"Timestamp Mode",
|
|
|
|
"Timestamp information to use for outgoing PTS",
|
|
|
|
TimestampMode::static_type(),
|
|
|
|
TimestampMode::ReceiveTime as i32,
|
|
|
|
glib::ParamFlags::READWRITE,
|
|
|
|
)
|
|
|
|
}),
|
2018-04-09 05:53:04 +00:00
|
|
|
];
|
|
|
|
|
|
|
|
struct State {
|
2018-06-15 13:16:25 +00:00
|
|
|
info: Option<gst_audio::AudioInfo>,
|
2019-07-19 08:32:04 +00:00
|
|
|
receiver: Option<Receiver<AudioReceiver>>,
|
2019-07-16 14:43:03 +00:00
|
|
|
current_latency: gst::ClockTime,
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Default for State {
|
|
|
|
fn default() -> State {
|
2019-07-15 15:10:34 +00:00
|
|
|
State {
|
|
|
|
info: None,
|
2019-07-17 16:10:20 +00:00
|
|
|
receiver: None,
|
2019-07-16 14:43:03 +00:00
|
|
|
current_latency: gst::CLOCK_TIME_NONE,
|
2019-07-15 15:10:34 +00:00
|
|
|
}
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-15 14:56:29 +00:00
|
|
|
pub(crate) struct NdiAudioSrc {
|
2018-04-09 05:53:04 +00:00
|
|
|
cat: gst::DebugCategory,
|
|
|
|
settings: Mutex<Settings>,
|
|
|
|
state: Mutex<State>,
|
2019-07-19 08:32:04 +00:00
|
|
|
receiver_controller: Mutex<Option<ReceiverControlHandle<AudioReceiver>>>,
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:47:03 +00:00
|
|
|
impl ObjectSubclass for NdiAudioSrc {
|
|
|
|
const NAME: &'static str = "NdiAudioSrc";
|
|
|
|
type ParentType = gst_base::BaseSrc;
|
|
|
|
type Instance = gst::subclass::ElementInstanceStruct<Self>;
|
|
|
|
type Class = subclass::simple::ClassStruct<Self>;
|
|
|
|
|
2021-02-23 10:37:24 +00:00
|
|
|
glib::glib_object_subclass!();
|
2018-04-09 05:53:04 +00:00
|
|
|
|
2018-12-11 16:47:03 +00:00
|
|
|
fn new() -> Self {
|
|
|
|
Self {
|
2018-04-09 05:53:04 +00:00
|
|
|
cat: gst::DebugCategory::new(
|
2018-06-12 12:40:17 +00:00
|
|
|
"ndiaudiosrc",
|
2018-04-09 05:53:04 +00:00
|
|
|
gst::DebugColorFlags::empty(),
|
2019-06-25 16:20:50 +00:00
|
|
|
Some("NewTek NDI Audio Source"),
|
2018-04-09 05:53:04 +00:00
|
|
|
),
|
|
|
|
settings: Mutex::new(Default::default()),
|
|
|
|
state: Mutex::new(Default::default()),
|
2019-07-17 16:10:20 +00:00
|
|
|
receiver_controller: Mutex::new(None),
|
2018-12-11 16:47:03 +00:00
|
|
|
}
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-11 16:47:03 +00:00
|
|
|
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
2018-04-09 05:53:04 +00:00
|
|
|
klass.set_metadata(
|
2018-06-12 12:40:17 +00:00
|
|
|
"NewTek NDI Audio Source",
|
2018-04-09 05:53:04 +00:00
|
|
|
"Source",
|
2018-06-25 08:38:45 +00:00
|
|
|
"NewTek NDI audio source",
|
2019-07-17 07:40:26 +00:00
|
|
|
"Ruben Gonzalez <rubenrua@teltek.es>, Daniel Vilar <daniel.peiteado@teltek.es>, Sebastian Dröge <sebastian@centricular.com>",
|
2018-04-09 05:53:04 +00:00
|
|
|
);
|
|
|
|
|
|
|
|
let caps = gst::Caps::new_simple(
|
2018-06-15 13:16:25 +00:00
|
|
|
"audio/x-raw",
|
2018-04-09 05:53:04 +00:00
|
|
|
&[
|
2019-03-26 16:41:28 +00:00
|
|
|
(
|
|
|
|
"format",
|
2019-07-15 16:43:55 +00:00
|
|
|
&gst::List::new(&[&gst_audio::AUDIO_FORMAT_S16.to_string()]),
|
2018-04-09 05:53:04 +00:00
|
|
|
),
|
2018-06-15 13:16:25 +00:00
|
|
|
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
|
|
("channels", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
|
|
|
("layout", &"interleaved"),
|
2019-03-26 16:41:28 +00:00
|
|
|
],
|
|
|
|
);
|
2018-12-11 16:47:03 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
let src_pad_template = gst::PadTemplate::new(
|
|
|
|
"src",
|
|
|
|
gst::PadDirection::Src,
|
|
|
|
gst::PadPresence::Always,
|
|
|
|
&caps,
|
|
|
|
)
|
|
|
|
.unwrap();
|
|
|
|
klass.add_pad_template(src_pad_template);
|
2018-12-11 16:47:03 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
klass.install_properties(&PROPERTIES);
|
2018-12-11 16:47:03 +00:00
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2018-09-18 11:25:24 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
impl ObjectImpl for NdiAudioSrc {
|
2021-02-23 10:37:24 +00:00
|
|
|
glib::glib_object_impl!();
|
2018-05-31 09:16:29 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
fn constructed(&self, obj: &glib::Object) {
|
|
|
|
self.parent_constructed(obj);
|
2018-12-11 16:47:03 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
let basesrc = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
|
|
|
|
// Initialize live-ness and notify the base class that
|
|
|
|
// we'd like to operate in Time format
|
|
|
|
basesrc.set_live(true);
|
|
|
|
basesrc.set_format(gst::Format::Time);
|
|
|
|
}
|
2018-12-11 16:47:03 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
fn set_property(&self, obj: &glib::Object, id: usize, value: &glib::Value) {
|
|
|
|
let prop = &PROPERTIES[id];
|
|
|
|
let basesrc = obj.downcast_ref::<gst_base::BaseSrc>().unwrap();
|
|
|
|
|
|
|
|
match *prop {
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("ndi-name", ..) => {
|
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2019-12-18 17:34:00 +00:00
|
|
|
let ndi_name = value.get().unwrap();
|
2019-07-17 07:40:26 +00:00
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: basesrc,
|
|
|
|
"Changing ndi-name from {:?} to {:?}",
|
|
|
|
settings.ndi_name,
|
|
|
|
ndi_name,
|
|
|
|
);
|
|
|
|
settings.ndi_name = ndi_name;
|
|
|
|
}
|
2020-01-16 09:14:10 +00:00
|
|
|
subclass::Property("url-address", ..) => {
|
|
|
|
let mut settings = self.settings.lock().unwrap();
|
|
|
|
let url_address = value.get().unwrap();
|
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: basesrc,
|
|
|
|
"Changing url-address from {:?} to {:?}",
|
|
|
|
settings.url_address,
|
|
|
|
url_address,
|
|
|
|
);
|
|
|
|
settings.url_address = url_address;
|
|
|
|
}
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("receiver-ndi-name", ..) => {
|
2019-03-26 16:41:28 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2019-12-18 17:34:00 +00:00
|
|
|
let receiver_ndi_name = value.get().unwrap();
|
2019-03-26 16:41:28 +00:00
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: basesrc,
|
2019-07-17 07:40:26 +00:00
|
|
|
"Changing receiver-ndi-name from {:?} to {:?}",
|
|
|
|
settings.receiver_ndi_name,
|
|
|
|
receiver_ndi_name,
|
2019-03-26 16:41:28 +00:00
|
|
|
);
|
2019-07-17 07:40:26 +00:00
|
|
|
settings.receiver_ndi_name =
|
|
|
|
receiver_ndi_name.unwrap_or_else(|| DEFAULT_RECEIVER_NDI_NAME.clone());
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("connect-timeout", ..) => {
|
2019-03-26 16:41:28 +00:00
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2019-12-18 17:34:00 +00:00
|
|
|
let connect_timeout = value.get_some().unwrap();
|
2019-03-26 16:41:28 +00:00
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: basesrc,
|
2019-07-17 07:40:26 +00:00
|
|
|
"Changing connect-timeout from {} to {}",
|
|
|
|
settings.connect_timeout,
|
|
|
|
connect_timeout,
|
2019-03-26 16:41:28 +00:00
|
|
|
);
|
2019-07-17 07:40:26 +00:00
|
|
|
settings.connect_timeout = connect_timeout;
|
|
|
|
}
|
|
|
|
subclass::Property("timeout", ..) => {
|
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2019-12-18 17:34:00 +00:00
|
|
|
let timeout = value.get_some().unwrap();
|
2019-07-17 07:40:26 +00:00
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: basesrc,
|
|
|
|
"Changing timeout from {} to {}",
|
|
|
|
settings.timeout,
|
|
|
|
timeout,
|
|
|
|
);
|
|
|
|
settings.timeout = timeout;
|
|
|
|
}
|
|
|
|
subclass::Property("bandwidth", ..) => {
|
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2019-12-18 17:34:00 +00:00
|
|
|
let bandwidth = value.get_some().unwrap();
|
2019-07-17 07:40:26 +00:00
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: basesrc,
|
|
|
|
"Changing bandwidth from {} to {}",
|
|
|
|
settings.bandwidth,
|
|
|
|
bandwidth,
|
|
|
|
);
|
|
|
|
settings.bandwidth = bandwidth;
|
2019-07-16 14:43:03 +00:00
|
|
|
}
|
|
|
|
subclass::Property("timestamp-mode", ..) => {
|
|
|
|
let mut settings = self.settings.lock().unwrap();
|
2019-12-18 17:34:00 +00:00
|
|
|
let timestamp_mode = value.get_some().unwrap();
|
2019-07-16 14:43:03 +00:00
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: basesrc,
|
|
|
|
"Changing timestamp mode from {:?} to {:?}",
|
|
|
|
settings.timestamp_mode,
|
|
|
|
timestamp_mode
|
|
|
|
);
|
|
|
|
if settings.timestamp_mode != timestamp_mode {
|
2020-07-27 13:08:56 +00:00
|
|
|
let _ =
|
|
|
|
basesrc.post_message(gst::message::Latency::builder().src(basesrc).build());
|
2019-07-16 14:43:03 +00:00
|
|
|
}
|
|
|
|
settings.timestamp_mode = timestamp_mode;
|
2018-12-05 11:55:01 +00:00
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
_ => unimplemented!(),
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2018-04-09 05:53:04 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
fn get_property(&self, _obj: &glib::Object, id: usize) -> Result<glib::Value, ()> {
|
|
|
|
let prop = &PROPERTIES[id];
|
2018-09-18 09:53:12 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
match *prop {
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("ndi-name", ..) => {
|
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
Ok(settings.ndi_name.to_value())
|
|
|
|
}
|
2020-01-16 09:14:10 +00:00
|
|
|
subclass::Property("url-address", ..) => {
|
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
Ok(settings.url_address.to_value())
|
|
|
|
}
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("receiver-ndi-name", ..) => {
|
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
Ok(settings.receiver_ndi_name.to_value())
|
|
|
|
}
|
|
|
|
subclass::Property("connect-timeout", ..) => {
|
2019-03-26 16:41:28 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2019-07-17 07:40:26 +00:00
|
|
|
Ok(settings.connect_timeout.to_value())
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("timeout", ..) => {
|
2019-03-26 16:41:28 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2019-07-17 07:40:26 +00:00
|
|
|
Ok(settings.timeout.to_value())
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2019-07-17 07:40:26 +00:00
|
|
|
subclass::Property("bandwidth", ..) => {
|
2019-03-26 16:41:28 +00:00
|
|
|
let settings = self.settings.lock().unwrap();
|
2019-07-17 07:40:26 +00:00
|
|
|
Ok(settings.bandwidth.to_value())
|
2018-12-05 11:55:01 +00:00
|
|
|
}
|
2019-07-16 14:43:03 +00:00
|
|
|
subclass::Property("timestamp-mode", ..) => {
|
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
Ok(settings.timestamp_mode.to_value())
|
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
_ => unimplemented!(),
|
2018-04-09 05:53:04 +00:00
|
|
|
}
|
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2018-04-09 05:53:04 +00:00
|
|
|
|
2019-07-17 16:10:20 +00:00
|
|
|
impl ElementImpl for NdiAudioSrc {
|
|
|
|
fn change_state(
|
|
|
|
&self,
|
|
|
|
element: &gst::Element,
|
|
|
|
transition: gst::StateChange,
|
|
|
|
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
|
|
|
|
match transition {
|
|
|
|
gst::StateChange::PausedToPlaying => {
|
|
|
|
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
|
|
|
|
controller.set_playing(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gst::StateChange::PlayingToPaused => {
|
|
|
|
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
|
|
|
|
controller.set_playing(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gst::StateChange::PausedToReady => {
|
|
|
|
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
|
|
|
|
controller.shutdown();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
|
|
|
|
self.parent_change_state(element, transition)
|
|
|
|
}
|
|
|
|
}
|
2018-04-09 05:53:04 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
impl BaseSrcImpl for NdiAudioSrc {
|
2019-08-06 15:48:24 +00:00
|
|
|
fn negotiate(&self, _element: &gst_base::BaseSrc) -> Result<(), gst::LoggableError> {
|
|
|
|
// Always succeed here without doing anything: we will set the caps once we received a
|
|
|
|
// buffer, there's nothing we can negotiate
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn unlock(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
2019-07-17 16:10:20 +00:00
|
|
|
gst_debug!(self.cat, obj: element, "Unlocking",);
|
|
|
|
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
|
|
|
|
controller.set_flushing(true);
|
|
|
|
}
|
2019-07-17 09:12:58 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-08-06 15:48:24 +00:00
|
|
|
fn unlock_stop(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
2019-07-17 16:10:20 +00:00
|
|
|
gst_debug!(self.cat, obj: element, "Stop unlocking",);
|
|
|
|
if let Some(ref controller) = *self.receiver_controller.lock().unwrap() {
|
|
|
|
controller.set_flushing(false);
|
|
|
|
}
|
2019-07-17 09:12:58 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
|
|
|
*self.state.lock().unwrap() = Default::default();
|
2019-07-15 15:10:34 +00:00
|
|
|
let settings = self.settings.lock().unwrap().clone();
|
2019-07-17 07:40:26 +00:00
|
|
|
|
2020-01-16 10:18:57 +00:00
|
|
|
if settings.ndi_name.is_none() && settings.url_address.is_none() {
|
2019-07-17 09:12:58 +00:00
|
|
|
return Err(gst_error_msg!(
|
|
|
|
gst::LibraryError::Settings,
|
2020-01-16 10:18:57 +00:00
|
|
|
["No NDI name or URL/address given"]
|
2019-07-17 09:12:58 +00:00
|
|
|
));
|
2020-01-16 09:14:10 +00:00
|
|
|
}
|
2019-07-17 09:12:58 +00:00
|
|
|
|
2019-07-17 16:10:20 +00:00
|
|
|
let receiver = connect_ndi(
|
2019-03-26 16:41:28 +00:00
|
|
|
self.cat,
|
|
|
|
element,
|
2020-01-16 10:18:57 +00:00
|
|
|
settings.ndi_name.as_ref().map(String::as_str),
|
2020-01-16 09:14:10 +00:00
|
|
|
settings.url_address.as_ref().map(String::as_str),
|
2019-07-17 07:40:26 +00:00
|
|
|
&settings.receiver_ndi_name,
|
|
|
|
settings.connect_timeout,
|
|
|
|
settings.bandwidth,
|
2019-07-17 16:10:20 +00:00
|
|
|
settings.timestamp_mode,
|
|
|
|
settings.timeout,
|
2019-03-26 16:41:28 +00:00
|
|
|
);
|
2018-04-09 05:53:04 +00:00
|
|
|
|
2019-07-17 09:12:58 +00:00
|
|
|
// settings.id_receiver exists
|
2019-07-17 16:10:20 +00:00
|
|
|
match receiver {
|
2019-07-11 18:56:30 +00:00
|
|
|
None => Err(gst_error_msg!(
|
2019-02-28 12:27:38 +00:00
|
|
|
gst::ResourceError::NotFound,
|
2019-02-28 11:13:40 +00:00
|
|
|
["Could not connect to this source"]
|
|
|
|
)),
|
2019-07-17 16:10:20 +00:00
|
|
|
Some(receiver) => {
|
|
|
|
*self.receiver_controller.lock().unwrap() =
|
|
|
|
Some(receiver.receiver_control_handle());
|
2019-07-17 09:12:58 +00:00
|
|
|
let mut state = self.state.lock().unwrap();
|
2019-07-17 16:10:20 +00:00
|
|
|
state.receiver = Some(receiver);
|
2019-07-17 09:12:58 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2018-12-11 16:47:03 +00:00
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2018-08-20 07:25:15 +00:00
|
|
|
|
2019-07-17 16:10:20 +00:00
|
|
|
fn stop(&self, _element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
|
|
|
if let Some(ref controller) = self.receiver_controller.lock().unwrap().take() {
|
|
|
|
controller.shutdown();
|
2019-07-15 15:10:34 +00:00
|
|
|
}
|
2019-07-17 16:10:20 +00:00
|
|
|
*self.state.lock().unwrap() = State::default();
|
2019-03-26 16:41:28 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn query(&self, element: &gst_base::BaseSrc, query: &mut gst::QueryRef) -> bool {
|
|
|
|
use gst::QueryView;
|
2019-07-15 15:34:09 +00:00
|
|
|
|
|
|
|
match query.view_mut() {
|
|
|
|
QueryView::Scheduling(ref mut q) => {
|
|
|
|
q.set(gst::SchedulingFlags::SEQUENTIAL, 1, -1, 0);
|
|
|
|
q.add_scheduling_modes(&[gst::PadMode::Push]);
|
|
|
|
true
|
2018-09-24 13:46:36 +00:00
|
|
|
}
|
2019-07-16 14:43:03 +00:00
|
|
|
QueryView::Latency(ref mut q) => {
|
|
|
|
let state = self.state.lock().unwrap();
|
|
|
|
let settings = self.settings.lock().unwrap();
|
|
|
|
|
|
|
|
if state.current_latency.is_some() {
|
2019-07-19 08:37:33 +00:00
|
|
|
let min = if settings.timestamp_mode != TimestampMode::Timecode {
|
2019-07-16 14:43:03 +00:00
|
|
|
state.current_latency
|
|
|
|
} else {
|
|
|
|
0.into()
|
|
|
|
};
|
|
|
|
|
2019-07-19 08:37:33 +00:00
|
|
|
let max = 5 * state.current_latency;
|
|
|
|
|
|
|
|
gst_debug!(
|
|
|
|
self.cat,
|
|
|
|
obj: element,
|
|
|
|
"Returning latency min {} max {}",
|
|
|
|
min,
|
|
|
|
max
|
|
|
|
);
|
|
|
|
q.set(true, min, max);
|
2019-07-16 14:43:03 +00:00
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
2019-07-15 15:34:09 +00:00
|
|
|
_ => BaseSrcImplExt::parent_query(self, element, query),
|
2018-09-24 13:46:36 +00:00
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2018-07-02 12:07:51 +00:00
|
|
|
|
2020-07-27 13:08:56 +00:00
|
|
|
fn fixate(&self, element: &gst_base::BaseSrc, mut caps: gst::Caps) -> gst::Caps {
|
|
|
|
caps.truncate();
|
2019-03-26 16:41:28 +00:00
|
|
|
{
|
|
|
|
let caps = caps.make_mut();
|
|
|
|
let s = caps.get_mut_structure(0).unwrap();
|
2019-07-15 15:59:58 +00:00
|
|
|
s.fixate_field_nearest_int("rate", 48_000);
|
|
|
|
s.fixate_field_nearest_int("channels", 2);
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2018-08-14 13:45:13 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
self.parent_fixate(element, caps)
|
|
|
|
}
|
2018-06-25 08:38:45 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
fn create(
|
|
|
|
&self,
|
|
|
|
element: &gst_base::BaseSrc,
|
|
|
|
_offset: u64,
|
2020-07-27 13:08:56 +00:00
|
|
|
_buffer: Option<&mut gst::BufferRef>,
|
2019-03-26 16:41:28 +00:00
|
|
|
_length: u32,
|
2020-07-27 13:08:56 +00:00
|
|
|
) -> Result<CreateSuccess, gst::FlowError> {
|
2019-07-17 08:44:24 +00:00
|
|
|
let recv = {
|
2019-07-17 16:10:20 +00:00
|
|
|
let mut state = self.state.lock().unwrap();
|
|
|
|
match state.receiver.take() {
|
|
|
|
Some(recv) => recv,
|
|
|
|
None => {
|
|
|
|
gst_error!(self.cat, obj: element, "Have no receiver");
|
2019-07-11 18:56:30 +00:00
|
|
|
return Err(gst::FlowError::Error);
|
2019-07-15 16:43:22 +00:00
|
|
|
}
|
2019-07-17 08:44:24 +00:00
|
|
|
}
|
2019-07-16 14:43:03 +00:00
|
|
|
};
|
2019-07-15 15:26:51 +00:00
|
|
|
|
2019-07-17 16:10:20 +00:00
|
|
|
match recv.capture() {
|
2019-07-19 08:32:04 +00:00
|
|
|
ReceiverItem::Buffer(buffer, info) => {
|
2019-07-17 16:10:20 +00:00
|
|
|
let mut state = self.state.lock().unwrap();
|
|
|
|
state.receiver = Some(recv);
|
|
|
|
if state.info.as_ref() != Some(&info) {
|
2019-12-18 17:34:00 +00:00
|
|
|
let caps = info.to_caps().map_err(|_| {
|
2019-07-19 09:51:06 +00:00
|
|
|
gst_element_error!(
|
|
|
|
element,
|
|
|
|
gst::ResourceError::Settings,
|
|
|
|
["Invalid audio info received: {:?}", info]
|
|
|
|
);
|
|
|
|
gst::FlowError::NotNegotiated
|
|
|
|
})?;
|
2020-01-02 11:06:53 +00:00
|
|
|
state.info = Some(info);
|
2019-07-17 16:10:20 +00:00
|
|
|
state.current_latency = buffer.get_duration();
|
|
|
|
drop(state);
|
|
|
|
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
2019-07-19 09:51:06 +00:00
|
|
|
element.set_caps(&caps).map_err(|_| {
|
|
|
|
gst_element_error!(
|
|
|
|
element,
|
|
|
|
gst::CoreError::Negotiation,
|
|
|
|
["Failed to negotiate caps: {:?}", caps]
|
|
|
|
);
|
|
|
|
gst::FlowError::NotNegotiated
|
|
|
|
})?;
|
2019-07-17 16:10:20 +00:00
|
|
|
|
2020-07-27 13:08:56 +00:00
|
|
|
let _ =
|
|
|
|
element.post_message(gst::message::Latency::builder().src(element).build());
|
2019-07-16 14:43:03 +00:00
|
|
|
}
|
2019-07-15 15:59:58 +00:00
|
|
|
|
2020-07-27 13:08:56 +00:00
|
|
|
Ok(CreateSuccess::NewBuffer(buffer))
|
2019-07-16 13:03:15 +00:00
|
|
|
}
|
2019-07-17 16:10:20 +00:00
|
|
|
ReceiverItem::Flushing => Err(gst::FlowError::Flushing),
|
|
|
|
ReceiverItem::Timeout => Err(gst::FlowError::Eos),
|
|
|
|
ReceiverItem::Error(err) => Err(err),
|
2018-05-31 09:16:29 +00:00
|
|
|
}
|
2018-09-18 09:53:12 +00:00
|
|
|
}
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|
2018-09-18 09:53:12 +00:00
|
|
|
|
2019-03-26 16:41:28 +00:00
|
|
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
2019-06-25 16:20:50 +00:00
|
|
|
gst::Element::register(
|
|
|
|
Some(plugin),
|
|
|
|
"ndiaudiosrc",
|
|
|
|
gst::Rank::None,
|
|
|
|
NdiAudioSrc::get_type(),
|
|
|
|
)
|
2019-03-26 16:41:28 +00:00
|
|
|
}
|