From 7a0bc76668285c5c4ab164e603abd2f3c55e4388 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Dr=C3=B6ge?= Date: Sat, 25 May 2019 10:11:06 +0200 Subject: [PATCH] video: Add VideoEncoder bindings Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/160 --- Gir_GstVideo.toml | 55 +- gstreamer-video/src/auto/mod.rs | 9 +- gstreamer-video/src/auto/video_encoder.rs | 146 +++++ gstreamer-video/src/lib.rs | 2 + gstreamer-video/src/subclass/mod.rs | 2 + gstreamer-video/src/subclass/video_encoder.rs | 536 ++++++++++++++++++ gstreamer-video/src/video_encoder.rs | 214 +++++++ 7 files changed, 961 insertions(+), 3 deletions(-) create mode 100644 gstreamer-video/src/auto/video_encoder.rs create mode 100644 gstreamer-video/src/subclass/video_encoder.rs create mode 100644 gstreamer-video/src/video_encoder.rs diff --git a/Gir_GstVideo.toml b/Gir_GstVideo.toml index c3bf8920a..94018f023 100644 --- a/Gir_GstVideo.toml +++ b/Gir_GstVideo.toml @@ -43,7 +43,6 @@ generate = [ manual = [ "GLib.DateTime", "GObject.Object", - "Gst.BufferPool", "Gst.Object", "Gst.Element", "Gst.Buffer", @@ -65,6 +64,11 @@ manual = [ "GstVideo.VideoTimeCodeInterval", ] +[[object]] +name = "Gst.ClockTime" +status = "manual" +conversion_type = "scalar" + [[object]] name = "Gst.Caps" status = "manual" @@ -143,3 +147,52 @@ status = "generate" [[object.function]] name = "negotiate" ignore = true + +[[object]] +name = "GstVideo.VideoEncoder" +subclassing = true +status = "generate" + + [[object.function]] + name = "allocate_output_frame" + ignore = true + + [[object.function]] + name = "allocate_output_frame_with_params" + ignore = true + + [[object.function]] + name = "finish_frame" + ignore = true + + [[object.function]] + name = "set_latency" + ignore = true + + [[object.function]] + name = "get_latency" + ignore = true + + [[object.function]] + name = "get_frame" + ignore = true + + [[object.function]] + name = "get_frames" + ignore = true + + [[object.function]] + name = "get_oldest_frame" + ignore = true + + [[object.function]] + name = "get_output_state" + ignore = true + + [[object.function]] + name = "set_output_state" + ignore = true + + [[object.function]] + name = "negotiate" + ignore = true diff --git a/gstreamer-video/src/auto/mod.rs b/gstreamer-video/src/auto/mod.rs index 613e3726d..5e6b487fc 100644 --- a/gstreamer-video/src/auto/mod.rs +++ b/gstreamer-video/src/auto/mod.rs @@ -2,12 +2,16 @@ // from gir-files (https://github.com/gtk-rs/gir-files) // DO NOT EDIT +mod video_buffer_pool; +pub use self::video_buffer_pool::{VideoBufferPool, VideoBufferPoolClass, NONE_VIDEO_BUFFER_POOL}; + mod video_decoder; pub use self::video_decoder::{VideoDecoder, VideoDecoderClass, NONE_VIDEO_DECODER}; pub use self::video_decoder::VideoDecoderExt; -mod video_buffer_pool; -pub use self::video_buffer_pool::{VideoBufferPool, VideoBufferPoolClass, NONE_VIDEO_BUFFER_POOL}; +mod video_encoder; +pub use self::video_encoder::{VideoEncoder, VideoEncoderClass, NONE_VIDEO_ENCODER}; +pub use self::video_encoder::VideoEncoderExt; mod video_filter; pub use self::video_filter::{VideoFilter, VideoFilterClass, NONE_VIDEO_FILTER}; @@ -44,5 +48,6 @@ pub use self::flags::VideoTimeCodeFlags; #[doc(hidden)] pub mod traits { pub use super::VideoDecoderExt; + pub use super::VideoEncoderExt; pub use super::VideoOverlayExt; } diff --git a/gstreamer-video/src/auto/video_encoder.rs b/gstreamer-video/src/auto/video_encoder.rs new file mode 100644 index 000000000..f44130ac6 --- /dev/null +++ b/gstreamer-video/src/auto/video_encoder.rs @@ -0,0 +1,146 @@ +// This file was generated by gir (https://github.com/gtk-rs/gir) +// from gir-files (https://github.com/gtk-rs/gir-files) +// DO NOT EDIT + +#[cfg(any(feature = "v1_14", feature = "dox"))] +use VideoCodecFrame; +use glib::StaticType; +use glib::Value; +use glib::object::Cast; +use glib::object::IsA; +use glib::signal::SignalHandlerId; +use glib::signal::connect_raw; +use glib::translate::*; +use glib_sys; +use gobject_sys; +use gst; +use gst_video_sys; +use std::boxed::Box as Box_; +use std::mem::transmute; + +glib_wrapper! { + pub struct VideoEncoder(Object) @extends gst::Element, gst::Object; + + match fn { + get_type => || gst_video_sys::gst_video_encoder_get_type(), + } +} + +unsafe impl Send for VideoEncoder {} +unsafe impl Sync for VideoEncoder {} + +pub const NONE_VIDEO_ENCODER: Option<&VideoEncoder> = None; + +pub trait VideoEncoderExt: 'static { + fn allocate_output_buffer(&self, size: usize) -> Option; + + //fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams); + + #[cfg(any(feature = "v1_14", feature = "dox"))] + fn get_max_encode_time(&self, frame: &VideoCodecFrame) -> gst::ClockTimeDiff; + + #[cfg(any(feature = "v1_14", feature = "dox"))] + fn is_qos_enabled(&self) -> bool; + + fn merge_tags(&self, tags: Option<&gst::TagList>, mode: gst::TagMergeMode); + + fn proxy_getcaps(&self, caps: Option<&gst::Caps>, filter: Option<&gst::Caps>) -> Option; + + fn set_headers(&self, headers: &[&gst::Buffer]); + + fn set_min_pts(&self, min_pts: gst::ClockTime); + + #[cfg(any(feature = "v1_14", feature = "dox"))] + fn set_qos_enabled(&self, enabled: bool); + + fn get_property_qos(&self) -> bool; + + fn set_property_qos(&self, qos: bool); + + fn connect_property_qos_notify(&self, f: F) -> SignalHandlerId; +} + +impl> VideoEncoderExt for O { + fn allocate_output_buffer(&self, size: usize) -> Option { + unsafe { + from_glib_full(gst_video_sys::gst_video_encoder_allocate_output_buffer(self.as_ref().to_glib_none().0, size)) + } + } + + //fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams) { + // unsafe { TODO: call gst_video_sys:gst_video_encoder_get_allocator() } + //} + + #[cfg(any(feature = "v1_14", feature = "dox"))] + fn get_max_encode_time(&self, frame: &VideoCodecFrame) -> gst::ClockTimeDiff { + unsafe { + gst_video_sys::gst_video_encoder_get_max_encode_time(self.as_ref().to_glib_none().0, frame.to_glib_none().0) + } + } + + #[cfg(any(feature = "v1_14", feature = "dox"))] + fn is_qos_enabled(&self) -> bool { + unsafe { + from_glib(gst_video_sys::gst_video_encoder_is_qos_enabled(self.as_ref().to_glib_none().0)) + } + } + + fn merge_tags(&self, tags: Option<&gst::TagList>, mode: gst::TagMergeMode) { + unsafe { + gst_video_sys::gst_video_encoder_merge_tags(self.as_ref().to_glib_none().0, tags.to_glib_none().0, mode.to_glib()); + } + } + + fn proxy_getcaps(&self, caps: Option<&gst::Caps>, filter: Option<&gst::Caps>) -> Option { + unsafe { + from_glib_full(gst_video_sys::gst_video_encoder_proxy_getcaps(self.as_ref().to_glib_none().0, caps.to_glib_none().0, filter.to_glib_none().0)) + } + } + + fn set_headers(&self, headers: &[&gst::Buffer]) { + unsafe { + gst_video_sys::gst_video_encoder_set_headers(self.as_ref().to_glib_none().0, headers.to_glib_full()); + } + } + + fn set_min_pts(&self, min_pts: gst::ClockTime) { + unsafe { + gst_video_sys::gst_video_encoder_set_min_pts(self.as_ref().to_glib_none().0, min_pts.to_glib()); + } + } + + #[cfg(any(feature = "v1_14", feature = "dox"))] + fn set_qos_enabled(&self, enabled: bool) { + unsafe { + gst_video_sys::gst_video_encoder_set_qos_enabled(self.as_ref().to_glib_none().0, enabled.to_glib()); + } + } + + fn get_property_qos(&self) -> bool { + unsafe { + let mut value = Value::from_type(::static_type()); + gobject_sys::g_object_get_property(self.to_glib_none().0 as *mut gobject_sys::GObject, b"qos\0".as_ptr() as *const _, value.to_glib_none_mut().0); + value.get().unwrap() + } + } + + fn set_property_qos(&self, qos: bool) { + unsafe { + gobject_sys::g_object_set_property(self.to_glib_none().0 as *mut gobject_sys::GObject, b"qos\0".as_ptr() as *const _, Value::from(&qos).to_glib_none().0); + } + } + + fn connect_property_qos_notify(&self, f: F) -> SignalHandlerId { + unsafe { + let f: Box_ = Box_::new(f); + connect_raw(self.as_ptr() as *mut _, b"notify::qos\0".as_ptr() as *const _, + Some(transmute(notify_qos_trampoline:: as usize)), Box_::into_raw(f)) + } + } +} + +unsafe extern "C" fn notify_qos_trampoline(this: *mut gst_video_sys::GstVideoEncoder, _param_spec: glib_sys::gpointer, f: glib_sys::gpointer) +where P: IsA { + let f: &F = &*(f as *const F); + f(&VideoEncoder::from_glib_borrow(this).unsafe_cast()) +} diff --git a/gstreamer-video/src/lib.rs b/gstreamer-video/src/lib.rs index f450e8437..ebae57a92 100644 --- a/gstreamer-video/src/lib.rs +++ b/gstreamer-video/src/lib.rs @@ -81,6 +81,7 @@ pub use video_buffer_pool::{ mod video_codec_frame; mod video_decoder; +mod video_encoder; pub use video_codec_frame::VideoCodecFrame; pub mod video_codec_state; pub use video_codec_state::VideoCodecState; @@ -95,6 +96,7 @@ pub mod prelude { pub use auto::traits::*; pub use video_buffer_pool::VideoBufferPoolConfig; pub use video_decoder::VideoDecoderExtManual; + pub use video_encoder::VideoEncoderExtManual; pub use video_overlay::VideoOverlayExtManual; } diff --git a/gstreamer-video/src/subclass/mod.rs b/gstreamer-video/src/subclass/mod.rs index f5f54cdff..2c042a06c 100644 --- a/gstreamer-video/src/subclass/mod.rs +++ b/gstreamer-video/src/subclass/mod.rs @@ -8,7 +8,9 @@ #![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))] pub mod video_decoder; +pub mod video_encoder; pub mod prelude { pub use super::video_decoder::{VideoDecoderImpl, VideoDecoderImplExt}; + pub use super::video_encoder::{VideoEncoderImpl, VideoEncoderImplExt}; } diff --git a/gstreamer-video/src/subclass/video_encoder.rs b/gstreamer-video/src/subclass/video_encoder.rs new file mode 100644 index 000000000..55ce3cec1 --- /dev/null +++ b/gstreamer-video/src/subclass/video_encoder.rs @@ -0,0 +1,536 @@ +// Copyright (C) 2019 Philippe Normand +// Copyright (C) 2019 Guillaume Desmottes +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use glib_sys; +use gst_sys; +use gst_video_sys; + +use glib::translate::*; + +use glib::subclass::prelude::*; +use gst; +use gst::subclass::prelude::*; +use gst::MiniObject; + +use video_codec_state::{Readable, VideoCodecState}; +use VideoCodecFrame; +use VideoEncoder; +use VideoEncoderClass; + +pub trait VideoEncoderImpl: VideoEncoderImplExt + ElementImpl + Send + Sync + 'static { + fn open(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + self.parent_open(element) + } + + fn close(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + self.parent_close(element) + } + + fn start(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + self.parent_start(element) + } + + fn stop(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + self.parent_stop(element) + } + + fn set_format( + &self, + element: &VideoEncoder, + state: &VideoCodecState, + ) -> Result<(), gst::LoggableError> { + self.parent_set_format(element, state) + } + + fn handle_frame( + &self, + element: &VideoEncoder, + frame: VideoCodecFrame, + ) -> Result { + self.parent_handle_frame(element, frame) + } + + fn flush(&self, element: &VideoEncoder) -> bool { + self.parent_flush(element) + } + + fn propose_allocation( + &self, + element: &VideoEncoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage> { + self.parent_propose_allocation(element, query) + } + + fn decide_allocation( + &self, + element: &VideoEncoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage> { + self.parent_decide_allocation(element, query) + } +} + +pub trait VideoEncoderImplExt { + fn parent_open(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>; + + fn parent_close(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>; + + fn parent_start(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>; + + fn parent_stop(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>; + + fn parent_set_format( + &self, + element: &VideoEncoder, + state: &VideoCodecState, + ) -> Result<(), gst::LoggableError>; + + fn parent_handle_frame( + &self, + element: &VideoEncoder, + frame: VideoCodecFrame, + ) -> Result; + + fn parent_flush(&self, element: &VideoEncoder) -> bool; + + fn parent_propose_allocation( + &self, + element: &VideoEncoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage>; + + fn parent_decide_allocation( + &self, + element: &VideoEncoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage>; +} + +impl VideoEncoderImplExt for T { + fn parent_open(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .open + .map(|f| { + if from_glib(f(element.to_glib_none().0)) { + Ok(()) + } else { + Err(gst_error_msg!( + gst::CoreError::StateChange, + ["Parent function `open` failed"] + )) + } + }) + .unwrap_or(Ok(())) + } + } + + fn parent_close(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .close + .map(|f| { + if from_glib(f(element.to_glib_none().0)) { + Ok(()) + } else { + Err(gst_error_msg!( + gst::CoreError::StateChange, + ["Parent function `close` failed"] + )) + } + }) + .unwrap_or(Ok(())) + } + } + + fn parent_start(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .start + .map(|f| { + if from_glib(f(element.to_glib_none().0)) { + Ok(()) + } else { + Err(gst_error_msg!( + gst::CoreError::StateChange, + ["Parent function `start` failed"] + )) + } + }) + .unwrap_or(Ok(())) + } + } + + fn parent_stop(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .stop + .map(|f| { + if from_glib(f(element.to_glib_none().0)) { + Ok(()) + } else { + Err(gst_error_msg!( + gst::CoreError::StateChange, + ["Parent function `stop` failed"] + )) + } + }) + .unwrap_or(Ok(())) + } + } + + fn parent_set_format( + &self, + element: &VideoEncoder, + state: &VideoCodecState, + ) -> Result<(), gst::LoggableError> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .set_format + .map(|f| { + gst_result_from_gboolean!( + f(element.to_glib_none().0, state.as_mut_ptr()), + gst::CAT_RUST, + "parent function `set_format` failed" + ) + }) + .unwrap_or(Ok(())) + } + } + + fn parent_handle_frame( + &self, + element: &VideoEncoder, + frame: VideoCodecFrame, + ) -> Result { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .handle_frame + .map(|f| { + gst::FlowReturn::from_glib(f(element.to_glib_none().0, frame.to_glib_none().0)) + }) + .unwrap_or(gst::FlowReturn::Error) + .into_result() + } + } + + fn parent_flush(&self, element: &VideoEncoder) -> bool { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .flush + .map(|f| from_glib(f(element.to_glib_none().0))) + .unwrap_or(false) + } + } + + fn parent_propose_allocation( + &self, + element: &VideoEncoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .propose_allocation + .map(|f| { + if from_glib(f(element.to_glib_none().0, query.as_mut_ptr())) { + Ok(()) + } else { + Err(gst_error_msg!( + gst::CoreError::StateChange, + ["Parent function `propose_allocation` failed"] + )) + } + }) + .unwrap_or(Ok(())) + } + } + + fn parent_decide_allocation( + &self, + element: &VideoEncoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass; + (*parent_class) + .decide_allocation + .map(|f| { + if from_glib(f(element.to_glib_none().0, query.as_mut_ptr())) { + Ok(()) + } else { + Err(gst_error_msg!( + gst::CoreError::StateChange, + ["Parent function `decide_allocation` failed"] + )) + } + }) + .unwrap_or(Ok(())) + } + } +} + +unsafe impl IsSubclassable for VideoEncoderClass +where + ::Instance: PanicPoison, +{ + fn override_vfuncs(&mut self) { + >::override_vfuncs(self); + unsafe { + let klass = &mut *(self as *mut Self as *mut gst_video_sys::GstVideoEncoderClass); + klass.open = Some(video_encoder_open::); + klass.close = Some(video_encoder_close::); + klass.start = Some(video_encoder_start::); + klass.stop = Some(video_encoder_stop::); + klass.set_format = Some(video_encoder_set_format::); + klass.handle_frame = Some(video_encoder_handle_frame::); + klass.flush = Some(video_encoder_flush::); + klass.propose_allocation = Some(video_encoder_propose_allocation::); + klass.decide_allocation = Some(video_encoder_decide_allocation::); + } + } +} + +unsafe extern "C" fn video_encoder_open( + ptr: *mut gst_video_sys::GstVideoEncoder, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + match imp.open(&wrap) { + Ok(()) => true, + Err(err) => { + wrap.post_error_message(&err); + false + } + } + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_close( + ptr: *mut gst_video_sys::GstVideoEncoder, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + match imp.close(&wrap) { + Ok(()) => true, + Err(err) => { + wrap.post_error_message(&err); + false + } + } + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_start( + ptr: *mut gst_video_sys::GstVideoEncoder, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + match imp.start(&wrap) { + Ok(()) => true, + Err(err) => { + wrap.post_error_message(&err); + false + } + } + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_stop( + ptr: *mut gst_video_sys::GstVideoEncoder, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + match imp.stop(&wrap) { + Ok(()) => true, + Err(err) => { + wrap.post_error_message(&err); + false + } + } + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_set_format( + ptr: *mut gst_video_sys::GstVideoEncoder, + state: *mut gst_video_sys::GstVideoCodecState, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + let mut wrap_state = VideoCodecState::::new(state); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + match imp.set_format(&wrap, &mut wrap_state) { + Ok(()) => true, + Err(err) => { + err.log_with_object(&wrap); + false + } + } + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_handle_frame( + ptr: *mut gst_video_sys::GstVideoEncoder, + frame: *mut gst_video_sys::GstVideoCodecFrame, +) -> gst_sys::GstFlowReturn +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + let wrap_frame = VideoCodecFrame::new(frame, &wrap); + + gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, { + imp.handle_frame(&wrap, wrap_frame).into() + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_flush( + ptr: *mut gst_video_sys::GstVideoEncoder, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + VideoEncoderImpl::flush(imp, &wrap) + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_propose_allocation( + ptr: *mut gst_video_sys::GstVideoEncoder, + query: *mut gst_sys::GstQuery, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + let query = gst::QueryRef::from_mut_ptr(query); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + match imp.propose_allocation(&wrap, query) { + Ok(()) => true, + Err(err) => { + wrap.post_error_message(&err); + false + } + } + }) + .to_glib() +} + +unsafe extern "C" fn video_encoder_decide_allocation( + ptr: *mut gst_video_sys::GstVideoEncoder, + query: *mut gst_sys::GstQuery, +) -> glib_sys::gboolean +where + T: VideoEncoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoEncoder = from_glib_borrow(ptr); + let query = gst::QueryRef::from_mut_ptr(query); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + match imp.decide_allocation(&wrap, query) { + Ok(()) => true, + Err(err) => { + wrap.post_error_message(&err); + false + } + } + }) + .to_glib() +} diff --git a/gstreamer-video/src/video_encoder.rs b/gstreamer-video/src/video_encoder.rs new file mode 100644 index 000000000..a753cff46 --- /dev/null +++ b/gstreamer-video/src/video_encoder.rs @@ -0,0 +1,214 @@ +// Copyright (C) 2019 Philippe Normand +// Copyright (C) 2019 Guillaume Desmottes +// Copyright (C) 2019 Sebastian Dröge +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use glib::object::IsA; +use glib::translate::*; +use gst; +use gst_video_sys; +use std::ptr; +use utils::HasStreamLock; +use video_codec_state::{InNegotiation, Readable, VideoCodecState, VideoCodecStateContext}; +use VideoCodecFrame; +use VideoEncoder; + +pub trait VideoEncoderExtManual: 'static { + #[cfg(any(feature = "v1_12", feature = "dox"))] + fn allocate_output_frame( + &self, + frame: &VideoCodecFrame, + size: usize, + ) -> Result; + + fn get_frame(&self, frame_number: i32) -> Option; + fn get_frames(&self) -> Vec; + fn get_oldest_frame(&self) -> Option; + + fn finish_frame(&self, frame: VideoCodecFrame) -> Result; + + fn get_latency(&self) -> (gst::ClockTime, gst::ClockTime); + fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime); + + fn get_output_state(&self) -> Option>; + fn set_output_state( + &self, + caps: gst::Caps, + reference: Option<&VideoCodecState>, + ) -> Result, gst::FlowError>; + + fn negotiate<'a>( + &'a self, + output_state: VideoCodecState<'a, InNegotiation<'a>>, + ) -> Result<(), gst::FlowError>; +} + +impl> VideoEncoderExtManual for O { + #[cfg(any(feature = "v1_12", feature = "dox"))] + fn allocate_output_frame( + &self, + frame: &VideoCodecFrame, + size: usize, + ) -> Result { + let ret: gst::FlowReturn = unsafe { + from_glib(gst_video_sys::gst_video_encoder_allocate_output_frame( + self.as_ref().to_glib_none().0, + frame.to_glib_none().0, + size, + )) + }; + ret.into_result() + } + + fn finish_frame(&self, frame: VideoCodecFrame) -> Result { + let ret: gst::FlowReturn = unsafe { + from_glib(gst_video_sys::gst_video_encoder_finish_frame( + self.as_ref().to_glib_none().0, + frame.into_ptr(), + )) + }; + ret.into_result() + } + + fn get_latency(&self) -> (gst::ClockTime, gst::ClockTime) { + let mut min_latency = gst_sys::GST_CLOCK_TIME_NONE; + let mut max_latency = gst_sys::GST_CLOCK_TIME_NONE; + + unsafe { + gst_video_sys::gst_video_encoder_get_latency( + self.as_ref().to_glib_none().0, + &mut min_latency, + &mut max_latency, + ); + + (from_glib(min_latency), from_glib(max_latency)) + } + } + + fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime) { + unsafe { + gst_video_sys::gst_video_encoder_set_latency( + self.as_ref().to_glib_none().0, + min_latency.to_glib(), + max_latency.to_glib(), + ); + } + } + + fn get_frame(&self, frame_number: i32) -> Option { + let frame = unsafe { + gst_video_sys::gst_video_encoder_get_frame(self.as_ref().to_glib_none().0, frame_number) + }; + + if frame.is_null() { + None + } else { + unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) } + } + } + + fn get_frames(&self) -> Vec { + unsafe { + let frames = + gst_video_sys::gst_video_encoder_get_frames(self.as_ref().to_glib_none().0); + let mut iter: *const glib_sys::GList = frames; + let mut vec = Vec::new(); + + while !iter.is_null() { + let frame_ptr = Ptr::from((*iter).data); + /* transfer ownership of the frame */ + let frame = VideoCodecFrame::new(frame_ptr, self.as_ref()); + vec.push(frame); + iter = (*iter).next; + } + + glib_sys::g_list_free(frames); + vec + } + } + + fn get_oldest_frame(&self) -> Option { + let frame = unsafe { + gst_video_sys::gst_video_encoder_get_oldest_frame(self.as_ref().to_glib_none().0) + }; + + if frame.is_null() { + None + } else { + unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) } + } + } + + fn get_output_state(&self) -> Option> { + let state = unsafe { + gst_video_sys::gst_video_encoder_get_output_state(self.as_ref().to_glib_none().0) + }; + + if state.is_null() { + None + } else { + unsafe { Some(VideoCodecState::::new(state)) } + } + } + + fn set_output_state( + &self, + caps: gst::Caps, + reference: Option<&VideoCodecState>, + ) -> Result, gst::FlowError> { + let state = unsafe { + let reference = match reference { + Some(reference) => reference.as_mut_ptr(), + None => ptr::null_mut(), + }; + gst_video_sys::gst_video_encoder_set_output_state( + self.as_ref().to_glib_none().0, + caps.into_ptr(), + reference, + ) + }; + + if state.is_null() { + Err(gst::FlowError::Error) + } else { + unsafe { Ok(VideoCodecState::::new(state, self.as_ref())) } + } + } + + fn negotiate<'a>( + &'a self, + output_state: VideoCodecState<'a, InNegotiation<'a>>, + ) -> Result<(), gst::FlowError> { + // Consume output_state so user won't be able to modify it anymore + let self_ptr = self.to_glib_none().0 as *const gst_sys::GstElement; + assert_eq!(output_state.context.get_element_as_ptr(), self_ptr); + + let ret = unsafe { + from_glib(gst_video_sys::gst_video_encoder_negotiate( + self.as_ref().to_glib_none().0, + )) + }; + if ret { + Ok(()) + } else { + Err(gst::FlowError::NotNegotiated) + } + } +} + +impl HasStreamLock for VideoEncoder { + fn get_stream_lock(&self) -> *mut glib_sys::GRecMutex { + let encoder_sys: *const gstreamer_video_sys::GstVideoEncoder = self.to_glib_none().0; + unsafe { &(*encoder_sys).stream_lock as *const _ as usize as *mut _ } + } + + fn get_element_as_ptr(&self) -> *const gst_sys::GstElement { + let encoder_sys: *const gstreamer_video_sys::GstVideoEncoder = self.to_glib_none().0; + encoder_sys as *const gst_sys::GstElement + } +}