mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-12-02 00:11:01 +00:00
Move to NDIlib_recv_capture_v3() and NDIlib_send_send_audio_v3()
These allow more control over the data that is being sent/received, but require NDI SDK 4.0 or newer.
This commit is contained in:
parent
7483a66b66
commit
8cf682d72b
5 changed files with 150 additions and 123 deletions
135
src/ndi.rs
135
src/ndi.rs
|
@ -292,7 +292,7 @@ impl RecvInstance {
|
||||||
let mut audio_frame = mem::zeroed();
|
let mut audio_frame = mem::zeroed();
|
||||||
let mut metadata_frame = mem::zeroed();
|
let mut metadata_frame = mem::zeroed();
|
||||||
|
|
||||||
let res = NDIlib_recv_capture_v2(
|
let res = NDIlib_recv_capture_v3(
|
||||||
ptr,
|
ptr,
|
||||||
&mut video_frame,
|
&mut video_frame,
|
||||||
&mut audio_frame,
|
&mut audio_frame,
|
||||||
|
@ -386,7 +386,7 @@ impl SendInstance {
|
||||||
|
|
||||||
pub fn send_audio(&mut self, frame: &AudioFrame) {
|
pub fn send_audio(&mut self, frame: &AudioFrame) {
|
||||||
unsafe {
|
unsafe {
|
||||||
NDIlib_send_send_audio_v2(self.0.as_ptr(), frame.as_ptr());
|
NDIlib_send_send_audio_v3(self.0.as_ptr(), frame.as_ptr());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -501,7 +501,26 @@ impl<'a> VideoFrame<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn data(&self) -> Option<&[u8]> {
|
||||||
|
let fourcc = self.fourcc();
|
||||||
|
if ![
|
||||||
|
NDIlib_FourCC_video_type_UYVY,
|
||||||
|
NDIlib_FourCC_video_type_UYVA,
|
||||||
|
NDIlib_FourCC_video_type_P216,
|
||||||
|
NDIlib_FourCC_video_type_PA16,
|
||||||
|
NDIlib_FourCC_video_type_YV12,
|
||||||
|
NDIlib_FourCC_video_type_I420,
|
||||||
|
NDIlib_FourCC_video_type_NV12,
|
||||||
|
NDIlib_FourCC_video_type_BGRA,
|
||||||
|
NDIlib_FourCC_video_type_BGRX,
|
||||||
|
NDIlib_FourCC_video_type_RGBA,
|
||||||
|
NDIlib_FourCC_video_type_RGBX,
|
||||||
|
]
|
||||||
|
.contains(&fourcc)
|
||||||
|
{
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: Unclear if this is correct. Needs to be validated against an actual
|
// FIXME: Unclear if this is correct. Needs to be validated against an actual
|
||||||
// interlaced stream
|
// interlaced stream
|
||||||
let frame_size = if self.frame_format_type()
|
let frame_size = if self.frame_format_type()
|
||||||
|
@ -518,7 +537,10 @@ impl<'a> VideoFrame<'a> {
|
||||||
use std::slice;
|
use std::slice;
|
||||||
match self {
|
match self {
|
||||||
VideoFrame::BorrowedRecv(ref frame, _) | VideoFrame::BorrowedGst(ref frame, _) => {
|
VideoFrame::BorrowedRecv(ref frame, _) | VideoFrame::BorrowedGst(ref frame, _) => {
|
||||||
slice::from_raw_parts(frame.p_data as *const u8, frame_size as usize)
|
Some(slice::from_raw_parts(
|
||||||
|
frame.p_data as *const u8,
|
||||||
|
frame_size as usize,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -710,11 +732,11 @@ impl<'a> Drop for VideoFrame<'a> {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AudioFrame<'a> {
|
pub enum AudioFrame<'a> {
|
||||||
Owned(
|
Owned(
|
||||||
NDIlib_audio_frame_v2_t,
|
NDIlib_audio_frame_v3_t,
|
||||||
Option<ffi::CString>,
|
Option<ffi::CString>,
|
||||||
Option<Vec<f32>>,
|
Option<Vec<f32>>,
|
||||||
),
|
),
|
||||||
BorrowedRecv(NDIlib_audio_frame_v2_t, &'a RecvInstance),
|
BorrowedRecv(NDIlib_audio_frame_v3_t, &'a RecvInstance),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> AudioFrame<'a> {
|
impl<'a> AudioFrame<'a> {
|
||||||
|
@ -750,24 +772,39 @@ impl<'a> AudioFrame<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &[u8] {
|
pub fn fourcc(&self) -> NDIlib_FourCC_audio_type_e {
|
||||||
|
match self {
|
||||||
|
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
||||||
|
frame.FourCC
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn data(&self) -> Option<&[u8]> {
|
||||||
unsafe {
|
unsafe {
|
||||||
use std::slice;
|
use std::slice;
|
||||||
|
|
||||||
|
let fourcc = self.fourcc();
|
||||||
|
|
||||||
|
if ![NDIlib_FourCC_audio_type_FLTp].contains(&fourcc) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
match self {
|
match self {
|
||||||
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
||||||
slice::from_raw_parts(
|
Some(slice::from_raw_parts(
|
||||||
frame.p_data as *const u8,
|
frame.p_data as *const u8,
|
||||||
(frame.no_samples * frame.channel_stride_in_bytes) as usize,
|
(frame.no_channels * frame.channel_stride_or_data_size_in_bytes) as usize,
|
||||||
)
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn channel_stride_in_bytes(&self) -> i32 {
|
pub fn channel_stride_or_data_size_in_bytes(&self) -> i32 {
|
||||||
match self {
|
match self {
|
||||||
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
||||||
frame.channel_stride_in_bytes
|
frame.channel_stride_or_data_size_in_bytes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -794,73 +831,55 @@ impl<'a> AudioFrame<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_ptr(&self) -> *const NDIlib_audio_frame_v2_t {
|
pub fn as_ptr(&self) -> *const NDIlib_audio_frame_v3_t {
|
||||||
match self {
|
match self {
|
||||||
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => frame,
|
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => frame,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_to_interleaved_16s(&self, data: &mut [i16]) {
|
pub fn try_from_buffer(
|
||||||
assert_eq!(
|
|
||||||
data.len(),
|
|
||||||
(self.no_samples() * self.no_channels()) as usize
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut dst = NDIlib_audio_frame_interleaved_16s_t {
|
|
||||||
sample_rate: self.sample_rate(),
|
|
||||||
no_channels: self.no_channels(),
|
|
||||||
no_samples: self.no_samples(),
|
|
||||||
timecode: self.timecode(),
|
|
||||||
reference_level: 0,
|
|
||||||
p_data: data.as_mut_ptr(),
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
NDIlib_util_audio_to_interleaved_16s_v2(self.as_ptr(), &mut dst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_from_interleaved_16s(
|
|
||||||
info: &gst_audio::AudioInfo,
|
info: &gst_audio::AudioInfo,
|
||||||
buffer: &gst::BufferRef,
|
buffer: &gst::BufferRef,
|
||||||
timecode: i64,
|
timecode: i64,
|
||||||
) -> Result<Self, ()> {
|
) -> Result<Self, ()> {
|
||||||
if info.format() != gst_audio::AUDIO_FORMAT_S16 {
|
if info.format() != gst_audio::AUDIO_FORMAT_F32 {
|
||||||
return Err(());
|
return Err(());
|
||||||
}
|
}
|
||||||
|
|
||||||
let map = buffer.map_readable().map_err(|_| ())?;
|
let map = buffer.map_readable().map_err(|_| ())?;
|
||||||
let src_data = map.as_slice_of::<i16>().map_err(|_| ())?;
|
let src_data = map.as_slice_of::<f32>().map_err(|_| ())?;
|
||||||
|
|
||||||
let src = NDIlib_audio_frame_interleaved_16s_t {
|
let no_samples = src_data.len() as i32 / info.channels() as i32;
|
||||||
|
let channel_stride_or_data_size_in_bytes = no_samples * mem::size_of::<f32>() as i32;
|
||||||
|
let mut dest_data =
|
||||||
|
Vec::<f32>::with_capacity(no_samples as usize * info.channels() as usize);
|
||||||
|
|
||||||
|
assert_eq!(dest_data.capacity(), src_data.len());
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let dest_ptr = dest_data.as_mut_ptr();
|
||||||
|
|
||||||
|
for (i, samples) in src_data.chunks_exact(info.channels() as usize).enumerate() {
|
||||||
|
for (c, sample) in samples.into_iter().enumerate() {
|
||||||
|
ptr::write(dest_ptr.add(c * no_samples as usize + i), *sample);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_data.set_len(no_samples as usize * info.channels() as usize);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dest = NDIlib_audio_frame_v3_t {
|
||||||
sample_rate: info.rate() as i32,
|
sample_rate: info.rate() as i32,
|
||||||
no_channels: info.channels() as i32,
|
no_channels: info.channels() as i32,
|
||||||
no_samples: src_data.len() as i32 / info.channels() as i32,
|
no_samples,
|
||||||
timecode,
|
timecode,
|
||||||
reference_level: 0,
|
FourCC: NDIlib_FourCC_audio_type_FLTp,
|
||||||
p_data: src_data.as_ptr() as *mut i16,
|
|
||||||
};
|
|
||||||
|
|
||||||
let channel_stride_in_bytes = src.no_samples * mem::size_of::<f32>() as i32;
|
|
||||||
let mut dest_data =
|
|
||||||
Vec::with_capacity(channel_stride_in_bytes as usize * info.channels() as usize);
|
|
||||||
|
|
||||||
let mut dest = NDIlib_audio_frame_v2_t {
|
|
||||||
sample_rate: src.sample_rate,
|
|
||||||
no_channels: src.no_channels,
|
|
||||||
no_samples: src.no_samples,
|
|
||||||
timecode: src.timecode,
|
|
||||||
p_data: dest_data.as_mut_ptr(),
|
p_data: dest_data.as_mut_ptr(),
|
||||||
channel_stride_in_bytes,
|
channel_stride_or_data_size_in_bytes,
|
||||||
p_metadata: ptr::null(),
|
p_metadata: ptr::null(),
|
||||||
timestamp: 0,
|
timestamp: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
unsafe {
|
|
||||||
NDIlib_util_audio_from_interleaved_16s_v2(&src, &mut dest);
|
|
||||||
dest_data.set_len(dest_data.capacity());
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(AudioFrame::Owned(dest, None, Some(dest_data)))
|
Ok(AudioFrame::Owned(dest, None, Some(dest_data)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -870,7 +889,7 @@ impl<'a> Drop for AudioFrame<'a> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
if let AudioFrame::BorrowedRecv(ref mut frame, recv) = *self {
|
if let AudioFrame::BorrowedRecv(ref mut frame, recv) = *self {
|
||||||
unsafe {
|
unsafe {
|
||||||
NDIlib_recv_free_audio_v2(recv.0.as_ptr() as *mut _, frame);
|
NDIlib_recv_free_audio_v3(recv.0.as_ptr() as *mut _, frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,7 +151,7 @@ impl ElementImpl for NdiSink {
|
||||||
)
|
)
|
||||||
.structure(
|
.structure(
|
||||||
gst::Structure::builder("audio/x-raw")
|
gst::Structure::builder("audio/x-raw")
|
||||||
.field("format", &gst_audio::AUDIO_FORMAT_S16.to_str())
|
.field("format", &gst_audio::AUDIO_FORMAT_F32.to_str())
|
||||||
.field("rate", &gst::IntRange::<i32>::new(1, i32::MAX))
|
.field("rate", &gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
.field("channels", &gst::IntRange::<i32>::new(1, i32::MAX))
|
.field("channels", &gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
.field("layout", &"interleaved")
|
.field("layout", &"interleaved")
|
||||||
|
@ -256,9 +256,8 @@ impl BaseSinkImpl for NdiSink {
|
||||||
if let Some(ref info) = state.video_info {
|
if let Some(ref info) = state.video_info {
|
||||||
if let Some(audio_meta) = buffer.meta::<crate::ndisinkmeta::NdiSinkAudioMeta>() {
|
if let Some(audio_meta) = buffer.meta::<crate::ndisinkmeta::NdiSinkAudioMeta>() {
|
||||||
for (buffer, info, timecode) in audio_meta.buffers() {
|
for (buffer, info, timecode) in audio_meta.buffers() {
|
||||||
let frame =
|
let frame = crate::ndi::AudioFrame::try_from_buffer(info, buffer, *timecode)
|
||||||
crate::ndi::AudioFrame::try_from_interleaved_16s(info, buffer, *timecode)
|
.map_err(|_| {
|
||||||
.map_err(|_| {
|
|
||||||
gst_error!(CAT, obj: element, "Unsupported audio frame");
|
gst_error!(CAT, obj: element, "Unsupported audio frame");
|
||||||
gst::FlowError::NotNegotiated
|
gst::FlowError::NotNegotiated
|
||||||
})?;
|
})?;
|
||||||
|
@ -334,8 +333,8 @@ impl BaseSinkImpl for NdiSink {
|
||||||
.map(|time| (time.nseconds() / 100) as i64)
|
.map(|time| (time.nseconds() / 100) as i64)
|
||||||
.unwrap_or(crate::ndisys::NDIlib_send_timecode_synthesize);
|
.unwrap_or(crate::ndisys::NDIlib_send_timecode_synthesize);
|
||||||
|
|
||||||
let frame = crate::ndi::AudioFrame::try_from_interleaved_16s(info, buffer, timecode)
|
let frame =
|
||||||
.map_err(|_| {
|
crate::ndi::AudioFrame::try_from_buffer(info, buffer, timecode).map_err(|_| {
|
||||||
gst_error!(CAT, obj: element, "Unsupported audio frame");
|
gst_error!(CAT, obj: element, "Unsupported audio frame");
|
||||||
gst::FlowError::NotNegotiated
|
gst::FlowError::NotNegotiated
|
||||||
})?;
|
})?;
|
||||||
|
|
|
@ -122,7 +122,7 @@ impl ElementImpl for NdiSinkCombiner {
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let caps = gst::Caps::builder("audio/x-raw")
|
let caps = gst::Caps::builder("audio/x-raw")
|
||||||
.field("format", &gst_audio::AUDIO_FORMAT_S16.to_str())
|
.field("format", &gst_audio::AUDIO_FORMAT_F32.to_str())
|
||||||
.field("rate", &gst::IntRange::<i32>::new(1, i32::MAX))
|
.field("rate", &gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
.field("channels", &gst::IntRange::<i32>::new(1, i32::MAX))
|
.field("channels", &gst::IntRange::<i32>::new(1, i32::MAX))
|
||||||
.field("layout", &"interleaved")
|
.field("layout", &"interleaved")
|
||||||
|
|
|
@ -39,10 +39,10 @@ extern "C" {
|
||||||
p_instance: NDIlib_recv_instance_t,
|
p_instance: NDIlib_recv_instance_t,
|
||||||
p_metadata: *const NDIlib_metadata_frame_t,
|
p_metadata: *const NDIlib_metadata_frame_t,
|
||||||
) -> bool;
|
) -> bool;
|
||||||
pub fn NDIlib_recv_capture_v2(
|
pub fn NDIlib_recv_capture_v3(
|
||||||
p_instance: NDIlib_recv_instance_t,
|
p_instance: NDIlib_recv_instance_t,
|
||||||
p_video_data: *mut NDIlib_video_frame_v2_t,
|
p_video_data: *mut NDIlib_video_frame_v2_t,
|
||||||
p_audio_data: *mut NDIlib_audio_frame_v2_t,
|
p_audio_data: *mut NDIlib_audio_frame_v3_t,
|
||||||
p_metadata: *mut NDIlib_metadata_frame_t,
|
p_metadata: *mut NDIlib_metadata_frame_t,
|
||||||
timeout_in_ms: u32,
|
timeout_in_ms: u32,
|
||||||
) -> NDIlib_frame_type_e;
|
) -> NDIlib_frame_type_e;
|
||||||
|
@ -50,9 +50,9 @@ extern "C" {
|
||||||
p_instance: NDIlib_recv_instance_t,
|
p_instance: NDIlib_recv_instance_t,
|
||||||
p_video_data: *mut NDIlib_video_frame_v2_t,
|
p_video_data: *mut NDIlib_video_frame_v2_t,
|
||||||
);
|
);
|
||||||
pub fn NDIlib_recv_free_audio_v2(
|
pub fn NDIlib_recv_free_audio_v3(
|
||||||
p_instance: NDIlib_recv_instance_t,
|
p_instance: NDIlib_recv_instance_t,
|
||||||
p_audio_data: *mut NDIlib_audio_frame_v2_t,
|
p_audio_data: *mut NDIlib_audio_frame_v3_t,
|
||||||
);
|
);
|
||||||
pub fn NDIlib_recv_free_metadata(
|
pub fn NDIlib_recv_free_metadata(
|
||||||
p_instance: NDIlib_recv_instance_t,
|
p_instance: NDIlib_recv_instance_t,
|
||||||
|
@ -70,9 +70,9 @@ extern "C" {
|
||||||
p_instance: NDIlib_send_instance_t,
|
p_instance: NDIlib_send_instance_t,
|
||||||
p_video_data: *const NDIlib_video_frame_v2_t,
|
p_video_data: *const NDIlib_video_frame_v2_t,
|
||||||
);
|
);
|
||||||
pub fn NDIlib_send_send_audio_v2(
|
pub fn NDIlib_send_send_audio_v3(
|
||||||
p_instance: NDIlib_send_instance_t,
|
p_instance: NDIlib_send_instance_t,
|
||||||
p_audio_data: *const NDIlib_audio_frame_v2_t,
|
p_audio_data: *const NDIlib_audio_frame_v3_t,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -139,6 +139,9 @@ pub const NDIlib_FourCC_video_type_BGRX: NDIlib_FourCC_video_type_e = make_fourc
|
||||||
pub const NDIlib_FourCC_video_type_RGBA: NDIlib_FourCC_video_type_e = make_fourcc(b"RGBA");
|
pub const NDIlib_FourCC_video_type_RGBA: NDIlib_FourCC_video_type_e = make_fourcc(b"RGBA");
|
||||||
pub const NDIlib_FourCC_video_type_RGBX: NDIlib_FourCC_video_type_e = make_fourcc(b"RGBX");
|
pub const NDIlib_FourCC_video_type_RGBX: NDIlib_FourCC_video_type_e = make_fourcc(b"RGBX");
|
||||||
|
|
||||||
|
pub type NDIlib_FourCC_audio_type_e = u32;
|
||||||
|
pub const NDIlib_FourCC_audio_type_FLTp: NDIlib_FourCC_video_type_e = make_fourcc(b"FLTp");
|
||||||
|
|
||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub enum NDIlib_frame_format_type_e {
|
pub enum NDIlib_frame_format_type_e {
|
||||||
|
@ -216,36 +219,14 @@ pub struct NDIlib_video_frame_v2_t {
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct NDIlib_audio_frame_v2_t {
|
pub struct NDIlib_audio_frame_v3_t {
|
||||||
pub sample_rate: ::std::os::raw::c_int,
|
pub sample_rate: ::std::os::raw::c_int,
|
||||||
pub no_channels: ::std::os::raw::c_int,
|
pub no_channels: ::std::os::raw::c_int,
|
||||||
pub no_samples: ::std::os::raw::c_int,
|
pub no_samples: ::std::os::raw::c_int,
|
||||||
pub timecode: i64,
|
pub timecode: i64,
|
||||||
|
pub FourCC: NDIlib_FourCC_audio_type_e,
|
||||||
pub p_data: *const ::std::os::raw::c_float,
|
pub p_data: *const ::std::os::raw::c_float,
|
||||||
pub channel_stride_in_bytes: ::std::os::raw::c_int,
|
pub channel_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,
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
pub fn NDIlib_util_audio_to_interleaved_16s_v2(
|
|
||||||
p_src: *const NDIlib_audio_frame_v2_t,
|
|
||||||
p_dst: *mut NDIlib_audio_frame_interleaved_16s_t,
|
|
||||||
);
|
|
||||||
|
|
||||||
pub fn NDIlib_util_audio_from_interleaved_16s_v2(
|
|
||||||
p_src: *const NDIlib_audio_frame_interleaved_16s_t,
|
|
||||||
p_dst: *mut NDIlib_audio_frame_v2_t,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub struct NDIlib_audio_frame_interleaved_16s_t {
|
|
||||||
pub sample_rate: ::std::os::raw::c_int,
|
|
||||||
pub no_channels: ::std::os::raw::c_int,
|
|
||||||
pub no_samples: ::std::os::raw::c_int,
|
|
||||||
pub timecode: i64,
|
|
||||||
pub reference_level: ::std::os::raw::c_int,
|
|
||||||
pub p_data: *mut i16,
|
|
||||||
}
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use gst::prelude::*;
|
||||||
use gst::{gst_debug, gst_error, gst_log, gst_trace, gst_warning};
|
use gst::{gst_debug, gst_error, gst_log, gst_trace, gst_warning};
|
||||||
use gst_video::prelude::*;
|
use gst_video::prelude::*;
|
||||||
|
|
||||||
use byte_slice_cast::AsMutSliceOf;
|
use byte_slice_cast::*;
|
||||||
|
|
||||||
use std::cmp;
|
use std::cmp;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
@ -731,7 +731,7 @@ impl Receiver {
|
||||||
|
|
||||||
let info = self.create_video_info(element, &video_frame)?;
|
let info = self.create_video_info(element, &video_frame)?;
|
||||||
|
|
||||||
let mut buffer = self.create_video_buffer(element, pts, duration, &info, &video_frame);
|
let mut buffer = self.create_video_buffer(element, pts, duration, &info, &video_frame)?;
|
||||||
if discont {
|
if discont {
|
||||||
buffer
|
buffer
|
||||||
.get_mut()
|
.get_mut()
|
||||||
|
@ -888,8 +888,8 @@ impl Receiver {
|
||||||
duration: Option<gst::ClockTime>,
|
duration: Option<gst::ClockTime>,
|
||||||
info: &gst_video::VideoInfo,
|
info: &gst_video::VideoInfo,
|
||||||
video_frame: &VideoFrame,
|
video_frame: &VideoFrame,
|
||||||
) -> gst::Buffer {
|
) -> Result<gst::Buffer, gst::FlowError> {
|
||||||
let mut buffer = gst::Buffer::with_size(info.size()).unwrap();
|
let mut buffer = self.copy_video_frame(element, info, video_frame)?;
|
||||||
{
|
{
|
||||||
let buffer = buffer.get_mut().unwrap();
|
let buffer = buffer.get_mut().unwrap();
|
||||||
buffer.set_pts(pts);
|
buffer.set_pts(pts);
|
||||||
|
@ -950,16 +950,18 @@ impl Receiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.copy_video_frame(element, info, buffer, video_frame)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn copy_video_frame(
|
fn copy_video_frame(
|
||||||
&self,
|
&self,
|
||||||
_element: &gst_base::BaseSrc,
|
_element: &gst_base::BaseSrc,
|
||||||
info: &gst_video::VideoInfo,
|
info: &gst_video::VideoInfo,
|
||||||
buffer: gst::Buffer,
|
|
||||||
video_frame: &VideoFrame,
|
video_frame: &VideoFrame,
|
||||||
) -> gst::Buffer {
|
) -> Result<gst::Buffer, gst::FlowError> {
|
||||||
|
let src = video_frame.data().ok_or(gst::FlowError::NotNegotiated)?;
|
||||||
|
|
||||||
|
let buffer = gst::Buffer::with_size(info.size()).unwrap();
|
||||||
let mut vframe = gst_video::VideoFrame::from_buffer_writable(buffer, info).unwrap();
|
let mut vframe = gst_video::VideoFrame::from_buffer_writable(buffer, info).unwrap();
|
||||||
|
|
||||||
match info.format() {
|
match info.format() {
|
||||||
|
@ -976,7 +978,6 @@ impl Receiver {
|
||||||
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_or_data_size_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src = video_frame.data();
|
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
.chunks_exact_mut(dest_stride)
|
.chunks_exact_mut(dest_stride)
|
||||||
|
@ -993,7 +994,6 @@ impl Receiver {
|
||||||
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_or_data_size_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src = video_frame.data();
|
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
.chunks_exact_mut(dest_stride)
|
.chunks_exact_mut(dest_stride)
|
||||||
|
@ -1009,7 +1009,7 @@ impl Receiver {
|
||||||
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_or_data_size_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 = &src[(video_frame.yres() as usize * src_stride)..];
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
.chunks_exact_mut(dest_stride)
|
.chunks_exact_mut(dest_stride)
|
||||||
|
@ -1026,7 +1026,6 @@ impl Receiver {
|
||||||
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_or_data_size_in_bytes() as usize;
|
let src_stride = video_frame.line_stride_or_data_size_in_bytes() as usize;
|
||||||
let src = video_frame.data();
|
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
.chunks_exact_mut(dest_stride)
|
.chunks_exact_mut(dest_stride)
|
||||||
|
@ -1043,7 +1042,7 @@ impl Receiver {
|
||||||
let dest = vframe.plane_data_mut(1).unwrap();
|
let dest = vframe.plane_data_mut(1).unwrap();
|
||||||
let src_stride = video_frame.line_stride_or_data_size_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_or_data_size_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 = &src[(video_frame.yres() as usize * src_stride)..];
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
.chunks_exact_mut(dest_stride)
|
.chunks_exact_mut(dest_stride)
|
||||||
|
@ -1060,7 +1059,7 @@ impl Receiver {
|
||||||
let dest = vframe.plane_data_mut(2).unwrap();
|
let dest = vframe.plane_data_mut(2).unwrap();
|
||||||
let src_stride = video_frame.line_stride_or_data_size_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_or_data_size_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 = &src[(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)..];
|
||||||
|
|
||||||
for (dest, src) in dest
|
for (dest, src) in dest
|
||||||
|
@ -1074,7 +1073,7 @@ impl Receiver {
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
vframe.into_buffer()
|
Ok(vframe.into_buffer())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_audio_buffer_and_info(
|
fn create_audio_buffer_and_info(
|
||||||
|
@ -1093,7 +1092,7 @@ impl Receiver {
|
||||||
|
|
||||||
let info = self.create_audio_info(element, &audio_frame)?;
|
let info = self.create_audio_info(element, &audio_frame)?;
|
||||||
|
|
||||||
let mut buffer = self.create_audio_buffer(element, pts, duration, &info, &audio_frame);
|
let mut buffer = self.create_audio_buffer(element, pts, duration, &info, &audio_frame)?;
|
||||||
if discont {
|
if discont {
|
||||||
buffer
|
buffer
|
||||||
.get_mut()
|
.get_mut()
|
||||||
|
@ -1129,13 +1128,22 @@ impl Receiver {
|
||||||
element: &gst_base::BaseSrc,
|
element: &gst_base::BaseSrc,
|
||||||
audio_frame: &AudioFrame,
|
audio_frame: &AudioFrame,
|
||||||
) -> Result<gst_audio::AudioInfo, gst::FlowError> {
|
) -> Result<gst_audio::AudioInfo, gst::FlowError> {
|
||||||
|
if audio_frame.fourcc() != NDIlib_FourCC_audio_type_FLTp {
|
||||||
|
gst::element_error!(
|
||||||
|
element,
|
||||||
|
gst::StreamError::Format,
|
||||||
|
["Unsupported audio fourcc {:08x}", audio_frame.fourcc()]
|
||||||
|
);
|
||||||
|
return Err(gst::FlowError::NotNegotiated);
|
||||||
|
}
|
||||||
|
|
||||||
let builder = gst_audio::AudioInfo::builder(
|
let builder = gst_audio::AudioInfo::builder(
|
||||||
gst_audio::AUDIO_FORMAT_S16,
|
gst_audio::AUDIO_FORMAT_F32,
|
||||||
audio_frame.sample_rate() as u32,
|
audio_frame.sample_rate() as u32,
|
||||||
audio_frame.no_channels() as u32,
|
audio_frame.no_channels() as u32,
|
||||||
);
|
);
|
||||||
|
|
||||||
builder.build().map_err(|_| {
|
let info = builder.build().map_err(|_| {
|
||||||
gst::element_error!(
|
gst::element_error!(
|
||||||
element,
|
element,
|
||||||
gst::StreamError::Format,
|
gst::StreamError::Format,
|
||||||
|
@ -1143,7 +1151,9 @@ impl Receiver {
|
||||||
);
|
);
|
||||||
|
|
||||||
gst::FlowError::NotNegotiated
|
gst::FlowError::NotNegotiated
|
||||||
})
|
})?;
|
||||||
|
|
||||||
|
Ok(info)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_audio_buffer(
|
fn create_audio_buffer(
|
||||||
|
@ -1153,9 +1163,10 @@ impl Receiver {
|
||||||
duration: Option<gst::ClockTime>,
|
duration: Option<gst::ClockTime>,
|
||||||
info: &gst_audio::AudioInfo,
|
info: &gst_audio::AudioInfo,
|
||||||
audio_frame: &AudioFrame,
|
audio_frame: &AudioFrame,
|
||||||
) -> gst::Buffer {
|
) -> Result<gst::Buffer, gst::FlowError> {
|
||||||
// We multiply by 2 because is the size in bytes of an i16 variable
|
let src = audio_frame.data().ok_or(gst::FlowError::NotNegotiated)?;
|
||||||
let buff_size = (audio_frame.no_samples() as u32 * info.bpf()) as usize;
|
let buff_size = (audio_frame.no_samples() as u32 * info.bpf()) as usize;
|
||||||
|
|
||||||
let mut buffer = gst::Buffer::with_size(buff_size).unwrap();
|
let mut buffer = gst::Buffer::with_size(buff_size).unwrap();
|
||||||
{
|
{
|
||||||
let buffer = buffer.get_mut().unwrap();
|
let buffer = buffer.get_mut().unwrap();
|
||||||
|
@ -1181,15 +1192,32 @@ impl Receiver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
audio_frame.copy_to_interleaved_16s(
|
let mut dest = buffer.map_writable().unwrap();
|
||||||
buffer
|
let dest = dest
|
||||||
.map_writable()
|
.as_mut_slice_of::<f32>()
|
||||||
.unwrap()
|
.map_err(|_| gst::FlowError::NotNegotiated)?;
|
||||||
.as_mut_slice_of::<i16>()
|
assert!(
|
||||||
.unwrap(),
|
dest.len()
|
||||||
|
== audio_frame.no_samples() as usize * audio_frame.no_channels() as usize
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for (channel, samples) in src
|
||||||
|
.chunks_exact(audio_frame.channel_stride_or_data_size_in_bytes() as usize)
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
let samples = samples
|
||||||
|
.as_slice_of::<f32>()
|
||||||
|
.map_err(|_| gst::FlowError::NotNegotiated)?;
|
||||||
|
|
||||||
|
for (i, sample) in samples[..audio_frame.no_samples() as usize]
|
||||||
|
.into_iter()
|
||||||
|
.enumerate()
|
||||||
|
{
|
||||||
|
dest[i * (audio_frame.no_channels() as usize) + channel] = *sample;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue