video: Add VideoEncoder bindings

Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/160
This commit is contained in:
Sebastian Dröge 2019-05-25 10:11:06 +02:00
parent 4096869075
commit 7a0bc76668
7 changed files with 961 additions and 3 deletions

View file

@ -43,7 +43,6 @@ generate = [
manual = [
"GLib.DateTime",
"GObject.Object",
"Gst.BufferPool",
"Gst.Object",
"Gst.Element",
"Gst.Buffer",
@ -65,6 +64,11 @@ manual = [
"GstVideo.VideoTimeCodeInterval",
]
[[object]]
name = "Gst.ClockTime"
status = "manual"
conversion_type = "scalar"
[[object]]
name = "Gst.Caps"
status = "manual"
@ -143,3 +147,52 @@ status = "generate"
[[object.function]]
name = "negotiate"
ignore = true
[[object]]
name = "GstVideo.VideoEncoder"
subclassing = true
status = "generate"
[[object.function]]
name = "allocate_output_frame"
ignore = true
[[object.function]]
name = "allocate_output_frame_with_params"
ignore = true
[[object.function]]
name = "finish_frame"
ignore = true
[[object.function]]
name = "set_latency"
ignore = true
[[object.function]]
name = "get_latency"
ignore = true
[[object.function]]
name = "get_frame"
ignore = true
[[object.function]]
name = "get_frames"
ignore = true
[[object.function]]
name = "get_oldest_frame"
ignore = true
[[object.function]]
name = "get_output_state"
ignore = true
[[object.function]]
name = "set_output_state"
ignore = true
[[object.function]]
name = "negotiate"
ignore = true

View file

@ -2,12 +2,16 @@
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
mod video_buffer_pool;
pub use self::video_buffer_pool::{VideoBufferPool, VideoBufferPoolClass, NONE_VIDEO_BUFFER_POOL};
mod video_decoder;
pub use self::video_decoder::{VideoDecoder, VideoDecoderClass, NONE_VIDEO_DECODER};
pub use self::video_decoder::VideoDecoderExt;
mod video_buffer_pool;
pub use self::video_buffer_pool::{VideoBufferPool, VideoBufferPoolClass, NONE_VIDEO_BUFFER_POOL};
mod video_encoder;
pub use self::video_encoder::{VideoEncoder, VideoEncoderClass, NONE_VIDEO_ENCODER};
pub use self::video_encoder::VideoEncoderExt;
mod video_filter;
pub use self::video_filter::{VideoFilter, VideoFilterClass, NONE_VIDEO_FILTER};
@ -44,5 +48,6 @@ pub use self::flags::VideoTimeCodeFlags;
#[doc(hidden)]
pub mod traits {
pub use super::VideoDecoderExt;
pub use super::VideoEncoderExt;
pub use super::VideoOverlayExt;
}

View file

@ -0,0 +1,146 @@
// This file was generated by gir (https://github.com/gtk-rs/gir)
// from gir-files (https://github.com/gtk-rs/gir-files)
// DO NOT EDIT
#[cfg(any(feature = "v1_14", feature = "dox"))]
use VideoCodecFrame;
use glib::StaticType;
use glib::Value;
use glib::object::Cast;
use glib::object::IsA;
use glib::signal::SignalHandlerId;
use glib::signal::connect_raw;
use glib::translate::*;
use glib_sys;
use gobject_sys;
use gst;
use gst_video_sys;
use std::boxed::Box as Box_;
use std::mem::transmute;
glib_wrapper! {
pub struct VideoEncoder(Object<gst_video_sys::GstVideoEncoder, gst_video_sys::GstVideoEncoderClass, VideoEncoderClass>) @extends gst::Element, gst::Object;
match fn {
get_type => || gst_video_sys::gst_video_encoder_get_type(),
}
}
unsafe impl Send for VideoEncoder {}
unsafe impl Sync for VideoEncoder {}
pub const NONE_VIDEO_ENCODER: Option<&VideoEncoder> = None;
pub trait VideoEncoderExt: 'static {
fn allocate_output_buffer(&self, size: usize) -> Option<gst::Buffer>;
//fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams);
#[cfg(any(feature = "v1_14", feature = "dox"))]
fn get_max_encode_time(&self, frame: &VideoCodecFrame) -> gst::ClockTimeDiff;
#[cfg(any(feature = "v1_14", feature = "dox"))]
fn is_qos_enabled(&self) -> bool;
fn merge_tags(&self, tags: Option<&gst::TagList>, mode: gst::TagMergeMode);
fn proxy_getcaps(&self, caps: Option<&gst::Caps>, filter: Option<&gst::Caps>) -> Option<gst::Caps>;
fn set_headers(&self, headers: &[&gst::Buffer]);
fn set_min_pts(&self, min_pts: gst::ClockTime);
#[cfg(any(feature = "v1_14", feature = "dox"))]
fn set_qos_enabled(&self, enabled: bool);
fn get_property_qos(&self) -> bool;
fn set_property_qos(&self, qos: bool);
fn connect_property_qos_notify<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId;
}
impl<O: IsA<VideoEncoder>> VideoEncoderExt for O {
fn allocate_output_buffer(&self, size: usize) -> Option<gst::Buffer> {
unsafe {
from_glib_full(gst_video_sys::gst_video_encoder_allocate_output_buffer(self.as_ref().to_glib_none().0, size))
}
}
//fn get_allocator(&self, allocator: /*Ignored*/gst::Allocator, params: /*Ignored*/gst::AllocationParams) {
// unsafe { TODO: call gst_video_sys:gst_video_encoder_get_allocator() }
//}
#[cfg(any(feature = "v1_14", feature = "dox"))]
fn get_max_encode_time(&self, frame: &VideoCodecFrame) -> gst::ClockTimeDiff {
unsafe {
gst_video_sys::gst_video_encoder_get_max_encode_time(self.as_ref().to_glib_none().0, frame.to_glib_none().0)
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
fn is_qos_enabled(&self) -> bool {
unsafe {
from_glib(gst_video_sys::gst_video_encoder_is_qos_enabled(self.as_ref().to_glib_none().0))
}
}
fn merge_tags(&self, tags: Option<&gst::TagList>, mode: gst::TagMergeMode) {
unsafe {
gst_video_sys::gst_video_encoder_merge_tags(self.as_ref().to_glib_none().0, tags.to_glib_none().0, mode.to_glib());
}
}
fn proxy_getcaps(&self, caps: Option<&gst::Caps>, filter: Option<&gst::Caps>) -> Option<gst::Caps> {
unsafe {
from_glib_full(gst_video_sys::gst_video_encoder_proxy_getcaps(self.as_ref().to_glib_none().0, caps.to_glib_none().0, filter.to_glib_none().0))
}
}
fn set_headers(&self, headers: &[&gst::Buffer]) {
unsafe {
gst_video_sys::gst_video_encoder_set_headers(self.as_ref().to_glib_none().0, headers.to_glib_full());
}
}
fn set_min_pts(&self, min_pts: gst::ClockTime) {
unsafe {
gst_video_sys::gst_video_encoder_set_min_pts(self.as_ref().to_glib_none().0, min_pts.to_glib());
}
}
#[cfg(any(feature = "v1_14", feature = "dox"))]
fn set_qos_enabled(&self, enabled: bool) {
unsafe {
gst_video_sys::gst_video_encoder_set_qos_enabled(self.as_ref().to_glib_none().0, enabled.to_glib());
}
}
fn get_property_qos(&self) -> bool {
unsafe {
let mut value = Value::from_type(<bool as StaticType>::static_type());
gobject_sys::g_object_get_property(self.to_glib_none().0 as *mut gobject_sys::GObject, b"qos\0".as_ptr() as *const _, value.to_glib_none_mut().0);
value.get().unwrap()
}
}
fn set_property_qos(&self, qos: bool) {
unsafe {
gobject_sys::g_object_set_property(self.to_glib_none().0 as *mut gobject_sys::GObject, b"qos\0".as_ptr() as *const _, Value::from(&qos).to_glib_none().0);
}
}
fn connect_property_qos_notify<F: Fn(&Self) + Send + Sync + 'static>(&self, f: F) -> SignalHandlerId {
unsafe {
let f: Box_<F> = Box_::new(f);
connect_raw(self.as_ptr() as *mut _, b"notify::qos\0".as_ptr() as *const _,
Some(transmute(notify_qos_trampoline::<Self, F> as usize)), Box_::into_raw(f))
}
}
}
unsafe extern "C" fn notify_qos_trampoline<P, F: Fn(&P) + Send + Sync + 'static>(this: *mut gst_video_sys::GstVideoEncoder, _param_spec: glib_sys::gpointer, f: glib_sys::gpointer)
where P: IsA<VideoEncoder> {
let f: &F = &*(f as *const F);
f(&VideoEncoder::from_glib_borrow(this).unsafe_cast())
}

View file

@ -81,6 +81,7 @@ pub use video_buffer_pool::{
mod video_codec_frame;
mod video_decoder;
mod video_encoder;
pub use video_codec_frame::VideoCodecFrame;
pub mod video_codec_state;
pub use video_codec_state::VideoCodecState;
@ -95,6 +96,7 @@ pub mod prelude {
pub use auto::traits::*;
pub use video_buffer_pool::VideoBufferPoolConfig;
pub use video_decoder::VideoDecoderExtManual;
pub use video_encoder::VideoEncoderExtManual;
pub use video_overlay::VideoOverlayExtManual;
}

View file

@ -8,7 +8,9 @@
#![cfg_attr(feature = "cargo-clippy", allow(cast_ptr_alignment))]
pub mod video_decoder;
pub mod video_encoder;
pub mod prelude {
pub use super::video_decoder::{VideoDecoderImpl, VideoDecoderImplExt};
pub use super::video_encoder::{VideoEncoderImpl, VideoEncoderImplExt};
}

View file

@ -0,0 +1,536 @@
// 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 video_codec_state::{Readable, VideoCodecState};
use VideoCodecFrame;
use VideoEncoder;
use VideoEncoderClass;
pub trait VideoEncoderImpl: VideoEncoderImplExt + ElementImpl + Send + Sync + 'static {
fn open(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
self.parent_open(element)
}
fn close(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
self.parent_close(element)
}
fn start(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
self.parent_start(element)
}
fn stop(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
self.parent_stop(element)
}
fn set_format(
&self,
element: &VideoEncoder,
state: &VideoCodecState<Readable>,
) -> Result<(), gst::LoggableError> {
self.parent_set_format(element, state)
}
fn handle_frame(
&self,
element: &VideoEncoder,
frame: VideoCodecFrame,
) -> Result<gst::FlowSuccess, gst::FlowError> {
self.parent_handle_frame(element, frame)
}
fn flush(&self, element: &VideoEncoder) -> bool {
self.parent_flush(element)
}
fn propose_allocation(
&self,
element: &VideoEncoder,
query: &mut gst::QueryRef,
) -> Result<(), gst::ErrorMessage> {
self.parent_propose_allocation(element, query)
}
fn decide_allocation(
&self,
element: &VideoEncoder,
query: &mut gst::QueryRef,
) -> Result<(), gst::ErrorMessage> {
self.parent_decide_allocation(element, query)
}
}
pub trait VideoEncoderImplExt {
fn parent_open(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>;
fn parent_close(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>;
fn parent_start(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>;
fn parent_stop(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage>;
fn parent_set_format(
&self,
element: &VideoEncoder,
state: &VideoCodecState<Readable>,
) -> Result<(), gst::LoggableError>;
fn parent_handle_frame(
&self,
element: &VideoEncoder,
frame: VideoCodecFrame,
) -> Result<gst::FlowSuccess, gst::FlowError>;
fn parent_flush(&self, element: &VideoEncoder) -> bool;
fn parent_propose_allocation(
&self,
element: &VideoEncoder,
query: &mut gst::QueryRef,
) -> Result<(), gst::ErrorMessage>;
fn parent_decide_allocation(
&self,
element: &VideoEncoder,
query: &mut gst::QueryRef,
) -> Result<(), gst::ErrorMessage>;
}
impl<T: VideoEncoderImpl + ObjectImpl> VideoEncoderImplExt for T {
fn parent_open(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass;
(*parent_class)
.open
.map(|f| {
if from_glib(f(element.to_glib_none().0)) {
Ok(())
} else {
Err(gst_error_msg!(
gst::CoreError::StateChange,
["Parent function `open` failed"]
))
}
})
.unwrap_or(Ok(()))
}
}
fn parent_close(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass;
(*parent_class)
.close
.map(|f| {
if from_glib(f(element.to_glib_none().0)) {
Ok(())
} else {
Err(gst_error_msg!(
gst::CoreError::StateChange,
["Parent function `close` failed"]
))
}
})
.unwrap_or(Ok(()))
}
}
fn parent_start(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass;
(*parent_class)
.start
.map(|f| {
if from_glib(f(element.to_glib_none().0)) {
Ok(())
} else {
Err(gst_error_msg!(
gst::CoreError::StateChange,
["Parent function `start` failed"]
))
}
})
.unwrap_or(Ok(()))
}
}
fn parent_stop(&self, element: &VideoEncoder) -> Result<(), gst::ErrorMessage> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass;
(*parent_class)
.stop
.map(|f| {
if from_glib(f(element.to_glib_none().0)) {
Ok(())
} else {
Err(gst_error_msg!(
gst::CoreError::StateChange,
["Parent function `stop` failed"]
))
}
})
.unwrap_or(Ok(()))
}
}
fn parent_set_format(
&self,
element: &VideoEncoder,
state: &VideoCodecState<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::GstVideoEncoderClass;
(*parent_class)
.set_format
.map(|f| {
gst_result_from_gboolean!(
f(element.to_glib_none().0, state.as_mut_ptr()),
gst::CAT_RUST,
"parent function `set_format` failed"
)
})
.unwrap_or(Ok(()))
}
}
fn parent_handle_frame(
&self,
element: &VideoEncoder,
frame: VideoCodecFrame,
) -> Result<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::GstVideoEncoderClass;
(*parent_class)
.handle_frame
.map(|f| {
gst::FlowReturn::from_glib(f(element.to_glib_none().0, frame.to_glib_none().0))
})
.unwrap_or(gst::FlowReturn::Error)
.into_result()
}
}
fn parent_flush(&self, element: &VideoEncoder) -> bool {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass;
(*parent_class)
.flush
.map(|f| from_glib(f(element.to_glib_none().0)))
.unwrap_or(false)
}
}
fn parent_propose_allocation(
&self,
element: &VideoEncoder,
query: &mut gst::QueryRef,
) -> Result<(), gst::ErrorMessage> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass;
(*parent_class)
.propose_allocation
.map(|f| {
if from_glib(f(element.to_glib_none().0, query.as_mut_ptr())) {
Ok(())
} else {
Err(gst_error_msg!(
gst::CoreError::StateChange,
["Parent function `propose_allocation` failed"]
))
}
})
.unwrap_or(Ok(()))
}
}
fn parent_decide_allocation(
&self,
element: &VideoEncoder,
query: &mut gst::QueryRef,
) -> Result<(), gst::ErrorMessage> {
unsafe {
let data = self.get_type_data();
let parent_class =
data.as_ref().get_parent_class() as *mut gst_video_sys::GstVideoEncoderClass;
(*parent_class)
.decide_allocation
.map(|f| {
if from_glib(f(element.to_glib_none().0, query.as_mut_ptr())) {
Ok(())
} else {
Err(gst_error_msg!(
gst::CoreError::StateChange,
["Parent function `decide_allocation` failed"]
))
}
})
.unwrap_or(Ok(()))
}
}
}
unsafe impl<T: ObjectSubclass + VideoEncoderImpl> IsSubclassable<T> for VideoEncoderClass
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::GstVideoEncoderClass);
klass.open = Some(video_encoder_open::<T>);
klass.close = Some(video_encoder_close::<T>);
klass.start = Some(video_encoder_start::<T>);
klass.stop = Some(video_encoder_stop::<T>);
klass.set_format = Some(video_encoder_set_format::<T>);
klass.handle_frame = Some(video_encoder_handle_frame::<T>);
klass.flush = Some(video_encoder_flush::<T>);
klass.propose_allocation = Some(video_encoder_propose_allocation::<T>);
klass.decide_allocation = Some(video_encoder_decide_allocation::<T>);
}
}
}
unsafe extern "C" fn video_encoder_open<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.open(&wrap) {
Ok(()) => true,
Err(err) => {
wrap.post_error_message(&err);
false
}
}
})
.to_glib()
}
unsafe extern "C" fn video_encoder_close<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.close(&wrap) {
Ok(()) => true,
Err(err) => {
wrap.post_error_message(&err);
false
}
}
})
.to_glib()
}
unsafe extern "C" fn video_encoder_start<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.start(&wrap) {
Ok(()) => true,
Err(err) => {
wrap.post_error_message(&err);
false
}
}
})
.to_glib()
}
unsafe extern "C" fn video_encoder_stop<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.stop(&wrap) {
Ok(()) => true,
Err(err) => {
wrap.post_error_message(&err);
false
}
}
})
.to_glib()
}
unsafe extern "C" fn video_encoder_set_format<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
state: *mut gst_video_sys::GstVideoCodecState,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
let mut wrap_state = VideoCodecState::<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_encoder_handle_frame<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
frame: *mut gst_video_sys::GstVideoCodecFrame,
) -> gst_sys::GstFlowReturn
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
let wrap_frame = VideoCodecFrame::new(frame, &wrap);
gst_panic_to_error!(&wrap, &instance.panicked(), gst::FlowReturn::Error, {
imp.handle_frame(&wrap, wrap_frame).into()
})
.to_glib()
}
unsafe extern "C" fn video_encoder_flush<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
VideoEncoderImpl::flush(imp, &wrap)
})
.to_glib()
}
unsafe extern "C" fn video_encoder_propose_allocation<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
query: *mut gst_sys::GstQuery,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
let query = gst::QueryRef::from_mut_ptr(query);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.propose_allocation(&wrap, query) {
Ok(()) => true,
Err(err) => {
wrap.post_error_message(&err);
false
}
}
})
.to_glib()
}
unsafe extern "C" fn video_encoder_decide_allocation<T: ObjectSubclass>(
ptr: *mut gst_video_sys::GstVideoEncoder,
query: *mut gst_sys::GstQuery,
) -> glib_sys::gboolean
where
T: VideoEncoderImpl,
T::Instance: PanicPoison,
{
glib_floating_reference_guard!(ptr);
let instance = &*(ptr as *mut T::Instance);
let imp = instance.get_impl();
let wrap: VideoEncoder = from_glib_borrow(ptr);
let query = gst::QueryRef::from_mut_ptr(query);
gst_panic_to_error!(&wrap, &instance.panicked(), false, {
match imp.decide_allocation(&wrap, query) {
Ok(()) => true,
Err(err) => {
wrap.post_error_message(&err);
false
}
}
})
.to_glib()
}

View file

@ -0,0 +1,214 @@
// Copyright (C) 2019 Philippe Normand <philn@igalia.com>
// Copyright (C) 2019 Guillaume Desmottes <guillaume.desmottes@collabora.com>
// Copyright (C) 2019 Sebastian Dröge <sebastian@centricular.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 VideoEncoder;
pub trait VideoEncoderExtManual: 'static {
#[cfg(any(feature = "v1_12", feature = "dox"))]
fn allocate_output_frame(
&self,
frame: &VideoCodecFrame,
size: usize,
) -> 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 finish_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,
caps: gst::Caps,
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<VideoEncoder>> VideoEncoderExtManual for O {
#[cfg(any(feature = "v1_12", feature = "dox"))]
fn allocate_output_frame(
&self,
frame: &VideoCodecFrame,
size: usize,
) -> Result<gst::FlowSuccess, gst::FlowError> {
let ret: gst::FlowReturn = unsafe {
from_glib(gst_video_sys::gst_video_encoder_allocate_output_frame(
self.as_ref().to_glib_none().0,
frame.to_glib_none().0,
size,
))
};
ret.into_result()
}
fn finish_frame(&self, frame: VideoCodecFrame) -> Result<gst::FlowSuccess, gst::FlowError> {
let ret: gst::FlowReturn = unsafe {
from_glib(gst_video_sys::gst_video_encoder_finish_frame(
self.as_ref().to_glib_none().0,
frame.into_ptr(),
))
};
ret.into_result()
}
fn get_latency(&self) -> (gst::ClockTime, gst::ClockTime) {
let mut min_latency = gst_sys::GST_CLOCK_TIME_NONE;
let mut max_latency = gst_sys::GST_CLOCK_TIME_NONE;
unsafe {
gst_video_sys::gst_video_encoder_get_latency(
self.as_ref().to_glib_none().0,
&mut min_latency,
&mut max_latency,
);
(from_glib(min_latency), from_glib(max_latency))
}
}
fn set_latency(&self, min_latency: gst::ClockTime, max_latency: gst::ClockTime) {
unsafe {
gst_video_sys::gst_video_encoder_set_latency(
self.as_ref().to_glib_none().0,
min_latency.to_glib(),
max_latency.to_glib(),
);
}
}
fn get_frame(&self, frame_number: i32) -> Option<VideoCodecFrame> {
let frame = unsafe {
gst_video_sys::gst_video_encoder_get_frame(self.as_ref().to_glib_none().0, frame_number)
};
if frame.is_null() {
None
} else {
unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) }
}
}
fn get_frames(&self) -> Vec<VideoCodecFrame> {
unsafe {
let frames =
gst_video_sys::gst_video_encoder_get_frames(self.as_ref().to_glib_none().0);
let mut iter: *const glib_sys::GList = frames;
let mut vec = Vec::new();
while !iter.is_null() {
let frame_ptr = Ptr::from((*iter).data);
/* transfer ownership of the frame */
let frame = VideoCodecFrame::new(frame_ptr, self.as_ref());
vec.push(frame);
iter = (*iter).next;
}
glib_sys::g_list_free(frames);
vec
}
}
fn get_oldest_frame(&self) -> Option<VideoCodecFrame> {
let frame = unsafe {
gst_video_sys::gst_video_encoder_get_oldest_frame(self.as_ref().to_glib_none().0)
};
if frame.is_null() {
None
} else {
unsafe { Some(VideoCodecFrame::new(frame, self.as_ref())) }
}
}
fn get_output_state(&self) -> Option<VideoCodecState<Readable>> {
let state = unsafe {
gst_video_sys::gst_video_encoder_get_output_state(self.as_ref().to_glib_none().0)
};
if state.is_null() {
None
} else {
unsafe { Some(VideoCodecState::<Readable>::new(state)) }
}
}
fn set_output_state(
&self,
caps: gst::Caps,
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_encoder_set_output_state(
self.as_ref().to_glib_none().0,
caps.into_ptr(),
reference,
)
};
if state.is_null() {
Err(gst::FlowError::Error)
} else {
unsafe { Ok(VideoCodecState::<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_encoder_negotiate(
self.as_ref().to_glib_none().0,
))
};
if ret {
Ok(())
} else {
Err(gst::FlowError::NotNegotiated)
}
}
}
impl HasStreamLock for VideoEncoder {
fn get_stream_lock(&self) -> *mut glib_sys::GRecMutex {
let encoder_sys: *const gstreamer_video_sys::GstVideoEncoder = self.to_glib_none().0;
unsafe { &(*encoder_sys).stream_lock as *const _ as usize as *mut _ }
}
fn get_element_as_ptr(&self) -> *const gst_sys::GstElement {
let encoder_sys: *const gstreamer_video_sys::GstVideoEncoder = self.to_glib_none().0;
encoder_sys as *const gst_sys::GstElement
}
}