gst-plugins-rs/src/lib.rs

203 lines
5.4 KiB
Rust
Raw Normal View History

2018-04-09 05:53:04 +00:00
#[macro_use]
2018-12-11 16:47:03 +00:00
extern crate glib;
use glib::prelude::*;
use glib::subclass::prelude::*;
2018-04-09 05:53:04 +00:00
#[macro_use]
extern crate gstreamer as gst;
extern crate gstreamer_audio as gst_audio;
extern crate gstreamer_base as gst_base;
extern crate gstreamer_video as gst_video;
#[macro_use]
extern crate lazy_static;
extern crate byte_slice_cast;
2018-04-09 05:53:04 +00:00
2019-07-11 17:35:43 +00:00
pub mod ndi;
mod ndiaudiosrc;
pub mod ndisys;
2018-09-18 09:53:12 +00:00
mod ndivideosrc;
use ndi::*;
use ndisys::*;
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Mutex;
2018-12-11 16:47:03 +00:00
fn plugin_init(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
if !ndi::initialize() {
return Err(glib_bool_error!("Cannot initialize NDI"));
}
2018-12-11 16:47:03 +00:00
ndivideosrc::register(plugin)?;
ndiaudiosrc::register(plugin)?;
Ok(())
2018-04-09 05:53:04 +00:00
}
struct ReceiverInfo {
id: usize,
stream_name: String,
ip: String,
video: bool,
audio: bool,
ndi_instance: RecvInstance,
}
lazy_static! {
static ref HASHMAP_RECEIVERS: Mutex<HashMap<usize, ReceiverInfo>> = {
2018-08-20 10:14:54 +00:00
let m = HashMap::new();
Mutex::new(m)
};
}
static ID_RECEIVER: AtomicUsize = AtomicUsize::new(0);
2019-03-26 16:41:28 +00:00
fn connect_ndi(
cat: gst::DebugCategory,
element: &gst_base::BaseSrc,
ip: &str,
stream_name: &str,
) -> Option<usize> {
2018-09-12 07:44:46 +00:00
gst_debug!(cat, obj: element, "Starting NDI connection...");
let mut receivers = HASHMAP_RECEIVERS.lock().unwrap();
let video = element.get_type() == ndivideosrc::NdiVideoSrc::get_type();
2018-09-18 09:53:12 +00:00
for val in receivers.values_mut() {
if val.ip == ip || val.stream_name == stream_name {
2019-07-15 16:46:22 +00:00
if (val.video || !video) && (val.audio || video) {
2018-09-12 07:44:46 +00:00
continue;
2018-09-18 09:53:12 +00:00
} else {
2018-09-12 07:44:46 +00:00
if video {
2019-07-15 16:46:22 +00:00
val.video = true;
2018-09-18 09:53:12 +00:00
} else {
2019-07-15 16:46:22 +00:00
val.audio = true;
}
return Some(val.id);
}
}
2018-09-12 07:44:46 +00:00
}
let mut find = match FindInstance::builder().build() {
None => {
2018-09-18 09:53:12 +00:00
gst_element_error!(
element,
gst::CoreError::Negotiation,
["Cannot run NDI: NDIlib_find_create_v2 error"]
);
return None;
}
Some(find) => find,
};
// TODO Sleep 1s to wait for all sources
find.wait_for_sources(2000);
2018-06-27 09:56:11 +00:00
let sources = find.get_current_sources();
// We need at least one source
if sources.is_empty() {
gst_element_error!(
element,
gst::CoreError::Negotiation,
["Error getting NDIlib_find_get_current_sources"]
2018-09-18 09:53:12 +00:00
);
return None;
}
2018-06-27 09:56:11 +00:00
let source = sources
.iter()
.find(|s| s.ndi_name() == stream_name || s.ip_address() == ip);
let source = match source {
None => {
gst_element_error!(element, gst::ResourceError::OpenRead, ["Stream not found"]);
return None;
}
Some(source) => source,
};
2018-06-27 09:56:11 +00:00
gst_debug!(
cat,
obj: element,
"Total sources in network {}: Connecting to NDI source with name '{}' and address '{}'",
sources.len(),
source.ndi_name(),
source.ip_address(),
);
// FIXME: Property for the name and bandwidth
// FIXME: Ideally we would use NDIlib_recv_color_format_fastest here but that seems to be
// broken with interlaced content currently
let recv = RecvInstance::builder(&source, "Galicaster NDI Receiver")
.bandwidth(NDIlib_recv_bandwidth_e::NDIlib_recv_bandwidth_highest)
.color_format(NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA)
.allow_video_fields(true)
.build();
let recv = match recv {
None => {
2018-09-18 09:53:12 +00:00
gst_element_error!(
element,
gst::CoreError::Negotiation,
["Cannot run NDI: NDIlib_recv_create_v3 error"]
);
return None;
}
Some(recv) => recv,
};
2018-06-27 09:56:11 +00:00
recv.set_tally(&Tally::default());
let enable_hw_accel = MetadataFrame::new(0, Some("<ndi_hwaccel enabled=\"true\"/>"));
recv.send_metadata(&enable_hw_accel);
let id_receiver = ID_RECEIVER.fetch_add(1, Ordering::SeqCst);
receivers.insert(
id_receiver,
ReceiverInfo {
stream_name: source.ndi_name().to_owned(),
ip: source.ip_address().to_owned(),
video,
audio: !video,
ndi_instance: recv,
id: id_receiver,
},
);
gst_debug!(cat, obj: element, "Started NDI connection");
Some(id_receiver)
2018-06-27 09:56:11 +00:00
}
fn stop_ndi(cat: gst::DebugCategory, element: &gst_base::BaseSrc, id: usize) -> bool {
2018-06-27 09:56:11 +00:00
gst_debug!(cat, obj: element, "Closing NDI connection...");
let mut receivers = HASHMAP_RECEIVERS.lock().unwrap();
2018-09-12 07:44:46 +00:00
{
let val = receivers.get_mut(&id).unwrap();
2018-09-18 09:53:12 +00:00
if val.video && val.audio {
let video = element.get_type() == ndivideosrc::NdiVideoSrc::get_type();
if video {
2018-09-12 07:44:46 +00:00
val.video = false;
} else {
val.audio = false;
2018-09-12 07:44:46 +00:00
}
return true;
}
}
2018-09-12 07:44:46 +00:00
receivers.remove(&id);
gst_debug!(cat, obj: element, "Closed NDI connection");
2018-09-18 11:12:04 +00:00
true
2018-06-27 09:56:11 +00:00
}
2018-12-11 16:47:03 +00:00
gst_plugin_define!(
ndi,
env!("CARGO_PKG_DESCRIPTION"),
2018-06-27 09:56:11 +00:00
plugin_init,
concat!(env!("CARGO_PKG_VERSION"), "-", env!("COMMIT_ID")),
2018-12-11 16:47:03 +00:00
"LGPL",
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_NAME"),
env!("CARGO_PKG_REPOSITORY"),
env!("BUILD_REL_DATE")
2018-06-27 09:56:11 +00:00
);