Merge pull request #21 from o-reo/master

FIX changed return for gstreamer functions, fixed NDI frames memory leaks
This commit is contained in:
Ruben Gonzalez 2019-03-26 17:37:51 +01:00 committed by GitHub
commit fb56cdc87c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 86 additions and 75 deletions

View file

@ -46,7 +46,7 @@ impl Default for Settings {
}
static PROPERTIES: [subclass::Property; 3] = [
subclass::Property("stream-name", || {
subclass::Property("stream-name", |_| {
glib::ParamSpec::string(
"stream-name",
"Sream Name",
@ -55,7 +55,7 @@ subclass::Property("stream-name", || {
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("ip", || {
subclass::Property("ip", |_| {
glib::ParamSpec::string(
"ip",
"Stream IP",
@ -64,7 +64,7 @@ subclass::Property("ip", || {
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("loss-threshold", || {
subclass::Property("loss-threshold", |_| {
glib::ParamSpec::uint(
"loss-threshold",
"Loss threshold",
@ -152,7 +152,8 @@ impl ObjectSubclass for NdiAudioSrc {
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
)
.unwrap();
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
@ -246,7 +247,7 @@ impl ObjectSubclass for NdiAudioSrc {
&self,
element: &gst::Element,
transition: gst::StateChange,
) -> gst::StateChangeReturn {
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
if transition == gst::StateChange::PausedToPlaying {
let mut receivers = hashmap_receivers.lock().unwrap();
let settings = self.settings.lock().unwrap();
@ -257,35 +258,29 @@ impl ObjectSubclass for NdiAudioSrc {
let audio_frame: NDIlib_audio_frame_v2_t = Default::default();
let mut frame_type: NDIlib_frame_type_e = NDIlib_frame_type_e::NDIlib_frame_type_none;
unsafe {
while frame_type != NDIlib_frame_type_e::NDIlib_frame_type_audio {
frame_type = NDIlib_recv_capture_v2(
pNDI_recv,
ptr::null(),
&audio_frame,
ptr::null(),
1000,
);
gst_debug!(self.cat, obj: element, "NDI audio frame received: {:?}", audio_frame);
}
if receiver.initial_timestamp <= audio_frame.timestamp as u64
|| receiver.initial_timestamp == 0
{
receiver.initial_timestamp = audio_frame.timestamp as u64;
}
gst_debug!(self.cat, obj: element, "Setting initial timestamp to {}", receiver.initial_timestamp);
while NDIlib_recv_capture_v2(pNDI_recv, ptr::null(), &audio_frame, ptr::null(), 1000)
!= NDIlib_frame_type_e::NDIlib_frame_type_audio {}
}
gst_debug!(self.cat, obj: element, "NDI audio frame received: {:?}", audio_frame);
if receiver.initial_timestamp <= audio_frame.timestamp as u64
|| receiver.initial_timestamp == 0 {
receiver.initial_timestamp = audio_frame.timestamp as u64;
}
unsafe {
NDIlib_recv_free_audio_v2(pNDI_recv, &audio_frame);
}
gst_debug!(self.cat, obj: element, "Setting initial timestamp to {}", receiver.initial_timestamp);
}
}
self.parent_change_state(element, transition)
}
}
impl BaseSrcImpl for NdiAudioSrc {
fn set_caps(&self, element: &gst_base::BaseSrc, caps: &gst::CapsRef) -> bool {
fn set_caps(&self, element: &gst_base::BaseSrc, caps: &gst::CapsRef) -> Result<(), gst::LoggableError> {
let info = match gst_audio::AudioInfo::from_caps(caps) {
None => return false,
None => return Err(gst_loggable_error!(self.cat, "Failed to build `AudioInfo` from caps {}", caps)),
Some(info) => info,
};
@ -294,10 +289,10 @@ impl ObjectSubclass for NdiAudioSrc {
let mut state = self.state.lock().unwrap();
state.info = Some(info);
true
Ok(())
}
fn start(&self, element: &gst_base::BaseSrc) -> bool {
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
*self.state.lock().unwrap() = Default::default();
let mut settings = self.settings.lock().unwrap();
@ -308,17 +303,23 @@ impl ObjectSubclass for NdiAudioSrc {
&settings.stream_name.clone(),
);
settings.id_receiver != 0
match settings.id_receiver {
0 => Err(gst_error_msg!(
gst::ResourceError::NotFound,
["Could not connect to this source"]
)),
_ => Ok(())
}
}
fn stop(&self, element: &gst_base::BaseSrc) -> bool {
fn stop(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
*self.state.lock().unwrap() = Default::default();
let settings = self.settings.lock().unwrap();
stop_ndi(self.cat, element, settings.id_receiver);
// Commented because when adding ndi destroy stopped in this line
//*self.state.lock().unwrap() = Default::default();
true
Ok(())
}
fn query(&self, element: &gst_base::BaseSrc, query: &mut gst::QueryRef) -> bool {
@ -341,7 +342,7 @@ impl ObjectSubclass for NdiAudioSrc {
return false;
}
}
BaseSrcImpl::parent_query(self, element, query)
BaseSrcImplExt::parent_query(self, element, query)
}
fn fixate(&self, element: &gst_base::BaseSrc, caps: gst::Caps) -> gst::Caps {
@ -355,13 +356,9 @@ impl ObjectSubclass for NdiAudioSrc {
let audio_frame: NDIlib_audio_frame_v2_t = Default::default();
let mut frame_type: NDIlib_frame_type_e = NDIlib_frame_type_e::NDIlib_frame_type_none;
while frame_type != NDIlib_frame_type_e::NDIlib_frame_type_audio {
unsafe {
frame_type =
NDIlib_recv_capture_v2(pNDI_recv, ptr::null(), &audio_frame, ptr::null(), 1000);
gst_debug!(self.cat, obj: element, "NDI audio frame received: {:?}", audio_frame);
}
unsafe {
while NDIlib_recv_capture_v2(pNDI_recv, ptr::null(), &audio_frame, ptr::null(), 1000)
!= NDIlib_frame_type_e::NDIlib_frame_type_audio {}
}
let no_samples = audio_frame.no_samples as u64;
@ -379,6 +376,10 @@ impl ObjectSubclass for NdiAudioSrc {
}
let _ = element.post_message(&gst::Message::new_latency().src(Some(element)).build());
unsafe {
NDIlib_recv_free_audio_v2(pNDI_recv, &audio_frame);
}
self.parent_fixate(element, caps)
}
@ -431,8 +432,9 @@ impl ObjectSubclass for NdiAudioSrc {
let buffer = gst::Buffer::with_size(0).unwrap();
return Ok(buffer)
}
if time >= (audio_frame.timestamp as u64) {
NDIlib_recv_free_audio_v2(pNDI_recv, &audio_frame);
gst_debug!(self.cat, obj: element, "Frame timestamp ({:?}) is lower than received in the first frame from NDI ({:?}), so skiping...", (audio_frame.timestamp as u64), time);
} else {
skip_frame = false;
@ -474,6 +476,7 @@ impl ObjectSubclass for NdiAudioSrc {
dst.reference_level = 0;
dst.p_data = buffer.map_writable().unwrap().as_mut_slice_of::<i16>().unwrap().as_mut_ptr();
NDIlib_util_audio_to_interleaved_16s_v2(&audio_frame, &mut dst);
NDIlib_recv_free_audio_v2(pNDI_recv, &audio_frame);
}
gst_log!(self.cat, obj: element, "Produced buffer {:?}", buffer);

View file

@ -33,6 +33,14 @@ extern "C" {
p_metadata: *const NDIlib_metadata_frame_t,
timeout_in_ms: u32,
) -> NDIlib_frame_type_e;
pub fn NDIlib_recv_free_video_v2(
p_instance: NDIlib_recv_instance_t,
p_video_data: *const NDIlib_video_frame_v2_t
);
pub fn NDIlib_recv_free_audio_v2(
p_instance: NDIlib_recv_instance_t,
p_audio_data: *const NDIlib_audio_frame_v2_t
);
}
pub type NDIlib_find_instance_t = *mut ::std::os::raw::c_void;

View file

@ -47,7 +47,7 @@ impl Default for Settings {
}
static PROPERTIES: [subclass::Property; 3] = [
subclass::Property("stream-name", || {
subclass::Property("stream-name", |_| {
glib::ParamSpec::string(
"stream-name",
"Stream Name",
@ -56,7 +56,7 @@ subclass::Property("stream-name", || {
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("ip", || {
subclass::Property("ip", |_| {
glib::ParamSpec::string(
"ip",
"Stream IP",
@ -65,7 +65,7 @@ subclass::Property("ip", || {
glib::ParamFlags::READWRITE,
)
}),
subclass::Property("loss-threshold", || {
subclass::Property("loss-threshold", |_| {
glib::ParamSpec::uint(
"loss-threshold",
"Loss threshold",
@ -160,7 +160,7 @@ impl ObjectSubclass for NdiVideoSrc {
gst::PadDirection::Src,
gst::PadPresence::Always,
&caps,
);
).unwrap();
klass.add_pad_template(src_pad_template);
klass.install_properties(&PROPERTIES);
@ -256,7 +256,7 @@ impl ObjectSubclass for NdiVideoSrc {
&self,
element: &gst::Element,
transition: gst::StateChange,
) -> gst::StateChangeReturn {
) -> Result<gst::StateChangeSuccess, gst::StateChangeError> {
if transition == gst::StateChange::PausedToPlaying {
let mut receivers = hashmap_receivers.lock().unwrap();
let settings = self.settings.lock().unwrap();
@ -267,35 +267,30 @@ impl ObjectSubclass for NdiVideoSrc {
let video_frame: NDIlib_video_frame_v2_t = Default::default();
let mut frame_type: NDIlib_frame_type_e = NDIlib_frame_type_e::NDIlib_frame_type_none;
unsafe {
while frame_type != NDIlib_frame_type_e::NDIlib_frame_type_video {
frame_type = NDIlib_recv_capture_v2(
pNDI_recv,
&video_frame,
ptr::null(),
ptr::null(),
1000,
);
gst_debug!(self.cat, obj: element, "NDI video frame received: {:?}", video_frame);
}
while NDIlib_recv_capture_v2(pNDI_recv, &video_frame, ptr::null(), ptr::null(), 1000)
!= NDIlib_frame_type_e::NDIlib_frame_type_video {}
}
gst_debug!(self.cat, obj: element, "NDI video frame received: {:?}", video_frame);
if receiver.initial_timestamp <= video_frame.timestamp as u64
|| receiver.initial_timestamp == 0
{
receiver.initial_timestamp = video_frame.timestamp as u64;
}
unsafe {
NDIlib_recv_free_video_v2(pNDI_recv, &video_frame);
}
gst_debug!(self.cat, obj: element, "Setting initial timestamp to {}", receiver.initial_timestamp);
}
}
self.parent_change_state(element, transition)
}
}
impl BaseSrcImpl for NdiVideoSrc {
fn set_caps(&self, element: &gst_base::BaseSrc, caps: &gst::CapsRef) -> bool {
fn set_caps(&self, element: &gst_base::BaseSrc, caps: &gst::CapsRef) -> Result<(), gst::LoggableError> {
let info = match gst_video::VideoInfo::from_caps(caps) {
None => return false,
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);
@ -303,10 +298,10 @@ impl ObjectSubclass for NdiVideoSrc {
let mut state = self.state.lock().unwrap();
state.info = Some(info);
let _ = element.post_message(&gst::Message::new_latency().src(Some(element)).build());
true
Ok(())
}
fn start(&self, element: &gst_base::BaseSrc) -> bool {
fn start(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
*self.state.lock().unwrap() = Default::default();
let mut settings = self.settings.lock().unwrap();
settings.id_receiver = connect_ndi(
@ -316,17 +311,24 @@ impl ObjectSubclass for NdiVideoSrc {
&settings.stream_name.clone(),
);
settings.id_receiver != 0
// settings.id_receiver != 0
match settings.id_receiver {
0 => Err(gst_error_msg!(
gst::ResourceError::NotFound,
["Could not connect to this source"]
)),
_ => Ok(())
}
}
fn stop(&self, element: &gst_base::BaseSrc) -> bool {
*self.state.lock().unwrap() = Default::default();
fn stop(&self, element: &gst_base::BaseSrc) -> Result<(), gst::ErrorMessage> {
*self.state.lock().unwrap() = Default::default();
let settings = self.settings.lock().unwrap();
stop_ndi(self.cat, element, settings.id_receiver);
// Commented because when adding ndi destroy stopped in this line
//*self.state.lock().unwrap() = Default::default();
true
Ok(())
}
fn query(&self, element: &gst_base::BaseSrc, query: &mut gst::QueryRef) -> bool {
@ -349,7 +351,7 @@ impl ObjectSubclass for NdiVideoSrc {
return false;
}
}
BaseSrcImpl::parent_query(self, element, query)
BaseSrcImplExt::parent_query(self, element, query)
}
fn fixate(&self, element: &gst_base::BaseSrc, caps: gst::Caps) -> gst::Caps {
@ -362,15 +364,10 @@ impl ObjectSubclass for NdiVideoSrc {
let video_frame: NDIlib_video_frame_v2_t = Default::default();
let mut frame_type: NDIlib_frame_type_e = NDIlib_frame_type_e::NDIlib_frame_type_none;
while frame_type != NDIlib_frame_type_e::NDIlib_frame_type_video {
unsafe {
frame_type =
NDIlib_recv_capture_v2(pNDI_recv, &video_frame, ptr::null(), ptr::null(), 1000);
gst_debug!(self.cat, obj: element, "NDI video frame received: {:?}", video_frame);
}
unsafe {
while NDIlib_recv_capture_v2(pNDI_recv, &video_frame, ptr::null(), ptr::null(), 1000)
!= NDIlib_frame_type_e::NDIlib_frame_type_video {}
}
settings.latency = gst::SECOND.mul_div_floor(
video_frame.frame_rate_D as u64,
video_frame.frame_rate_N as u64,
@ -387,7 +384,9 @@ impl ObjectSubclass for NdiVideoSrc {
Fraction::new(video_frame.frame_rate_N, video_frame.frame_rate_D),
);
}
unsafe {
NDIlib_recv_free_video_v2(pNDI_recv, &video_frame);
}
let _ = element.post_message(&gst::Message::new_latency().src(Some(element)).build());
self.parent_fixate(element, caps)
}
@ -443,6 +442,7 @@ impl ObjectSubclass for NdiVideoSrc {
}
if time >= (video_frame.timestamp as u64) {
NDIlib_recv_free_video_v2(pNDI_recv, &video_frame);
gst_debug!(self.cat, obj: element, "Frame timestamp ({:?}) is lower than received in the first frame from NDI ({:?}), so skiping...", (video_frame.timestamp as u64), time);
} else {
skip_frame = false;