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