1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{fmt, marker::PhantomData, mem, ops, ptr, slice}; |
4 | |
5 | use crate::ffi; |
6 | use glib::translate::{from_glib, from_glib_none, Borrowed, ToGlibPtr}; |
7 | |
8 | pub enum Readable {} |
9 | pub enum Writable {} |
10 | |
11 | pub trait IsVideoFrame { |
12 | fn as_raw(&self) -> &ffi::GstVideoFrame; |
13 | } |
14 | |
15 | impl<T> IsVideoFrame for VideoFrame<T> { |
16 | #[inline ] |
17 | fn as_raw(&self) -> &ffi::GstVideoFrame { |
18 | &self.frame |
19 | } |
20 | } |
21 | |
22 | fn plane_buffer_info<T: IsVideoFrame>( |
23 | frame: &T, |
24 | plane: u32, |
25 | ) -> Result<(usize, usize), glib::BoolError> { |
26 | skip_assert_initialized!(); |
27 | |
28 | if plane >= frame.n_planes() { |
29 | return Err(glib::bool_error!( |
30 | "Plane index higher than number of planes" |
31 | )); |
32 | } |
33 | |
34 | let format_info = frame.format_info(); |
35 | |
36 | // Just get the palette |
37 | if format_info.has_palette() && plane == 1 { |
38 | return Ok((1, 256 * 4)); |
39 | } |
40 | |
41 | let w = frame.plane_stride()[plane as usize] as u32; |
42 | let h = frame.plane_height(plane); |
43 | |
44 | if w == 0 || h == 0 { |
45 | return Ok((0, 0)); |
46 | } |
47 | |
48 | Ok((plane as usize, (w * h) as usize)) |
49 | } |
50 | |
51 | pub struct VideoFrame<T> { |
52 | frame: ffi::GstVideoFrame, |
53 | buffer: gst::Buffer, |
54 | phantom: PhantomData<T>, |
55 | } |
56 | |
57 | unsafe impl<T> Send for VideoFrame<T> {} |
58 | unsafe impl<T> Sync for VideoFrame<T> {} |
59 | |
60 | impl<T> fmt::Debug for VideoFrame<T> { |
61 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
62 | f&mut DebugStruct<'_, '_>.debug_struct("VideoFrame" ) |
63 | .field("flags" , &self.flags()) |
64 | .field("id" , &self.id()) |
65 | .field("buffer" , &self.buffer()) |
66 | .field(name:"info" , &self.info()) |
67 | .finish() |
68 | } |
69 | } |
70 | |
71 | mod sealed { |
72 | pub trait Sealed {} |
73 | impl<T: super::IsVideoFrame> Sealed for T {} |
74 | } |
75 | |
76 | pub trait VideoFrameExt: sealed::Sealed + IsVideoFrame { |
77 | #[inline ] |
78 | fn as_ptr(&self) -> *const ffi::GstVideoFrame { |
79 | self.as_raw() as _ |
80 | } |
81 | |
82 | #[inline ] |
83 | fn info(&self) -> &crate::VideoInfo { |
84 | unsafe { |
85 | let frame = self.as_raw(); |
86 | let info = &frame.info as *const ffi::GstVideoInfo as *const crate::VideoInfo; |
87 | &*info |
88 | } |
89 | } |
90 | |
91 | #[inline ] |
92 | fn flags(&self) -> crate::VideoFrameFlags { |
93 | unsafe { from_glib(self.as_raw().flags) } |
94 | } |
95 | |
96 | #[inline ] |
97 | fn id(&self) -> i32 { |
98 | self.as_raw().id |
99 | } |
100 | |
101 | #[inline ] |
102 | fn buffer(&self) -> &gst::BufferRef { |
103 | unsafe { gst::BufferRef::from_ptr(self.as_raw().buffer) } |
104 | } |
105 | |
106 | #[inline ] |
107 | fn format(&self) -> crate::VideoFormat { |
108 | self.info().format() |
109 | } |
110 | |
111 | #[inline ] |
112 | fn format_info(&self) -> crate::VideoFormatInfo { |
113 | self.info().format_info() |
114 | } |
115 | |
116 | #[inline ] |
117 | fn width(&self) -> u32 { |
118 | self.info().width() |
119 | } |
120 | |
121 | #[inline ] |
122 | fn height(&self) -> u32 { |
123 | self.info().height() |
124 | } |
125 | |
126 | #[inline ] |
127 | fn size(&self) -> usize { |
128 | self.info().size() |
129 | } |
130 | |
131 | #[inline ] |
132 | fn is_interlaced(&self) -> bool { |
133 | self.flags().contains(crate::VideoFrameFlags::INTERLACED) |
134 | } |
135 | |
136 | #[inline ] |
137 | fn is_tff(&self) -> bool { |
138 | self.flags().contains(crate::VideoFrameFlags::TFF) |
139 | } |
140 | |
141 | #[inline ] |
142 | fn is_rff(&self) -> bool { |
143 | self.flags().contains(crate::VideoFrameFlags::RFF) |
144 | } |
145 | |
146 | #[inline ] |
147 | fn is_onefield(&self) -> bool { |
148 | self.flags().contains(crate::VideoFrameFlags::ONEFIELD) |
149 | } |
150 | |
151 | #[inline ] |
152 | fn is_bottom_field(&self) -> bool { |
153 | self.flags().contains(crate::VideoFrameFlags::ONEFIELD) |
154 | && !self.flags().contains(crate::VideoFrameFlags::TFF) |
155 | } |
156 | |
157 | #[inline ] |
158 | fn is_top_field(&self) -> bool { |
159 | self.flags().contains(crate::VideoFrameFlags::ONEFIELD) |
160 | && self.flags().contains(crate::VideoFrameFlags::TFF) |
161 | } |
162 | |
163 | #[inline ] |
164 | fn n_planes(&self) -> u32 { |
165 | self.info().n_planes() |
166 | } |
167 | |
168 | #[inline ] |
169 | fn n_components(&self) -> u32 { |
170 | self.info().n_components() |
171 | } |
172 | |
173 | #[inline ] |
174 | fn plane_stride(&self) -> &[i32] { |
175 | self.info().stride() |
176 | } |
177 | |
178 | #[inline ] |
179 | fn plane_offset(&self) -> &[usize] { |
180 | self.info().offset() |
181 | } |
182 | |
183 | #[inline ] |
184 | fn plane_height(&self, plane: u32) -> u32 { |
185 | cfg_if::cfg_if! { |
186 | if #[cfg(feature = "v1_18" )] { |
187 | let comp = self.format_info().component(plane)[0]; |
188 | if comp == -1 { |
189 | 0 |
190 | } else { |
191 | self.comp_height(comp as u32) |
192 | } |
193 | } else { |
194 | // FIXME: This assumes that the horizontal subsampling of all |
195 | // components in the plane is the same, which is probably safe |
196 | |
197 | // Legacy implementation that does not support video formats |
198 | // where plane index and component index are not the same. |
199 | // See #536 |
200 | self.format_info().scale_height(plane as u8, self.height()) |
201 | } |
202 | } |
203 | } |
204 | |
205 | #[inline ] |
206 | fn comp_depth(&self, component: u32) -> u32 { |
207 | self.info().comp_depth(component as u8) |
208 | } |
209 | |
210 | #[inline ] |
211 | fn comp_height(&self, component: u32) -> u32 { |
212 | self.info().comp_height(component as u8) |
213 | } |
214 | |
215 | #[inline ] |
216 | fn comp_width(&self, component: u32) -> u32 { |
217 | self.info().comp_width(component as u8) |
218 | } |
219 | |
220 | #[inline ] |
221 | fn comp_offset(&self, component: u32) -> usize { |
222 | self.info().comp_offset(component as u8) |
223 | } |
224 | |
225 | #[inline ] |
226 | fn comp_poffset(&self, component: u32) -> u32 { |
227 | self.info().comp_poffset(component as u8) |
228 | } |
229 | |
230 | #[inline ] |
231 | fn comp_pstride(&self, component: u32) -> i32 { |
232 | self.info().comp_pstride(component as u8) |
233 | } |
234 | |
235 | #[inline ] |
236 | fn comp_stride(&self, component: u32) -> i32 { |
237 | self.info().comp_stride(component as u8) |
238 | } |
239 | |
240 | #[inline ] |
241 | fn comp_plane(&self, component: u32) -> u32 { |
242 | self.info().comp_plane(component as u8) |
243 | } |
244 | } |
245 | |
246 | impl<O: IsVideoFrame> VideoFrameExt for O {} |
247 | |
248 | impl<T> VideoFrame<T> { |
249 | #[inline ] |
250 | pub fn into_buffer(self) -> gst::Buffer { |
251 | unsafe { |
252 | let mut s = mem::ManuallyDrop::new(self); |
253 | let buffer = ptr::read(&s.buffer); |
254 | ffi::gst_video_frame_unmap(&mut s.frame); |
255 | buffer |
256 | } |
257 | } |
258 | |
259 | #[doc (alias = "gst_video_frame_copy" )] |
260 | pub fn copy(&self, dest: &mut VideoFrame<Writable>) -> Result<(), glib::BoolError> { |
261 | unsafe { |
262 | let res: bool = from_glib(ffi::gst_video_frame_copy(&mut dest.frame, &self.frame)); |
263 | if res { |
264 | Ok(()) |
265 | } else { |
266 | Err(glib::bool_error!("Failed to copy video frame" )) |
267 | } |
268 | } |
269 | } |
270 | |
271 | #[doc (alias = "gst_video_frame_copy_plane" )] |
272 | pub fn copy_plane( |
273 | &self, |
274 | dest: &mut VideoFrame<Writable>, |
275 | plane: u32, |
276 | ) -> Result<(), glib::BoolError> { |
277 | skip_assert_initialized!(); |
278 | |
279 | unsafe { |
280 | let res: bool = from_glib(ffi::gst_video_frame_copy_plane( |
281 | &mut dest.frame, |
282 | &self.frame, |
283 | plane, |
284 | )); |
285 | if res { |
286 | Ok(()) |
287 | } else { |
288 | Err(glib::bool_error!("Failed to copy video frame plane" )) |
289 | } |
290 | } |
291 | } |
292 | |
293 | #[inline ] |
294 | pub fn comp_data(&self, component: u32) -> Result<&[u8], glib::BoolError> { |
295 | let poffset = self.info().comp_poffset(component as u8) as usize; |
296 | Ok(&self.plane_data(self.format_info().plane()[component as usize])?[poffset..]) |
297 | } |
298 | |
299 | #[inline ] |
300 | pub fn buffer(&self) -> &gst::BufferRef { |
301 | unsafe { gst::BufferRef::from_ptr(self.frame.buffer) } |
302 | } |
303 | |
304 | pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> { |
305 | match plane_buffer_info(self, plane) { |
306 | Ok((plane, size)) => { |
307 | if size == 0 { |
308 | return Ok(&[]); |
309 | } |
310 | |
311 | unsafe { |
312 | Ok(slice::from_raw_parts( |
313 | self.frame.data[plane] as *const u8, |
314 | size, |
315 | )) |
316 | } |
317 | } |
318 | Err(err) => Err(err), |
319 | } |
320 | } |
321 | |
322 | pub fn planes_data(&self) -> [&[u8]; 4] { |
323 | let mut planes = [[].as_slice(); 4]; |
324 | |
325 | for plane in 0..self.n_planes() { |
326 | planes[plane as usize] = self.plane_data(plane).unwrap(); |
327 | } |
328 | |
329 | planes |
330 | } |
331 | |
332 | #[inline ] |
333 | pub unsafe fn from_glib_full(frame: ffi::GstVideoFrame) -> Self { |
334 | let buffer = gst::Buffer::from_glib_none(frame.buffer); |
335 | Self { |
336 | frame, |
337 | buffer, |
338 | phantom: PhantomData, |
339 | } |
340 | } |
341 | |
342 | #[inline ] |
343 | pub fn as_video_frame_ref(&self) -> VideoFrameRef<&gst::BufferRef> { |
344 | let frame = unsafe { ptr::read(&self.frame) }; |
345 | VideoFrameRef { |
346 | frame, |
347 | unmap: false, |
348 | phantom: PhantomData, |
349 | } |
350 | } |
351 | |
352 | #[inline ] |
353 | pub fn into_raw(self) -> ffi::GstVideoFrame { |
354 | unsafe { |
355 | let mut s = mem::ManuallyDrop::new(self); |
356 | ptr::drop_in_place(&mut s.buffer); |
357 | s.frame |
358 | } |
359 | } |
360 | } |
361 | |
362 | impl<T> Drop for VideoFrame<T> { |
363 | #[inline ] |
364 | fn drop(&mut self) { |
365 | unsafe { |
366 | ffi::gst_video_frame_unmap(&mut self.frame); |
367 | } |
368 | } |
369 | } |
370 | |
371 | impl VideoFrame<Readable> { |
372 | #[inline ] |
373 | pub fn from_buffer_readable( |
374 | buffer: gst::Buffer, |
375 | info: &crate::VideoInfo, |
376 | ) -> Result<Self, gst::Buffer> { |
377 | skip_assert_initialized!(); |
378 | |
379 | assert!(info.is_valid()); |
380 | |
381 | unsafe { |
382 | let mut frame = mem::MaybeUninit::uninit(); |
383 | let res: bool = from_glib(ffi::gst_video_frame_map( |
384 | frame.as_mut_ptr(), |
385 | info.to_glib_none().0 as *mut _, |
386 | buffer.to_glib_none().0, |
387 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst::ffi::GST_MAP_READ, |
388 | )); |
389 | |
390 | if !res { |
391 | Err(buffer) |
392 | } else { |
393 | let frame = frame.assume_init(); |
394 | Ok(Self { |
395 | frame, |
396 | buffer, |
397 | phantom: PhantomData, |
398 | }) |
399 | } |
400 | } |
401 | } |
402 | |
403 | #[inline ] |
404 | pub fn from_buffer_id_readable( |
405 | buffer: gst::Buffer, |
406 | id: i32, |
407 | info: &crate::VideoInfo, |
408 | ) -> Result<Self, gst::Buffer> { |
409 | skip_assert_initialized!(); |
410 | |
411 | assert!(info.is_valid()); |
412 | |
413 | unsafe { |
414 | let mut frame = mem::MaybeUninit::uninit(); |
415 | let res: bool = from_glib(ffi::gst_video_frame_map_id( |
416 | frame.as_mut_ptr(), |
417 | info.to_glib_none().0 as *mut _, |
418 | buffer.to_glib_none().0, |
419 | id, |
420 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst::ffi::GST_MAP_READ, |
421 | )); |
422 | |
423 | if !res { |
424 | Err(buffer) |
425 | } else { |
426 | let frame = frame.assume_init(); |
427 | Ok(Self { |
428 | frame, |
429 | buffer, |
430 | phantom: PhantomData, |
431 | }) |
432 | } |
433 | } |
434 | } |
435 | |
436 | #[inline ] |
437 | pub fn buffer_owned(&self) -> gst::Buffer { |
438 | unsafe { from_glib_none(self.frame.buffer) } |
439 | } |
440 | } |
441 | |
442 | impl VideoFrame<Writable> { |
443 | #[inline ] |
444 | pub fn from_buffer_writable( |
445 | buffer: gst::Buffer, |
446 | info: &crate::VideoInfo, |
447 | ) -> Result<Self, gst::Buffer> { |
448 | skip_assert_initialized!(); |
449 | |
450 | assert!(info.is_valid()); |
451 | |
452 | unsafe { |
453 | let mut frame = mem::MaybeUninit::uninit(); |
454 | let res: bool = from_glib(ffi::gst_video_frame_map( |
455 | frame.as_mut_ptr(), |
456 | info.to_glib_none().0 as *mut _, |
457 | buffer.to_glib_none().0, |
458 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF |
459 | | gst::ffi::GST_MAP_READ |
460 | | gst::ffi::GST_MAP_WRITE, |
461 | )); |
462 | |
463 | if !res { |
464 | Err(buffer) |
465 | } else { |
466 | let frame = frame.assume_init(); |
467 | Ok(Self { |
468 | frame, |
469 | buffer, |
470 | phantom: PhantomData, |
471 | }) |
472 | } |
473 | } |
474 | } |
475 | |
476 | #[inline ] |
477 | pub fn from_buffer_id_writable( |
478 | buffer: gst::Buffer, |
479 | id: i32, |
480 | info: &crate::VideoInfo, |
481 | ) -> Result<Self, gst::Buffer> { |
482 | skip_assert_initialized!(); |
483 | |
484 | assert!(info.is_valid()); |
485 | |
486 | unsafe { |
487 | let mut frame = mem::MaybeUninit::uninit(); |
488 | let res: bool = from_glib(ffi::gst_video_frame_map_id( |
489 | frame.as_mut_ptr(), |
490 | info.to_glib_none().0 as *mut _, |
491 | buffer.to_glib_none().0, |
492 | id, |
493 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF |
494 | | gst::ffi::GST_MAP_READ |
495 | | gst::ffi::GST_MAP_WRITE, |
496 | )); |
497 | |
498 | if !res { |
499 | Err(buffer) |
500 | } else { |
501 | let frame = frame.assume_init(); |
502 | Ok(Self { |
503 | frame, |
504 | buffer, |
505 | phantom: PhantomData, |
506 | }) |
507 | } |
508 | } |
509 | } |
510 | |
511 | pub fn comp_data_mut(&mut self, component: u32) -> Result<&mut [u8], glib::BoolError> { |
512 | let poffset = self.info().comp_poffset(component as u8) as usize; |
513 | Ok(&mut self.plane_data_mut(self.format_info().plane()[component as usize])?[poffset..]) |
514 | } |
515 | |
516 | pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> { |
517 | match plane_buffer_info(self, plane) { |
518 | Ok((plane, size)) => { |
519 | if size == 0 { |
520 | return Ok(&mut []); |
521 | } |
522 | |
523 | unsafe { |
524 | Ok(slice::from_raw_parts_mut( |
525 | self.frame.data[plane] as *mut u8, |
526 | size, |
527 | )) |
528 | } |
529 | } |
530 | Err(err) => Err(err), |
531 | } |
532 | } |
533 | |
534 | pub fn planes_data_mut(&mut self) -> [&mut [u8]; 4] { |
535 | unsafe { |
536 | let mut planes = [ |
537 | [].as_mut_slice(), |
538 | [].as_mut_slice(), |
539 | [].as_mut_slice(), |
540 | [].as_mut_slice(), |
541 | ]; |
542 | |
543 | for plane in 0..self.n_planes() { |
544 | let slice = self.plane_data_mut(plane).unwrap(); |
545 | planes[plane as usize] = slice::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()); |
546 | } |
547 | |
548 | planes |
549 | } |
550 | } |
551 | |
552 | #[inline ] |
553 | pub fn as_mut_video_frame_ref(&mut self) -> VideoFrameRef<&mut gst::BufferRef> { |
554 | let frame = unsafe { ptr::read(&self.frame) }; |
555 | VideoFrameRef { |
556 | frame, |
557 | unmap: false, |
558 | phantom: PhantomData, |
559 | } |
560 | } |
561 | |
562 | #[inline ] |
563 | pub fn as_mut_ptr(&mut self) -> *mut ffi::GstVideoFrame { |
564 | &mut self.frame |
565 | } |
566 | } |
567 | |
568 | pub struct VideoFrameRef<T> { |
569 | frame: ffi::GstVideoFrame, |
570 | unmap: bool, |
571 | phantom: PhantomData<T>, |
572 | } |
573 | |
574 | impl<T> IsVideoFrame for VideoFrameRef<T> { |
575 | #[inline ] |
576 | fn as_raw(&self) -> &ffi::GstVideoFrame { |
577 | &self.frame |
578 | } |
579 | } |
580 | |
581 | impl<T> fmt::Debug for VideoFrameRef<T> { |
582 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
583 | f&mut DebugStruct<'_, '_>.debug_struct("VideoFrameRef" ) |
584 | .field("flags" , &self.flags()) |
585 | .field("id" , &self.id()) |
586 | .field("buffer" , &unsafe { |
587 | gst::BufferRef::from_ptr(self.frame.buffer) |
588 | }) |
589 | .field(name:"info" , &self.info()) |
590 | .finish() |
591 | } |
592 | } |
593 | |
594 | impl<T> VideoFrameRef<T> { |
595 | #[doc (alias = "gst_video_frame_copy" )] |
596 | pub fn copy( |
597 | &self, |
598 | dest: &mut VideoFrameRef<&mut gst::BufferRef>, |
599 | ) -> Result<(), glib::BoolError> { |
600 | unsafe { |
601 | let res: bool = from_glib(ffi::gst_video_frame_copy(&mut dest.frame, &self.frame)); |
602 | if res { |
603 | Ok(()) |
604 | } else { |
605 | Err(glib::bool_error!("Failed to copy video frame" )) |
606 | } |
607 | } |
608 | } |
609 | |
610 | #[doc (alias = "gst_video_frame_copy_plane" )] |
611 | pub fn copy_plane( |
612 | &self, |
613 | dest: &mut VideoFrameRef<&mut gst::BufferRef>, |
614 | plane: u32, |
615 | ) -> Result<(), glib::BoolError> { |
616 | skip_assert_initialized!(); |
617 | |
618 | unsafe { |
619 | let res: bool = from_glib(ffi::gst_video_frame_copy_plane( |
620 | &mut dest.frame, |
621 | &self.frame, |
622 | plane, |
623 | )); |
624 | if res { |
625 | Ok(()) |
626 | } else { |
627 | Err(glib::bool_error!("Failed to copy video frame plane" )) |
628 | } |
629 | } |
630 | } |
631 | |
632 | pub fn comp_data(&self, component: u32) -> Result<&[u8], glib::BoolError> { |
633 | let poffset = self.info().comp_poffset(component as u8) as usize; |
634 | Ok(&self.plane_data(self.format_info().plane()[component as usize])?[poffset..]) |
635 | } |
636 | |
637 | pub fn plane_data(&self, plane: u32) -> Result<&[u8], glib::BoolError> { |
638 | match plane_buffer_info(self, plane) { |
639 | Ok((plane, size)) => { |
640 | if size == 0 { |
641 | return Ok(&[]); |
642 | } |
643 | |
644 | unsafe { |
645 | Ok(slice::from_raw_parts( |
646 | self.frame.data[plane] as *const u8, |
647 | size, |
648 | )) |
649 | } |
650 | } |
651 | Err(err) => Err(err), |
652 | } |
653 | } |
654 | |
655 | pub fn planes_data(&self) -> [&[u8]; 4] { |
656 | let mut planes = [[].as_slice(); 4]; |
657 | |
658 | for plane in 0..self.n_planes() { |
659 | planes[plane as usize] = self.plane_data(plane).unwrap(); |
660 | } |
661 | |
662 | planes |
663 | } |
664 | } |
665 | |
666 | impl<'a> VideoFrameRef<&'a gst::BufferRef> { |
667 | #[inline ] |
668 | pub unsafe fn from_glib_borrow(frame: *const ffi::GstVideoFrame) -> Borrowed<Self> { |
669 | debug_assert!(!frame.is_null()); |
670 | |
671 | let frame = ptr::read(frame); |
672 | Borrowed::new(Self { |
673 | frame, |
674 | unmap: false, |
675 | phantom: PhantomData, |
676 | }) |
677 | } |
678 | |
679 | #[inline ] |
680 | pub unsafe fn from_glib_full(frame: ffi::GstVideoFrame) -> Self { |
681 | Self { |
682 | frame, |
683 | unmap: true, |
684 | phantom: PhantomData, |
685 | } |
686 | } |
687 | |
688 | #[inline ] |
689 | pub fn from_buffer_ref_readable<'b>( |
690 | buffer: &'a gst::BufferRef, |
691 | info: &'b crate::VideoInfo, |
692 | ) -> Result<Self, glib::BoolError> { |
693 | skip_assert_initialized!(); |
694 | |
695 | assert!(info.is_valid()); |
696 | |
697 | unsafe { |
698 | let mut frame = mem::MaybeUninit::uninit(); |
699 | let res: bool = from_glib(ffi::gst_video_frame_map( |
700 | frame.as_mut_ptr(), |
701 | info.to_glib_none().0 as *mut _, |
702 | buffer.as_mut_ptr(), |
703 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst::ffi::GST_MAP_READ, |
704 | )); |
705 | |
706 | if !res { |
707 | Err(glib::bool_error!("Failed to map VideoFrame" )) |
708 | } else { |
709 | let frame = frame.assume_init(); |
710 | Ok(Self { |
711 | frame, |
712 | unmap: true, |
713 | phantom: PhantomData, |
714 | }) |
715 | } |
716 | } |
717 | } |
718 | |
719 | #[inline ] |
720 | pub fn from_buffer_ref_id_readable<'b>( |
721 | buffer: &'a gst::BufferRef, |
722 | id: i32, |
723 | info: &'b crate::VideoInfo, |
724 | ) -> Result<Self, glib::BoolError> { |
725 | skip_assert_initialized!(); |
726 | |
727 | assert!(info.is_valid()); |
728 | |
729 | unsafe { |
730 | let mut frame = mem::MaybeUninit::uninit(); |
731 | let res: bool = from_glib(ffi::gst_video_frame_map_id( |
732 | frame.as_mut_ptr(), |
733 | info.to_glib_none().0 as *mut _, |
734 | buffer.as_mut_ptr(), |
735 | id, |
736 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF | gst::ffi::GST_MAP_READ, |
737 | )); |
738 | |
739 | if !res { |
740 | Err(glib::bool_error!("Failed to map VideoFrame" )) |
741 | } else { |
742 | let frame = frame.assume_init(); |
743 | Ok(Self { |
744 | frame, |
745 | unmap: true, |
746 | phantom: PhantomData, |
747 | }) |
748 | } |
749 | } |
750 | } |
751 | } |
752 | |
753 | impl<'a> VideoFrameRef<&'a mut gst::BufferRef> { |
754 | #[inline ] |
755 | pub unsafe fn from_glib_borrow_mut(frame: *mut ffi::GstVideoFrame) -> Self { |
756 | debug_assert!(!frame.is_null()); |
757 | |
758 | let frame = ptr::read(frame); |
759 | Self { |
760 | frame, |
761 | unmap: false, |
762 | phantom: PhantomData, |
763 | } |
764 | } |
765 | |
766 | #[inline ] |
767 | pub unsafe fn from_glib_full_mut(frame: ffi::GstVideoFrame) -> Self { |
768 | Self { |
769 | frame, |
770 | unmap: true, |
771 | phantom: PhantomData, |
772 | } |
773 | } |
774 | |
775 | #[inline ] |
776 | pub fn from_buffer_ref_writable<'b>( |
777 | buffer: &'a mut gst::BufferRef, |
778 | info: &'b crate::VideoInfo, |
779 | ) -> Result<Self, glib::BoolError> { |
780 | skip_assert_initialized!(); |
781 | |
782 | assert!(info.is_valid()); |
783 | |
784 | unsafe { |
785 | let mut frame = mem::MaybeUninit::uninit(); |
786 | let res: bool = from_glib(ffi::gst_video_frame_map( |
787 | frame.as_mut_ptr(), |
788 | info.to_glib_none().0 as *mut _, |
789 | buffer.as_mut_ptr(), |
790 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF |
791 | | gst::ffi::GST_MAP_READ |
792 | | gst::ffi::GST_MAP_WRITE, |
793 | )); |
794 | |
795 | if !res { |
796 | Err(glib::bool_error!("Failed to map VideoFrame" )) |
797 | } else { |
798 | let frame = frame.assume_init(); |
799 | Ok(Self { |
800 | frame, |
801 | unmap: true, |
802 | phantom: PhantomData, |
803 | }) |
804 | } |
805 | } |
806 | } |
807 | |
808 | #[inline ] |
809 | pub fn from_buffer_ref_id_writable<'b>( |
810 | buffer: &'a mut gst::BufferRef, |
811 | id: i32, |
812 | info: &'b crate::VideoInfo, |
813 | ) -> Result<Self, glib::BoolError> { |
814 | skip_assert_initialized!(); |
815 | |
816 | assert!(info.is_valid()); |
817 | |
818 | unsafe { |
819 | let mut frame = mem::MaybeUninit::uninit(); |
820 | let res: bool = from_glib(ffi::gst_video_frame_map_id( |
821 | frame.as_mut_ptr(), |
822 | info.to_glib_none().0 as *mut _, |
823 | buffer.as_mut_ptr(), |
824 | id, |
825 | ffi::GST_VIDEO_FRAME_MAP_FLAG_NO_REF |
826 | | gst::ffi::GST_MAP_READ |
827 | | gst::ffi::GST_MAP_WRITE, |
828 | )); |
829 | |
830 | if !res { |
831 | Err(glib::bool_error!("Failed to map VideoFrame" )) |
832 | } else { |
833 | let frame = frame.assume_init(); |
834 | Ok(Self { |
835 | frame, |
836 | unmap: true, |
837 | phantom: PhantomData, |
838 | }) |
839 | } |
840 | } |
841 | } |
842 | |
843 | pub fn comp_data_mut(&mut self, component: u32) -> Result<&mut [u8], glib::BoolError> { |
844 | let poffset = self.info().comp_poffset(component as u8) as usize; |
845 | Ok(&mut self.plane_data_mut(self.format_info().plane()[component as usize])?[poffset..]) |
846 | } |
847 | |
848 | pub fn plane_data_mut(&mut self, plane: u32) -> Result<&mut [u8], glib::BoolError> { |
849 | match plane_buffer_info(self, plane) { |
850 | Ok((plane, size)) => { |
851 | if size == 0 { |
852 | return Ok(&mut []); |
853 | } |
854 | |
855 | unsafe { |
856 | Ok(slice::from_raw_parts_mut( |
857 | self.frame.data[plane] as *mut u8, |
858 | size, |
859 | )) |
860 | } |
861 | } |
862 | Err(err) => Err(err), |
863 | } |
864 | } |
865 | |
866 | pub fn planes_data_mut(&mut self) -> [&mut [u8]; 4] { |
867 | unsafe { |
868 | let mut planes = [ |
869 | [].as_mut_slice(), |
870 | [].as_mut_slice(), |
871 | [].as_mut_slice(), |
872 | [].as_mut_slice(), |
873 | ]; |
874 | |
875 | for plane in 0..self.n_planes() { |
876 | let slice = self.plane_data_mut(plane).unwrap(); |
877 | planes[plane as usize] = slice::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()); |
878 | } |
879 | |
880 | planes |
881 | } |
882 | } |
883 | |
884 | #[inline ] |
885 | pub fn as_mut_ptr(&mut self) -> *mut ffi::GstVideoFrame { |
886 | &mut self.frame |
887 | } |
888 | } |
889 | |
890 | impl<'a> ops::Deref for VideoFrameRef<&'a mut gst::BufferRef> { |
891 | type Target = VideoFrameRef<&'a gst::BufferRef>; |
892 | |
893 | #[inline ] |
894 | fn deref(&self) -> &Self::Target { |
895 | unsafe { &*(self as *const Self as *const Self::Target) } |
896 | } |
897 | } |
898 | |
899 | unsafe impl<T> Send for VideoFrameRef<T> {} |
900 | unsafe impl<T> Sync for VideoFrameRef<T> {} |
901 | |
902 | impl<T> Drop for VideoFrameRef<T> { |
903 | #[inline ] |
904 | fn drop(&mut self) { |
905 | unsafe { |
906 | if self.unmap { |
907 | ffi::gst_video_frame_unmap(&mut self.frame); |
908 | } |
909 | } |
910 | } |
911 | } |
912 | |
913 | pub trait VideoBufferExt { |
914 | #[doc (alias = "get_video_flags" )] |
915 | fn video_flags(&self) -> crate::VideoBufferFlags; |
916 | fn set_video_flags(&mut self, flags: crate::VideoBufferFlags); |
917 | fn unset_video_flags(&mut self, flags: crate::VideoBufferFlags); |
918 | } |
919 | |
920 | impl VideoBufferExt for gst::BufferRef { |
921 | #[inline ] |
922 | fn video_flags(&self) -> crate::VideoBufferFlags { |
923 | unsafe { |
924 | let ptr = self.as_mut_ptr(); |
925 | crate::VideoBufferFlags::from_bits_truncate((*ptr).mini_object.flags) |
926 | } |
927 | } |
928 | |
929 | #[inline ] |
930 | fn set_video_flags(&mut self, flags: crate::VideoBufferFlags) { |
931 | unsafe { |
932 | let ptr = self.as_mut_ptr(); |
933 | (*ptr).mini_object.flags |= flags.bits(); |
934 | } |
935 | } |
936 | |
937 | #[inline ] |
938 | fn unset_video_flags(&mut self, flags: crate::VideoBufferFlags) { |
939 | unsafe { |
940 | let ptr = self.as_mut_ptr(); |
941 | (*ptr).mini_object.flags &= !flags.bits(); |
942 | } |
943 | } |
944 | } |
945 | |
946 | #[cfg (test)] |
947 | mod tests { |
948 | use super::*; |
949 | |
950 | #[test ] |
951 | fn test_map_read() { |
952 | gst::init().unwrap(); |
953 | |
954 | let info = crate::VideoInfo::builder(crate::VideoFormat::Gray8, 320, 240) |
955 | .build() |
956 | .unwrap(); |
957 | let buffer = gst::Buffer::with_size(info.size()).unwrap(); |
958 | let frame = VideoFrame::from_buffer_readable(buffer, &info).unwrap(); |
959 | |
960 | assert!(frame.plane_data(0).is_ok()); |
961 | assert_eq!(frame.plane_data(0).unwrap().len(), 320 * 240); |
962 | assert!(frame.plane_data(1).is_err()); |
963 | assert!(frame.info() == &info); |
964 | |
965 | { |
966 | let frame = frame.as_video_frame_ref(); |
967 | |
968 | assert!(frame.plane_data(0).is_ok()); |
969 | assert_eq!(frame.plane_data(0).unwrap().len(), 320 * 240); |
970 | assert!(frame.plane_data(1).is_err()); |
971 | assert!(frame.info() == &info); |
972 | } |
973 | |
974 | assert!(frame.plane_data(0).is_ok()); |
975 | assert_eq!(frame.plane_data(0).unwrap().len(), 320 * 240); |
976 | assert!(frame.plane_data(1).is_err()); |
977 | assert!(frame.info() == &info); |
978 | } |
979 | |
980 | #[test ] |
981 | fn test_map_write() { |
982 | gst::init().unwrap(); |
983 | |
984 | let info = crate::VideoInfo::builder(crate::VideoFormat::Gray8, 320, 240) |
985 | .build() |
986 | .unwrap(); |
987 | let buffer = gst::Buffer::with_size(info.size()).unwrap(); |
988 | let mut frame = VideoFrame::from_buffer_writable(buffer, &info).unwrap(); |
989 | |
990 | assert!(frame.plane_data_mut(0).is_ok()); |
991 | assert_eq!(frame.plane_data_mut(0).unwrap().len(), 320 * 240); |
992 | assert!(frame.plane_data_mut(1).is_err()); |
993 | assert!(frame.info() == &info); |
994 | |
995 | { |
996 | let mut frame = frame.as_mut_video_frame_ref(); |
997 | |
998 | assert!(frame.plane_data_mut(0).is_ok()); |
999 | assert_eq!(frame.plane_data_mut(0).unwrap().len(), 320 * 240); |
1000 | assert!(frame.plane_data_mut(1).is_err()); |
1001 | assert!(frame.info() == &info); |
1002 | } |
1003 | |
1004 | assert!(frame.plane_data_mut(0).is_ok()); |
1005 | assert_eq!(frame.plane_data_mut(0).unwrap().len(), 320 * 240); |
1006 | assert!(frame.plane_data_mut(1).is_err()); |
1007 | assert!(frame.info() == &info); |
1008 | } |
1009 | |
1010 | #[test ] |
1011 | fn test_map_ref_read() { |
1012 | gst::init().unwrap(); |
1013 | |
1014 | let info = crate::VideoInfo::builder(crate::VideoFormat::Gray8, 320, 240) |
1015 | .build() |
1016 | .unwrap(); |
1017 | let buffer = gst::Buffer::with_size(info.size()).unwrap(); |
1018 | let frame = VideoFrameRef::from_buffer_ref_readable(&buffer, &info).unwrap(); |
1019 | |
1020 | assert!(frame.plane_data(0).is_ok()); |
1021 | assert_eq!(frame.plane_data(0).unwrap().len(), 320 * 240); |
1022 | assert!(frame.plane_data(1).is_err()); |
1023 | assert!(frame.info() == &info); |
1024 | } |
1025 | |
1026 | #[test ] |
1027 | fn test_map_ref_write() { |
1028 | gst::init().unwrap(); |
1029 | |
1030 | let info = crate::VideoInfo::builder(crate::VideoFormat::Gray8, 320, 240) |
1031 | .build() |
1032 | .unwrap(); |
1033 | let mut buffer = gst::Buffer::with_size(info.size()).unwrap(); |
1034 | { |
1035 | let buffer = buffer.get_mut().unwrap(); |
1036 | let mut frame = VideoFrameRef::from_buffer_ref_writable(buffer, &info).unwrap(); |
1037 | |
1038 | assert!(frame.plane_data_mut(0).is_ok()); |
1039 | assert_eq!(frame.plane_data_mut(0).unwrap().len(), 320 * 240); |
1040 | assert!(frame.plane_data_mut(1).is_err()); |
1041 | assert!(frame.info() == &info); |
1042 | } |
1043 | } |
1044 | |
1045 | #[cfg (feature = "v1_20" )] |
1046 | #[test ] |
1047 | fn test_plane_data() { |
1048 | gst::init().unwrap(); |
1049 | |
1050 | let info = crate::VideoInfo::builder(crate::VideoFormat::Av12, 320, 240) |
1051 | .build() |
1052 | .unwrap(); |
1053 | let buffer = gst::Buffer::with_size(info.size()).unwrap(); |
1054 | let mut frame = VideoFrame::from_buffer_writable(buffer, &info).unwrap(); |
1055 | |
1056 | // Alpha plane |
1057 | { |
1058 | let mut frame = frame.as_mut_video_frame_ref(); |
1059 | let data = frame.plane_data_mut(2).unwrap(); |
1060 | assert_eq!(data.len(), 320 * 240); |
1061 | data[0] = 42; |
1062 | } |
1063 | |
1064 | // UV plane |
1065 | { |
1066 | let mut frame = frame.as_mut_video_frame_ref(); |
1067 | let data = frame.plane_data_mut(1).unwrap(); |
1068 | assert_eq!(data.len(), 320 * 120); |
1069 | data[0] = 42; |
1070 | } |
1071 | |
1072 | let frame = frame.into_buffer(); |
1073 | let frame = VideoFrame::from_buffer_readable(frame, &info).unwrap(); |
1074 | |
1075 | let alpha_data = frame.plane_data(2).unwrap(); |
1076 | assert_eq!(alpha_data.len(), 320 * 240); |
1077 | assert_eq!(alpha_data[0], 42); |
1078 | |
1079 | let uv_data = frame.plane_data(1).unwrap(); |
1080 | assert_eq!(uv_data.len(), 320 * 120); |
1081 | assert_eq!(uv_data[0], 42); |
1082 | } |
1083 | } |
1084 | |