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 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        debug_assert!(!frame.is_null());
283
284        let frame = ptr::read(frame);
285        Borrowed::new(Self {
286            frame,
287            unmap: false,
288            phantom: PhantomData,
289        })
290    }
291
292    #[inline]
293    pub unsafe fn from_glib_full(frame: gst_video::ffi::GstVideoFrame) -> Self {
294        Self {
295            frame,
296            unmap: true,
297            phantom: PhantomData,
298        }
299    }
300
301    #[inline]
302    pub fn from_buffer_ref_readable<'b>(
303        buffer: &'a gst::BufferRef,
304        info: &'b gst_video::VideoInfo,
305    ) -> Result<GLVideoFrameRef<&'a gst::BufferRef>, glib::error::BoolError> {
306        skip_assert_initialized!();
307
308        let n_mem = match buffer_n_gl_memory(buffer) {
309            Some(n) => n,
310            None => return Err(glib::bool_error!("Memory is not a GstGLMemory")),
311        };
312
313        // FIXME: planes are not memories, in multiview use case,
314        // number of memories = planes * views, but the raw memory is
315        // not exposed in videoframe
316        if n_mem != info.n_planes() {
317            return Err(glib::bool_error!(
318                "Number of planes and memories is not matching"
319            ));
320        }
321
322        unsafe {
323            let mut frame = mem::MaybeUninit::uninit();
324            let res: bool = from_glib(gst_video::ffi::gst_video_frame_map(
325                frame.as_mut_ptr(),
326                info.to_glib_none().0 as *mut _,
327                buffer.as_mut_ptr(),
328                gst_video::ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF
329                    | gst::ffi::GST_MAP_READ
330                    | ffi::GST_MAP_GL as u32,
331            ));
332
333            if !res {
334                Err(glib::bool_error!(
335                    "Failed to fill in the values of GstVideoFrame"
336                ))
337            } else {
338                let mut frame = frame.assume_init();
339                // Reset size/stride/offset to 0 as the memory pointers
340                // are the GL texture ID and accessing them would read
341                // random memory.
342                frame.info.size = 0;
343                frame.info.stride.fill(0);
344                frame.info.offset.fill(0);
345                Ok(Self {
346                    frame,
347                    unmap: true,
348                    phantom: PhantomData,
349                })
350            }
351        }
352    }
353}
354
355impl<'a> GLVideoFrameRef<&'a mut gst::BufferRef> {
356    #[inline]
357    pub unsafe fn from_glib_borrow_mut(frame: *mut gst_video::ffi::GstVideoFrame) -> Self {
358        debug_assert!(!frame.is_null());
359
360        let frame = ptr::read(frame);
361        Self {
362            frame,
363            unmap: false,
364            phantom: PhantomData,
365        }
366    }
367
368    #[inline]
369    pub unsafe fn from_glib_full_mut(frame: gst_video::ffi::GstVideoFrame) -> Self {
370        Self {
371            frame,
372            unmap: true,
373            phantom: PhantomData,
374        }
375    }
376
377    #[inline]
378    pub fn from_buffer_ref_writable<'b>(
379        buffer: &'a mut gst::BufferRef,
380        info: &'b gst_video::VideoInfo,
381    ) -> Result<GLVideoFrameRef<&'a mut gst::BufferRef>, glib::error::BoolError> {
382        skip_assert_initialized!();
383
384        let n_mem = match buffer_n_gl_memory(buffer) {
385            Some(n) => n,
386            None => return Err(glib::bool_error!("Memory is not a GstGLMemory")),
387        };
388
389        // FIXME: planes are not memories, in multiview use case,
390        // number of memories = planes * views, but the raw memory is
391        // not exposed in videoframe
392        if n_mem != info.n_planes() {
393            return Err(glib::bool_error!(
394                "Number of planes and memories is not matching"
395            ));
396        }
397
398        unsafe {
399            let mut frame = mem::MaybeUninit::uninit();
400            let res: bool = from_glib(gst_video::ffi::gst_video_frame_map(
401                frame.as_mut_ptr(),
402                info.to_glib_none().0 as *mut _,
403                buffer.as_mut_ptr(),
404                gst_video::ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF
405                    | gst::ffi::GST_MAP_READ
406                    | gst::ffi::GST_MAP_WRITE
407                    | ffi::GST_MAP_GL as u32,
408            ));
409
410            if !res {
411                Err(glib::bool_error!(
412                    "Failed to fill in the values of GstVideoFrame"
413                ))
414            } else {
415                let mut frame = frame.assume_init();
416                // Reset size/stride/offset to 0 as the memory pointers
417                // are the GL texture ID and accessing them would read
418                // random memory.
419                frame.info.size = 0;
420                frame.info.stride.fill(0);
421                frame.info.offset.fill(0);
422                Ok(Self {
423                    frame,
424                    unmap: true,
425                    phantom: PhantomData,
426                })
427            }
428        }
429    }
430
431    #[inline]
432    pub fn buffer_mut(&mut self) -> &mut gst::BufferRef {
433        unsafe { gst::BufferRef::from_mut_ptr(self.frame.buffer) }
434    }
435
436    #[inline]
437    pub fn as_mut_ptr(&mut self) -> *mut gst_video::ffi::GstVideoFrame {
438        &mut self.frame
439    }
440
441    #[inline]
442    pub fn memory_mut(&mut self, idx: u32) -> Result<&mut GLMemoryRef, glib::BoolError> {
443        unsafe { Ok(GLMemoryRef::from_mut_ptr(self.memory(idx)?.as_ptr() as _)) }
444    }
445}
446
447impl<'a> std::ops::Deref for GLVideoFrameRef<&'a mut gst::BufferRef> {
448    type Target = GLVideoFrameRef<&'a gst::BufferRef>;
449
450    #[inline]
451    fn deref(&self) -> &Self::Target {
452        unsafe { &*(self as *const Self as *const Self::Target) }
453    }
454}
455
456impl<T> Drop for GLVideoFrameRef<T> {
457    #[inline]
458    fn drop(&mut self) {
459        unsafe {
460            if self.unmap {
461                gst_video::ffi::gst_video_frame_unmap(&mut self.frame);
462            }
463        }
464    }
465}
466
467fn buffer_n_gl_memory(buffer: &gst::BufferRef) -> Option<u32> {
468    skip_assert_initialized!();
469    unsafe {
470        let buf = buffer.as_mut_ptr();
471        let num = gst::ffi::gst_buffer_n_memory(buf);
472        for i in 0..num - 1 {
473            let mem = gst::ffi::gst_buffer_peek_memory(buf, i);
474            if ffi::gst_is_gl_memory(mem) != glib::ffi::GTRUE {
475                return None;
476            }
477        }
478        Some(num)
479    }
480}