// This example demonstrates how custom GstMeta can be defined and used on buffers. // // It simply attaches a GstMeta with a Rust String to buffers that are passed into // an appsrc and retrieves them again from an appsink. #![allow(clippy::non_send_fields_in_send_ty)] use gst::{element_error, prelude::*}; #[path = "../examples-common.rs"] mod examples_common; mod custom_meta { use std::{fmt, mem}; use gst::prelude::*; // Public Rust type for the custom meta. #[repr(transparent)] pub struct CustomMeta(imp::CustomMeta); // Metas must be Send+Sync. unsafe impl Send for CustomMeta {} unsafe impl Sync for CustomMeta {} impl CustomMeta { // Add a new custom meta to the buffer with the given label. pub fn add( buffer: &mut gst::BufferRef, label: String, ) -> gst::MetaRefMut { unsafe { // Manually dropping because gst_buffer_add_meta() takes ownership of the // content of the struct. let mut params = mem::ManuallyDrop::new(imp::CustomMetaParams { label }); // The label is passed through via the params to custom_meta_init(). let meta = gst::ffi::gst_buffer_add_meta( buffer.as_mut_ptr(), imp::custom_meta_get_info(), &mut *params as *mut imp::CustomMetaParams as glib::ffi::gpointer, ) as *mut imp::CustomMeta; Self::from_mut_ptr(buffer, meta) } } // Retrieve the stored label. pub fn label(&self) -> &str { self.0.label.as_str() } } // Trait to allow using the gst::Buffer API with this meta. unsafe impl MetaAPI for CustomMeta { type GstType = imp::CustomMeta; fn meta_api() -> glib::Type { imp::custom_meta_api_get_type() } } impl fmt::Debug for CustomMeta { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("CustomMeta") .field("label", &self.label()) .finish() } } // Actual unsafe implementation of the meta. mod imp { use std::{mem, ptr}; use glib::once_cell::sync::Lazy; use glib::translate::*; pub(super) struct CustomMetaParams { pub label: String, } // This is the C type that is actually stored as meta inside the buffers. #[repr(C)] pub struct CustomMeta { parent: gst::ffi::GstMeta, pub(super) label: String, } // Function to register the meta API and get a type back. pub(super) fn custom_meta_api_get_type() -> glib::Type { static TYPE: Lazy = Lazy::new(|| unsafe { let t = from_glib(gst::ffi::gst_meta_api_type_register( b"MyCustomMetaAPI\0".as_ptr() as *const _, // We provide no tags here as our meta is just a label and does // not refer to any specific aspect of the buffer. [ptr::null::()].as_ptr() as *mut *const _, )); assert_ne!(t, glib::Type::INVALID); t }); *TYPE } // Initialization function for our meta. This needs to ensure all fields are correctly // initialized. They will contain random memory before. unsafe extern "C" fn custom_meta_init( meta: *mut gst::ffi::GstMeta, params: glib::ffi::gpointer, _buffer: *mut gst::ffi::GstBuffer, ) -> glib::ffi::gboolean { assert!(!params.is_null()); let meta = &mut *(meta as *mut CustomMeta); let params = ptr::read(params as *const CustomMetaParams); // Need to initialize all our fields correctly here. ptr::write(&mut meta.label, params.label); true.into_glib() } // Free function for our meta. This needs to free/drop all memory we allocated. unsafe extern "C" fn custom_meta_free( meta: *mut gst::ffi::GstMeta, _buffer: *mut gst::ffi::GstBuffer, ) { let meta = &mut *(meta as *mut CustomMeta); // Need to free/drop all our fields here. ptr::drop_in_place(&mut meta.label); } // Transform function for our meta. This needs to get it from the old buffer to the new one // in a way that is compatible with the transformation type. In this case we just always // copy it over. unsafe extern "C" fn custom_meta_transform( dest: *mut gst::ffi::GstBuffer, meta: *mut gst::ffi::GstMeta, _buffer: *mut gst::ffi::GstBuffer, _type_: glib::ffi::GQuark, _data: glib::ffi::gpointer, ) -> glib::ffi::gboolean { let meta = &*(meta as *mut CustomMeta); // We simply copy over our meta here. Other metas might have to look at the type // and do things conditional on that, or even just drop the meta. super::CustomMeta::add(gst::BufferRef::from_mut_ptr(dest), meta.label.clone()); true.into_glib() } // Register the meta itself with its functions. pub(super) fn custom_meta_get_info() -> *const gst::ffi::GstMetaInfo { struct MetaInfo(ptr::NonNull); unsafe impl Send for MetaInfo {} unsafe impl Sync for MetaInfo {} static META_INFO: Lazy = Lazy::new(|| unsafe { MetaInfo( ptr::NonNull::new(gst::ffi::gst_meta_register( custom_meta_api_get_type().into_glib(), b"MyCustomMeta\0".as_ptr() as *const _, mem::size_of::(), Some(custom_meta_init), Some(custom_meta_free), Some(custom_meta_transform), ) as *mut gst::ffi::GstMetaInfo) .expect("Failed to register meta API"), ) }); META_INFO.0.as_ptr() } } } fn example_main() { gst::init().unwrap(); // This creates a pipeline with appsrc and appsink. let pipeline = gst::Pipeline::default(); let appsrc = gst_app::AppSrc::builder().build(); let appsink = gst_app::AppSink::builder().build(); pipeline.add(&appsrc).unwrap(); pipeline.add(&appsink).unwrap(); appsrc.link(&appsink).unwrap(); // Our buffer counter, that is stored in the mutable environment // of the closure of the need-data callback. let mut i = 0; appsrc.set_callbacks( gst_app::AppSrcCallbacks::builder() .need_data(move |appsrc, _| { // We only produce 5 buffers. if i == 5 { let _ = appsrc.end_of_stream(); return; } println!("Producing buffer {i}"); // Add a custom meta with a label to this buffer. let mut buffer = gst::Buffer::new(); { let buffer = buffer.get_mut().unwrap(); custom_meta::CustomMeta::add(buffer, format!("This is buffer {i}")); } i += 1; // appsrc already handles the error here for us. let _ = appsrc.push_buffer(buffer); }) .build(), ); // Getting data out of the appsink is done by setting callbacks on it. // The appsink will then call those handlers, as soon as data is available. appsink.set_callbacks( gst_app::AppSinkCallbacks::builder() // Add a handler to the "new-sample" signal. .new_sample(|appsink| { // Pull the sample in question out of the appsink's buffer. let sample = appsink.pull_sample().map_err(|_| gst::FlowError::Eos)?; let buffer = sample.buffer().ok_or_else(|| { element_error!( appsink, gst::ResourceError::Failed, ("Failed to get buffer from appsink") ); gst::FlowError::Error })?; // Retrieve the custom meta from the buffer and print it. let meta = buffer .meta::() .expect("No custom meta found"); println!("Got buffer with label: {}", meta.label()); Ok(gst::FlowSuccess::Ok) }) .build(), ); // Actually start the pipeline. pipeline .set_state(gst::State::Playing) .expect("Unable to set the pipeline to the `Playing` state"); let pipeline = pipeline.dynamic_cast::().unwrap(); let bus = pipeline .bus() .expect("Pipeline without bus. Shouldn't happen!"); // And run until EOS or an error happened. for msg in bus.iter_timed(gst::ClockTime::NONE) { use gst::MessageView; match msg.view() { MessageView::Eos(..) => break, MessageView::Error(err) => { println!( "Error from {:?}: {} ({:?})", err.src().map(|s| s.path_string()), err.error(), err.debug() ); break; } _ => (), } } // Finally shut down everything. pipeline .set_state(gst::State::Null) .expect("Unable to set the pipeline to the `Null` state"); } fn main() { // tutorials_common::run is only required to set up the application environment on macOS // (but not necessary in normal Cocoa applications where this is set up automatically). examples_common::run(example_main); }