Properly support interlaced video and signal it correctly in the caps and buffer flags

This commit is contained in:
Sebastian Dröge 2019-07-16 12:14:37 +03:00
parent a500b5297b
commit fabcc65460
4 changed files with 124 additions and 11 deletions

View file

@ -8,16 +8,20 @@ description = "NewTek NDI Plugin"
[dependencies]
glib = { version = "0.8.0", features = ["subclassing"] }
gstreamer = { version = "0.14.0", features = ["subclassing"] }
gstreamer = { version = "0.14.3", features = ["subclassing"] }
gstreamer-base = { version = "0.14.0", features = ["subclassing"] }
gstreamer-audio = "0.14.0"
gstreamer-video = "0.14.0"
gstreamer-video = "0.14.3"
lazy_static = "1.1.0"
byte-slice-cast = "0.2.0"
[build-dependencies]
gst-plugin-version-helper = "0.1"
[features]
default = ["interlaced-fields"]
interlaced-fields = ["gstreamer/v1_16", "gstreamer-video/v1_16"]
[lib]
name = "gstndi"
crate-type = ["cdylib"]

View file

@ -127,10 +127,13 @@ fn connect_ndi(
source.ip_address(),
);
// FIXME: Property for the name and bandwidth
// FIXME: Ideally we would use NDIlib_recv_color_format_fastest here but that seems to be
// broken with interlaced content currently
let recv = RecvInstance::builder(&source, "Galicaster NDI Receiver")
.bandwidth(NDIlib_recv_bandwidth_e::NDIlib_recv_bandwidth_highest)
.color_format(NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA)
.allow_video_fields(false)
.allow_video_fields(true)
.build();
let recv = match recv {
None => {

View file

@ -360,13 +360,24 @@ impl<'a> VideoFrame<'a> {
}
pub fn data(&self) -> &[u8] {
// 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_in_bytes() / 2
} else {
self.yres() * self.line_stride_in_bytes()
};
unsafe {
use std::slice;
match self {
VideoFrame::Borrowed(ref frame, _) => slice::from_raw_parts(
frame.p_data as *const u8,
(frame.yres * frame.line_stride_in_bytes) as usize,
),
VideoFrame::Borrowed(ref frame, _) => {
slice::from_raw_parts(frame.p_data as *const u8, frame_size as usize)
}
}
}
}

View file

@ -9,6 +9,7 @@ use gst_base::prelude::*;
use gst_base::subclass::prelude::*;
use gst_video;
use gst_video::prelude::*;
use std::sync::Mutex;
use std::{i32, u32};
@ -148,6 +149,23 @@ impl ObjectSubclass for NdiVideoSrc {
],
);
#[cfg(feature = "interlaced-fields")]
let caps = {
let mut tmp = caps.copy();
{
let tmp = tmp.get_mut().unwrap();
tmp.set_features_simple(Some(gst::CapsFeatures::new(&["format:Interlaced"])));
}
let mut caps = caps;
{
let caps = caps.get_mut().unwrap();
caps.append(tmp);
}
caps
};
let src_pad_template = gst::PadTemplate::new(
"src",
gst::PadDirection::Src,
@ -393,7 +411,48 @@ impl BaseSrcImpl for NdiVideoSrc {
let par = gst::Fraction::approximate_f32(video_frame.picture_aspect_ratio()).unwrap()
* gst::Fraction::new(video_frame.yres(), video_frame.xres());
let info =
#[cfg(feature = "interlaced-fields")]
let info = {
let mut builder = gst_video::VideoInfo::new(
format,
video_frame.xres() as u32,
video_frame.yres() as u32,
)
.fps(gst::Fraction::from(video_frame.frame_rate()))
.par(par)
.interlace_mode(match video_frame.frame_format_type() {
ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_progressive => {
gst_video::VideoInterlaceMode::Progressive
}
ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_interleaved => {
gst_video::VideoInterlaceMode::Interleaved
}
_ => gst_video::VideoInterlaceMode::Alternate,
});
/* Requires GStreamer 1.12 at least */
if video_frame.frame_format_type()
== ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_interleaved
{
builder = builder.field_order(gst_video::VideoFieldOrder::TopFieldFirst);
}
builder.build().unwrap()
};
#[cfg(not(feature = "interlaced-fields"))]
let info = if video_frame.frame_format_type()
!= ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_progressive
&& video_frame.frame_format_type()
!= ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_interleaved
{
gst_element_error!(
element,
gst::StreamError::Format,
["Separate field interlacing not supported"]
);
return Err(gst::FlowError::NotNegotiated);
} else {
gst_video::VideoInfo::new(format, video_frame.xres() as u32, video_frame.yres() as u32)
.fps(gst::Fraction::from(video_frame.frame_rate()))
.par(par)
@ -407,7 +466,8 @@ impl BaseSrcImpl for NdiVideoSrc {
},
)
.build()
.unwrap();
.unwrap()
};
if state.info.as_ref() != Some(&info) {
let caps = info.to_caps().unwrap();
@ -425,8 +485,7 @@ impl BaseSrcImpl for NdiVideoSrc {
pts
);
let buff_size = (video_frame.yres() * video_frame.line_stride_in_bytes()) as usize;
let mut buffer = gst::Buffer::with_size(buff_size).unwrap();
let mut buffer = gst::Buffer::with_size(state.info.as_ref().unwrap().size()).unwrap();
{
let duration = gst::SECOND
.mul_div_floor(
@ -437,6 +496,42 @@ impl BaseSrcImpl for NdiVideoSrc {
let buffer = buffer.get_mut().unwrap();
buffer.set_pts(pts);
buffer.set_duration(duration);
#[cfg(feature = "interlaced-fields")]
{
match video_frame.frame_format_type() {
ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_interleaved => {
buffer.set_video_flags(
gst_video::VideoBufferFlags::INTERLACED
| gst_video::VideoBufferFlags::TFF,
);
}
ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_0 => {
buffer.set_video_flags(
gst_video::VideoBufferFlags::INTERLACED
| gst_video::VideoBufferFlags::TOP_FIELD,
);
}
ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_field_1 => {
buffer.set_video_flags(
gst_video::VideoBufferFlags::INTERLACED
| gst_video::VideoBufferFlags::BOTTOM_FIELD,
);
}
_ => (),
};
}
#[cfg(not(feature = "interlaced-fields"))]
{
if video_frame.frame_format_type()
== ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_interleaved
{
buffer.set_video_flags(
gst_video::VideoBufferFlags::INTERLACED | gst_video::VideoBufferFlags::TFF,
);
}
}
}
let buffer = {