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
This commit is contained in:
Guillaume Desmottes 2019-05-11 14:58:39 +02:00 committed by Sebastian Dröge
parent a986914bad
commit 47121fe9d6
13 changed files with 1649 additions and 1 deletions

View file

@ -18,6 +18,7 @@ external_libraries = [
] ]
generate = [ generate = [
"GstVideo.VideoCodecFrameFlags",
"GstVideo.VideoFormat", "GstVideo.VideoFormat",
"GstVideo.VideoFormatFlags", "GstVideo.VideoFormatFlags",
"GstVideo.VideoTileMode", "GstVideo.VideoTileMode",
@ -43,7 +44,16 @@ manual = [
"GObject.Object", "GObject.Object",
"Gst.Object", "Gst.Object",
"Gst.Element", "Gst.Element",
"Gst.Buffer",
"Gst.BufferPool",
"Gst.BufferPoolAcquireParams",
"Gst.ClockTimeDiff",
"Gst.FlowReturn",
"Gst.TagList",
"Gst.TagMergeMode",
"GstBase.BaseTransform", "GstBase.BaseTransform",
"GstVideo.VideoCodecState",
"GstVideo.VideoCodecFrame",
"GstVideo.VideoInfo", "GstVideo.VideoInfo",
"GstVideo.VideoFormatInfo", "GstVideo.VideoFormatInfo",
"GstVideo.VideoColorimetry", "GstVideo.VideoColorimetry",
@ -66,3 +76,68 @@ status = "generate"
name = "set_render_rectangle" name = "set_render_rectangle"
[object.function.return] [object.function.return]
bool_return_is_error = "Failed to set render rectangle" 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

View file

@ -37,3 +37,4 @@ v1_16 = ["gstreamer-sys/v1_16", "gstreamer-video-sys/v1_16", "v1_14"]
embed-lgpl-docs = ["rustdoc-stripper"] embed-lgpl-docs = ["rustdoc-stripper"]
purge-lgpl-docs = ["rustdoc-stripper"] purge-lgpl-docs = ["rustdoc-stripper"]
dox = ["gstreamer-video-sys/dox", "glib/dox", "gstreamer/dox"] dox = ["gstreamer-video-sys/dox", "glib/dox", "gstreamer/dox"]
subclassing = ["gstreamer/subclassing"]

View file

@ -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<gst_video_sys::GstVideoCodecFrameFlags> for VideoCodecFrameFlags {
fn from_glib(value: gst_video_sys::GstVideoCodecFrameFlags) -> VideoCodecFrameFlags {
skip_assert_initialized!();
VideoCodecFrameFlags::from_bits_truncate(value)
}
}
bitflags! { bitflags! {
pub struct VideoFlags: u32 { pub struct VideoFlags: u32 {
const NONE = 0; const NONE = 0;

View file

@ -2,6 +2,10 @@
// from gir-files (https://github.com/gtk-rs/gir-files) // from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT // 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; mod video_filter;
pub use self::video_filter::{VideoFilter, VideoFilterClass, NONE_VIDEO_FILTER}; pub use self::video_filter::{VideoFilter, VideoFilterClass, NONE_VIDEO_FILTER};
@ -25,6 +29,7 @@ pub use self::enums::VideoTransferFunction;
mod flags; mod flags;
pub use self::flags::VideoChromaSite; pub use self::flags::VideoChromaSite;
pub use self::flags::VideoCodecFrameFlags;
pub use self::flags::VideoFlags; pub use self::flags::VideoFlags;
pub use self::flags::VideoFormatFlags; pub use self::flags::VideoFormatFlags;
pub use self::flags::VideoFrameFlags; pub use self::flags::VideoFrameFlags;
@ -35,5 +40,6 @@ pub use self::flags::VideoTimeCodeFlags;
#[doc(hidden)] #[doc(hidden)]
pub mod traits { pub mod traits {
pub use super::VideoDecoderExt;
pub use super::VideoOverlayExt; pub use super::VideoOverlayExt;
} }

View file

@ -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 @ ???) from gir-files (https://github.com/gtk-rs/gir-files @ ???)

View file

@ -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<gst_video_sys::GstVideoDecoder, gst_video_sys::GstVideoDecoderClass, VideoDecoderClass>) @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<gst::Buffer>;
//fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams);
fn get_buffer_pool(&self) -> Option<gst::BufferPool>;
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<gst::Caps>;
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<O: IsA<VideoDecoder>> 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<gst::Buffer> {
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<gst::BufferPool> {
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<gst::Caps> {
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());
}
}
}

View file

@ -17,6 +17,7 @@ extern crate gobject_sys;
#[macro_use] #[macro_use]
extern crate gstreamer as gst; extern crate gstreamer as gst;
extern crate gstreamer_base as gst_base; 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_sys as gst_sys;
extern crate gstreamer_video_sys as gst_video_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"))] #[cfg(any(feature = "v1_12", feature = "dox"))]
pub use video_time_code_interval::VideoTimeCodeInterval; 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 // Re-export all the traits in a prelude module, so that applications
// can always "use gst::prelude::*" without getting conflicts // can always "use gst::prelude::*" without getting conflicts
pub mod prelude { pub mod prelude {
@ -77,5 +85,9 @@ pub mod prelude {
pub use gst::prelude::*; pub use gst::prelude::*;
pub use auto::traits::*; pub use auto::traits::*;
pub use video_decoder::VideoDecoderExtManual;
pub use video_overlay::VideoOverlayExtManual; pub use video_overlay::VideoOverlayExtManual;
} }
#[cfg(feature = "subclassing")]
pub mod subclass;

View file

@ -0,0 +1,14 @@
// Copyright (C) 2019 Philippe Normand <philn@igalia.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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};
}

View file

@ -0,0 +1,608 @@
// Copyright (C) 2019 Philippe Normand <philn@igalia.com>
// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<Readable>,
) -> Result<(), gst::LoggableError> {
self.parent_set_format(element, state)
}
fn parse(
&self,
element: &VideoDecoder,
frame: &VideoCodecFrame,
adapter: &gst_base::Adapter,
at_eos: bool,
) -> Result<gst::FlowSuccess, gst::FlowError> {
self.parent_parse(element, frame, adapter, at_eos)
}
fn handle_frame(
&self,
element: &VideoDecoder,
frame: VideoCodecFrame,
) -> Result<gst::FlowSuccess, gst::FlowError> {
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<Readable>,
) -> Result<(), gst::LoggableError>;
fn parent_parse(
&self,
element: &VideoDecoder,
frame: &VideoCodecFrame,
adapter: &gst_base::Adapter,
at_eos: bool,
) -> Result<gst::FlowSuccess, gst::FlowError>;
fn parent_handle_frame(
&self,
element: &VideoDecoder,
frame: VideoCodecFrame,
) -> Result<gst::FlowSuccess, gst::FlowError>;
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<T: VideoDecoderImpl + ObjectImpl> 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<Readable>,
) -> 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<gst::FlowSuccess, gst::FlowError> {
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<gst::FlowSuccess, gst::FlowError> {
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<T: ObjectSubclass + VideoDecoderImpl> IsSubclassable<T> for VideoDecoderClass
where
<T as ObjectSubclass>::Instance: PanicPoison,
{
fn override_vfuncs(&mut self) {
<gst::ElementClass as IsSubclassable<T>>::override_vfuncs(self);
unsafe {
let klass = &mut *(self as *mut Self as *mut gst_video_sys::GstVideoDecoderClass);
klass.open = Some(video_decoder_open::<T>);
klass.close = Some(video_decoder_close::<T>);
klass.start = Some(video_decoder_start::<T>);
klass.stop = Some(video_decoder_stop::<T>);
klass.set_format = Some(video_decoder_set_format::<T>);
klass.parse = Some(video_decoder_parse::<T>);
klass.handle_frame = Some(video_decoder_handle_frame::<T>);
klass.flush = Some(video_decoder_flush::<T>);
klass.propose_allocation = Some(video_decoder_propose_allocation::<T>);
klass.decide_allocation = Some(video_decoder_decide_allocation::<T>);
}
}
}
unsafe extern "C" fn video_decoder_open<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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::<Readable>::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<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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<T: ObjectSubclass>(
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()
}

View file

@ -0,0 +1,12 @@
// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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;
}

View file

@ -0,0 +1,206 @@
// Copyright (C) 2017 Thibault Saunier <tsaunier@gnome.org>
// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T: HasStreamLock>(
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);
}
}
}

