ges: Allow subclassing GESFormatter

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer-rs/-/merge_requests/1290>
This commit is contained in:
Thibault Saunier 2023-06-15 18:13:23 -04:00 committed by Sebastian Dröge
parent 9b2a0f55c7
commit 559720693d
4 changed files with 454 additions and 0 deletions

View 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(),
);
}
}
}

View file

@ -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;
}

View 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());
}
}

View 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};
}