forked from mirrors/gstreamer-rs
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:
parent
a986914bad
commit
47121fe9d6
13 changed files with 1649 additions and 1 deletions
|
@ -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
|
||||||
|
|
|
@ -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"]
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 @ ???)
|
||||||
|
|
168
gstreamer-video/src/auto/video_decoder.rs
Normal file
168
gstreamer-video/src/auto/video_decoder.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
14
gstreamer-video/src/subclass/mod.rs
Normal file
14
gstreamer-video/src/subclass/mod.rs
Normal 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};
|
||||||
|
}
|
608
gstreamer-video/src/subclass/video_decoder.rs
Normal file
608
gstreamer-video/src/subclass/video_decoder.rs
Normal 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()
|
||||||
|
}
|
12
gstreamer-video/src/utils.rs
Normal file
12
gstreamer-video/src/utils.rs
Normal 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;
|
||||||
|
}
|
206
gstreamer-video/src/video_codec_frame.rs
Normal file
206
gstreamer-video/src/video_codec_frame.rs
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
224
gstreamer-video/src/video_codec_state.rs
Normal file
224
gstreamer-video/src/video_codec_state.rs
Normal 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> {}
|
296
gstreamer-video/src/video_decoder.rs
Normal file
296
gstreamer-video/src/video_decoder.rs
Normal 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
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue