mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-02-02 06:02:20 +00:00
Properly support interlaced video and signal it correctly in the caps and buffer flags
This commit is contained in:
parent
a500b5297b
commit
fabcc65460
4 changed files with 124 additions and 11 deletions
|
@ -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"]
|
||||
|
|
|
@ -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 => {
|
||||
|
|
19
src/ndi.rs
19
src/ndi.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 = {
|
||||
|
|
Loading…
Reference in a new issue