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:
Sebastian Dröge 2021-10-01 12:47:43 +03:00
parent db6b9531ca
commit f890abe5cb
5 changed files with 947 additions and 306 deletions

View file

@ -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"

View file

@ -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
}
}
}
}

View file

@ -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>),

View file

@ -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>,

File diff suppressed because it is too large Load diff