From 47121fe9d6878f66a68bdc0ad6ec850f97cf1a9c Mon Sep 17 00:00:00 2001 From: Guillaume Desmottes Date: Sat, 11 May 2019 14:58:39 +0200 Subject: [PATCH] gstreamer-video: VideoDecoder bindings The VideoCodecFrame and VideoCodecState is C API is unfortunatelly unsafe by design. So we workarounded it by ensuring the decoder stream lock was hold while user has a writable reference on those objects. Based on previous work from Thibault Saunier and Philippe Normand. Fixes #161 --- Gir_GstVideo.toml | 75 +++ gstreamer-video/Cargo.toml | 1 + gstreamer-video/src/auto/flags.rs | 26 + gstreamer-video/src/auto/mod.rs | 6 + gstreamer-video/src/auto/versions.txt | 2 +- gstreamer-video/src/auto/video_decoder.rs | 168 +++++ gstreamer-video/src/lib.rs | 12 + gstreamer-video/src/subclass/mod.rs | 14 + gstreamer-video/src/subclass/video_decoder.rs | 608 ++++++++++++++++++ gstreamer-video/src/utils.rs | 12 + gstreamer-video/src/video_codec_frame.rs | 206 ++++++ gstreamer-video/src/video_codec_state.rs | 224 +++++++ gstreamer-video/src/video_decoder.rs | 296 +++++++++ 13 files changed, 1649 insertions(+), 1 deletion(-) create mode 100644 gstreamer-video/src/auto/video_decoder.rs create mode 100644 gstreamer-video/src/subclass/mod.rs create mode 100644 gstreamer-video/src/subclass/video_decoder.rs create mode 100644 gstreamer-video/src/utils.rs create mode 100644 gstreamer-video/src/video_codec_frame.rs create mode 100644 gstreamer-video/src/video_codec_state.rs create mode 100644 gstreamer-video/src/video_decoder.rs diff --git a/Gir_GstVideo.toml b/Gir_GstVideo.toml index 798476d54..a0778138d 100644 --- a/Gir_GstVideo.toml +++ b/Gir_GstVideo.toml @@ -18,6 +18,7 @@ external_libraries = [ ] generate = [ + "GstVideo.VideoCodecFrameFlags", "GstVideo.VideoFormat", "GstVideo.VideoFormatFlags", "GstVideo.VideoTileMode", @@ -43,7 +44,16 @@ manual = [ "GObject.Object", "Gst.Object", "Gst.Element", + "Gst.Buffer", + "Gst.BufferPool", + "Gst.BufferPoolAcquireParams", + "Gst.ClockTimeDiff", + "Gst.FlowReturn", + "Gst.TagList", + "Gst.TagMergeMode", "GstBase.BaseTransform", + "GstVideo.VideoCodecState", + "GstVideo.VideoCodecFrame", "GstVideo.VideoInfo", "GstVideo.VideoFormatInfo", "GstVideo.VideoColorimetry", @@ -66,3 +76,68 @@ status = "generate" name = "set_render_rectangle" [object.function.return] bool_return_is_error = "Failed to set render rectangle" + +[[object]] +name = "GstVideo.VideoDecoder" +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 = "release_frame" + ignore = true + + [[object.function]] + name = "drop_frame" + ignore = true + + [[object.function]] + name = "have_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 = "set_interlaced_output_state" + ignore = true + + [[object.function]] + name = "negotiate" + ignore = true diff --git a/gstreamer-video/Cargo.toml b/gstreamer-video/Cargo.toml index 91fcd648d..03b92dc93 100644 --- a/gstreamer-video/Cargo.toml +++ b/gstreamer-video/Cargo.toml @@ -37,3 +37,4 @@ v1_16 = ["gstreamer-sys/v1_16", "gstreamer-video-sys/v1_16", "v1_14"] embed-lgpl-docs = ["rustdoc-stripper"] purge-lgpl-docs = ["rustdoc-stripper"] dox = ["gstreamer-video-sys/dox", "glib/dox", "gstreamer/dox"] +subclassing = ["gstreamer/subclassing"] diff --git a/gstreamer-video/src/auto/flags.rs b/gstreamer-video/src/auto/flags.rs index 5b148e383..7c2f07904 100644 --- a/gstreamer-video/src/auto/flags.rs +++ b/gstreamer-video/src/auto/flags.rs @@ -67,6 +67,32 @@ impl SetValue for VideoChromaSite { } } +bitflags! { + pub struct VideoCodecFrameFlags: u32 { + const DECODE_ONLY = 1; + const SYNC_POINT = 2; + const FORCE_KEYFRAME = 4; + const FORCE_KEYFRAME_HEADERS = 8; + } +} + +#[doc(hidden)] +impl ToGlib for VideoCodecFrameFlags { + type GlibType = gst_video_sys::GstVideoCodecFrameFlags; + + fn to_glib(&self) -> gst_video_sys::GstVideoCodecFrameFlags { + self.bits() + } +} + +#[doc(hidden)] +impl FromGlib for VideoCodecFrameFlags { + fn from_glib(value: gst_video_sys::GstVideoCodecFrameFlags) -> VideoCodecFrameFlags { + skip_assert_initialized!(); + VideoCodecFrameFlags::from_bits_truncate(value) + } +} + bitflags! { pub struct VideoFlags: u32 { const NONE = 0; diff --git a/gstreamer-video/src/auto/mod.rs b/gstreamer-video/src/auto/mod.rs index 413b58c44..ed91f8fa6 100644 --- a/gstreamer-video/src/auto/mod.rs +++ b/gstreamer-video/src/auto/mod.rs @@ -2,6 +2,10 @@ // from gir-files (https://github.com/gtk-rs/gir-files) // DO NOT EDIT +mod video_decoder; +pub use self::video_decoder::{VideoDecoder, VideoDecoderClass, NONE_VIDEO_DECODER}; +pub use self::video_decoder::VideoDecoderExt; + mod video_filter; pub use self::video_filter::{VideoFilter, VideoFilterClass, NONE_VIDEO_FILTER}; @@ -25,6 +29,7 @@ pub use self::enums::VideoTransferFunction; mod flags; pub use self::flags::VideoChromaSite; +pub use self::flags::VideoCodecFrameFlags; pub use self::flags::VideoFlags; pub use self::flags::VideoFormatFlags; pub use self::flags::VideoFrameFlags; @@ -35,5 +40,6 @@ pub use self::flags::VideoTimeCodeFlags; #[doc(hidden)] pub mod traits { + pub use super::VideoDecoderExt; pub use super::VideoOverlayExt; } diff --git a/gstreamer-video/src/auto/versions.txt b/gstreamer-video/src/auto/versions.txt index a9e0ccc5d..ef8c963e8 100644 --- a/gstreamer-video/src/auto/versions.txt +++ b/gstreamer-video/src/auto/versions.txt @@ -1,2 +1,2 @@ -Generated by gir (https://github.com/gtk-rs/gir @ 58cffd4) +Generated by gir (https://github.com/gtk-rs/gir @ 6d4ca01) from gir-files (https://github.com/gtk-rs/gir-files @ ???) diff --git a/gstreamer-video/src/auto/video_decoder.rs b/gstreamer-video/src/auto/video_decoder.rs new file mode 100644 index 000000000..2148ad101 --- /dev/null +++ b/gstreamer-video/src/auto/video_decoder.rs @@ -0,0 +1,168 @@ +// 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 + +use VideoCodecFrame; +use glib::object::IsA; +use glib::translate::*; +use gst; +use gst_video_sys; + +glib_wrapper! { + pub struct VideoDecoder(Object) @extends gst::Element, gst::Object; + + match fn { + get_type => || gst_video_sys::gst_video_decoder_get_type(), + } +} + +unsafe impl Send for VideoDecoder {} +unsafe impl Sync for VideoDecoder {} + +pub const NONE_VIDEO_DECODER: Option<&VideoDecoder> = None; + +pub trait VideoDecoderExt: 'static { + fn add_to_frame(&self, n_bytes: i32); + + fn allocate_output_buffer(&self) -> Option; + + //fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams); + + fn get_buffer_pool(&self) -> Option; + + fn get_estimate_rate(&self) -> i32; + + fn get_max_decode_time(&self, frame: &VideoCodecFrame) -> gst::ClockTimeDiff; + + fn get_max_errors(&self) -> i32; + + fn get_needs_format(&self) -> bool; + + fn get_packetized(&self) -> bool; + + fn get_pending_frame_size(&self) -> usize; + + fn get_qos_proportion(&self) -> f64; + + 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_estimate_rate(&self, enabled: bool); + + fn set_max_errors(&self, num: i32); + + fn set_needs_format(&self, enabled: bool); + + fn set_packetized(&self, packetized: bool); + + fn set_use_default_pad_acceptcaps(&self, use_: bool); +} + +impl> VideoDecoderExt for O { + fn add_to_frame(&self, n_bytes: i32) { + unsafe { + gst_video_sys::gst_video_decoder_add_to_frame(self.as_ref().to_glib_none().0, n_bytes); + } + } + + fn allocate_output_buffer(&self) -> Option { + unsafe { + from_glib_full(gst_video_sys::gst_video_decoder_allocate_output_buffer(self.as_ref().to_glib_none().0)) + } + } + + //fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams) { + // unsafe { TODO: call gst_video_sys:gst_video_decoder_get_allocator() } + //} + + fn get_buffer_pool(&self) -> Option { + unsafe { + from_glib_full(gst_video_sys::gst_video_decoder_get_buffer_pool(self.as_ref().to_glib_none().0)) + } + } + + fn get_estimate_rate(&self) -> i32 { + unsafe { + gst_video_sys::gst_video_decoder_get_estimate_rate(self.as_ref().to_glib_none().0) + } + } + + fn get_max_decode_time(&self, frame: &VideoCodecFrame) -> gst::ClockTimeDiff { + unsafe { + gst_video_sys::gst_video_decoder_get_max_decode_time(self.as_ref().to_glib_none().0, frame.to_glib_none().0) + } + } + + fn get_max_errors(&self) -> i32 { + unsafe { + gst_video_sys::gst_video_decoder_get_max_errors(self.as_ref().to_glib_none().0) + } + } + + fn get_needs_format(&self) -> bool { + unsafe { + from_glib(gst_video_sys::gst_video_decoder_get_needs_format(self.as_ref().to_glib_none().0)) + } + } + + fn get_packetized(&self) -> bool { + unsafe { + from_glib(gst_video_sys::gst_video_decoder_get_packetized(self.as_ref().to_glib_none().0)) + } + } + + fn get_pending_frame_size(&self) -> usize { + unsafe { + gst_video_sys::gst_video_decoder_get_pending_frame_size(self.as_ref().to_glib_none().0) + } + } + + fn get_qos_proportion(&self) -> f64 { + unsafe { + gst_video_sys::gst_video_decoder_get_qos_proportion(self.as_ref().to_glib_none().0) + } + } + + fn merge_tags(&self, tags: Option<&gst::TagList>, mode: gst::TagMergeMode) { + unsafe { + gst_video_sys::gst_video_decoder_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_decoder_proxy_getcaps(self.as_ref().to_glib_none().0, caps.to_glib_none().0, filter.to_glib_none().0)) + } + } + + fn set_estimate_rate(&self, enabled: bool) { + unsafe { + gst_video_sys::gst_video_decoder_set_estimate_rate(self.as_ref().to_glib_none().0, enabled.to_glib()); + } + } + + fn set_max_errors(&self, num: i32) { + unsafe { + gst_video_sys::gst_video_decoder_set_max_errors(self.as_ref().to_glib_none().0, num); + } + } + + fn set_needs_format(&self, enabled: bool) { + unsafe { + gst_video_sys::gst_video_decoder_set_needs_format(self.as_ref().to_glib_none().0, enabled.to_glib()); + } + } + + fn set_packetized(&self, packetized: bool) { + unsafe { + gst_video_sys::gst_video_decoder_set_packetized(self.as_ref().to_glib_none().0, packetized.to_glib()); + } + } + + fn set_use_default_pad_acceptcaps(&self, use_: bool) { + unsafe { + gst_video_sys::gst_video_decoder_set_use_default_pad_acceptcaps(self.as_ref().to_glib_none().0, use_.to_glib()); + } + } +} diff --git a/gstreamer-video/src/lib.rs b/gstreamer-video/src/lib.rs index bc233c4ee..fc1e5be8b 100644 --- a/gstreamer-video/src/lib.rs +++ b/gstreamer-video/src/lib.rs @@ -17,6 +17,7 @@ extern crate gobject_sys; #[macro_use] extern crate gstreamer as gst; extern crate gstreamer_base as gst_base; +extern crate gstreamer_base_sys as gst_base_sys; extern crate gstreamer_sys as gst_sys; extern crate gstreamer_video_sys as gst_video_sys; @@ -70,6 +71,13 @@ mod video_time_code_interval; #[cfg(any(feature = "v1_12", feature = "dox"))] pub use video_time_code_interval::VideoTimeCodeInterval; +mod video_codec_frame; +mod video_decoder; +pub use video_codec_frame::VideoCodecFrame; +pub mod video_codec_state; +pub use video_codec_state::VideoCodecState; +mod utils; + // Re-export all the traits in a prelude module, so that applications // can always "use gst::prelude::*" without getting conflicts pub mod prelude { @@ -77,5 +85,9 @@ pub mod prelude { pub use gst::prelude::*; pub use auto::traits::*; + pub use video_decoder::VideoDecoderExtManual; pub use video_overlay::VideoOverlayExtManual; } + +#[cfg(feature = "subclassing")] +pub mod subclass; diff --git a/gstreamer-video/src/subclass/mod.rs b/gstreamer-video/src/subclass/mod.rs new file mode 100644 index 000000000..f5f54cdff --- /dev/null +++ b/gstreamer-video/src/subclass/mod.rs @@ -0,0 +1,14 @@ +// Copyright (C) 2019 Philippe Normand +// +// 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. +#![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))] + +pub mod video_decoder; + +pub mod prelude { + pub use super::video_decoder::{VideoDecoderImpl, VideoDecoderImplExt}; +} diff --git a/gstreamer-video/src/subclass/video_decoder.rs b/gstreamer-video/src/subclass/video_decoder.rs new file mode 100644 index 000000000..2547d44c1 --- /dev/null +++ b/gstreamer-video/src/subclass/video_decoder.rs @@ -0,0 +1,608 @@ +// 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 gst_base; + +use video_codec_state::{Readable, VideoCodecState}; +use VideoCodecFrame; +use VideoDecoder; +use VideoDecoderClass; + +pub trait VideoDecoderImpl: VideoDecoderImplExt + ElementImpl + Send + Sync + 'static { + fn open(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + self.parent_open(element) + } + + fn close(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + self.parent_close(element) + } + + fn start(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + self.parent_start(element) + } + + fn stop(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + self.parent_stop(element) + } + + fn set_format( + &self, + element: &VideoDecoder, + state: &VideoCodecState, + ) -> Result<(), gst::LoggableError> { + self.parent_set_format(element, state) + } + + fn parse( + &self, + element: &VideoDecoder, + frame: &VideoCodecFrame, + adapter: &gst_base::Adapter, + at_eos: bool, + ) -> Result { + self.parent_parse(element, frame, adapter, at_eos) + } + + fn handle_frame( + &self, + element: &VideoDecoder, + frame: VideoCodecFrame, + ) -> Result { + self.parent_handle_frame(element, frame) + } + + fn flush(&self, element: &VideoDecoder) -> bool { + self.parent_flush(element) + } + + fn propose_allocation( + &self, + element: &VideoDecoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage> { + self.parent_propose_allocation(element, query) + } + + fn decide_allocation( + &self, + element: &VideoDecoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage> { + self.parent_decide_allocation(element, query) + } +} + +pub trait VideoDecoderImplExt { + fn parent_open(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage>; + + fn parent_close(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage>; + + fn parent_start(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage>; + + fn parent_stop(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage>; + + fn parent_set_format( + &self, + element: &VideoDecoder, + state: &VideoCodecState, + ) -> Result<(), gst::LoggableError>; + + fn parent_parse( + &self, + element: &VideoDecoder, + frame: &VideoCodecFrame, + adapter: &gst_base::Adapter, + at_eos: bool, + ) -> Result; + + fn parent_handle_frame( + &self, + element: &VideoDecoder, + frame: VideoCodecFrame, + ) -> Result; + + fn parent_flush(&self, element: &VideoDecoder) -> bool; + + fn parent_propose_allocation( + &self, + element: &VideoDecoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage>; + + fn parent_decide_allocation( + &self, + element: &VideoDecoder, + query: &mut gst::QueryRef, + ) -> Result<(), gst::ErrorMessage>; +} + +impl VideoDecoderImplExt for T { + fn parent_open(&self, element: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoDecoderClass; + (*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: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoDecoderClass; + (*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: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoDecoderClass; + (*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: &VideoDecoder) -> Result<(), gst::ErrorMessage> { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoDecoderClass; + (*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: &VideoDecoder, + 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::GstVideoDecoderClass; + (*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_parse( + &self, + element: &VideoDecoder, + frame: &VideoCodecFrame, + adapter: &gst_base::Adapter, + at_eos: bool, + ) -> Result { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoDecoderClass; + (*parent_class) + .parse + .map(|f| { + gst::FlowReturn::from_glib(f( + element.to_glib_none().0, + frame.to_glib_none().0, + adapter.to_glib_none().0, + at_eos.to_glib(), + )) + }) + .unwrap_or(gst::FlowReturn::Ok) + .into_result() + } + } + + fn parent_handle_frame( + &self, + element: &VideoDecoder, + frame: VideoCodecFrame, + ) -> Result { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoDecoderClass; + (*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: &VideoDecoder) -> bool { + unsafe { + let data = self.get_type_data(); + let parent_class = + data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoDecoderClass; + (*parent_class) + .flush + .map(|f| from_glib(f(element.to_glib_none().0))) + .unwrap_or(false) + } + } + + fn parent_propose_allocation( + &self, + element: &VideoDecoder, + 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::GstVideoDecoderClass; + (*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: &VideoDecoder, + 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::GstVideoDecoderClass; + (*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 VideoDecoderClass +where + ::Instance: PanicPoison, +{ + fn override_vfuncs(&mut self) { + >::override_vfuncs(self); + unsafe { + let klass = &mut *(self as *mut Self as *mut gst_video_sys::GstVideoDecoderClass); + klass.open = Some(video_decoder_open::); + klass.close = Some(video_decoder_close::); + klass.start = Some(video_decoder_start::); + klass.stop = Some(video_decoder_stop::); + klass.set_format = Some(video_decoder_set_format::); + klass.parse = Some(video_decoder_parse::); + klass.handle_frame = Some(video_decoder_handle_frame::); + klass.flush = Some(video_decoder_flush::); + klass.propose_allocation = Some(video_decoder_propose_allocation::); + klass.decide_allocation = Some(video_decoder_decide_allocation::); + } + } +} + +unsafe extern "C" fn video_decoder_open( + ptr: *mut gst_video_sys::GstVideoDecoder, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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_decoder_close( + ptr: *mut gst_video_sys::GstVideoDecoder, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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_decoder_start( + ptr: *mut gst_video_sys::GstVideoDecoder, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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_decoder_stop( + ptr: *mut gst_video_sys::GstVideoDecoder, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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_decoder_set_format( + ptr: *mut gst_video_sys::GstVideoDecoder, + state: *mut gst_video_sys::GstVideoCodecState, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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_decoder_parse( + ptr: *mut gst_video_sys::GstVideoDecoder, + frame: *mut gst_video_sys::GstVideoCodecFrame, + adapter: *mut gst_base_sys::GstAdapter, + at_eos: glib_sys::gboolean, +) -> gst_sys::GstFlowReturn +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = from_glib_borrow(ptr); + gst_video_sys::gst_video_codec_frame_ref(frame); + let mut wrap_frame = VideoCodecFrame::new(frame, &wrap); + let mut wrap_adapter: gst_base::Adapter = from_glib_borrow(adapter); + let at_eos: bool = from_glib(at_eos); + + gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, { + imp.parse(&wrap, &mut wrap_frame, &mut wrap_adapter, at_eos) + .into() + }) + .to_glib() +} + +unsafe extern "C" fn video_decoder_handle_frame( + ptr: *mut gst_video_sys::GstVideoDecoder, + frame: *mut gst_video_sys::GstVideoCodecFrame, +) -> gst_sys::GstFlowReturn +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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_decoder_flush( + ptr: *mut gst_video_sys::GstVideoDecoder, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = from_glib_borrow(ptr); + + gst_panic_to_error!(&wrap, &instance.panicked(), false, { + VideoDecoderImpl::flush(imp, &wrap) + }) + .to_glib() +} + +unsafe extern "C" fn video_decoder_propose_allocation( + ptr: *mut gst_video_sys::GstVideoDecoder, + query: *mut gst_sys::GstQuery, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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_decoder_decide_allocation( + ptr: *mut gst_video_sys::GstVideoDecoder, + query: *mut gst_sys::GstQuery, +) -> glib_sys::gboolean +where + T: VideoDecoderImpl, + T::Instance: PanicPoison, +{ + glib_floating_reference_guard!(ptr); + let instance = &*(ptr as *mut T::Instance); + let imp = instance.get_impl(); + let wrap: VideoDecoder = 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/utils.rs b/gstreamer-video/src/utils.rs new file mode 100644 index 000000000..43215bd0f --- /dev/null +++ b/gstreamer-video/src/utils.rs @@ -0,0 +1,12 @@ +// 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. + +pub trait HasStreamLock { + fn get_stream_lock(&self) -> *mut glib_sys::GRecMutex; + fn get_element_as_ptr(&self) -> *const gst_sys::GstElement; +} diff --git a/gstreamer-video/src/video_codec_frame.rs b/gstreamer-video/src/video_codec_frame.rs new file mode 100644 index 000000000..a854d906e --- /dev/null +++ b/gstreamer-video/src/video_codec_frame.rs @@ -0,0 +1,206 @@ +// Copyright (C) 2017 Thibault Saunier +// 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::translate::*; +use gst; +use gst::miniobject::MiniObject; +use gst_video_sys; +use std::fmt; +use std::mem; +use utils::HasStreamLock; +use VideoCodecFrameFlags; + +pub struct VideoCodecFrame<'a> { + frame: *mut gst_video_sys::GstVideoCodecFrame, + /* GstVideoCodecFrame API isn't safe so protect the frame using the + * element (decoder or encoder) stream lock */ + element: &'a dyn HasStreamLock, +} + +#[doc(hidden)] +impl<'a> ::glib::translate::ToGlibPtr<'a, *mut gst_video_sys::GstVideoCodecFrame> + for VideoCodecFrame<'a> +{ + type Storage = &'a Self; + + fn to_glib_none( + &'a self, + ) -> ::glib::translate::Stash<'a, *mut gst_video_sys::GstVideoCodecFrame, Self> { + Stash(self.frame, self) + } + + fn to_glib_full(&self) -> *mut gst_video_sys::GstVideoCodecFrame { + unimplemented!() + } +} + +impl<'a> fmt::Debug for VideoCodecFrame<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + let mut b = f.debug_struct("VideoCodecFrame"); + + b.field("flags", &self.get_flags()) + .field("system_frame_number", &self.get_system_frame_number()) + .field("decode_frame_number", &self.get_decode_frame_number()) + .field( + "presentation_frame_number", + &self.get_presentation_frame_number(), + ) + .field("dts", &self.get_dts()) + .field("pts", &self.get_pts()) + .field("duration", &self.get_duration()) + .field("distance_from_sync", &self.get_distance_from_sync()) + .field("input_buffer", &self.get_input_buffer()) + .field("output_buffer", &self.get_output_buffer()) + .field("deadline", &self.get_deadline()); + + b.finish() + } +} + +impl<'a> VideoCodecFrame<'a> { + // Take ownership of @frame + pub(crate) unsafe fn new( + frame: *mut gst_video_sys::GstVideoCodecFrame, + element: &'a T, + ) -> Self { + let stream_lock = element.get_stream_lock(); + glib_sys::g_rec_mutex_lock(stream_lock); + Self { frame, element } + } + + pub fn get_flags(&self) -> VideoCodecFrameFlags { + let flags = unsafe { (*self.to_glib_none().0).flags }; + VideoCodecFrameFlags::from_bits_truncate(flags) + } + + pub fn set_flags(&self, flags: VideoCodecFrameFlags) { + unsafe { (*self.to_glib_none().0).flags |= flags.bits() } + } + + pub fn unset_flags(&self, flags: VideoCodecFrameFlags) { + unsafe { (*self.to_glib_none().0).flags &= !flags.bits() } + } + + pub fn get_system_frame_number(&self) -> u32 { + unsafe { (*self.to_glib_none().0).system_frame_number } + } + + pub fn get_decode_frame_number(&self) -> u32 { + unsafe { (*self.to_glib_none().0).decode_frame_number } + } + + pub fn get_presentation_frame_number(&self) -> u32 { + unsafe { (*self.to_glib_none().0).presentation_frame_number } + } + + pub fn get_dts(&self) -> gst::ClockTime { + unsafe { from_glib((*self.to_glib_none().0).dts) } + } + + pub fn set_dts(&self, dts: gst::ClockTime) { + unsafe { + (*self.to_glib_none().0).dts = dts.to_glib(); + } + } + + pub fn get_pts(&self) -> gst::ClockTime { + unsafe { from_glib((*self.to_glib_none().0).pts) } + } + + pub fn set_pts(&self, pts: gst::ClockTime) { + unsafe { + (*self.to_glib_none().0).pts = pts.to_glib(); + } + } + + pub fn get_duration(&self) -> gst::ClockTime { + unsafe { from_glib((*self.to_glib_none().0).duration) } + } + + pub fn set_duration(&self, duration: gst::ClockTime) { + unsafe { + (*self.to_glib_none().0).duration = duration.to_glib(); + } + } + + pub fn get_distance_from_sync(&self) -> i32 { + unsafe { (*self.to_glib_none().0).distance_from_sync } + } + + pub fn get_input_buffer(&self) -> Option<&gst::BufferRef> { + unsafe { + let ptr = (*self.to_glib_none().0).input_buffer; + if ptr.is_null() { + None + } else { + Some(gst::BufferRef::from_mut_ptr(ptr)) + } + } + } + + pub fn get_output_buffer(&self) -> Option<&mut gst::BufferRef> { + unsafe { + let ptr = (*self.to_glib_none().0).output_buffer; + if ptr.is_null() { + None + } else { + let writable: bool = from_glib(gst_sys::gst_mini_object_is_writable( + ptr as *const gst_sys::GstMiniObject, + )); + assert!(writable); + + Some(gst::BufferRef::from_mut_ptr(ptr)) + } + } + } + + pub fn set_output_buffer(&self, output_buffer: gst::Buffer) { + unsafe { + let prev = (*self.to_glib_none().0).output_buffer; + + if !prev.is_null() { + gst_sys::gst_mini_object_unref(prev as *mut gst_sys::GstMiniObject); + } + + let ptr = output_buffer.into_ptr(); + let writable: bool = from_glib(gst_sys::gst_mini_object_is_writable( + ptr as *const gst_sys::GstMiniObject, + )); + assert!(writable); + + (*self.to_glib_none().0).output_buffer = ptr; + } + } + + pub fn get_deadline(&self) -> gst::ClockTime { + unsafe { from_glib((*self.to_glib_none().0).deadline) } + } + + #[doc(hidden)] + pub unsafe fn into_ptr(self) -> *mut gst_video_sys::GstVideoCodecFrame { + let stream_lock = self.element.get_stream_lock(); + glib_sys::g_rec_mutex_unlock(stream_lock); + + let ptr = self.to_glib_none().0; + mem::forget(self); + + ptr + } +} + +impl<'a> Drop for VideoCodecFrame<'a> { + fn drop(&mut self) { + unsafe { + let stream_lock = self.element.get_stream_lock(); + glib_sys::g_rec_mutex_unlock(stream_lock); + + gst_video_sys::gst_video_codec_frame_unref(self.frame); + } + } +} diff --git a/gstreamer-video/src/video_codec_state.rs b/gstreamer-video/src/video_codec_state.rs new file mode 100644 index 000000000..0fe675303 --- /dev/null +++ b/gstreamer-video/src/video_codec_state.rs @@ -0,0 +1,224 @@ +// Copyright (C) 2017 Thibault Saunier +// 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::translate::*; +use gst_sys; +use gst_video_sys; +use std::fmt; +use std::marker::PhantomData; +use std::ptr; +use utils::HasStreamLock; + +use gst; +use gst::miniobject::MiniObject; + +use video_info::VideoInfo; + +pub trait VideoCodecStateContext<'a> { + fn get_element(&self) -> Option<&'a dyn HasStreamLock>; + fn get_element_as_ptr(&self) -> *const gst_sys::GstElement; +} + +pub struct InNegotiation<'a> { + /* GstVideoCodecState API isn't safe so protect the state using the + * element (decoder or encoder) stream lock */ + element: &'a dyn HasStreamLock, +} +pub struct Readable {} + +impl<'a> VideoCodecStateContext<'a> for InNegotiation<'a> { + fn get_element(&self) -> Option<&'a dyn HasStreamLock> { + Some(self.element) + } + + fn get_element_as_ptr(&self) -> *const gst_sys::GstElement { + self.element.get_element_as_ptr() + } +} + +impl<'a> VideoCodecStateContext<'a> for Readable { + fn get_element(&self) -> Option<&'a dyn HasStreamLock> { + None + } + + fn get_element_as_ptr(&self) -> *const gst_sys::GstElement { + ptr::null() + } +} + +pub struct VideoCodecState<'a, T: VideoCodecStateContext<'a>> { + state: *mut gst_video_sys::GstVideoCodecState, + pub(crate) context: T, + /* FIXME: should not be needed because lifetime is actually used */ + phantom: PhantomData<&'a T>, +} + +impl<'a, T: VideoCodecStateContext<'a>> fmt::Debug for VideoCodecState<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + f.debug_struct("VideoCodecState") + .field("info", &self.get_info()) + .field("caps", &self.get_caps()) + .field("codec_data", &self.get_codec_data()) + .field("allocation_caps", &self.get_allocation_caps()) + .finish() + } +} + +impl<'a> VideoCodecState<'a, Readable> { + // Take ownership of @state + pub(crate) unsafe fn new(state: *mut gst_video_sys::GstVideoCodecState) -> Self { + Self { + state, + context: Readable {}, + phantom: PhantomData, + } + } +} + +impl<'a> VideoCodecState<'a, InNegotiation<'a>> { + // Take ownership of @state + pub(crate) unsafe fn new( + state: *mut gst_video_sys::GstVideoCodecState, + element: &'a T, + ) -> Self { + let stream_lock = element.get_stream_lock(); + glib_sys::g_rec_mutex_lock(stream_lock); + Self { + state, + context: InNegotiation { element }, + phantom: PhantomData, + } + } +} + +impl<'a, T: VideoCodecStateContext<'a>> VideoCodecState<'a, T> { + pub fn get_info(&self) -> VideoInfo { + unsafe { + let ptr = &((*self.as_mut_ptr()).info) as *const _ as usize as *mut _; + VideoInfo::from_glib_none(ptr) + } + } + + pub fn get_caps(&self) -> Option<&gst::CapsRef> { + unsafe { + let ptr = (*self.as_mut_ptr()).caps; + + if ptr.is_null() { + None + } else { + Some(gst::CapsRef::from_ptr(ptr)) + } + } + } + + pub fn get_codec_data(&self) -> Option<&gst::BufferRef> { + unsafe { + let ptr = (*self.as_mut_ptr()).codec_data; + + if ptr.is_null() { + None + } else { + Some(gst::BufferRef::from_ptr(ptr)) + } + } + } + + pub fn get_allocation_caps(&self) -> Option<&gst::CapsRef> { + unsafe { + let ptr = (*self.as_mut_ptr()).allocation_caps; + + if ptr.is_null() { + None + } else { + Some(gst::CapsRef::from_ptr(ptr)) + } + } + } + #[doc(hidden)] + pub fn as_mut_ptr(&self) -> *mut gst_video_sys::GstVideoCodecState { + self.state + } +} + +impl<'a, T: VideoCodecStateContext<'a>> Drop for VideoCodecState<'a, T> { + fn drop(&mut self) { + unsafe { + if let Some(element) = self.context.get_element() { + let stream_lock = element.get_stream_lock(); + glib_sys::g_rec_mutex_unlock(stream_lock); + } + gst_video_sys::gst_video_codec_state_unref(self.state); + } + } +} + +impl<'a> VideoCodecState<'a, InNegotiation<'a>> { + pub fn set_info(&mut self, info: VideoInfo) { + unsafe { + ptr::write(&mut (*self.as_mut_ptr()).info, *(info.to_glib_none().0)); + } + } + + pub fn set_caps(&mut self, caps: &gst::Caps) { + unsafe { + let prev = (*self.as_mut_ptr()).caps; + + if !prev.is_null() { + gst_sys::gst_mini_object_unref(prev as *mut gst_sys::GstMiniObject) + } + + ptr::write( + &mut (*self.as_mut_ptr()).caps, + gst_sys::gst_mini_object_ref(caps.as_mut_ptr() as *mut _) as *mut _, + ); + } + } + + pub fn set_codec_data(&mut self, codec_data: &gst::Buffer) { + unsafe { + let prev = (*self.as_mut_ptr()).codec_data; + + if !prev.is_null() { + gst_sys::gst_mini_object_unref(prev as *mut gst_sys::GstMiniObject) + } + + ptr::write( + &mut (*self.as_mut_ptr()).codec_data, + gst_sys::gst_mini_object_ref(codec_data.as_mut_ptr() as *mut _) as *mut _, + ); + } + } + + pub fn set_allocation_caps(&mut self, allocation_caps: &gst::Caps) { + unsafe { + let prev = (*self.as_mut_ptr()).allocation_caps; + + if !prev.is_null() { + gst_sys::gst_mini_object_unref(prev as *mut gst_sys::GstMiniObject) + } + + ptr::write( + &mut (*self.as_mut_ptr()).allocation_caps, + gst_sys::gst_mini_object_ref(allocation_caps.as_mut_ptr() as *mut _) as *mut _, + ); + } + } +} + +impl<'a> Clone for VideoCodecState<'a, Readable> { + fn clone(&self) -> Self { + unsafe { + let state = gst_video_sys::gst_video_codec_state_ref(self.state); + Self::new(state) + } + } +} + +unsafe impl<'a> Send for VideoCodecState<'a, Readable> {} +unsafe impl<'a> Sync for VideoCodecState<'a, Readable> {} diff --git a/gstreamer-video/src/video_decoder.rs b/gstreamer-video/src/video_decoder.rs new file mode 100644 index 000000000..fb134ba9d --- /dev/null +++ b/gstreamer-video/src/video_decoder.rs @@ -0,0 +1,296 @@ +// 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::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 VideoDecoder; +use VideoFormat; +#[cfg(any(feature = "v1_16", feature = "dox"))] +use VideoInterlaceMode; + +pub trait VideoDecoderExtManual: 'static { + #[cfg(any(feature = "v1_12", feature = "dox"))] + fn allocate_output_frame( + &self, + frame: &VideoCodecFrame, + params: Option<&gst::BufferPoolAcquireParams>, + ) -> Result; + + fn get_frame(&self, frame_number: i32) -> Option; + fn get_frames(&self) -> Vec; + fn get_oldest_frame(&self) -> Option; + + fn have_frame(&self) -> Result; + fn finish_frame(&self, frame: VideoCodecFrame) -> Result; + fn release_frame(&self, frame: VideoCodecFrame); + fn drop_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, + fmt: VideoFormat, + width: u32, + height: u32, + reference: Option<&VideoCodecState>, + ) -> Result, gst::FlowError>; + #[cfg(any(feature = "v1_16", feature = "dox"))] + fn set_interlaced_output_state( + &self, + fmt: VideoFormat, + mode: VideoInterlaceMode, + width: u32, + height: u32, + reference: Option<&VideoCodecState>, + ) -> Result, gst::FlowError>; + + fn negotiate<'a>( + &'a self, + output_state: VideoCodecState<'a, InNegotiation<'a>>, + ) -> Result<(), gst::FlowError>; +} + +impl> VideoDecoderExtManual for O { + #[cfg(any(feature = "v1_12", feature = "dox"))] + fn allocate_output_frame( + &self, + frame: &VideoCodecFrame, + params: Option<&gst::BufferPoolAcquireParams>, + ) -> Result { + let ret: gst::FlowReturn = unsafe { + let params_ptr = params.to_glib_none().0 as *mut _; + from_glib( + gst_video_sys::gst_video_decoder_allocate_output_frame_with_params( + self.as_ref().to_glib_none().0, + frame.to_glib_none().0, + params_ptr, + ), + ) + }; + ret.into_result() + } + + fn have_frame(&self) -> Result { + let ret: gst::FlowReturn = unsafe { + from_glib(gst_video_sys::gst_video_decoder_have_frame( + self.as_ref().to_glib_none().0, + )) + }; + ret.into_result() + } + + fn finish_frame(&self, frame: VideoCodecFrame) -> Result { + let ret: gst::FlowReturn = unsafe { + from_glib(gst_video_sys::gst_video_decoder_finish_frame( + self.as_ref().to_glib_none().0, + frame.into_ptr(), + )) + }; + ret.into_result() + } + + fn release_frame(&self, frame: VideoCodecFrame) { + unsafe { + gst_video_sys::gst_video_decoder_release_frame( + self.as_ref().to_glib_none().0, + frame.into_ptr(), + ) + } + } + + fn drop_frame(&self, frame: VideoCodecFrame) -> Result { + let ret: gst::FlowReturn = unsafe { + from_glib(gst_video_sys::gst_video_decoder_drop_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_decoder_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_decoder_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_decoder_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_decoder_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_decoder_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_decoder_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, + fmt: VideoFormat, + width: u32, + height: u32, + 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_decoder_set_output_state( + self.as_ref().to_glib_none().0, + fmt.to_glib(), + width, + height, + reference, + ) + }; + + if state.is_null() { + Err(gst::FlowError::Error) + } else { + unsafe { Ok(VideoCodecState::::new(state, self.as_ref())) } + } + } + + #[cfg(any(feature = "v1_16", feature = "dox"))] + fn set_interlaced_output_state( + &self, + fmt: VideoFormat, + mode: VideoInterlaceMode, + width: u32, + height: u32, + 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_decoder_set_interlaced_output_state( + self.as_ref().to_glib_none().0, + fmt.to_glib(), + mode.to_glib(), + width, + height, + 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_decoder_negotiate( + self.as_ref().to_glib_none().0, + )) + }; + if ret { + Ok(()) + } else { + Err(gst::FlowError::NotNegotiated) + } + } +} + +impl HasStreamLock for VideoDecoder { + fn get_stream_lock(&self) -> *mut glib_sys::GRecMutex { + let decoder_sys: *const gstreamer_video_sys::GstVideoDecoder = self.to_glib_none().0; + unsafe { &(*decoder_sys).stream_lock as *const _ as usize as *mut _ } + } + + fn get_element_as_ptr(&self) -> *const gst_sys::GstElement { + let decoder_sys: *const gstreamer_video_sys::GstVideoDecoder = self.to_glib_none().0; + decoder_sys as *const gst_sys::GstElement + } +}