View file

@ -0,0 +1,224 @@
// Copyright (C) 2017 Thibault Saunier <tsaunier@gnome.org>
// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<T: HasStreamLock>(
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> {}

View file

@ -0,0 +1,296 @@
// Copyright (C) 2019 Philippe Normand <philn@igalia.com>
// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, 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<gst::FlowSuccess, gst::FlowError>;
fn get_frame(&self, frame_number: i32) -> Option<VideoCodecFrame>;
fn get_frames(&self) -> Vec<VideoCodecFrame>;
fn get_oldest_frame(&self) -> Option<VideoCodecFrame>;
fn have_frame(&self) -> Result<gst::FlowSuccess, gst::FlowError>;
fn finish_frame(&self, frame: VideoCodecFrame) -> Result<gst::FlowSuccess, gst::FlowError>;
fn release_frame(&self, frame: VideoCodecFrame);
fn drop_frame(&self, frame: VideoCodecFrame) -> Result<gst::FlowSuccess, gst::FlowError>;
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<VideoCodecState<Readable>>;
fn set_output_state(
&self,
fmt: VideoFormat,
width: u32,
height: u32,
reference: Option<&VideoCodecState<Readable>>,
) -> Result<VideoCodecState<InNegotiation>, 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<Readable>>,
) -> Result<VideoCodecState<InNegotiation>, gst::FlowError>;
fn negotiate<'a>(
&'a self,
output_state: VideoCodecState<'a, InNegotiation<'a>>,
) -> Result<(), gst::FlowError>;
}
impl<O: IsA<VideoDecoder>> VideoDecoderExtManual for O {
#[cfg(any(feature = "v1_12", feature = "dox"))]
fn allocate_output_frame(
&self,
frame: &VideoCodecFrame,
params: Option<&gst::BufferPoolAcquireParams>,
) -> Result<gst::FlowSuccess, gst::FlowError> {
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<gst::FlowSuccess, gst::FlowError> {
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<gst::FlowSuccess, gst::FlowError> {
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<gst::FlowSuccess, gst::FlowError> {
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<VideoCodecFrame> {
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<VideoCodecFrame> {
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<VideoCodecFrame> {
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<VideoCodecState<Readable>> {
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::<Readable>::new(state)) }
}
}
fn set_output_state(
&self,
fmt: VideoFormat,
width: u32,
height: u32,
reference: Option<&VideoCodecState<Readable>>,
) -> Result<VideoCodecState<InNegotiation>, 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::<InNegotiation>::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<Readable>>,
) -> Result<VideoCodecState<InNegotiation>, 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::<InNegotiation>::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
}
}