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