Allow connecting to either an NDI name or URL/address again

Apparently the SDK allows both but the documentation was a bit
confusing.
This commit is contained in:
Sebastian Dröge 2020-01-16 12:18:57 +02:00
parent 8d2c025e47
commit 4d620cd737
4 changed files with 46 additions and 23 deletions

View file

@ -182,7 +182,8 @@ impl<'a> PartialEq for Source<'a> {
#[derive(Debug)] #[derive(Debug)]
pub struct RecvBuilder<'a> { pub struct RecvBuilder<'a> {
source_to_connect_to: (&'a str, Option<&'a str>), ndi_name: Option<&'a str>,
url_address: Option<&'a str>,
allow_video_fields: bool, allow_video_fields: bool,
bandwidth: NDIlib_recv_bandwidth_e, bandwidth: NDIlib_recv_bandwidth_e,
color_format: NDIlib_recv_color_format_e, color_format: NDIlib_recv_color_format_e,
@ -211,15 +212,20 @@ impl<'a> RecvBuilder<'a> {
pub fn build(self) -> Option<RecvInstance> { pub fn build(self) -> Option<RecvInstance> {
unsafe { unsafe {
let ndi_recv_name = ffi::CString::new(self.ndi_recv_name).unwrap(); let ndi_recv_name = ffi::CString::new(self.ndi_recv_name).unwrap();
let ndi_name = ffi::CString::new(self.source_to_connect_to.0).unwrap(); let ndi_name = self
.ndi_name
.as_ref()
.map(|s| ffi::CString::new(*s).unwrap());
let url_address = self let url_address = self
.source_to_connect_to .url_address
.1
.as_ref() .as_ref()
.map(|s| ffi::CString::new(*s).unwrap()); .map(|s| ffi::CString::new(*s).unwrap());
let ptr = NDIlib_recv_create_v3(&NDIlib_recv_create_v3_t { let ptr = NDIlib_recv_create_v3(&NDIlib_recv_create_v3_t {
source_to_connect_to: NDIlib_source_t { source_to_connect_to: NDIlib_source_t {
p_ndi_name: ndi_name.as_ptr(), p_ndi_name: ndi_name
.as_ref()
.map(|s| s.as_ptr())
.unwrap_or_else(|| ptr::null_mut()),
p_url_address: url_address p_url_address: url_address
.as_ref() .as_ref()
.map(|s| s.as_ptr()) .map(|s| s.as_ptr())
@ -258,11 +264,13 @@ unsafe impl Sync for RecvInstanceInner {}
impl RecvInstance { impl RecvInstance {
pub fn builder<'a>( pub fn builder<'a>(
source_to_connect_to: (&'a str, Option<&'a str>), ndi_name: Option<&'a str>,
url_address: Option<&'a str>,
ndi_recv_name: &'a str, ndi_recv_name: &'a str,
) -> RecvBuilder<'a> { ) -> RecvBuilder<'a> {
RecvBuilder { RecvBuilder {
source_to_connect_to, ndi_name,
url_address,
allow_video_fields: true, allow_video_fields: true,
bandwidth: NDIlib_recv_bandwidth_highest, bandwidth: NDIlib_recv_bandwidth_highest,
color_format: NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA, color_format: NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA,

View file

@ -60,7 +60,7 @@ static PROPERTIES: [subclass::Property; 7] = [
glib::ParamSpec::string( glib::ParamSpec::string(
name, name,
"URL/Address", "URL/Address",
"URL/address and port of the sender, e.g. 127.0.0.1:5961. This is used as an additional filter together with the NDI name.", "URL/address and port of the sender, e.g. 127.0.0.1:5961",
None, None,
glib::ParamFlags::READWRITE, glib::ParamFlags::READWRITE,
) )
@ -401,17 +401,17 @@ impl BaseSrcImpl for NdiAudioSrc {
*self.state.lock().unwrap() = Default::default(); *self.state.lock().unwrap() = Default::default();
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
if settings.ndi_name.is_none() { if settings.ndi_name.is_none() && settings.url_address.is_none() {
return Err(gst_error_msg!( return Err(gst_error_msg!(
gst::LibraryError::Settings, gst::LibraryError::Settings,
["No IP address or NDI name given"] ["No NDI name or URL/address given"]
)); ));
} }
let receiver = connect_ndi( let receiver = connect_ndi(
self.cat, self.cat,
element, element,
settings.ndi_name.as_ref().unwrap().as_str(), settings.ndi_name.as_ref().map(String::as_str),
settings.url_address.as_ref().map(String::as_str), settings.url_address.as_ref().map(String::as_str),
&settings.receiver_ndi_name, &settings.receiver_ndi_name,
settings.connect_timeout, settings.connect_timeout,

View file

@ -61,7 +61,7 @@ static PROPERTIES: [subclass::Property; 7] = [
glib::ParamSpec::string( glib::ParamSpec::string(
name, name,
"URL/Address", "URL/Address",
"URL/address and port of the sender, e.g. 127.0.0.1:5961. This is used as an additional filter together with the NDI name.", "URL/address and port of the sender, e.g. 127.0.0.1:5961",
None, None,
glib::ParamFlags::READWRITE, glib::ParamFlags::READWRITE,
) )
@ -436,17 +436,17 @@ impl BaseSrcImpl for NdiVideoSrc {
*self.state.lock().unwrap() = Default::default(); *self.state.lock().unwrap() = Default::default();
let settings = self.settings.lock().unwrap().clone(); let settings = self.settings.lock().unwrap().clone();
if settings.ndi_name.is_none() { if settings.ndi_name.is_none() && settings.url_address.is_none() {
return Err(gst_error_msg!( return Err(gst_error_msg!(
gst::LibraryError::Settings, gst::LibraryError::Settings,
["No NDI name given"] ["No NDI name or URL/address given"]
)); ));
} }
let receiver = connect_ndi( let receiver = connect_ndi(
self.cat, self.cat,
element, element,
settings.ndi_name.as_ref().unwrap().as_str(), settings.ndi_name.as_ref().map(String::as_str),
settings.url_address.as_ref().map(String::as_str), settings.url_address.as_ref().map(String::as_str),
&settings.receiver_ndi_name, &settings.receiver_ndi_name,
settings.connect_timeout, settings.connect_timeout,
@ -502,6 +502,8 @@ impl BaseSrcImpl for NdiVideoSrc {
let max = 5 * state.current_latency; let max = 5 * state.current_latency;
println!("Returning latency min {} max {}", min, max,);
gst_debug!( gst_debug!(
self.cat, self.cat,
obj: element, obj: element,

View file

@ -17,7 +17,7 @@ use super::*;
pub struct ReceiverInfo { pub struct ReceiverInfo {
id: usize, id: usize,
ndi_name: String, ndi_name: Option<String>,
url_address: Option<String>, url_address: Option<String>,
recv: RecvInstance, recv: RecvInstance,
video: Option<Weak<ReceiverInner<VideoReceiver>>>, video: Option<Weak<ReceiverInner<VideoReceiver>>>,
@ -521,7 +521,7 @@ impl<T: ReceiverType> Drop for ReceiverInner<T> {
pub fn connect_ndi<T: ReceiverType>( pub fn connect_ndi<T: ReceiverType>(
cat: gst::DebugCategory, cat: gst::DebugCategory,
element: &gst_base::BaseSrc, element: &gst_base::BaseSrc,
ndi_name: &str, ndi_name: Option<&str>,
url_address: Option<&str>, url_address: Option<&str>,
receiver_ndi_name: &str, receiver_ndi_name: &str,
connect_timeout: u32, connect_timeout: u32,
@ -534,12 +534,24 @@ where
{ {
gst_debug!(cat, obj: element, "Starting NDI connection..."); gst_debug!(cat, obj: element, "Starting NDI connection...");
assert!(ndi_name.is_some() || url_address.is_some());
let mut receivers = HASHMAP_RECEIVERS.lock().unwrap(); let mut receivers = HASHMAP_RECEIVERS.lock().unwrap();
// Check if we already have a receiver for this very stream // Check if we already have a receiver for this very stream
for receiver in receivers.values_mut() { for receiver in receivers.values_mut() {
if receiver.ndi_name == ndi_name // If both are provided they both must match, if only one is provided
&& receiver.url_address.as_ref().map(String::as_str) == url_address // then that one has to match and the other one does not matter
if (ndi_name.is_some()
&& url_address.is_some()
&& receiver.ndi_name.as_ref().map(String::as_str) == ndi_name
&& receiver.url_address.as_ref().map(String::as_str) == url_address)
|| (ndi_name.is_some()
&& url_address.is_none()
&& receiver.ndi_name.as_ref().map(String::as_str) == ndi_name)
|| (ndi_name.is_none()
&& url_address.is_some()
&& receiver.url_address.as_ref().map(String::as_str) == url_address)
{ {
if (receiver.video.is_some() || !T::IS_VIDEO) if (receiver.video.is_some() || !T::IS_VIDEO)
&& (receiver.audio.is_some() || T::IS_VIDEO) && (receiver.audio.is_some() || T::IS_VIDEO)
@ -548,8 +560,9 @@ where
element, element,
gst::ResourceError::OpenRead, gst::ResourceError::OpenRead,
[ [
"Source with ndi-name '{}' already in use for {}", "Source with NDI name '{:?}' / URL/address '{:?}' already in use for {}",
receiver.ndi_name, receiver.ndi_name,
receiver.url_address,
if T::IS_VIDEO { "video" } else { "audio" } if T::IS_VIDEO { "video" } else { "audio" }
] ]
); );
@ -572,14 +585,14 @@ where
gst_debug!( gst_debug!(
cat, cat,
obj: element, obj: element,
"Connecting to NDI source with ndi-name '{}' and URL/Address {:?}", "Connecting to NDI source with NDI name '{:?}' and URL/Address {:?}",
ndi_name, ndi_name,
url_address, url_address,
); );
// FIXME: Ideally we would use NDIlib_recv_color_format_fastest here but that seems to be // FIXME: Ideally we would use NDIlib_recv_color_format_fastest here but that seems to be
// broken with interlaced content currently // broken with interlaced content currently
let recv = RecvInstance::builder((ndi_name, url_address), &receiver_ndi_name) let recv = RecvInstance::builder(ndi_name, url_address, &receiver_ndi_name)
.bandwidth(bandwidth) .bandwidth(bandwidth)
.color_format(NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA) .color_format(NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA)
.allow_video_fields(true) .allow_video_fields(true)
@ -604,7 +617,7 @@ where
let id_receiver = ID_RECEIVER.fetch_add(1, Ordering::SeqCst); let id_receiver = ID_RECEIVER.fetch_add(1, Ordering::SeqCst);
let mut info = ReceiverInfo { let mut info = ReceiverInfo {
id: id_receiver, id: id_receiver,
ndi_name: String::from(ndi_name), ndi_name: ndi_name.map(String::from),
url_address: url_address.map(String::from), url_address: url_address.map(String::from),
recv, recv,
video: None, video: None,