mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-01-22 08:58:15 +00:00
Add support for receiving compressed data from the source
This requires building against and using the NDI Advanced SDK and is opt-in via the "advanced-sdk" cargo feature.
This commit is contained in:
parent
db6b9531ca
commit
f890abe5cb
5 changed files with 947 additions and 306 deletions
|
@ -15,6 +15,7 @@ gst-audio = { package = "gstreamer-audio", version = "0.17" }
|
|||
gst-video = { package = "gstreamer-video", version = "0.17", features = ["v1_12"] }
|
||||
byte-slice-cast = "1"
|
||||
once_cell = "1.0"
|
||||
byteorder = "1.0"
|
||||
|
||||
[build-dependencies]
|
||||
gst-plugin-version-helper = "0.7"
|
||||
|
|
46
src/lib.rs
46
src/lib.rs
|
@ -52,6 +52,30 @@ pub enum RecvColorFormat {
|
|||
Fastest = 4,
|
||||
#[genum(name = "Best", nick = "best")]
|
||||
Best = 5,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v1", nick = "compressed-v1")]
|
||||
CompressedV1 = 6,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v2", nick = "compressed-v2")]
|
||||
CompressedV2 = 7,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v3", nick = "compressed-v3")]
|
||||
CompressedV3 = 8,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v3 with audio", nick = "compressed-v3-with-audio")]
|
||||
CompressedV3WithAudio = 9,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v4", nick = "compressed-v4")]
|
||||
CompressedV4 = 10,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v4 with audio", nick = "compressed-v4-with-audio")]
|
||||
CompressedV4WithAudio = 11,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v5", nick = "compressed-v5")]
|
||||
CompressedV5 = 12,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
#[genum(name = "Compressed v5 with audio", nick = "compressed-v5-with-audio")]
|
||||
CompressedV5WithAudio = 13,
|
||||
}
|
||||
|
||||
impl From<RecvColorFormat> for NDIlib_recv_color_format_e {
|
||||
|
@ -63,6 +87,28 @@ impl From<RecvColorFormat> for NDIlib_recv_color_format_e {
|
|||
RecvColorFormat::UyvyRgba => NDIlib_recv_color_format_UYVY_RGBA,
|
||||
RecvColorFormat::Fastest => NDIlib_recv_color_format_fastest,
|
||||
RecvColorFormat::Best => NDIlib_recv_color_format_best,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV1 => NDIlib_recv_color_format_ex_compressed,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV2 => NDIlib_recv_color_format_ex_compressed_v2,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV3 => NDIlib_recv_color_format_ex_compressed_v3,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV3WithAudio => {
|
||||
NDIlib_recv_color_format_ex_compressed_v3_with_audio
|
||||
}
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV4 => NDIlib_recv_color_format_ex_compressed_v4,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV4WithAudio => {
|
||||
NDIlib_recv_color_format_ex_compressed_v4_with_audio
|
||||
}
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV5 => NDIlib_recv_color_format_ex_compressed_v5,
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
RecvColorFormat::CompressedV5WithAudio => {
|
||||
NDIlib_recv_color_format_ex_compressed_v5_with_audio
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
235
src/ndi.rs
235
src/ndi.rs
|
@ -503,7 +503,8 @@ impl<'a> VideoFrame<'a> {
|
|||
|
||||
pub fn data(&self) -> Option<&[u8]> {
|
||||
let fourcc = self.fourcc();
|
||||
if ![
|
||||
|
||||
if [
|
||||
NDIlib_FourCC_video_type_UYVY,
|
||||
NDIlib_FourCC_video_type_UYVA,
|
||||
NDIlib_FourCC_video_type_P216,
|
||||
|
@ -518,31 +519,126 @@ impl<'a> VideoFrame<'a> {
|
|||
]
|
||||
.contains(&fourcc)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
// FIXME: Unclear if this is correct. Needs to be validated against an actual
|
||||
// interlaced stream
|
||||
let frame_size = if self.frame_format_type()
|
||||
== NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_0
|
||||
|| self.frame_format_type()
|
||||
== NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_1
|
||||
{
|
||||
self.yres() * self.line_stride_or_data_size_in_bytes() / 2
|
||||
} else {
|
||||
self.yres() * self.line_stride_or_data_size_in_bytes()
|
||||
};
|
||||
|
||||
// FIXME: Unclear if this is correct. Needs to be validated against an actual
|
||||
// interlaced stream
|
||||
let frame_size = if self.frame_format_type()
|
||||
== NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_0
|
||||
|| self.frame_format_type()
|
||||
== NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_1
|
||||
{
|
||||
self.yres() * self.line_stride_or_data_size_in_bytes() / 2
|
||||
} else {
|
||||
self.yres() * self.line_stride_or_data_size_in_bytes()
|
||||
};
|
||||
|
||||
unsafe {
|
||||
use std::slice;
|
||||
match self {
|
||||
VideoFrame::BorrowedRecv(ref frame, _) | VideoFrame::BorrowedGst(ref frame, _) => {
|
||||
Some(slice::from_raw_parts(
|
||||
return unsafe {
|
||||
use std::slice;
|
||||
match self {
|
||||
VideoFrame::BorrowedRecv(ref frame, _)
|
||||
| VideoFrame::BorrowedGst(ref frame, _) => Some(slice::from_raw_parts(
|
||||
frame.p_data as *const u8,
|
||||
frame_size as usize,
|
||||
))
|
||||
)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
if [
|
||||
NDIlib_FourCC_video_type_ex_SHQ0_highest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_SHQ2_highest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_SHQ7_highest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_SHQ0_lowest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_SHQ2_lowest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_SHQ7_lowest_bandwidth,
|
||||
]
|
||||
.contains(&fourcc)
|
||||
{
|
||||
return unsafe {
|
||||
use std::slice;
|
||||
match self {
|
||||
VideoFrame::BorrowedRecv(ref frame, _)
|
||||
| VideoFrame::BorrowedGst(ref frame, _) => Some(slice::from_raw_parts(
|
||||
frame.p_data as *const u8,
|
||||
frame.line_stride_or_data_size_in_bytes as usize,
|
||||
)),
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
pub fn compressed_packet(&self) -> Option<CompressedPacket> {
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::io::Cursor;
|
||||
use std::slice;
|
||||
|
||||
unsafe {
|
||||
let fourcc = self.fourcc();
|
||||
|
||||
if ![
|
||||
NDIlib_FourCC_video_type_ex_H264_highest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_H264_lowest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_HEVC_highest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_HEVC_lowest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_H264_alpha_highest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_H264_alpha_lowest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_HEVC_alpha_highest_bandwidth,
|
||||
NDIlib_FourCC_video_type_ex_HEVC_alpha_lowest_bandwidth,
|
||||
]
|
||||
.contains(&fourcc)
|
||||
{
|
||||
return None;
|
||||
}
|
||||
|
||||
let data = match self {
|
||||
VideoFrame::BorrowedRecv(ref frame, _) | VideoFrame::BorrowedGst(ref frame, _) => {
|
||||
slice::from_raw_parts(
|
||||
frame.p_data as *const u8,
|
||||
frame.line_stride_or_data_size_in_bytes as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut cursor = Cursor::new(data);
|
||||
let version = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
if version != ndisys::NDIlib_compressed_packet_version_0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let fourcc = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
let pts = cursor.read_i64::<LittleEndian>().ok()?;
|
||||
let dts = cursor.read_i64::<LittleEndian>().ok()?;
|
||||
let _reserved = cursor.read_u64::<LittleEndian>().ok()?;
|
||||
let flags = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
let data_size = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
let extra_data_size = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
|
||||
let expected_size = (ndisys::NDIlib_compressed_packet_version_0 as usize)
|
||||
.checked_add(data_size as usize)?
|
||||
.checked_add(extra_data_size as usize)?;
|
||||
if data.len() < expected_size {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(CompressedPacket {
|
||||
fourcc,
|
||||
pts,
|
||||
dts,
|
||||
key_frame: flags & ndisys::NDIlib_compressed_packet_flags_keyframe != 0,
|
||||
data: &data[ndisys::NDIlib_compressed_packet_version_0 as usize..]
|
||||
[..data_size as usize],
|
||||
extra_data: if extra_data_size > 0 {
|
||||
Some(
|
||||
&data[ndisys::NDIlib_compressed_packet_version_0 as usize
|
||||
+ data_size as usize..][..extra_data_size as usize],
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -786,18 +882,93 @@ impl<'a> AudioFrame<'a> {
|
|||
|
||||
let fourcc = self.fourcc();
|
||||
|
||||
if ![NDIlib_FourCC_audio_type_FLTp].contains(&fourcc) {
|
||||
if [NDIlib_FourCC_audio_type_FLTp].contains(&fourcc) {
|
||||
return match self {
|
||||
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
||||
Some(slice::from_raw_parts(
|
||||
frame.p_data as *const u8,
|
||||
(frame.no_channels * frame.channel_stride_or_data_size_in_bytes)
|
||||
as usize,
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
if [NDIlib_FourCC_audio_type_Opus].contains(&fourcc) {
|
||||
return match self {
|
||||
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
||||
Some(slice::from_raw_parts(
|
||||
frame.p_data as *const u8,
|
||||
frame.channel_stride_or_data_size_in_bytes as usize,
|
||||
))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
pub fn compressed_packet(&self) -> Option<CompressedPacket> {
|
||||
use byteorder::{LittleEndian, ReadBytesExt};
|
||||
use std::io::Cursor;
|
||||
use std::slice;
|
||||
|
||||
unsafe {
|
||||
let fourcc = self.fourcc();
|
||||
|
||||
if ![NDIlib_FourCC_audio_type_AAC].contains(&fourcc) {
|
||||
return None;
|
||||
}
|
||||
|
||||
match self {
|
||||
let data = match self {
|
||||
AudioFrame::BorrowedRecv(ref frame, _) | AudioFrame::Owned(ref frame, _, _) => {
|
||||
Some(slice::from_raw_parts(
|
||||
slice::from_raw_parts(
|
||||
frame.p_data as *const u8,
|
||||
(frame.no_channels * frame.channel_stride_or_data_size_in_bytes) as usize,
|
||||
))
|
||||
frame.channel_stride_or_data_size_in_bytes as usize,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
let mut cursor = Cursor::new(data);
|
||||
let version = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
if version != ndisys::NDIlib_compressed_packet_version_0 {
|
||||
return None;
|
||||
}
|
||||
|
||||
let fourcc = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
let pts = cursor.read_i64::<LittleEndian>().ok()?;
|
||||
let dts = cursor.read_i64::<LittleEndian>().ok()?;
|
||||
let _reserved = cursor.read_u64::<LittleEndian>().ok()?;
|
||||
let flags = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
let data_size = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
let extra_data_size = cursor.read_u32::<LittleEndian>().ok()?;
|
||||
|
||||
let expected_size = (ndisys::NDIlib_compressed_packet_version_0 as usize)
|
||||
.checked_add(data_size as usize)?
|
||||
.checked_add(extra_data_size as usize)?;
|
||||
if data.len() < expected_size {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(CompressedPacket {
|
||||
fourcc,
|
||||
pts,
|
||||
dts,
|
||||
key_frame: flags & ndisys::NDIlib_compressed_packet_flags_keyframe != 0,
|
||||
data: &data[ndisys::NDIlib_compressed_packet_version_0 as usize..]
|
||||
[..data_size as usize],
|
||||
extra_data: if extra_data_size > 0 {
|
||||
Some(
|
||||
&data[ndisys::NDIlib_compressed_packet_version_0 as usize
|
||||
+ data_size as usize..][..extra_data_size as usize],
|
||||
)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -860,7 +1031,7 @@ impl<'a> AudioFrame<'a> {
|
|||
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() {
|
||||
for (c, sample) in samples.iter().enumerate() {
|
||||
ptr::write(dest_ptr.add(c * no_samples as usize + i), *sample);
|
||||
}
|
||||
}
|
||||
|
@ -895,6 +1066,16 @@ impl<'a> Drop for AudioFrame<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "advanced-sdk")]
|
||||
pub struct CompressedPacket<'a> {
|
||||
pub fourcc: ndisys::NDIlib_compressed_FourCC_type_e,
|
||||
pub pts: i64,
|
||||
pub dts: i64,
|
||||
pub key_frame: bool,
|
||||
pub data: &'a [u8],
|
||||
pub extra_data: Option<&'a [u8]>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum MetadataFrame<'a> {
|
||||
Owned(NDIlib_metadata_frame_t, Option<ffi::CString>),
|
||||
|
|
|
@ -59,9 +59,9 @@ impl Default for Settings {
|
|||
}
|
||||
|
||||
struct State {
|
||||
video_info: Option<gst_video::VideoInfo>,
|
||||
video_info: Option<crate::VideoInfo>,
|
||||
video_caps: Option<gst::Caps>,
|
||||
audio_info: Option<gst_audio::AudioInfo>,
|
||||
audio_info: Option<crate::AudioInfo>,
|
||||
audio_caps: Option<gst::Caps>,
|
||||
current_latency: Option<gst::ClockTime>,
|
||||
receiver: Option<Receiver>,
|
||||
|
|
967
src/receiver.rs
967
src/receiver.rs
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue