gst-plugins-rs/net/ndi/src/ndisrcmeta.rs
Sebastian Dröge 39155ef81c ndisrc: Implement zerocopy handling for the received frames if possible
Also move processing from the capture thread to the streaming thread.
The NDI SDK can cause frame drops if not reading fast enough from it.

All frame processing is now handled inside the ndisrcdemux.

Also use a buffer pool for video if copying is necessary.

Additionally, make sure to use different stream ids in the stream-start
event for the audio and video pad.

This plugin now requires GStreamer 1.16 or newer.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1365>
2023-11-13 13:22:48 +02:00

176 lines
4.9 KiB
Rust

// SPDX-License-Identifier: MPL-2.0
use gst::prelude::*;
use std::fmt;
use std::mem;
use crate::ndi::{AudioFrame, MetadataFrame, VideoFrame};
use crate::TimestampMode;
#[repr(transparent)]
pub struct NdiSrcMeta(imp::NdiSrcMeta);
#[derive(Debug)]
#[allow(clippy::large_enum_variant)]
pub enum Buffer {
Audio {
frame: AudioFrame,
discont: bool,
receive_time_gst: gst::ClockTime,
receive_time_real: gst::ClockTime,
},
Video {
frame: VideoFrame,
discont: bool,
receive_time_gst: gst::ClockTime,
receive_time_real: gst::ClockTime,
},
Metadata {
frame: MetadataFrame,
receive_time_gst: gst::ClockTime,
receive_time_real: gst::ClockTime,
},
}
unsafe impl Send for NdiSrcMeta {}
unsafe impl Sync for NdiSrcMeta {}
impl NdiSrcMeta {
pub fn add(
buffer: &mut gst::BufferRef,
ndi_buffer: Buffer,
timestamp_mode: TimestampMode,
) -> gst::MetaRefMut<Self, gst::meta::Standalone> {
unsafe {
// Manually dropping because gst_buffer_add_meta() takes ownership of the
// content of the struct
let mut params = mem::ManuallyDrop::new(imp::NdiSrcMetaParams {
ndi_buffer,
timestamp_mode,
});
let meta = gst::ffi::gst_buffer_add_meta(
buffer.as_mut_ptr(),
imp::ndi_src_meta_get_info(),
&mut *params as *mut imp::NdiSrcMetaParams as glib::ffi::gpointer,
) as *mut imp::NdiSrcMeta;
Self::from_mut_ptr(buffer, meta)
}
}
pub fn take_ndi_buffer(&mut self) -> Buffer {
self.0.ndi_buffer.take().expect("can only take buffer once")
}
}
unsafe impl MetaAPI for NdiSrcMeta {
type GstType = imp::NdiSrcMeta;
fn meta_api() -> glib::Type {
imp::ndi_src_meta_api_get_type()
}
}
impl fmt::Debug for NdiSrcMeta {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.debug_struct("NdiSrcMeta")
.field("ndi_buffer", &self.0.ndi_buffer)
.finish()
}
}
mod imp {
use crate::TimestampMode;
use super::Buffer;
use glib::translate::*;
use gst::glib::once_cell::sync::Lazy;
use std::mem;
use std::ptr;
pub(super) struct NdiSrcMetaParams {
pub ndi_buffer: Buffer,
pub timestamp_mode: TimestampMode,
}
#[repr(C)]
pub struct NdiSrcMeta {
parent: gst::ffi::GstMeta,
pub(super) ndi_buffer: Option<Buffer>,
pub(super) timestamp_mode: TimestampMode,
}
pub(super) fn ndi_src_meta_api_get_type() -> glib::Type {
static TYPE: Lazy<glib::Type> = Lazy::new(|| unsafe {
let t = from_glib(gst::ffi::gst_meta_api_type_register(
b"GstNdiSrcMetaAPI\0".as_ptr() as *const _,
[ptr::null::<std::os::raw::c_char>()].as_ptr() as *mut *const _,
));
assert_ne!(t, glib::Type::INVALID);
t
});
*TYPE
}
unsafe extern "C" fn ndi_src_meta_init(
meta: *mut gst::ffi::GstMeta,
params: glib::ffi::gpointer,
_buffer: *mut gst::ffi::GstBuffer,
) -> glib::ffi::gboolean {
assert!(!params.is_null());
let meta = &mut *(meta as *mut NdiSrcMeta);
let params = ptr::read(params as *const NdiSrcMetaParams);
ptr::write(&mut meta.ndi_buffer, Some(params.ndi_buffer));
ptr::write(&mut meta.timestamp_mode, params.timestamp_mode);
true.into_glib()
}
unsafe extern "C" fn ndi_src_meta_free(
meta: *mut gst::ffi::GstMeta,
_buffer: *mut gst::ffi::GstBuffer,
) {
let meta = &mut *(meta as *mut NdiSrcMeta);
ptr::drop_in_place(&mut meta.ndi_buffer);
}
unsafe extern "C" fn ndi_src_meta_transform(
_dest: *mut gst::ffi::GstBuffer,
_meta: *mut gst::ffi::GstMeta,
_buffer: *mut gst::ffi::GstBuffer,
_type_: glib::ffi::GQuark,
_data: glib::ffi::gpointer,
) -> glib::ffi::gboolean {
false.into_glib()
}
pub(super) fn ndi_src_meta_get_info() -> *const gst::ffi::GstMetaInfo {
struct MetaInfo(ptr::NonNull<gst::ffi::GstMetaInfo>);
unsafe impl Send for MetaInfo {}
unsafe impl Sync for MetaInfo {}
static META_INFO: Lazy<MetaInfo> = Lazy::new(|| unsafe {
MetaInfo(
ptr::NonNull::new(gst::ffi::gst_meta_register(
ndi_src_meta_api_get_type().into_glib(),
b"GstNdiSrcMeta\0".as_ptr() as *const _,
mem::size_of::<NdiSrcMeta>(),
Some(ndi_src_meta_init),
Some(ndi_src_meta_free),
Some(ndi_src_meta_transform),
) as *mut gst::ffi::GstMetaInfo)
.expect("Failed to register meta API"),
)
});
META_INFO.0.as_ptr()
}
}