forked from mirrors/gstreamer-rs
ges: Allow subclassing GESFormatter
Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1278>
This commit is contained in:
parent
7c67375d99
commit
7f7c7a4165
4 changed files with 454 additions and 0 deletions
73
gstreamer-editing-services/src/formatter.rs
Normal file
73
gstreamer-editing-services/src/formatter.rs
Normal file
|
@ -0,0 +1,73 @@
|
|||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
use crate::prelude::*;
|
||||
use gst::glib::translate::*;
|
||||
|
||||
pub trait FormatterExtManual: 'static {
|
||||
fn can_load_uri(&self, uri: &str) -> Result<(), glib::Error>;
|
||||
#[doc(alias = "ges_formatter_class_register_metas")]
|
||||
fn register(
|
||||
type_: glib::types::Type,
|
||||
name: &str,
|
||||
description: Option<&str>,
|
||||
extensions: Option<&str>,
|
||||
caps: Option<&str>,
|
||||
version: f64,
|
||||
rank: gst::Rank,
|
||||
);
|
||||
}
|
||||
|
||||
impl<O: IsA<crate::Formatter>> FormatterExtManual for O {
|
||||
fn can_load_uri(&self, uri: &str) -> Result<(), glib::Error> {
|
||||
unsafe {
|
||||
let klass = self.class_of::<crate::Formatter>().unwrap();
|
||||
|
||||
let f = klass.as_ref().can_load_uri.ok_or_else(|| {
|
||||
glib::Error::new(gst::CoreError::Failed, "No `can_load_uri` method defined")
|
||||
})?;
|
||||
|
||||
let mut err = std::ptr::null_mut();
|
||||
let res = f(
|
||||
self.as_ref().to_glib_none().0,
|
||||
uri.to_glib_none().0,
|
||||
&mut err,
|
||||
);
|
||||
|
||||
if res == glib::ffi::GTRUE {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(from_glib_full(err))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(alias = "ges_formatter_class_register_metas")]
|
||||
fn register(
|
||||
type_: glib::types::Type,
|
||||
name: &str,
|
||||
description: Option<&str>,
|
||||
extensions: Option<&str>,
|
||||
caps: Option<&str>,
|
||||
version: f64,
|
||||
rank: gst::Rank,
|
||||
) {
|
||||
skip_assert_initialized!();
|
||||
|
||||
unsafe {
|
||||
let klass = mut_override(
|
||||
gst::glib::Class::<crate::Formatter>::from_type(type_)
|
||||
.unwrap()
|
||||
.as_ref(),
|
||||
);
|
||||
|
||||
ffi::ges_formatter_class_register_metas(
|
||||
klass,
|
||||
name.to_glib_none().0,
|
||||
description.to_glib_none().0,
|
||||
extensions.to_glib_none().0,
|
||||
caps.to_glib_none().0,
|
||||
version,
|
||||
rank.into_glib(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -54,7 +54,9 @@ macro_rules! skip_assert_initialized {
|
|||
#[allow(deprecated)]
|
||||
#[allow(unused_imports)]
|
||||
mod auto;
|
||||
mod formatter;
|
||||
pub use crate::auto::*;
|
||||
pub mod subclass;
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod flag_serde;
|
||||
|
@ -65,9 +67,12 @@ pub mod prelude {
|
|||
#[doc(hidden)]
|
||||
pub use gio::prelude::*;
|
||||
#[doc(hidden)]
|
||||
pub use glib::prelude::*;
|
||||
#[doc(hidden)]
|
||||
pub use gst_base::prelude::*;
|
||||
#[doc(hidden)]
|
||||
pub use gst_pbutils::prelude::*;
|
||||
|
||||
pub use crate::auto::traits::*;
|
||||
pub use crate::formatter::FormatterExtManual;
|
||||
}
|
||||
|
|
363
gstreamer-editing-services/src/subclass/formatter.rs
Normal file
363
gstreamer-editing-services/src/subclass/formatter.rs
Normal file
|
@ -0,0 +1,363 @@
|
|||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
use crate::prelude::*;
|
||||
use crate::Formatter;
|
||||
use glib::{subclass::prelude::*, translate::*};
|
||||
|
||||
pub trait FormatterImpl: FormatterImplExt + ObjectImpl + Send + Sync {
|
||||
fn can_load_uri(&self, uri: &str) -> Result<(), glib::Error> {
|
||||
self.parent_can_load_uri(uri)
|
||||
}
|
||||
|
||||
fn load_from_uri(&self, timeline: &crate::Timeline, uri: &str) -> Result<(), glib::Error> {
|
||||
self.parent_load_from_uri(timeline, uri)
|
||||
}
|
||||
|
||||
fn save_to_uri(
|
||||
&self,
|
||||
timeline: &crate::Timeline,
|
||||
uri: &str,
|
||||
overwrite: bool,
|
||||
) -> Result<(), glib::Error> {
|
||||
self.parent_save_to_uri(timeline, uri, overwrite)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait FormatterImplExt: ObjectSubclass {
|
||||
fn parent_can_load_uri(&self, uri: &str) -> Result<(), glib::Error>;
|
||||
|
||||
fn parent_load_from_uri(
|
||||
&self,
|
||||
timeline: &crate::Timeline,
|
||||
uri: &str,
|
||||
) -> Result<(), glib::Error>;
|
||||
|
||||
fn parent_save_to_uri(
|
||||
&self,
|
||||
timeline: &crate::Timeline,
|
||||
uri: &str,
|
||||
overwrite: bool,
|
||||
) -> Result<(), glib::Error>;
|
||||
}
|
||||
|
||||
impl<T: FormatterImpl> FormatterImplExt for T {
|
||||
fn parent_can_load_uri(&self, uri: &str) -> Result<(), glib::Error> {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut ffi::GESFormatterClass;
|
||||
|
||||
let f = (*parent_class)
|
||||
.can_load_uri
|
||||
.expect("Missing parent function `can_load_uri`");
|
||||
|
||||
let mut error = std::ptr::null_mut();
|
||||
let res = f(
|
||||
self.obj()
|
||||
.unsafe_cast_ref::<crate::Formatter>()
|
||||
.to_glib_none()
|
||||
.0,
|
||||
uri.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
|
||||
if res == glib::ffi::GFALSE {
|
||||
if error.is_null() {
|
||||
Err(glib::Error::new(
|
||||
gst::CoreError::Failed,
|
||||
"Can load uri failed",
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parent_load_from_uri(
|
||||
&self,
|
||||
timeline: &crate::Timeline,
|
||||
uri: &str,
|
||||
) -> Result<(), glib::Error> {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut ffi::GESFormatterClass;
|
||||
|
||||
let f = (*parent_class)
|
||||
.load_from_uri
|
||||
.expect("Missing parent function `load_from_uri`");
|
||||
|
||||
let mut error = std::ptr::null_mut();
|
||||
let res = f(
|
||||
self.obj()
|
||||
.unsafe_cast_ref::<crate::Formatter>()
|
||||
.to_glib_none()
|
||||
.0,
|
||||
timeline
|
||||
.unsafe_cast_ref::<crate::Timeline>()
|
||||
.to_glib_none()
|
||||
.0,
|
||||
uri.to_glib_none().0,
|
||||
&mut error,
|
||||
);
|
||||
|
||||
if res == glib::ffi::GFALSE {
|
||||
if error.is_null() {
|
||||
Err(glib::Error::new(
|
||||
gst::CoreError::Failed,
|
||||
"Load from uri failed",
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
fn parent_save_to_uri(
|
||||
&self,
|
||||
timeline: &crate::Timeline,
|
||||
uri: &str,
|
||||
overwrite: bool,
|
||||
) -> Result<(), glib::Error> {
|
||||
unsafe {
|
||||
let data = Self::type_data();
|
||||
let parent_class = data.as_ref().parent_class() as *mut ffi::GESFormatterClass;
|
||||
|
||||
let f = (*parent_class)
|
||||
.save_to_uri
|
||||
.expect("Missing parent function `save_to_uri`");
|
||||
|
||||
let mut error = std::ptr::null_mut();
|
||||
let res = f(
|
||||
self.obj()
|
||||
.unsafe_cast_ref::<crate::Formatter>()
|
||||
.to_glib_none()
|
||||
.0,
|
||||
timeline
|
||||
.unsafe_cast_ref::<crate::Timeline>()
|
||||
.to_glib_none()
|
||||
.0,
|
||||
uri.to_glib_none().0,
|
||||
overwrite.into_glib(),
|
||||
&mut error,
|
||||
);
|
||||
|
||||
if res == glib::ffi::GFALSE {
|
||||
if error.is_null() {
|
||||
Err(glib::Error::new(
|
||||
gst::CoreError::Failed,
|
||||
"Save to uri failed",
|
||||
))
|
||||
} else {
|
||||
Err(from_glib_full(error))
|
||||
}
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<T: FormatterImpl> IsSubclassable<T> for Formatter {
|
||||
fn class_init(klass: &mut glib::Class<Self>) {
|
||||
Self::parent_class_init::<T>(klass);
|
||||
let klass = klass.as_mut();
|
||||
klass.can_load_uri = Some(formatter_can_load_uri::<T>);
|
||||
klass.load_from_uri = Some(formatter_load_from_uri::<T>);
|
||||
klass.save_to_uri = Some(formatter_save_to_uri::<T>);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn formatter_can_load_uri<T: FormatterImpl>(
|
||||
ptr: *mut ffi::GESFormatter,
|
||||
uri: *const libc::c_char,
|
||||
error: *mut *mut glib::ffi::GError,
|
||||
) -> glib::ffi::gboolean {
|
||||
let instance = &*(ptr as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
|
||||
match imp.can_load_uri(glib::GString::from_glib_borrow(uri).as_str()) {
|
||||
Err(err) => {
|
||||
if !error.is_null() {
|
||||
*error = err.into_glib_ptr();
|
||||
}
|
||||
|
||||
glib::ffi::GFALSE
|
||||
}
|
||||
Ok(_) => glib::ffi::GTRUE,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn formatter_load_from_uri<T: FormatterImpl>(
|
||||
ptr: *mut ffi::GESFormatter,
|
||||
timeline: *mut ffi::GESTimeline,
|
||||
uri: *const libc::c_char,
|
||||
error: *mut *mut glib::ffi::GError,
|
||||
) -> glib::ffi::gboolean {
|
||||
let instance = &*(ptr as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
let timeline = from_glib_borrow(timeline);
|
||||
|
||||
match imp.load_from_uri(&timeline, glib::GString::from_glib_borrow(uri).as_str()) {
|
||||
Err(err) => {
|
||||
if !error.is_null() {
|
||||
*error = err.into_glib_ptr();
|
||||
}
|
||||
|
||||
glib::ffi::GFALSE
|
||||
}
|
||||
Ok(_) => glib::ffi::GTRUE,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn formatter_save_to_uri<T: FormatterImpl>(
|
||||
ptr: *mut ffi::GESFormatter,
|
||||
timeline: *mut ffi::GESTimeline,
|
||||
uri: *const libc::c_char,
|
||||
overwrite: glib::ffi::gboolean,
|
||||
error: *mut *mut glib::ffi::GError,
|
||||
) -> glib::ffi::gboolean {
|
||||
let instance = &*(ptr as *mut T::Instance);
|
||||
let imp = instance.imp();
|
||||
let timeline = from_glib_borrow(timeline);
|
||||
|
||||
match imp.save_to_uri(
|
||||
&timeline,
|
||||
glib::GString::from_glib_borrow(uri).as_str(),
|
||||
from_glib(overwrite),
|
||||
) {
|
||||
Err(err) => {
|
||||
if !error.is_null() {
|
||||
*error = err.into_glib_ptr();
|
||||
}
|
||||
|
||||
glib::ffi::GFALSE
|
||||
}
|
||||
Ok(_) => glib::ffi::GTRUE,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::Formatter;
|
||||
|
||||
pub mod imp {
|
||||
use super::*;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct SimpleFormatter;
|
||||
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for SimpleFormatter {
|
||||
const NAME: &'static str = "SimpleFormatter";
|
||||
type Type = super::SimpleFormatter;
|
||||
type ParentType = Formatter;
|
||||
}
|
||||
impl ObjectImpl for SimpleFormatter {}
|
||||
impl FormatterImpl for SimpleFormatter {
|
||||
fn can_load_uri(&self, uri: &str) -> Result<(), glib::Error> {
|
||||
if uri.starts_with("ges:test") {
|
||||
Ok(())
|
||||
} else {
|
||||
self.parent_can_load_uri(uri)
|
||||
}
|
||||
}
|
||||
|
||||
fn load_from_uri(
|
||||
&self,
|
||||
timeline: &crate::Timeline,
|
||||
_uri: &str,
|
||||
) -> Result<(), glib::Error> {
|
||||
timeline.append_layer();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_to_uri(
|
||||
&self,
|
||||
timeline: &crate::Timeline,
|
||||
uri: &str,
|
||||
_overwrite: bool,
|
||||
) -> Result<(), glib::Error> {
|
||||
unsafe { timeline.set_data("saved", uri.to_string()) };
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct SimpleFormatter(ObjectSubclass<imp::SimpleFormatter>) @extends Formatter, gst::Object;
|
||||
}
|
||||
|
||||
impl SimpleFormatter {
|
||||
pub fn new() -> Self {
|
||||
glib::Object::builder().build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SimpleFormatter {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_formatter_subclass() {
|
||||
crate::init().unwrap();
|
||||
|
||||
let formatter = SimpleFormatter::new();
|
||||
formatter
|
||||
.can_load_uri("ges:test:")
|
||||
.expect("We can load anything...");
|
||||
|
||||
assert!(formatter.can_load_uri("nottest").is_err());
|
||||
|
||||
let timeline = crate::Timeline::new();
|
||||
assert_eq!(timeline.layers().len(), 0);
|
||||
#[allow(deprecated)]
|
||||
formatter
|
||||
.load_from_uri(&timeline, "test")
|
||||
.expect("We can load anything...");
|
||||
assert_eq!(timeline.layers().len(), 1);
|
||||
|
||||
unsafe {
|
||||
assert_eq!(timeline.data::<Option<String>>("saved"), None);
|
||||
}
|
||||
#[allow(deprecated)]
|
||||
formatter
|
||||
.save_to_uri(&timeline, "test", false)
|
||||
.expect("We can save anything...");
|
||||
unsafe {
|
||||
assert_eq!(
|
||||
timeline.data::<String>("saved").unwrap().as_ref(),
|
||||
&"test".to_string()
|
||||
);
|
||||
}
|
||||
|
||||
Formatter::register(
|
||||
SimpleFormatter::static_type(),
|
||||
"SimpleFormatter",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
1.0,
|
||||
gst::Rank::Primary,
|
||||
);
|
||||
|
||||
let proj = crate::Project::new(Some("ges:test:"));
|
||||
let timeline = proj
|
||||
.extract()
|
||||
.unwrap()
|
||||
.downcast::<crate::Timeline>()
|
||||
.unwrap();
|
||||
assert_eq!(timeline.layers().len(), 1);
|
||||
|
||||
let proj = crate::Project::new(Some("ges:notest:"));
|
||||
assert!(proj.extract().is_err());
|
||||
}
|
||||
}
|
13
gstreamer-editing-services/src/subclass/mod.rs
Normal file
13
gstreamer-editing-services/src/subclass/mod.rs
Normal file
|
@ -0,0 +1,13 @@
|
|||
// Take a look at the license at the top of the repository in the LICENSE file.
|
||||
|
||||
#![allow(clippy::cast_ptr_alignment)]
|
||||
|
||||
mod formatter;
|
||||
|
||||
pub mod prelude {
|
||||
#[doc(hidden)]
|
||||
pub use glib::subclass::prelude::*;
|
||||
pub use gst::subclass::prelude::*;
|
||||
|
||||
pub use super::formatter::{FormatterImpl, FormatterImplExt};
|
||||
}
|
Loading…
Reference in a new issue