| 1 | // Take a look at the license at the top of the repository in the LICENSE file. |
| 2 | |
| 3 | use std::ptr; |
| 4 | |
| 5 | use glib::{prelude::*, subclass::prelude::*, translate::*}; |
| 6 | use gst::prelude::*; |
| 7 | |
| 8 | use super::base_src::{BaseSrcImpl, CreateSuccess}; |
| 9 | use crate::{ffi, prelude::*, PushSrc}; |
| 10 | |
| 11 | pub trait PushSrcImpl: PushSrcImplExt + BaseSrcImpl { |
| 12 | fn fill(&self, buffer: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> { |
| 13 | PushSrcImplExt::parent_fill(self, buffer) |
| 14 | } |
| 15 | |
| 16 | fn alloc(&self) -> Result<gst::Buffer, gst::FlowError> { |
| 17 | PushSrcImplExt::parent_alloc(self) |
| 18 | } |
| 19 | |
| 20 | fn create(&self, buffer: Option<&mut gst::BufferRef>) -> Result<CreateSuccess, gst::FlowError> { |
| 21 | PushSrcImplExt::parent_create(self, buffer) |
| 22 | } |
| 23 | } |
| 24 | |
| 25 | mod sealed { |
| 26 | pub trait Sealed {} |
| 27 | impl<T: super::PushSrcImplExt> Sealed for T {} |
| 28 | } |
| 29 | |
| 30 | pub trait PushSrcImplExt: sealed::Sealed + ObjectSubclass { |
| 31 | fn parent_fill(&self, buffer: &mut gst::BufferRef) -> Result<gst::FlowSuccess, gst::FlowError> { |
| 32 | unsafe { |
| 33 | let data = Self::type_data(); |
| 34 | let parent_class = data.as_ref().parent_class() as *mut ffi::GstPushSrcClass; |
| 35 | (*parent_class) |
| 36 | .fill |
| 37 | .map(|f| { |
| 38 | try_from_glib(f( |
| 39 | self.obj().unsafe_cast_ref::<PushSrc>().to_glib_none().0, |
| 40 | buffer.as_mut_ptr(), |
| 41 | )) |
| 42 | }) |
| 43 | .unwrap_or(Err(gst::FlowError::NotSupported)) |
| 44 | } |
| 45 | } |
| 46 | |
| 47 | fn parent_alloc(&self) -> Result<gst::Buffer, gst::FlowError> { |
| 48 | unsafe { |
| 49 | let data = Self::type_data(); |
| 50 | let parent_class = data.as_ref().parent_class() as *mut ffi::GstPushSrcClass; |
| 51 | (*parent_class) |
| 52 | .alloc |
| 53 | .map(|f| { |
| 54 | let mut buffer_ptr: *mut gst::ffi::GstBuffer = ptr::null_mut(); |
| 55 | |
| 56 | // FIXME: Wrong signature in -sys bindings |
| 57 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
| 58 | let buffer_ref = &mut buffer_ptr as *mut _ as *mut gst::ffi::GstBuffer; |
| 59 | |
| 60 | gst::FlowSuccess::try_from_glib(f( |
| 61 | self.obj().unsafe_cast_ref::<PushSrc>().to_glib_none().0, |
| 62 | buffer_ref, |
| 63 | )) |
| 64 | .map(|_| from_glib_full(buffer_ref)) |
| 65 | }) |
| 66 | .unwrap_or(Err(gst::FlowError::NotSupported)) |
| 67 | } |
| 68 | } |
| 69 | |
| 70 | fn parent_create( |
| 71 | &self, |
| 72 | mut buffer: Option<&mut gst::BufferRef>, |
| 73 | ) -> Result<CreateSuccess, gst::FlowError> { |
| 74 | unsafe { |
| 75 | let data = Self::type_data(); |
| 76 | let parent_class = data.as_ref().parent_class() as *mut ffi::GstPushSrcClass; |
| 77 | (*parent_class) |
| 78 | .create |
| 79 | .map(|f| { |
| 80 | let instance = self.obj(); |
| 81 | let instance = instance.unsafe_cast_ref::<PushSrc>(); |
| 82 | let orig_buffer_ptr = buffer |
| 83 | .as_mut() |
| 84 | .map(|b| b.as_mut_ptr()) |
| 85 | .unwrap_or(ptr::null_mut()); |
| 86 | let mut buffer_ptr = orig_buffer_ptr; |
| 87 | |
| 88 | // FIXME: Wrong signature in -sys bindings |
| 89 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
| 90 | let buffer_ref = &mut buffer_ptr as *mut _ as *mut gst::ffi::GstBuffer; |
| 91 | let instance_data = self.instance_data::<super::base_src::InstanceData>(crate::BaseSrc::static_type()).unwrap(); |
| 92 | |
| 93 | if let Err(err) = gst::FlowSuccess::try_from_glib( |
| 94 | f( |
| 95 | instance.to_glib_none().0, |
| 96 | buffer_ref, |
| 97 | ) |
| 98 | ) { |
| 99 | *instance_data.pending_buffer_list.borrow_mut() = None; |
| 100 | return Err(err); |
| 101 | } |
| 102 | |
| 103 | let pending_buffer_list = instance_data.pending_buffer_list.borrow_mut().take(); |
| 104 | if pending_buffer_list.is_some() && |
| 105 | (buffer.is_some() || instance.src_pad().mode() == gst::PadMode::Pull) { |
| 106 | panic!("Buffer lists can only be returned in push mode" ); |
| 107 | } |
| 108 | |
| 109 | let pending_buffer_list = instance_data.pending_buffer_list.borrow_mut().take(); |
| 110 | if buffer_ptr.is_null() && pending_buffer_list.is_none() { |
| 111 | gst::error!( |
| 112 | gst::CAT_RUST, |
| 113 | obj = instance, |
| 114 | "No buffer and no buffer list returned" |
| 115 | ); |
| 116 | return Err(gst::FlowError::Error); |
| 117 | } |
| 118 | |
| 119 | if !buffer_ptr.is_null() && pending_buffer_list.is_some() { |
| 120 | gst::error!( |
| 121 | gst::CAT_RUST, |
| 122 | obj = instance, |
| 123 | "Both buffer and buffer list returned" |
| 124 | ); |
| 125 | return Err(gst::FlowError::Error); |
| 126 | } |
| 127 | |
| 128 | if let Some(passed_buffer) = buffer { |
| 129 | if buffer_ptr != orig_buffer_ptr { |
| 130 | let new_buffer = gst::Buffer::from_glib_full(buffer_ptr); |
| 131 | |
| 132 | gst::debug!( |
| 133 | gst::CAT_PERFORMANCE, |
| 134 | obj = instance, |
| 135 | "Returned new buffer from parent create function, copying into passed buffer" |
| 136 | ); |
| 137 | |
| 138 | let mut map = match passed_buffer.map_writable() { |
| 139 | Ok(map) => map, |
| 140 | Err(_) => { |
| 141 | gst::error!( |
| 142 | gst::CAT_RUST, |
| 143 | obj = instance, |
| 144 | "Failed to map passed buffer writable" |
| 145 | ); |
| 146 | return Err(gst::FlowError::Error); |
| 147 | } |
| 148 | }; |
| 149 | |
| 150 | let copied_size = new_buffer.copy_to_slice(0, &mut map); |
| 151 | drop(map); |
| 152 | |
| 153 | if let Err(copied_size) = copied_size { |
| 154 | passed_buffer.set_size(copied_size); |
| 155 | } |
| 156 | |
| 157 | match new_buffer.copy_into(passed_buffer, gst::BUFFER_COPY_METADATA, ..) { |
| 158 | Ok(_) => Ok(CreateSuccess::FilledBuffer), |
| 159 | Err(_) => { |
| 160 | gst::error!( |
| 161 | gst::CAT_RUST, |
| 162 | obj = instance, |
| 163 | "Failed to copy buffer metadata" |
| 164 | ); |
| 165 | |
| 166 | Err(gst::FlowError::Error) |
| 167 | } |
| 168 | } |
| 169 | } else { |
| 170 | Ok(CreateSuccess::FilledBuffer) |
| 171 | } |
| 172 | } else if let Some(buffer_list) = pending_buffer_list { |
| 173 | Ok(CreateSuccess::NewBufferList(buffer_list)) |
| 174 | } else { |
| 175 | Ok(CreateSuccess::NewBuffer(from_glib_full(buffer_ptr))) |
| 176 | } |
| 177 | }) |
| 178 | .unwrap_or(Err(gst::FlowError::NotSupported)) |
| 179 | } |
| 180 | } |
| 181 | } |
| 182 | |
| 183 | impl<T: PushSrcImpl> PushSrcImplExt for T {} |
| 184 | |
| 185 | unsafe impl<T: PushSrcImpl> IsSubclassable<T> for PushSrc { |
| 186 | fn class_init(klass: &mut glib::Class<Self>) { |
| 187 | Self::parent_class_init::<T>(class:klass); |
| 188 | let klass: &mut GstPushSrcClass = klass.as_mut(); |
| 189 | klass.fill = Some(push_src_fill::<T>); |
| 190 | klass.alloc = Some(push_src_alloc::<T>); |
| 191 | klass.create = Some(push_src_create::<T>); |
| 192 | } |
| 193 | } |
| 194 | |
| 195 | unsafe extern "C" fn push_src_fill<T: PushSrcImpl>( |
| 196 | ptr: *mut ffi::GstPushSrc, |
| 197 | buffer: *mut gst::ffi::GstBuffer, |
| 198 | ) -> gst::ffi::GstFlowReturn { |
| 199 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
| 200 | let imp: &T = instance.imp(); |
| 201 | let buffer: &mut BufferRef = gst::BufferRef::from_mut_ptr(buffer); |
| 202 | |
| 203 | gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, { |
| 204 | PushSrcImpl::fill(imp, buffer).into() |
| 205 | }) |
| 206 | .into_glib() |
| 207 | } |
| 208 | |
| 209 | unsafe extern "C" fn push_src_alloc<T: PushSrcImpl>( |
| 210 | ptr: *mut ffi::GstPushSrc, |
| 211 | buffer_ptr: *mut gst::ffi::GstBuffer, |
| 212 | ) -> gst::ffi::GstFlowReturn { |
| 213 | let instance: &::Instance = &*(ptr as *mut T::Instance); |
| 214 | let imp: &T = instance.imp(); |
| 215 | // FIXME: Wrong signature in -sys bindings |
| 216 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
| 217 | let buffer_ptr: *mut *mut GstBuffer = buffer_ptr as *mut *mut gst::ffi::GstBuffer; |
| 218 | |
| 219 | gstFlowReturn::panic_to_error!(imp, gst::FlowReturn::Error, { |
| 220 | match PushSrcImpl::alloc(imp) { |
| 221 | Ok(buffer) => { |
| 222 | *buffer_ptr = buffer.into_glib_ptr(); |
| 223 | gst::FlowReturn::Ok |
| 224 | } |
| 225 | Err(err) => gst::FlowReturn::from(err), |
| 226 | } |
| 227 | }) |
| 228 | .into_glib() |
| 229 | } |
| 230 | |
| 231 | #[allow (clippy::needless_option_as_deref)] |
| 232 | unsafe extern "C" fn push_src_create<T: PushSrcImpl>( |
| 233 | ptr: *mut ffi::GstPushSrc, |
| 234 | buffer_ptr: *mut gst::ffi::GstBuffer, |
| 235 | ) -> gst::ffi::GstFlowReturn { |
| 236 | let instance = &*(ptr as *mut T::Instance); |
| 237 | let imp = instance.imp(); |
| 238 | // FIXME: Wrong signature in -sys bindings |
| 239 | // https://gitlab.freedesktop.org/gstreamer/gstreamer-rs-sys/issues/3 |
| 240 | let buffer_ptr = buffer_ptr as *mut *mut gst::ffi::GstBuffer; |
| 241 | |
| 242 | let mut buffer = if (*buffer_ptr).is_null() { |
| 243 | None |
| 244 | } else { |
| 245 | Some(gst::BufferRef::from_mut_ptr(*buffer_ptr)) |
| 246 | }; |
| 247 | |
| 248 | let instance_data = imp |
| 249 | .instance_data::<super::base_src::InstanceData>(crate::BaseSrc::static_type()) |
| 250 | .unwrap(); |
| 251 | |
| 252 | gst::panic_to_error!(imp, gst::FlowReturn::Error, { |
| 253 | match PushSrcImpl::create(imp, buffer.as_deref_mut()) { |
| 254 | Ok(CreateSuccess::NewBuffer(new_buffer)) => { |
| 255 | // Clear any pending buffer list |
| 256 | *instance_data.pending_buffer_list.borrow_mut() = None; |
| 257 | |
| 258 | if let Some(passed_buffer) = buffer { |
| 259 | if passed_buffer.as_ptr() != new_buffer.as_ptr() { |
| 260 | gst::debug!( |
| 261 | gst::CAT_PERFORMANCE, |
| 262 | imp = imp, |
| 263 | "Returned new buffer from create function, copying into passed buffer" |
| 264 | ); |
| 265 | |
| 266 | let mut map = match passed_buffer.map_writable() { |
| 267 | Ok(map) => map, |
| 268 | Err(_) => { |
| 269 | gst::error!( |
| 270 | gst::CAT_RUST, |
| 271 | imp = imp, |
| 272 | "Failed to map passed buffer writable" |
| 273 | ); |
| 274 | return gst::FlowReturn::Error; |
| 275 | } |
| 276 | }; |
| 277 | |
| 278 | let copied_size = new_buffer.copy_to_slice(0, &mut map); |
| 279 | drop(map); |
| 280 | |
| 281 | if let Err(copied_size) = copied_size { |
| 282 | passed_buffer.set_size(copied_size); |
| 283 | } |
| 284 | |
| 285 | match new_buffer.copy_into(passed_buffer, gst::BUFFER_COPY_METADATA, ..) { |
| 286 | Ok(_) => gst::FlowReturn::Ok, |
| 287 | Err(_) => { |
| 288 | gst::error!( |
| 289 | gst::CAT_RUST, |
| 290 | imp = imp, |
| 291 | "Failed to copy buffer metadata" |
| 292 | ); |
| 293 | |
| 294 | gst::FlowReturn::Error |
| 295 | } |
| 296 | } |
| 297 | } else { |
| 298 | gst::FlowReturn::Ok |
| 299 | } |
| 300 | } else { |
| 301 | *buffer_ptr = new_buffer.into_glib_ptr(); |
| 302 | gst::FlowReturn::Ok |
| 303 | } |
| 304 | } |
| 305 | Ok(CreateSuccess::NewBufferList(new_buffer_list)) => { |
| 306 | if buffer.is_some() |
| 307 | || imp.obj().unsafe_cast_ref::<PushSrc>().src_pad().mode() == gst::PadMode::Pull |
| 308 | { |
| 309 | panic!("Buffer lists can only be returned in push mode" ); |
| 310 | } |
| 311 | |
| 312 | *buffer_ptr = ptr::null_mut(); |
| 313 | |
| 314 | // Store it in the instance data so that in the end base_src_create() can |
| 315 | // submit it. |
| 316 | *instance_data.pending_buffer_list.borrow_mut() = Some(new_buffer_list); |
| 317 | |
| 318 | gst::FlowReturn::Ok |
| 319 | } |
| 320 | Ok(CreateSuccess::FilledBuffer) => { |
| 321 | // Clear any pending buffer list |
| 322 | *instance_data.pending_buffer_list.borrow_mut() = None; |
| 323 | |
| 324 | gst::FlowReturn::Ok |
| 325 | } |
| 326 | Err(err) => gst::FlowReturn::from(err), |
| 327 | } |
| 328 | }) |
| 329 | .into_glib() |
| 330 | } |
| 331 | |