Skip to main content

gstreamer_gl/
gl_video_frame.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{fmt::Debug, marker::PhantomData, mem, ptr};
4
5use crate::{GLMemoryRef, ffi};
6use glib::translate::*;
7use gst_video::{VideoFrameExt, video_frame::IsVideoFrame};
8
9pub enum Readable {}
10pub enum Writable {}
11
12// TODO: implement copy for videoframes. This would need to go through all the individual memories
13//       and copy them. Some GL textures can be copied, others cannot.
14
15pub unsafe trait IsGLVideoFrame: IsVideoFrame {}
16
17pub trait GLVideoFrameExt: IsGLVideoFrame + VideoFrameExt {
18    #[inline]
19    fn memory(&self, idx: u32) -> Result<&GLMemoryRef, glib::BoolError> {
20        if idx >= self.info().n_planes() {
21            return Err(glib::bool_error!(
22                "Memory index higher than number of memories"
23            ));
24        }
25
26        unsafe {
27            let ptr = self.as_raw().map[idx as usize].memory;
28            if ffi::gst_is_gl_memory(ptr) == glib::ffi::GTRUE {
29                Ok(GLMemoryRef::from_ptr(ptr as _))
30            } else {
31                Err(glib::bool_error!("Memory is not a GLMemory"))
32            }
33        }
34    }
35
36    #[inline]
37    #[doc(alias = "get_texture_id")]
38    fn texture_id(&self, idx: u32) -> Result<u32, glib::BoolError> {
39        Ok(self.memory(idx)?.texture_id())
40    }
41
42    #[inline]
43    #[doc(alias = "get_texture_format")]
44    fn texture_format(&self, idx: u32) -> Result<crate::GLFormat, glib::BoolError> {
45        Ok(self.memory(idx)?.texture_format())
46    }
47
48    #[inline]
49    #[doc(alias = "get_texture_height")]
50    fn texture_height(&self, idx: u32) -> Result<i32, glib::BoolError> {
51        Ok(self.memory(idx)?.texture_height())
52    }
53
54    #[inline]
55    #[doc(alias = "get_texture_target")]
56    fn texture_target(&self, idx: u32) -> Result<crate::GLTextureTarget, glib::BoolError> {
57        Ok(self.memory(idx)?.texture_target())
58    }
59
60    #[inline]
61    #[doc(alias = "get_texture_width")]
62    fn texture_width(&self, idx: u32) -> Result<i32, glib::BoolError> {
63        Ok(self.memory(idx)?.texture_width())
64    }
65}
66
67impl<O: IsGLVideoFrame> GLVideoFrameExt for O {}
68
69pub struct GLVideoFrame<T> {
70    frame: gst_video::ffi::GstVideoFrame,
71    phantom: PhantomData<T>,
72}
73
74unsafe impl<T> Send for GLVideoFrame<T> {}
75unsafe impl<T> Sync for GLVideoFrame<T> {}
76
77unsafe impl<T> IsVideoFrame for GLVideoFrame<T> {
78    #[inline]
79    fn as_raw(&self) -> &gst_video::ffi::GstVideoFrame {
80        &self.frame
81    }
82}
83
84unsafe impl<T> IsGLVideoFrame for GLVideoFrame<T> {}
85
86impl<T> Debug for GLVideoFrame<T> {
87    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
88        f.debug_struct("GLVideoFrame")
89            .field("flags", &self.flags())
90            .field("id", &self.id())
91            .field("buffer", &self.buffer())
92            .field("info", &self.info())
93            .finish()
94    }
95}
96
97impl<T> GLVideoFrame<T> {
98    #[inline]
99    pub fn into_buffer(self) -> gst::Buffer {
100        unsafe {
101            let mut s = mem::ManuallyDrop::new(self);
102            let buffer = from_glib_none(s.frame.buffer);
103            gst_video::ffi::gst_video_frame_unmap(&mut s.frame);
104            buffer
105        }
106    }
107
108    #[inline]
109    pub unsafe fn from_glib_full(frame: gst_video::ffi::GstVideoFrame) -> Self {
110        Self {
111            frame,
112            phantom: PhantomData,
113        }
114    }
115
116    #[inline]
117    pub fn into_raw(self) -> gst_video::ffi::GstVideoFrame {
118        let s = mem::ManuallyDrop::new(self);
119        s.frame
120    }
121
122    #[inline]
123    pub fn as_video_frame_gl_ref(&self) -> GLVideoFrameRef<&gst::BufferRef> {
124        let frame = unsafe { ptr::read(&self.frame) };
125        GLVideoFrameRef {
126            frame,
127            unmap: false,
128            phantom: PhantomData,
129        }
130    }
131}
132
133impl<T> Drop for GLVideoFrame<T> {
134    #[inline]
135    fn drop(&mut self) {
136        unsafe {
137            gst_video::ffi::gst_video_frame_unmap(&mut self.frame);
138        }
139    }
140}
141
142impl GLVideoFrame<Readable> {
143    #[inline]
144    pub fn from_buffer_readable(
145        buffer: gst::Buffer,
146        info: &gst_video::VideoInfo,
147    ) -> Result<Self, gst::Buffer> {
148        skip_assert_initialized!();
149
150        let n_mem = match buffer_n_gl_memory(buffer.as_ref()) {
151            Some(n) => n,
152            None => return Err(buffer),
153        };
154
155        // FIXME: planes are not memories, in multiview use case,
156        // number of memories = planes * views, but the raw memory is
157        // not exposed in videoframe
158        if n_mem != info.n_planes() {
159            return Err(buffer);
160        }
161
162        unsafe {
163            let mut frame = mem::MaybeUninit::uninit();
164            let res: bool = from_glib(gst_video::ffi::gst_video_frame_map(
165                frame.as_mut_ptr(),
166                info.to_glib_none().0 as *mut _,
167                buffer.to_glib_none().0,
168                gst::ffi::GST_MAP_READ | ffi::GST_MAP_GL as u32,
169            ));
170
171            if !res {
172                Err(buffer)
173            } else {
174                let mut frame = frame.assume_init();
175                // Reset size/stride/offset to 0 as the memory pointers
176                // are the GL texture ID and accessing them would read
177                // random memory.
178                frame.info.size = 0;
179                frame.info.stride.fill(0);
180                frame.info.offset.fill(0);
181                Ok(Self {
182                    frame,
183                    phantom: PhantomData,
184                })
185            }
186        }
187    }
188}
189
190impl GLVideoFrame<Writable> {
191    #[inline]
192    pub fn from_buffer_writable(
193        buffer: gst::Buffer,
194        info: &gst_video::VideoInfo,
195    ) -> Result<Self, gst::Buffer> {
196        skip_assert_initialized!();
197
198        let n_mem = match buffer_n_gl_memory(buffer.as_ref()) {
199            Some(n) => n,
200            None => return Err(buffer),
201        };
202
203        // FIXME: planes are not memories, in multiview use case,
204        // number of memories = planes * views, but the raw memory is
205        // not exposed in videoframe
206        if n_mem != info.n_planes() {
207            return Err(buffer);
208        }
209
210        unsafe {
211            let mut frame = mem::MaybeUninit::uninit();
212            let res: bool = from_glib(gst_video::ffi::gst_video_frame_map(
213                frame.as_mut_ptr(),
214                info.to_glib_none().0 as *mut _,
215                buffer.to_glib_none().0,
216                gst::ffi::GST_MAP_READ | gst::ffi::GST_MAP_WRITE | ffi::GST_MAP_GL as u32,
217            ));
218
219            if !res {
220                Err(buffer)
221            } else {
222                let mut frame = frame.assume_init();
223                // Reset size/stride/offset to 0 as the memory pointers
224                // are the GL texture ID and accessing them would read
225                // random memory.
226                frame.info.size = 0;
227                frame.info.stride.fill(0);
228                frame.info.offset.fill(0);
229                Ok(Self {
230                    frame,
231                    phantom: PhantomData,
232                })
233            }
234        }
235    }
236
237    #[inline]
238    pub fn memory_mut(&mut self, idx: u32) -> Result<&mut GLMemoryRef, glib::BoolError> {
239        unsafe { Ok(GLMemoryRef::from_mut_ptr(self.memory(idx)?.as_ptr() as _)) }
240    }
241
242    #[inline]
243    pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
244        unsafe { gst::BufferRef::from_mut_ptr(self.frame.buffer) }
245    }
246}
247
248pub struct GLVideoFrameRef<T> {
249    frame: gst_video::ffi::GstVideoFrame,
250    unmap: bool,
251    phantom: PhantomData<T>,
252}
253
254unsafe impl<T> Send for GLVideoFrameRef<T> {}
255unsafe impl<T> Sync for GLVideoFrameRef<T> {}
256
257unsafe impl<T> IsVideoFrame for GLVideoFrameRef<T> {
258    #[inline]
259    fn as_raw(&self) -> &gst_video::ffi::GstVideoFrame {
260        &self.frame
261    }
262}
263
264unsafe impl<T> IsGLVideoFrame for GLVideoFrameRef<T> {}
265
266impl<T> Debug for GLVideoFrameRef<T> {
267    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
268        f.debug_struct("GLVideoFrameRef")
269            .field("flags", &self.flags())
270            .field("id", &self.id())
271            .field("buffer", &unsafe {
272                gst::BufferRef::from_ptr(self.frame.buffer)
273            })
274            .field("info", &self.info())
275            .finish()
276    }
277}
278
279impl<'a> GLVideoFrameRef<&'a gst::BufferRef> {
280    #[inline]
281    pub unsafe fn from_glib_borrow(frame: *const gst_video::ffi::GstVideoFrame) -> Borrowed<Self> {
282        unsafe {
283            debug_assert!(!frame.is_null());
284
285            let frame = ptr::read(frame);
286            Borrowed::new(Self {
287                frame,
288                unmap: false,
289                phantom: PhantomData,
290            })
291        }
292    }
293
294    #[inline]
295    pub unsafe fn from_glib_full(frame: gst_video::ffi::GstVideoFrame) -> Self {
296        Self {
297            frame,
298            unmap: true,
299            phantom: PhantomData,
300        }
301    }
302
303    #[inline]
304    pub fn from_buffer_ref_readable<'b>(
305        buffer: &'a gst::BufferRef,
306        info: &'b gst_video::VideoInfo,
307    ) -> Result<GLVideoFrameRef<&'a gst::BufferRef>, glib::error::BoolError> {
308        skip_assert_initialized!();
309
310        let n_mem = match buffer_n_gl_memory(buffer) {
311            Some(n) => n,
312            None => return Err(glib::bool_error!("Memory is not a GstGLMemory")),
313        };
314
315        // FIXME: planes are not memories, in multiview use case,
316        // number of memories = planes * views, but the raw memory is
317        // not exposed in videoframe
318        if n_mem != info.n_planes() {
319            return Err(glib::bool_error!(
320                "Number of planes and memories is not matching"
321            ));
322        }
323
324        unsafe {
325            let mut frame = mem::MaybeUninit::uninit();
326            let res: bool = from_glib(gst_video::ffi::gst_video_frame_map(
327                frame.as_mut_ptr(),
328                info.to_glib_none().0 as *mut _,
329                buffer.as_mut_ptr(),
330                gst_video::ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF
331                    | gst::ffi::GST_MAP_READ
332                    | ffi::GST_MAP_GL as u32,
333            ));
334
335            if !res {
336                Err(glib::bool_error!(
337                    "Failed to fill in the values of GstVideoFrame"
338                ))
339            } else {
340                let mut frame = frame.assume_init();
341                // Reset size/stride/offset to 0 as the memory pointers
342                // are the GL texture ID and accessing them would read
343                // random memory.
344                frame.info.size = 0;
345                frame.info.stride.fill(0);
346                frame.info.offset.fill(0);
347                Ok(Self {
348                    frame,
349                    unmap: true,
350                    phantom: PhantomData,
351                })
352            }
353        }
354    }
355}
356
357impl<'a> GLVideoFrameRef<&'a mut gst::BufferRef> {
358    #[inline]
359    pub unsafe fn from_glib_borrow_mut(frame: *mut gst_video::ffi::GstVideoFrame) -> Self {
360        unsafe {
361            debug_assert!(!frame.is_null());
362
363            let frame = ptr::read(frame);
364            Self {
365                frame,
366                unmap: false,
367                phantom: PhantomData,
368            }
369        }
370    }
371
372    #[inline]
373    pub unsafe fn from_glib_full_mut(frame: gst_video::ffi::GstVideoFrame) -> Self {
374        Self {
375            frame,
376            unmap: true,
377            phantom: PhantomData,
378        }
379    }
380
381    #[inline]
382    pub fn from_buffer_ref_writable<'b>(
383        buffer: &'a mut gst::BufferRef,
384        info: &'b gst_video::VideoInfo,
385    ) -> Result<GLVideoFrameRef<&'a mut gst::BufferRef>, glib::error::BoolError> {
386        skip_assert_initialized!();
387
388        let n_mem = match buffer_n_gl_memory(buffer) {
389            Some(n) => n,
390            None => return Err(glib::bool_error!("Memory is not a GstGLMemory")),
391        };
392
393        // FIXME: planes are not memories, in multiview use case,
394        // number of memories = planes * views, but the raw memory is
395        // not exposed in videoframe
396        if n_mem != info.n_planes() {
397            return Err(glib::bool_error!(
398                "Number of planes and memories is not matching"
399            ));
400        }
401
402        unsafe {
403            let mut frame = mem::MaybeUninit::uninit();
404            let res: bool = from_glib(gst_video::ffi::gst_video_frame_map(
405                frame.as_mut_ptr(),
406                info.to_glib_none().0 as *mut _,
407                buffer.as_mut_ptr(),
408                gst_video::ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF
409                    | gst::ffi::GST_MAP_READ
410                    | gst::ffi::GST_MAP_WRITE
411                    | ffi::GST_MAP_GL as u32,
412            ));
413
414            if !res {
415                Err(glib::bool_error!(
416                    "Failed to fill in the values of GstVideoFrame"
417                ))
418            } else {
419                let mut frame = frame.assume_init();
420                // Reset size/stride/offset to 0 as the memory pointers
421                // are the GL texture ID and accessing them would read
422                // random memory.
423                frame.info.size = 0;
424                frame.info.stride.fill(0);
425                frame.info.offset.fill(0);
426                Ok(Self {
427                    frame,
428                    unmap: true,
429                    phantom: PhantomData,
430                })
431            }
432        }
433    }
434
435    #[inline]
436    pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
437        unsafe { gst::BufferRef::from_mut_ptr(self.frame.buffer) }
438    }
439
440    #[inline]
441    pub fn as_mut_ptr(&mut self) -> *mut gst_video::ffi::GstVideoFrame {
442        &mut self.frame
443    }
444
445    #[inline]
446    pub fn memory_mut(&mut self, idx: u32) -> Result<&mut GLMemoryRef, glib::BoolError> {
447        unsafe { Ok(GLMemoryRef::from_mut_ptr(self.memory(idx)?.as_ptr() as _)) }
448    }
449}
450
451impl<'a> std::ops::Deref for GLVideoFrameRef<&'a mut gst::BufferRef> {
452    type Target = GLVideoFrameRef<&'a gst::BufferRef>;
453
454    #[inline]
455    fn deref(&self) -> &Self::Target {
456        unsafe { &*(self as *const Self as *const Self::Target) }
457    }
458}
459
460impl<T> Drop for GLVideoFrameRef<T> {
461    #[inline]
462    fn drop(&mut self) {
463        unsafe {
464            if self.unmap {
465                gst_video::ffi::gst_video_frame_unmap(&mut self.frame);
466            }
467        }
468    }
469}
470
471fn buffer_n_gl_memory(buffer: &gst::BufferRef) -> Option<u32> {
472    skip_assert_initialized!();
473    unsafe {
474        let buf = buffer.as_mut_ptr();
475        let num = gst::ffi::gst_buffer_n_memory(buf);
476        for i in 0..num - 1 {
477            let mem = gst::ffi::gst_buffer_peek_memory(buf, i);
478            if ffi::gst_is_gl_memory(mem) != glib::ffi::GTRUE {
479                return None;
480            }
481        }
482        Some(num)
483    }
484}