mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-30 07:20:59 +00:00
Fix caps negotiation
We can't just fixate to any close to what we receive right now but only support exactly the caps we receive. So check the format of each frame and negotiate exactly those caps as needed when receiving frames. Also re-negotiate if the caps are ever changing.
This commit is contained in:
parent
4443e03cd2
commit
5022ff412c
3 changed files with 55 additions and 117 deletions
|
@ -130,7 +130,7 @@ fn connect_ndi(
|
||||||
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(true)
|
.allow_video_fields(false)
|
||||||
.build();
|
.build();
|
||||||
let recv = match recv {
|
let recv = match recv {
|
||||||
None => {
|
None => {
|
||||||
|
|
|
@ -132,7 +132,6 @@ impl ObjectSubclass for NdiAudioSrc {
|
||||||
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
("rate", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
("channels", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
("channels", &gst::IntRange::<i32>::new(1, i32::MAX)),
|
||||||
("layout", &"interleaved"),
|
("layout", &"interleaved"),
|
||||||
("channel-mask", &gst::Bitmask::new(0)),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -235,30 +234,6 @@ impl ElementImpl for NdiAudioSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseSrcImpl for NdiAudioSrc {
|
impl BaseSrcImpl for NdiAudioSrc {
|
||||||
fn set_caps(
|
|
||||||
&self,
|
|
||||||
element: &gst_base::BaseSrc,
|
|
||||||
caps: &gst::Caps,
|
|
||||||
) -> Result<(), gst::LoggableError> {
|
|
||||||
let info = match gst_audio::AudioInfo::from_caps(caps) {
|
|
||||||
None => {
|
|
||||||
return Err(gst_loggable_error!(
|
|
||||||
self.cat,
|
|
||||||
"Failed to build `AudioInfo` from caps {}",
|
|
||||||
caps
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Some(info) => info,
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
|
||||||
|
|
||||||
let mut state = self.state.lock().unwrap();
|
|
||||||
state.info = Some(info);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
||||||
*self.state.lock().unwrap() = Default::default();
|
*self.state.lock().unwrap() = Default::default();
|
||||||
|
|
||||||
|
@ -305,39 +280,12 @@ impl BaseSrcImpl for NdiAudioSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixate(&self, element: &gst_base::BaseSrc, caps: gst::Caps) -> gst::Caps {
|
fn fixate(&self, element: &gst_base::BaseSrc, caps: gst::Caps) -> gst::Caps {
|
||||||
let receivers = HASHMAP_RECEIVERS.lock().unwrap();
|
|
||||||
let state = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
let receiver = receivers.get(&state.id_receiver.unwrap()).unwrap();
|
|
||||||
|
|
||||||
let recv = &receiver.ndi_instance;
|
|
||||||
|
|
||||||
// FIXME: Should be done in create() and caps be updated as needed
|
|
||||||
|
|
||||||
let audio_frame =
|
|
||||||
loop {
|
|
||||||
match recv.capture(false, true, false, 1000) {
|
|
||||||
Err(_) => unimplemented!(),
|
|
||||||
Ok(None) => continue,
|
|
||||||
Ok(Some(Frame::Audio(frame))) => break frame,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::truncate(caps);
|
let mut caps = gst::Caps::truncate(caps);
|
||||||
{
|
{
|
||||||
let caps = caps.make_mut();
|
let caps = caps.make_mut();
|
||||||
let s = caps.get_mut_structure(0).unwrap();
|
let s = caps.get_mut_structure(0).unwrap();
|
||||||
s.fixate_field_nearest_int("rate", audio_frame.sample_rate());
|
s.fixate_field_nearest_int("rate", 48_000);
|
||||||
s.fixate_field_nearest_int("channels", audio_frame.no_channels());
|
s.fixate_field_nearest_int("channels", 2);
|
||||||
s.fixate_field_str("layout", "interleaved");
|
|
||||||
s.set_value(
|
|
||||||
"channel-mask",
|
|
||||||
gst::Bitmask::new(gst_audio::AudioChannelPosition::get_fallback_mask(
|
|
||||||
audio_frame.no_channels() as u32,
|
|
||||||
))
|
|
||||||
.to_send_value(),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.parent_fixate(element, caps)
|
self.parent_fixate(element, caps)
|
||||||
|
@ -351,14 +299,7 @@ impl BaseSrcImpl for NdiAudioSrc {
|
||||||
) -> Result<gst::Buffer, gst::FlowError> {
|
) -> Result<gst::Buffer, gst::FlowError> {
|
||||||
// FIXME: Make sure to not have any mutexes locked while wait
|
// FIXME: Make sure to not have any mutexes locked while wait
|
||||||
let settings = self.settings.lock().unwrap().clone();
|
let settings = self.settings.lock().unwrap().clone();
|
||||||
let state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
let _info = match state.info {
|
|
||||||
None => {
|
|
||||||
gst_element_error!(element, gst::CoreError::Negotiation, ["Have no caps yet"]);
|
|
||||||
return Err(gst::FlowError::NotNegotiated);
|
|
||||||
}
|
|
||||||
Some(ref info) => info.clone(),
|
|
||||||
};
|
|
||||||
let receivers = HASHMAP_RECEIVERS.lock().unwrap();
|
let receivers = HASHMAP_RECEIVERS.lock().unwrap();
|
||||||
|
|
||||||
let receiver = &receivers.get(&state.id_receiver.unwrap()).unwrap();
|
let receiver = &receivers.get(&state.id_receiver.unwrap()).unwrap();
|
||||||
|
@ -421,6 +362,21 @@ impl BaseSrcImpl for NdiAudioSrc {
|
||||||
audio_frame
|
audio_frame
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let info = gst_audio::AudioInfo::new(
|
||||||
|
gst_audio::AUDIO_FORMAT_S16,
|
||||||
|
audio_frame.sample_rate() as u32,
|
||||||
|
audio_frame.no_channels() as u32,
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if state.info.as_ref() != Some(&info) {
|
||||||
|
let caps = info.to_caps().unwrap();
|
||||||
|
state.info = Some(info);
|
||||||
|
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
||||||
|
element.set_caps(&caps).map_err(|_| gst::FlowError::NotNegotiated)?;
|
||||||
|
}
|
||||||
|
|
||||||
gst_log!(
|
gst_log!(
|
||||||
self.cat,
|
self.cat,
|
||||||
obj: element,
|
obj: element,
|
||||||
|
|
|
@ -8,13 +8,13 @@ use gst_base;
|
||||||
use gst_base::prelude::*;
|
use gst_base::prelude::*;
|
||||||
use gst_base::subclass::prelude::*;
|
use gst_base::subclass::prelude::*;
|
||||||
|
|
||||||
use gst::Fraction;
|
|
||||||
use gst_video;
|
use gst_video;
|
||||||
|
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::{i32, u32};
|
use std::{i32, u32};
|
||||||
|
|
||||||
use ndi::*;
|
use ndi::*;
|
||||||
|
use ndisys;
|
||||||
|
|
||||||
use connect_ndi;
|
use connect_ndi;
|
||||||
use stop_ndi;
|
use stop_ndi;
|
||||||
|
@ -243,28 +243,6 @@ impl ElementImpl for NdiVideoSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BaseSrcImpl for NdiVideoSrc {
|
impl BaseSrcImpl for NdiVideoSrc {
|
||||||
fn set_caps(
|
|
||||||
&self,
|
|
||||||
element: &gst_base::BaseSrc,
|
|
||||||
caps: &gst::Caps,
|
|
||||||
) -> Result<(), gst::LoggableError> {
|
|
||||||
let info = match gst_video::VideoInfo::from_caps(caps) {
|
|
||||||
None => {
|
|
||||||
return Err(gst_loggable_error!(
|
|
||||||
self.cat,
|
|
||||||
"Failed to build `VideoInfo` from caps {}",
|
|
||||||
caps
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Some(info) => info,
|
|
||||||
};
|
|
||||||
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
|
||||||
|
|
||||||
let mut state = self.state.lock().unwrap();
|
|
||||||
state.info = Some(info);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
|
||||||
*self.state.lock().unwrap() = Default::default();
|
*self.state.lock().unwrap() = Default::default();
|
||||||
let mut state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
|
@ -311,34 +289,17 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fixate(&self, element: &gst_base::BaseSrc, caps: gst::Caps) -> gst::Caps {
|
fn fixate(&self, element: &gst_base::BaseSrc, caps: gst::Caps) -> gst::Caps {
|
||||||
let receivers = HASHMAP_RECEIVERS.lock().unwrap();
|
|
||||||
let state = self.state.lock().unwrap();
|
|
||||||
|
|
||||||
let receiver = receivers.get(&state.id_receiver.unwrap()).unwrap();
|
|
||||||
let recv = &receiver.ndi_instance;
|
|
||||||
|
|
||||||
// FIXME: Should be done in create() and caps be updated as needed
|
|
||||||
let video_frame =
|
|
||||||
loop {
|
|
||||||
match recv.capture(true, false, false, 1000) {
|
|
||||||
Err(_) => unimplemented!(),
|
|
||||||
Ok(None) => continue,
|
|
||||||
Ok(Some(Frame::Video(frame))) => break frame,
|
|
||||||
_ => unreachable!(),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut caps = gst::Caps::truncate(caps);
|
let mut caps = gst::Caps::truncate(caps);
|
||||||
{
|
{
|
||||||
let caps = caps.make_mut();
|
let caps = caps.make_mut();
|
||||||
let s = caps.get_mut_structure(0).unwrap();
|
let s = caps.get_mut_structure(0).unwrap();
|
||||||
s.fixate_field_nearest_int("width", video_frame.xres());
|
s.fixate_field_nearest_int("width", 1920);
|
||||||
s.fixate_field_nearest_int("height", video_frame.yres());
|
s.fixate_field_nearest_int("height", 1080);
|
||||||
s.fixate_field_nearest_fraction(
|
if s.has_field("pixel-aspect-ratio") {
|
||||||
"framerate",
|
s.fixate_field_nearest_fraction("pixel-aspect-ratio", gst::Fraction::new(1, 1));
|
||||||
Fraction::new(video_frame.frame_rate().0, video_frame.frame_rate().1),
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.parent_fixate(element, caps)
|
self.parent_fixate(element, caps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -351,14 +312,7 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
) -> Result<gst::Buffer, gst::FlowError> {
|
) -> Result<gst::Buffer, gst::FlowError> {
|
||||||
// FIXME: Make sure to not have any mutexes locked while wait
|
// FIXME: Make sure to not have any mutexes locked while wait
|
||||||
let settings = self.settings.lock().unwrap().clone();
|
let settings = self.settings.lock().unwrap().clone();
|
||||||
let state = self.state.lock().unwrap();
|
let mut state = self.state.lock().unwrap();
|
||||||
let _info = match state.info {
|
|
||||||
None => {
|
|
||||||
gst_element_error!(element, gst::CoreError::Negotiation, ["Have no caps yet"]);
|
|
||||||
return Err(gst::FlowError::NotNegotiated);
|
|
||||||
}
|
|
||||||
Some(ref info) => info.clone(),
|
|
||||||
};
|
|
||||||
let receivers = HASHMAP_RECEIVERS.lock().unwrap();
|
let receivers = HASHMAP_RECEIVERS.lock().unwrap();
|
||||||
|
|
||||||
let receiver = &receivers.get(&state.id_receiver.unwrap()).unwrap();
|
let receiver = &receivers.get(&state.id_receiver.unwrap()).unwrap();
|
||||||
|
@ -421,6 +375,32 @@ impl BaseSrcImpl for NdiVideoSrc {
|
||||||
video_frame
|
video_frame
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let par = gst::Fraction::approximate_f32(video_frame.picture_aspect_ratio()).unwrap() *
|
||||||
|
gst::Fraction::new(video_frame.yres(), video_frame.xres());
|
||||||
|
|
||||||
|
let info = gst_video::VideoInfo::new(
|
||||||
|
gst_video::VideoFormat::Uyvy,
|
||||||
|
video_frame.xres() as u32,
|
||||||
|
video_frame.yres() as u32,
|
||||||
|
)
|
||||||
|
.fps(gst::Fraction::from(video_frame.frame_rate()))
|
||||||
|
.par(par)
|
||||||
|
.interlace_mode(if video_frame.frame_format_type() == ndisys::NDIlib_frame_format_type_e::NDIlib_frame_format_type_progressive {
|
||||||
|
gst_video::VideoInterlaceMode::Progressive
|
||||||
|
} else {
|
||||||
|
gst_video::VideoInterlaceMode::Interleaved
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if state.info.as_ref() != Some(&info) {
|
||||||
|
let caps = info.to_caps().unwrap();
|
||||||
|
state.info = Some(info);
|
||||||
|
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps);
|
||||||
|
element.set_caps(&caps).map_err(|_| gst::FlowError::NotNegotiated)?;
|
||||||
|
}
|
||||||
|
|
||||||
gst_log!(
|
gst_log!(
|
||||||
self.cat,
|
self.cat,
|
||||||
obj: element,
|
obj: element,
|
||||||
|
@ -435,6 +415,8 @@ 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);
|
||||||
|
|
||||||
|
// FIXME: This assumes that the strides match up
|
||||||
buffer.copy_from_slice(0, video_frame.data()).unwrap();
|
buffer.copy_from_slice(0, video_frame.data()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue