mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2025-06-05 15:08:58 +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]
|
[dependencies]
|
||||||
glib = { version = "0.8.0", features = ["subclassing"] }
|
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-base = { version = "0.14.0", features = ["subclassing"] }
|
||||||
gstreamer-audio = "0.14.0"
|
gstreamer-audio = "0.14.0"
|
||||||
gstreamer-video = "0.14.0"
|
gstreamer-video = "0.14.3"
|
||||||
lazy_static = "1.1.0"
|
lazy_static = "1.1.0"
|
||||||
byte-slice-cast = "0.2.0"
|
byte-slice-cast = "0.2.0"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
gst-plugin-version-helper = "0.1"
|
gst-plugin-version-helper = "0.1"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["interlaced-fields"]
|
||||||
|
interlaced-fields = ["gstreamer/v1_16", "gstreamer-video/v1_16"]
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "gstndi"
|
name = "gstndi"
|
||||||
crate-type = ["cdylib"]
|
crate-type = ["cdylib"]
|
||||||
|
|
|
@ -127,10 +127,13 @@ fn connect_ndi(
|
||||||
source.ip_address(),
|
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")
|
let recv = RecvInstance::builder(&source, "Galicaster NDI Receiver")
|
||||||
.bandwidth(NDIlib_recv_bandwidth_e::NDIlib_recv_bandwidth_highest)
|
.bandwidth(NDIlib_recv_bandwidth_e::NDIlib_recv_bandwidth_highest)
|
||||||
.color_format(NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA)
|
.color_format(NDIlib_recv_color_format_e::NDIlib_recv_color_format_UYVY_BGRA)
|
||||||
.allow_video_fields(false)
|
.allow_video_fields(true)
|
||||||
.build();
|
.build();
|
||||||
let recv = match recv {
|
let recv = match recv {
|
||||||
None => {
|
None => {
|
||||||
|
|
19
src/ndi.rs
19
src/ndi.rs
|
@ -360,13 +360,24 @@ impl<'a> VideoFrame<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn data(&self) -> &[u8] {
|
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 {
|
unsafe {
|
||||||
use std::slice;
|
use std::slice;
|
||||||
match self {
|
match self {
|
||||||
VideoFrame::Borrowed(ref frame, _) => slice::from_raw_parts(
|
VideoFrame::Borrowed(ref frame, _) => {
|
||||||
frame.p_data as *const u8,
|
slice::from_raw_parts(frame.p_data as *const u8, frame_size as usize)
|
||||||
(frame.yres * frame.line_stride_in_bytes) as usize,
|
}
|
||||||
),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ use gst_base::prelude::*;
|
||||||
use gst_base::subclass::prelude::*;
|
use gst_base::subclass::prelude::*;
|
||||||
|
|
||||||
use gst_video;
|
use gst_video;
|
||||||
|
use gst_video::prelude::*;
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::{i32, u32};
|
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(
|
let src_pad_template = gst::PadTemplate::new(
|
||||||
"src",
|
"src",
|
||||||
gst::PadDirection::Src,
|
gst::PadDirection::Src,
|
||||||
|
@ -393,7 +411,48 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
let par = gst::Fraction::approximate_f32(video_frame.picture_aspect_ratio()).unwrap()
|
let par = gst::Fraction::approximate_f32(video_frame.picture_aspect_ratio()).unwrap()
|
||||||
* gst::Fraction::new(video_frame.yres(), video_frame.xres());
|
* 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)
|
gst_video::VideoInfo::new(format, video_frame.xres() as u32, video_frame.yres() as u32)
|
||||||
.fps(gst::Fraction::from(video_frame.frame_rate()))
|
.fps(gst::Fraction::from(video_frame.frame_rate()))
|
||||||
.par(par)
|
.par(par)
|
||||||
|
@ -407,7 +466,8 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.build()
|
.build()
|
||||||
.unwrap();
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
if state.info.as_ref() != Some(&info) {
|
if state.info.as_ref() != Some(&info) {
|
||||||
let caps = info.to_caps().unwrap();
|
let caps = info.to_caps().unwrap();
|
||||||
|
@ -425,8 +485,7 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
pts
|
pts
|
||||||
);
|
);
|
||||||
|
|
||||||
let buff_size = (video_frame.yres() * video_frame.line_stride_in_bytes()) as usize;
|
let mut buffer = gst::Buffer::with_size(state.info.as_ref().unwrap().size()).unwrap();
|
||||||
let mut buffer = gst::Buffer::with_size(buff_size).unwrap();
|
|
||||||
{
|
{
|
||||||
let duration = gst::SECOND
|
let duration = gst::SECOND
|
||||||
.mul_div_floor(
|
.mul_div_floor(
|
||||||
|
@ -437,6 +496,42 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
let buffer = buffer.get_mut().unwrap();
|
let buffer = buffer.get_mut().unwrap();
|
||||||
buffer.set_pts(pts);
|
buffer.set_pts(pts);
|
||||||
buffer.set_duration(duration);
|
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 = {
|
let buffer = {
|
||||||
|
|
Loading…
Reference in a new issue