mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-12-02 00:11:01 +00:00
commit
21d7298843
6 changed files with 123 additions and 161 deletions
|
@ -1,7 +1,7 @@
|
||||||
GStreamer NDI Plugin for Linux
|
GStreamer NDI Plugin for Linux
|
||||||
====================
|
====================
|
||||||
|
|
||||||
*Compiled and tested with Ubuntu 16.04.5, GStreamer 1.12 and NDI SDK 3.5.1*
|
*Compiled and tested with NDI SDK 3.5, 3.8, 4.0 and 4.1*
|
||||||
|
|
||||||
This is a plugin for the [GStreamer](https://gstreamer.freedesktop.org/) multimedia framework that allows GStreamer to receive a stream from a [NDI](https://www.newtek.com/ndi/) source. This plugin has been developed by [Teltek](http://teltek.es/) and was funded by the [University of the Arts London](https://www.arts.ac.uk/) and [The University of Manchester](https://www.manchester.ac.uk/).
|
This is a plugin for the [GStreamer](https://gstreamer.freedesktop.org/) multimedia framework that allows GStreamer to receive a stream from a [NDI](https://www.newtek.com/ndi/) source. This plugin has been developed by [Teltek](http://teltek.es/) and was funded by the [University of the Arts London](https://www.arts.ac.uk/) and [The University of Manchester](https://www.manchester.ac.uk/).
|
||||||
|
|
||||||
|
|
65
src/ndi.rs
65
src/ndi.rs
|
@ -1,3 +1,4 @@
|
||||||
|
use crate::ndisys;
|
||||||
use crate::ndisys::*;
|
use crate::ndisys::*;
|
||||||
use std::ffi;
|
use std::ffi;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
@ -135,15 +136,15 @@ impl<'a> Source<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ip_address(&self) -> &str {
|
pub fn url_address(&self) -> &str {
|
||||||
unsafe {
|
unsafe {
|
||||||
let ptr = match *self {
|
let ptr = match *self {
|
||||||
Source::Borrowed(ptr, _) => &*ptr.as_ptr(),
|
Source::Borrowed(ptr, _) => &*ptr.as_ptr(),
|
||||||
Source::Owned(ref source, _, _) => source,
|
Source::Owned(ref source, _, _) => source,
|
||||||
};
|
};
|
||||||
|
|
||||||
assert!(!ptr.p_ip_address.is_null());
|
assert!(!ptr.p_url_address.is_null());
|
||||||
ffi::CStr::from_ptr(ptr.p_ip_address).to_str().unwrap()
|
ffi::CStr::from_ptr(ptr.p_url_address).to_str().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,34 +157,35 @@ impl<'a> Source<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ip_address_ptr(&self) -> *const ::std::os::raw::c_char {
|
fn url_address_ptr(&self) -> *const ::std::os::raw::c_char {
|
||||||
unsafe {
|
unsafe {
|
||||||
match *self {
|
match *self {
|
||||||
Source::Borrowed(ptr, _) => ptr.as_ref().p_ip_address,
|
Source::Borrowed(ptr, _) => ptr.as_ref().p_url_address,
|
||||||
Source::Owned(_, _, ref ip_address) => ip_address.as_ptr(),
|
Source::Owned(_, _, ref url_address) => url_address.as_ptr(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn to_owned<'b>(&self) -> Source<'b> {
|
pub fn to_owned<'b>(&self) -> Source<'b> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let (ndi_name, ip_address) = match *self {
|
let (ndi_name, url_address) = match *self {
|
||||||
Source::Borrowed(ptr, _) => (ptr.as_ref().p_ndi_name, ptr.as_ref().p_ip_address),
|
Source::Borrowed(ptr, _) => (ptr.as_ref().p_ndi_name, ptr.as_ref().p_url_address),
|
||||||
Source::Owned(_, ref ndi_name, ref ip_address) => {
|
Source::Owned(_, ref ndi_name, ref url_address) => {
|
||||||
(ndi_name.as_ptr(), ip_address.as_ptr())
|
(ndi_name.as_ptr(), url_address.as_ptr())
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let ndi_name = ffi::CString::new(ffi::CStr::from_ptr(ndi_name).to_bytes()).unwrap();
|
let ndi_name = ffi::CString::new(ffi::CStr::from_ptr(ndi_name).to_bytes()).unwrap();
|
||||||
let ip_address = ffi::CString::new(ffi::CStr::from_ptr(ip_address).to_bytes()).unwrap();
|
let url_address =
|
||||||
|
ffi::CString::new(ffi::CStr::from_ptr(url_address).to_bytes()).unwrap();
|
||||||
|
|
||||||
Source::Owned(
|
Source::Owned(
|
||||||
NDIlib_source_t {
|
NDIlib_source_t {
|
||||||
p_ndi_name: ndi_name.as_ptr(),
|
p_ndi_name: ndi_name.as_ptr(),
|
||||||
p_ip_address: ip_address.as_ptr(),
|
p_url_address: url_address.as_ptr(),
|
||||||
},
|
},
|
||||||
ndi_name,
|
ndi_name,
|
||||||
ip_address,
|
url_address,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -223,12 +225,12 @@ impl<'a> RecvBuilder<'a> {
|
||||||
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: self.source_to_connect_to.ndi_name_ptr(),
|
p_ndi_name: self.source_to_connect_to.ndi_name_ptr(),
|
||||||
p_ip_address: self.source_to_connect_to.ip_address_ptr(),
|
p_url_address: self.source_to_connect_to.url_address_ptr(),
|
||||||
},
|
},
|
||||||
allow_video_fields: self.allow_video_fields,
|
allow_video_fields: self.allow_video_fields,
|
||||||
bandwidth: self.bandwidth,
|
bandwidth: self.bandwidth,
|
||||||
color_format: self.color_format,
|
color_format: self.color_format,
|
||||||
p_ndi_name: ndi_name.as_ptr(),
|
p_ndi_recv_name: ndi_name.as_ptr(),
|
||||||
});
|
});
|
||||||
|
|
||||||
if ptr.is_null() {
|
if ptr.is_null() {
|
||||||
|
@ -410,7 +412,7 @@ impl<'a> VideoFrame<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fourcc(&self) -> NDIlib_FourCC_type_e {
|
pub fn fourcc(&self) -> NDIlib_FourCC_video_type_e {
|
||||||
match self {
|
match self {
|
||||||
VideoFrame::Borrowed(ref frame, _) => frame.FourCC,
|
VideoFrame::Borrowed(ref frame, _) => frame.FourCC,
|
||||||
}
|
}
|
||||||
|
@ -448,9 +450,9 @@ impl<'a> VideoFrame<'a> {
|
||||||
|| self.frame_format_type()
|
|| self.frame_format_type()
|
||||||
== NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_1
|
== NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_1
|
||||||
{
|
{
|
||||||
self.yres() * self.line_stride_in_bytes() / 2
|
self.yres() * self.line_stride_or_data_size_in_bytes() / 2
|
||||||
} else {
|
} else {
|
||||||
self.yres() * self.line_stride_in_bytes()
|
self.yres() * self.line_stride_or_data_size_in_bytes()
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -463,9 +465,32 @@ impl<'a> VideoFrame<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn line_stride_in_bytes(&self) -> i32 {
|
pub fn line_stride_or_data_size_in_bytes(&self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
VideoFrame::Borrowed(ref frame, _) => frame.line_stride_in_bytes,
|
VideoFrame::Borrowed(ref frame, _) => {
|
||||||
|
let stride = frame.line_stride_or_data_size_in_bytes;
|
||||||
|
|
||||||
|
if stride != 0 {
|
||||||
|
return stride;
|
||||||
|
}
|
||||||
|
|
||||||
|
let xres = frame.xres;
|
||||||
|
|
||||||
|
match frame.FourCC {
|
||||||
|
ndisys::NDIlib_FourCC_video_type_UYVY
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_UYVA
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_YV12
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_NV12
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_I420
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_BGRA
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_BGRX
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_RGBA
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_RGBX => xres,
|
||||||
|
ndisys::NDIlib_FourCC_video_type_P216
|
||||||
|
| ndisys::NDIlib_FourCC_video_type_PA16 => 2 * xres,
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ use crate::DEFAULT_RECEIVER_NDI_NAME;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
ndi_name: Option<String>,
|
ndi_name: Option<String>,
|
||||||
ip_address: Option<String>,
|
|
||||||
connect_timeout: u32,
|
connect_timeout: u32,
|
||||||
timeout: u32,
|
timeout: u32,
|
||||||
receiver_ndi_name: String,
|
receiver_ndi_name: String,
|
||||||
|
@ -36,7 +35,6 @@ impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Settings {
|
Settings {
|
||||||
ndi_name: None,
|
ndi_name: None,
|
||||||
ip_address: None,
|
|
||||||
receiver_ndi_name: DEFAULT_RECEIVER_NDI_NAME.clone(),
|
receiver_ndi_name: DEFAULT_RECEIVER_NDI_NAME.clone(),
|
||||||
connect_timeout: 10000,
|
connect_timeout: 10000,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
|
@ -46,7 +44,7 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 7] = [
|
static PROPERTIES: [subclass::Property; 6] = [
|
||||||
subclass::Property("ndi-name", |name| {
|
subclass::Property("ndi-name", |name| {
|
||||||
glib::ParamSpec::string(
|
glib::ParamSpec::string(
|
||||||
name,
|
name,
|
||||||
|
@ -56,15 +54,6 @@ static PROPERTIES: [subclass::Property; 7] = [
|
||||||
glib::ParamFlags::READWRITE,
|
glib::ParamFlags::READWRITE,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
subclass::Property("ip-address", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"IP Address",
|
|
||||||
"IP address and port of the sender, e.g. 127.0.0.1:5961",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("receiver-ndi-name", |name| {
|
subclass::Property("receiver-ndi-name", |name| {
|
||||||
glib::ParamSpec::string(
|
glib::ParamSpec::string(
|
||||||
name,
|
name,
|
||||||
|
@ -227,18 +216,6 @@ impl ObjectImpl for NdiAudioSrc {
|
||||||
);
|
);
|
||||||
settings.ndi_name = ndi_name;
|
settings.ndi_name = ndi_name;
|
||||||
}
|
}
|
||||||
subclass::Property("ip-address", ..) => {
|
|
||||||
let mut settings = self.settings.lock().unwrap();
|
|
||||||
let ip_address = value.get().unwrap();
|
|
||||||
gst_debug!(
|
|
||||||
self.cat,
|
|
||||||
obj: basesrc,
|
|
||||||
"Changing ip from {:?} to {:?}",
|
|
||||||
settings.ip_address,
|
|
||||||
ip_address,
|
|
||||||
);
|
|
||||||
settings.ip_address = ip_address;
|
|
||||||
}
|
|
||||||
subclass::Property("receiver-ndi-name", ..) => {
|
subclass::Property("receiver-ndi-name", ..) => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let receiver_ndi_name = value.get().unwrap();
|
let receiver_ndi_name = value.get().unwrap();
|
||||||
|
@ -316,10 +293,6 @@ impl ObjectImpl for NdiAudioSrc {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
Ok(settings.ndi_name.to_value())
|
Ok(settings.ndi_name.to_value())
|
||||||
}
|
}
|
||||||
subclass::Property("ip-address", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
|
||||||
Ok(settings.ip_address.to_value())
|
|
||||||
}
|
|
||||||
subclass::Property("receiver-ndi-name", ..) => {
|
subclass::Property("receiver-ndi-name", ..) => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
Ok(settings.receiver_ndi_name.to_value())
|
Ok(settings.receiver_ndi_name.to_value())
|
||||||
|
@ -401,18 +374,19 @@ 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.ip_address.is_none() && settings.ndi_name.is_none() {
|
let ndi_name = if let Some(ref ndi_name) = settings.ndi_name {
|
||||||
|
ndi_name
|
||||||
|
} else {
|
||||||
return Err(gst_error_msg!(
|
return Err(gst_error_msg!(
|
||||||
gst::LibraryError::Settings,
|
gst::LibraryError::Settings,
|
||||||
["No IP address or NDI name given"]
|
["No IP address or NDI name given"]
|
||||||
));
|
));
|
||||||
}
|
};
|
||||||
|
|
||||||
let receiver = connect_ndi(
|
let receiver = connect_ndi(
|
||||||
self.cat,
|
self.cat,
|
||||||
element,
|
element,
|
||||||
settings.ip_address.as_ref().map(String::as_str),
|
ndi_name,
|
||||||
settings.ndi_name.as_ref().map(String::as_str),
|
|
||||||
&settings.receiver_ndi_name,
|
&settings.receiver_ndi_name,
|
||||||
settings.connect_timeout,
|
settings.connect_timeout,
|
||||||
settings.bandwidth,
|
settings.bandwidth,
|
||||||
|
@ -526,7 +500,7 @@ impl BaseSrcImpl for NdiAudioSrc {
|
||||||
);
|
);
|
||||||
gst::FlowError::NotNegotiated
|
gst::FlowError::NotNegotiated
|
||||||
})?;
|
})?;
|
||||||
state.info = Some(info.clone());
|
state.info = Some(info);
|
||||||
state.current_latency = buffer.get_duration();
|
state.current_latency = buffer.get_duration();
|
||||||
drop(state);
|
drop(state);
|
||||||
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
||||||
|
|
|
@ -78,7 +78,7 @@ pub struct NDIlib_find_create_t {
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct NDIlib_source_t {
|
pub struct NDIlib_source_t {
|
||||||
pub p_ndi_name: *const ::std::os::raw::c_char,
|
pub p_ndi_name: *const ::std::os::raw::c_char,
|
||||||
pub p_ip_address: *const ::std::os::raw::c_char,
|
pub p_url_address: *const ::std::os::raw::c_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(i32)]
|
#[repr(i32)]
|
||||||
|
@ -107,21 +107,21 @@ pub enum NDIlib_recv_color_format_e {
|
||||||
NDIlib_recv_color_format_RGBX_RGBA = 2,
|
NDIlib_recv_color_format_RGBX_RGBA = 2,
|
||||||
NDIlib_recv_color_format_UYVY_RGBA = 3,
|
NDIlib_recv_color_format_UYVY_RGBA = 3,
|
||||||
NDIlib_recv_color_format_fastest = 100,
|
NDIlib_recv_color_format_fastest = 100,
|
||||||
|
NDIlib_recv_color_format_best = 101,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[repr(u32)]
|
pub type NDIlib_FourCC_video_type_e = u32;
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
pub const NDIlib_FourCC_video_type_UYVY: NDIlib_FourCC_video_type_e = 0x59_56_59_55;
|
||||||
pub enum NDIlib_FourCC_type_e {
|
pub const NDIlib_FourCC_video_type_UYVA: NDIlib_FourCC_video_type_e = 0x41_56_56_55;
|
||||||
NDIlib_FourCC_type_UYVY = 0x59_56_59_55,
|
pub const NDIlib_FourCC_video_type_P216: NDIlib_FourCC_video_type_e = 0x36_31_32_50;
|
||||||
NDIlib_FourCC_type_YV12 = 0x32_31_56_59,
|
pub const NDIlib_FourCC_video_type_PA16: NDIlib_FourCC_video_type_e = 0x36_31_41_50;
|
||||||
NDIlib_FourCC_type_NV12 = 0x32_31_56_4e,
|
pub const NDIlib_FourCC_video_type_YV12: NDIlib_FourCC_video_type_e = 0x32_31_56_59;
|
||||||
NDIlib_FourCC_type_I420 = 0x30_32_34_49,
|
pub const NDIlib_FourCC_video_type_I420: NDIlib_FourCC_video_type_e = 0x30_32_34_49;
|
||||||
NDIlib_FourCC_type_BGRA = 0x41_52_47_42,
|
pub const NDIlib_FourCC_video_type_NV12: NDIlib_FourCC_video_type_e = 0x32_31_56_4e;
|
||||||
NDIlib_FourCC_type_BGRX = 0x58_52_47_42,
|
pub const NDIlib_FourCC_video_type_BGRA: NDIlib_FourCC_video_type_e = 0x41_52_47_42;
|
||||||
NDIlib_FourCC_type_RGBA = 0x41_42_47_52,
|
pub const NDIlib_FourCC_video_type_BGRX: NDIlib_FourCC_video_type_e = 0x58_52_47_42;
|
||||||
NDIlib_FourCC_type_RGBX = 0x58_42_47_52,
|
pub const NDIlib_FourCC_video_type_RGBA: NDIlib_FourCC_video_type_e = 0x41_42_47_52;
|
||||||
NDIlib_FourCC_type_UYVA = 0x41_56_56_55,
|
pub const NDIlib_FourCC_video_type_RGBX: NDIlib_FourCC_video_type_e = 0x58_42_47_52;
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
|
@ -133,7 +133,6 @@ pub enum NDIlib_frame_format_type_e {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const NDIlib_send_timecode_synthesize: i64 = ::std::i64::MAX;
|
pub const NDIlib_send_timecode_synthesize: i64 = ::std::i64::MAX;
|
||||||
pub const NDIlib_send_timecode_empty: i64 = 0;
|
|
||||||
pub const NDIlib_recv_timestamp_undefined: i64 = ::std::i64::MAX;
|
pub const NDIlib_recv_timestamp_undefined: i64 = ::std::i64::MAX;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
@ -143,7 +142,7 @@ pub struct NDIlib_recv_create_v3_t {
|
||||||
pub color_format: NDIlib_recv_color_format_e,
|
pub color_format: NDIlib_recv_color_format_e,
|
||||||
pub bandwidth: NDIlib_recv_bandwidth_e,
|
pub bandwidth: NDIlib_recv_bandwidth_e,
|
||||||
pub allow_video_fields: bool,
|
pub allow_video_fields: bool,
|
||||||
pub p_ndi_name: *const ::std::os::raw::c_char,
|
pub p_ndi_recv_name: *const ::std::os::raw::c_char,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type NDIlib_recv_instance_t = *mut ::std::os::raw::c_void;
|
pub type NDIlib_recv_instance_t = *mut ::std::os::raw::c_void;
|
||||||
|
@ -176,14 +175,14 @@ pub struct NDIlib_metadata_frame_t {
|
||||||
pub struct NDIlib_video_frame_v2_t {
|
pub struct NDIlib_video_frame_v2_t {
|
||||||
pub xres: ::std::os::raw::c_int,
|
pub xres: ::std::os::raw::c_int,
|
||||||
pub yres: ::std::os::raw::c_int,
|
pub yres: ::std::os::raw::c_int,
|
||||||
pub FourCC: NDIlib_FourCC_type_e,
|
pub FourCC: NDIlib_FourCC_video_type_e,
|
||||||
pub frame_rate_N: ::std::os::raw::c_int,
|
pub frame_rate_N: ::std::os::raw::c_int,
|
||||||
pub frame_rate_D: ::std::os::raw::c_int,
|
pub frame_rate_D: ::std::os::raw::c_int,
|
||||||
pub picture_aspect_ratio: ::std::os::raw::c_float,
|
pub picture_aspect_ratio: ::std::os::raw::c_float,
|
||||||
pub frame_format_type: NDIlib_frame_format_type_e,
|
pub frame_format_type: NDIlib_frame_format_type_e,
|
||||||
pub timecode: i64,
|
pub timecode: i64,
|
||||||
pub p_data: *const ::std::os::raw::c_char,
|
pub p_data: *const ::std::os::raw::c_char,
|
||||||
pub line_stride_in_bytes: ::std::os::raw::c_int,
|
pub line_stride_or_data_size_in_bytes: ::std::os::raw::c_int,
|
||||||
pub p_metadata: *const ::std::os::raw::c_char,
|
pub p_metadata: *const ::std::os::raw::c_char,
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
}
|
}
|
||||||
|
@ -221,5 +220,5 @@ pub struct NDIlib_audio_frame_interleaved_16s_t {
|
||||||
pub no_samples: ::std::os::raw::c_int,
|
pub no_samples: ::std::os::raw::c_int,
|
||||||
pub timecode: i64,
|
pub timecode: i64,
|
||||||
pub reference_level: ::std::os::raw::c_int,
|
pub reference_level: ::std::os::raw::c_int,
|
||||||
pub p_data: *mut ::std::os::raw::c_short,
|
pub p_data: *mut i16,
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ use crate::DEFAULT_RECEIVER_NDI_NAME;
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
struct Settings {
|
struct Settings {
|
||||||
ndi_name: Option<String>,
|
ndi_name: Option<String>,
|
||||||
ip_address: Option<String>,
|
|
||||||
connect_timeout: u32,
|
connect_timeout: u32,
|
||||||
timeout: u32,
|
timeout: u32,
|
||||||
receiver_ndi_name: String,
|
receiver_ndi_name: String,
|
||||||
|
@ -37,7 +36,6 @@ impl Default for Settings {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Settings {
|
Settings {
|
||||||
ndi_name: None,
|
ndi_name: None,
|
||||||
ip_address: None,
|
|
||||||
receiver_ndi_name: DEFAULT_RECEIVER_NDI_NAME.clone(),
|
receiver_ndi_name: DEFAULT_RECEIVER_NDI_NAME.clone(),
|
||||||
connect_timeout: 10000,
|
connect_timeout: 10000,
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
|
@ -47,7 +45,7 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PROPERTIES: [subclass::Property; 7] = [
|
static PROPERTIES: [subclass::Property; 6] = [
|
||||||
subclass::Property("ndi-name", |name| {
|
subclass::Property("ndi-name", |name| {
|
||||||
glib::ParamSpec::string(
|
glib::ParamSpec::string(
|
||||||
name,
|
name,
|
||||||
|
@ -57,15 +55,6 @@ static PROPERTIES: [subclass::Property; 7] = [
|
||||||
glib::ParamFlags::READWRITE,
|
glib::ParamFlags::READWRITE,
|
||||||
)
|
)
|
||||||
}),
|
}),
|
||||||
subclass::Property("ip-address", |name| {
|
|
||||||
glib::ParamSpec::string(
|
|
||||||
name,
|
|
||||||
"IP Address",
|
|
||||||
"IP address and port of the sender, e.g. 127.0.0.1:5961",
|
|
||||||
None,
|
|
||||||
glib::ParamFlags::READWRITE,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
subclass::Property("receiver-ndi-name", |name| {
|
subclass::Property("receiver-ndi-name", |name| {
|
||||||
glib::ParamSpec::string(
|
glib::ParamSpec::string(
|
||||||
name,
|
name,
|
||||||
|
@ -262,18 +251,6 @@ impl ObjectImpl for NdiVideoSrc {
|
||||||
);
|
);
|
||||||
settings.ndi_name = ndi_name;
|
settings.ndi_name = ndi_name;
|
||||||
}
|
}
|
||||||
subclass::Property("ip-address", ..) => {
|
|
||||||
let mut settings = self.settings.lock().unwrap();
|
|
||||||
let ip_address = value.get().unwrap();
|
|
||||||
gst_debug!(
|
|
||||||
self.cat,
|
|
||||||
obj: basesrc,
|
|
||||||
"Changing ip from {:?} to {:?}",
|
|
||||||
settings.ip_address,
|
|
||||||
ip_address,
|
|
||||||
);
|
|
||||||
settings.ip_address = ip_address;
|
|
||||||
}
|
|
||||||
subclass::Property("receiver-ndi-name", ..) => {
|
subclass::Property("receiver-ndi-name", ..) => {
|
||||||
let mut settings = self.settings.lock().unwrap();
|
let mut settings = self.settings.lock().unwrap();
|
||||||
let receiver_ndi_name = value.get().unwrap();
|
let receiver_ndi_name = value.get().unwrap();
|
||||||
|
@ -351,10 +328,6 @@ impl ObjectImpl for NdiVideoSrc {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
Ok(settings.ndi_name.to_value())
|
Ok(settings.ndi_name.to_value())
|
||||||
}
|
}
|
||||||
subclass::Property("ip-address", ..) => {
|
|
||||||
let settings = self.settings.lock().unwrap();
|
|
||||||
Ok(settings.ip_address.to_value())
|
|
||||||
}
|
|
||||||
subclass::Property("receiver-ndi-name", ..) => {
|
subclass::Property("receiver-ndi-name", ..) => {
|
||||||
let settings = self.settings.lock().unwrap();
|
let settings = self.settings.lock().unwrap();
|
||||||
Ok(settings.receiver_ndi_name.to_value())
|
Ok(settings.receiver_ndi_name.to_value())
|
||||||
|
@ -436,18 +409,19 @@ 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.ip_address.is_none() && settings.ndi_name.is_none() {
|
let ndi_name = if let Some(ref ndi_name) = settings.ndi_name {
|
||||||
|
ndi_name
|
||||||
|
} else {
|
||||||
return Err(gst_error_msg!(
|
return Err(gst_error_msg!(
|
||||||
gst::LibraryError::Settings,
|
gst::LibraryError::Settings,
|
||||||
["No IP address or NDI name given"]
|
["No IP address or NDI name given"]
|
||||||
));
|
));
|
||||||
}
|
};
|
||||||
|
|
||||||
let receiver = connect_ndi(
|
let receiver = connect_ndi(
|
||||||
self.cat,
|
self.cat,
|
||||||
element,
|
element,
|
||||||
settings.ip_address.as_ref().map(String::as_str),
|
ndi_name,
|
||||||
settings.ndi_name.as_ref().map(String::as_str),
|
|
||||||
&settings.receiver_ndi_name,
|
&settings.receiver_ndi_name,
|
||||||
settings.connect_timeout,
|
settings.connect_timeout,
|
||||||
settings.bandwidth,
|
settings.bandwidth,
|
||||||
|
@ -565,7 +539,7 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
);
|
);
|
||||||
gst::FlowError::NotNegotiated
|
gst::FlowError::NotNegotiated
|
||||||
})?;
|
})?;
|
||||||
state.info = Some(info.clone());
|
state.info = Some(info);
|
||||||
state.current_latency = buffer.get_duration();
|
state.current_latency = buffer.get_duration();
|
||||||
drop(state);
|
drop(state);
|
||||||
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
||||||
|
|
100
src/receiver.rs
100
src/receiver.rs
|
@ -18,8 +18,7 @@ use super::*;
|
||||||
enum ReceiverInfo {
|
enum ReceiverInfo {
|
||||||
Connecting {
|
Connecting {
|
||||||
id: usize,
|
id: usize,
|
||||||
ndi_name: Option<String>,
|
ndi_name: String,
|
||||||
ip_address: Option<String>,
|
|
||||||
video: Option<Weak<ReceiverInner<VideoReceiver>>>,
|
video: Option<Weak<ReceiverInner<VideoReceiver>>>,
|
||||||
audio: Option<Weak<ReceiverInner<AudioReceiver>>>,
|
audio: Option<Weak<ReceiverInner<AudioReceiver>>>,
|
||||||
observations: Observations,
|
observations: Observations,
|
||||||
|
@ -27,7 +26,6 @@ enum ReceiverInfo {
|
||||||
Connected {
|
Connected {
|
||||||
id: usize,
|
id: usize,
|
||||||
ndi_name: String,
|
ndi_name: String,
|
||||||
ip_address: String,
|
|
||||||
recv: RecvInstance,
|
recv: RecvInstance,
|
||||||
video: Option<Weak<ReceiverInner<VideoReceiver>>>,
|
video: Option<Weak<ReceiverInner<VideoReceiver>>>,
|
||||||
audio: Option<Weak<ReceiverInner<AudioReceiver>>>,
|
audio: Option<Weak<ReceiverInner<AudioReceiver>>>,
|
||||||
|
@ -557,8 +555,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,
|
||||||
ip_address: Option<&str>,
|
ndi_name: &str,
|
||||||
ndi_name: Option<&str>,
|
|
||||||
receiver_ndi_name: &str,
|
receiver_ndi_name: &str,
|
||||||
connect_timeout: u32,
|
connect_timeout: u32,
|
||||||
bandwidth: NDIlib_recv_bandwidth_e,
|
bandwidth: NDIlib_recv_bandwidth_e,
|
||||||
|
@ -570,45 +567,33 @@ where
|
||||||
{
|
{
|
||||||
gst_debug!(cat, obj: element, "Starting NDI connection...");
|
gst_debug!(cat, obj: element, "Starting NDI connection...");
|
||||||
|
|
||||||
let ip_address = ip_address.map(str::to_lowercase);
|
|
||||||
|
|
||||||
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 val in receivers.values_mut() {
|
for val in receivers.values_mut() {
|
||||||
let (val_audio, val_video, val_ip_address, val_ndi_name) = match val {
|
let (val_audio, val_video, val_ndi_name) = match val {
|
||||||
ReceiverInfo::Connecting {
|
ReceiverInfo::Connecting {
|
||||||
ref mut audio,
|
ref mut audio,
|
||||||
ref mut video,
|
ref mut video,
|
||||||
ref ip_address,
|
|
||||||
ref ndi_name,
|
ref ndi_name,
|
||||||
..
|
..
|
||||||
} => (
|
} => (audio, video, ndi_name.as_str()),
|
||||||
audio,
|
|
||||||
video,
|
|
||||||
ip_address.as_ref(),
|
|
||||||
ndi_name.as_ref().map(String::as_ref),
|
|
||||||
),
|
|
||||||
ReceiverInfo::Connected {
|
ReceiverInfo::Connected {
|
||||||
ref mut audio,
|
ref mut audio,
|
||||||
ref mut video,
|
ref mut video,
|
||||||
ref ip_address,
|
|
||||||
ref ndi_name,
|
ref ndi_name,
|
||||||
..
|
..
|
||||||
} => (audio, video, Some(ip_address), Some(ndi_name.as_str())),
|
} => (audio, video, ndi_name.as_str()),
|
||||||
};
|
};
|
||||||
|
|
||||||
if (val_ip_address.is_some() && val_ip_address == ip_address.as_ref())
|
if val_ndi_name == ndi_name {
|
||||||
|| (val_ip_address.is_none() && val_ndi_name == ndi_name)
|
|
||||||
{
|
|
||||||
if (val_video.is_some() || !T::IS_VIDEO) && (val_audio.is_some() || T::IS_VIDEO) {
|
if (val_video.is_some() || !T::IS_VIDEO) && (val_audio.is_some() || T::IS_VIDEO) {
|
||||||
gst_element_error!(
|
gst_element_error!(
|
||||||
element,
|
element,
|
||||||
gst::ResourceError::OpenRead,
|
gst::ResourceError::OpenRead,
|
||||||
[
|
[
|
||||||
"Source with ndi-name '{:?}' and ip-address '{:?}' already in use for {}",
|
"Source with ndi-name '{}' already in use for {}",
|
||||||
val_ndi_name,
|
val_ndi_name,
|
||||||
val_ip_address,
|
|
||||||
if T::IS_VIDEO { "video" } else { "audio" }
|
if T::IS_VIDEO { "video" } else { "audio" }
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
@ -624,8 +609,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::Connecting {
|
let mut info = ReceiverInfo::Connecting {
|
||||||
id: id_receiver,
|
id: id_receiver,
|
||||||
ndi_name: ndi_name.map(String::from),
|
ndi_name: String::from(ndi_name),
|
||||||
ip_address,
|
|
||||||
video: None,
|
video: None,
|
||||||
audio: None,
|
audio: None,
|
||||||
observations: Observations::new(),
|
observations: Observations::new(),
|
||||||
|
@ -746,9 +730,9 @@ fn connect_ndi_async(
|
||||||
gst_debug!(
|
gst_debug!(
|
||||||
cat,
|
cat,
|
||||||
obj: element,
|
obj: element,
|
||||||
"Found source '{}' with IP {}",
|
"Found source '{}' with URL {}",
|
||||||
source.ndi_name(),
|
source.ndi_name(),
|
||||||
source.ip_address(),
|
source.url_address(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -758,24 +742,20 @@ fn connect_ndi_async(
|
||||||
Some(val) => val,
|
Some(val) => val,
|
||||||
};
|
};
|
||||||
|
|
||||||
let (ndi_name, ip_address) = match info {
|
let ndi_name = match info {
|
||||||
ReceiverInfo::Connecting {
|
ReceiverInfo::Connecting {
|
||||||
ref ndi_name,
|
ref ndi_name,
|
||||||
ref ip_address,
|
|
||||||
ref audio,
|
ref audio,
|
||||||
ref video,
|
ref video,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
assert!(audio.is_some() || video.is_some());
|
assert!(audio.is_some() || video.is_some());
|
||||||
(ndi_name, ip_address)
|
ndi_name
|
||||||
}
|
}
|
||||||
ReceiverInfo::Connected { .. } => unreachable!(),
|
ReceiverInfo::Connected { .. } => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let source = sources.iter().find(|s| {
|
let source = sources.iter().find(|s| s.ndi_name() == ndi_name.as_str());
|
||||||
Some(s.ndi_name()) == ndi_name.as_ref().map(String::as_str)
|
|
||||||
|| Some(&s.ip_address().to_lowercase()) == ip_address.as_ref()
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(source) = source {
|
if let Some(source) = source {
|
||||||
break source.to_owned();
|
break source.to_owned();
|
||||||
|
@ -793,9 +773,9 @@ fn connect_ndi_async(
|
||||||
gst_debug!(
|
gst_debug!(
|
||||||
cat,
|
cat,
|
||||||
obj: element,
|
obj: element,
|
||||||
"Connecting to NDI source with ndi-name '{}' and ip-address '{}'",
|
"Connecting to NDI source with ndi-name '{}' and URL {}",
|
||||||
source.ndi_name(),
|
source.ndi_name(),
|
||||||
source.ip_address(),
|
source.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
|
||||||
|
@ -841,7 +821,6 @@ fn connect_ndi_async(
|
||||||
*info = ReceiverInfo::Connected {
|
*info = ReceiverInfo::Connected {
|
||||||
id: id_receiver,
|
id: id_receiver,
|
||||||
ndi_name: source.ndi_name().to_owned(),
|
ndi_name: source.ndi_name().to_owned(),
|
||||||
ip_address: source.ip_address().to_lowercase(),
|
|
||||||
recv: recv.clone(),
|
recv: recv.clone(),
|
||||||
video: video.clone(),
|
video: video.clone(),
|
||||||
audio: audio.clone(),
|
audio: audio.clone(),
|
||||||
|
@ -860,7 +839,7 @@ fn connect_ndi_async(
|
||||||
if let Some(video) = video.and_then(|v| v.upgrade()).map(Receiver) {
|
if let Some(video) = video.and_then(|v| v.upgrade()).map(Receiver) {
|
||||||
let mut video_recv = video.0.recv.lock().unwrap();
|
let mut video_recv = video.0.recv.lock().unwrap();
|
||||||
assert!(video_recv.is_none());
|
assert!(video_recv.is_none());
|
||||||
*video_recv = Some(recv.clone());
|
*video_recv = Some(recv);
|
||||||
video.0.recv_cond.notify_one();
|
video.0.recv_cond.notify_one();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1252,19 +1231,30 @@ impl Receiver<VideoReceiver> {
|
||||||
) -> Result<gst_video::VideoInfo, gst::FlowError> {
|
) -> Result<gst_video::VideoInfo, gst::FlowError> {
|
||||||
// YV12 and I420 are swapped in the NDI SDK compared to GStreamer
|
// YV12 and I420 are swapped in the NDI SDK compared to GStreamer
|
||||||
let format = match video_frame.fourcc() {
|
let format = match video_frame.fourcc() {
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_UYVY => gst_video::VideoFormat::Uyvy,
|
ndisys::NDIlib_FourCC_video_type_UYVY => gst_video::VideoFormat::Uyvy,
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_YV12 => gst_video::VideoFormat::I420,
|
// FIXME: This drops the alpha plane!
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_NV12 => gst_video::VideoFormat::Nv12,
|
ndisys::NDIlib_FourCC_video_type_UYVA => gst_video::VideoFormat::Uyvy,
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_I420 => gst_video::VideoFormat::Yv12,
|
ndisys::NDIlib_FourCC_video_type_YV12 => gst_video::VideoFormat::I420,
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_BGRA => gst_video::VideoFormat::Bgra,
|
ndisys::NDIlib_FourCC_video_type_NV12 => gst_video::VideoFormat::Nv12,
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_BGRX => gst_video::VideoFormat::Bgrx,
|
ndisys::NDIlib_FourCC_video_type_I420 => gst_video::VideoFormat::Yv12,
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_RGBA => gst_video::VideoFormat::Rgba,
|
ndisys::NDIlib_FourCC_video_type_BGRA => gst_video::VideoFormat::Bgra,
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_RGBX => gst_video::VideoFormat::Rgbx,
|
ndisys::NDIlib_FourCC_video_type_BGRX => gst_video::VideoFormat::Bgrx,
|
||||||
ndisys::NDIlib_FourCC_type_e::NDIlib_FourCC_type_UYVA => gst_video::VideoFormat::Uyvy,
|
ndisys::NDIlib_FourCC_video_type_RGBA => gst_video::VideoFormat::Rgba,
|
||||||
|
ndisys::NDIlib_FourCC_video_type_RGBX => gst_video::VideoFormat::Rgbx,
|
||||||
|
_ => {
|
||||||
|
gst_element_error!(
|
||||||
|
element,
|
||||||
|
gst::StreamError::Format,
|
||||||
|
["Unsupported video fourcc {:08x}", video_frame.fourcc()]
|
||||||
|
);
|
||||||
|
|
||||||
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
|
} // TODO: NDIlib_FourCC_video_type_P216 and NDIlib_FourCC_video_type_PA16 not
|
||||||
|
// supported by GStreamer
|
||||||
};
|
};
|
||||||
|
|
||||||
let par = gst::Fraction::approximate_f32(video_frame.picture_aspect_ratio())
|
let par = gst::Fraction::approximate_f32(video_frame.picture_aspect_ratio())
|
||||||
.unwrap_or(gst::Fraction::new(1, 1))
|
.unwrap_or_else(|| gst::Fraction::new(1, 1))
|
||||||
* gst::Fraction::new(video_frame.yres(), video_frame.xres());
|
* gst::Fraction::new(video_frame.yres(), video_frame.xres());
|
||||||
|
|
||||||
#[cfg(feature = "interlaced-fields")]
|
#[cfg(feature = "interlaced-fields")]
|
||||||
|
@ -1447,7 +1437,7 @@ impl Receiver<VideoReceiver> {
|
||||||
};
|
};
|
||||||
let dest_stride = vframe.plane_stride()[0] as usize;
|
let dest_stride = vframe.plane_stride()[0] as usize;
|
||||||
let dest = vframe.plane_data_mut(0).unwrap();
|
let dest = vframe.plane_data_mut(0).unwrap();
|
||||||
let src_stride = video_frame.line_stride_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src = video_frame.data();
|
let src = video_frame.data();
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
|
@ -1464,7 +1454,7 @@ impl Receiver<VideoReceiver> {
|
||||||
let line_bytes = vframe.width() as usize;
|
let line_bytes = vframe.width() as usize;
|
||||||
let dest_stride = vframe.plane_stride()[0] as usize;
|
let dest_stride = vframe.plane_stride()[0] as usize;
|
||||||
let dest = vframe.plane_data_mut(0).unwrap();
|
let dest = vframe.plane_data_mut(0).unwrap();
|
||||||
let src_stride = video_frame.line_stride_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src = video_frame.data();
|
let src = video_frame.data();
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
|
@ -1480,7 +1470,7 @@ impl Receiver<VideoReceiver> {
|
||||||
let line_bytes = vframe.width() as usize;
|
let line_bytes = vframe.width() as usize;
|
||||||
let dest_stride = vframe.plane_stride()[1] as usize;
|
let dest_stride = vframe.plane_stride()[1] as usize;
|
||||||
let dest = vframe.plane_data_mut(1).unwrap();
|
let dest = vframe.plane_data_mut(1).unwrap();
|
||||||
let src_stride = video_frame.line_stride_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src = &video_frame.data()[(video_frame.yres() as usize * src_stride)..];
|
let src = &video_frame.data()[(video_frame.yres() as usize * src_stride)..];
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
|
@ -1497,7 +1487,7 @@ impl Receiver<VideoReceiver> {
|
||||||
let line_bytes = vframe.width() as usize;
|
let line_bytes = vframe.width() as usize;
|
||||||
let dest_stride = vframe.plane_stride()[0] as usize;
|
let dest_stride = vframe.plane_stride()[0] as usize;
|
||||||
let dest = vframe.plane_data_mut(0).unwrap();
|
let dest = vframe.plane_data_mut(0).unwrap();
|
||||||
let src_stride = video_frame.line_stride_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src = video_frame.data();
|
let src = video_frame.data();
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
|
@ -1513,8 +1503,8 @@ impl Receiver<VideoReceiver> {
|
||||||
let line_bytes = (vframe.width() as usize + 1) / 2;
|
let line_bytes = (vframe.width() as usize + 1) / 2;
|
||||||
let dest_stride = vframe.plane_stride()[1] as usize;
|
let dest_stride = vframe.plane_stride()[1] as usize;
|
||||||
let dest = vframe.plane_data_mut(1).unwrap();
|
let dest = vframe.plane_data_mut(1).unwrap();
|
||||||
let src_stride = video_frame.line_stride_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src_stride1 = video_frame.line_stride_in_bytes() as usize / 2;
|
let src_stride1 = video_frame.line_stride_or_data_size_in_bytes() as usize / 2;
|
||||||
let src = &video_frame.data()[(video_frame.yres() as usize * src_stride)..];
|
let src = &video_frame.data()[(video_frame.yres() as usize * src_stride)..];
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
|
@ -1530,8 +1520,8 @@ impl Receiver<VideoReceiver> {
|
||||||
let line_bytes = (vframe.width() as usize + 1) / 2;
|
let line_bytes = (vframe.width() as usize + 1) / 2;
|
||||||
let dest_stride = vframe.plane_stride()[2] as usize;
|
let dest_stride = vframe.plane_stride()[2] as usize;
|
||||||
let dest = vframe.plane_data_mut(2).unwrap();
|
let dest = vframe.plane_data_mut(2).unwrap();
|
||||||
let src_stride = video_frame.line_stride_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src_stride1 = video_frame.line_stride_in_bytes() as usize / 2;
|
let src_stride1 = video_frame.line_stride_or_data_size_in_bytes() as usize / 2;
|
||||||
let src = &video_frame.data()[(video_frame.yres() as usize * src_stride
|
let src = &video_frame.data()[(video_frame.yres() as usize * src_stride
|
||||||
+ (video_frame.yres() as usize + 1) / 2 * src_stride1)..];
|
+ (video_frame.yres() as usize + 1) / 2 * src_stride1)..];
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue