forked from mirrors/gstreamer-rs
video: Add VideoEncoder bindings
Fixes https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/issues/160
This commit is contained in:
parent
4096869075
commit
7a0bc76668
7 changed files with 961 additions and 3 deletions
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
146
gstreamer-video/src/auto/video_encoder.rs
Normal file
146
gstreamer-video/src/auto/video_encoder.rs
Normal 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())
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
536
gstreamer-video/src/subclass/video_encoder.rs
Normal file
536
gstreamer-video/src/subclass/video_encoder.rs
Normal 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()
|
||||
}
|
214
gstreamer-video/src/video_encoder.rs
Normal file
214
gstreamer-video/src/video_encoder.rs
Normal 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
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue