1use std::{fmt::Debug, num::NonZeroU32, sync::LazyLock};
2
3use bytemuck::{self, Pod};
4use strum::EnumCount;
5use yuv::{
6 self, BufferStoreMut, Rgb30ByteOrder::Network, YuvBiPlanarImage, YuvBiPlanarImageMut, YuvConversionMode, YuvConversionMode::Fast, YuvPackedImage,
7 YuvPackedImageMut, YuvPlanarImage, YuvPlanarImageMut, YuvRange, YuvStandardMatrix,
8};
9
10use super::{
11 frame::VideoFrame,
12 video::{ColorMatrix, ColorRange, PixelFormat, VideoFrameDescriptor},
13};
14use crate::{
15 error::Error,
16 frame::{DataMappable, Frame, FrameData, MappedPlanes},
17 FrameDescriptor, Result,
18};
19
20fn into_yuv_planar_image<'a, T>(src: &'a MappedPlanes, width: NonZeroU32, height: NonZeroU32) -> Result<YuvPlanarImage<'a, T>>
21where
22 T: Debug + Pod,
23{
24 if src.planes.len() != 3 {
25 return Err(Error::Invalid("invalid plane count".to_string()));
26 }
27
28 let planes = &src.planes;
29 let size = size_of::<T>();
30
31 Ok(YuvPlanarImage::<T> {
32 y_plane: bytemuck::cast_slice(planes[0].data().unwrap()),
33 y_stride: (planes[0].stride().unwrap() / size) as u32,
34 u_plane: bytemuck::cast_slice(planes[1].data().unwrap()),
35 u_stride: (planes[1].stride().unwrap() / size) as u32,
36 v_plane: bytemuck::cast_slice(planes[2].data().unwrap()),
37 v_stride: (planes[2].stride().unwrap() / size) as u32,
38 width: width.get(),
39 height: height.get(),
40 })
41}
42
43fn into_yuv_planar_image_mut<'a, T>(dst: &'a mut MappedPlanes, width: NonZeroU32, height: NonZeroU32) -> Result<YuvPlanarImageMut<'a, T>>
44where
45 T: Debug + Pod,
46{
47 if dst.planes.len() != 3 {
48 return Err(Error::Invalid("invalid plane count".to_string()));
49 }
50
51 let planes = dst.planes.as_mut_slice();
52 let size = size_of::<T>();
53
54 let y_stride = (planes[0].stride().unwrap() / size) as u32;
55 let u_stride = (planes[1].stride().unwrap() / size) as u32;
56 let v_stride = (planes[2].stride().unwrap() / size) as u32;
57
58 let (y_plane, rest) = planes.split_at_mut(1);
59 let (u_plane, v_plane) = rest.split_at_mut(1);
60
61 Ok(YuvPlanarImageMut::<T> {
62 y_plane: BufferStoreMut::Borrowed(bytemuck::cast_slice_mut(y_plane[0].data_mut().unwrap())),
63 y_stride,
64 u_plane: BufferStoreMut::Borrowed(bytemuck::cast_slice_mut(u_plane[0].data_mut().unwrap())),
65 u_stride,
66 v_plane: BufferStoreMut::Borrowed(bytemuck::cast_slice_mut(v_plane[0].data_mut().unwrap())),
67 v_stride,
68 width: width.get(),
69 height: height.get(),
70 })
71}
72
73fn into_yuv_bi_planar_image<'a, T>(src: &'a MappedPlanes, width: NonZeroU32, height: NonZeroU32) -> Result<YuvBiPlanarImage<'a, T>>
74where
75 T: Debug + Pod,
76{
77 if src.planes.len() != 2 {
78 return Err(Error::Invalid("invalid plane count".to_string()));
79 }
80
81 let planes = &src.planes;
82 let size = size_of::<T>();
83
84 Ok(YuvBiPlanarImage::<T> {
85 y_plane: bytemuck::cast_slice(planes[0].data().unwrap()),
86 y_stride: (planes[0].stride().unwrap() / size) as u32,
87 uv_plane: bytemuck::cast_slice(planes[1].data().unwrap()),
88 uv_stride: (planes[1].stride().unwrap() / size) as u32,
89 width: width.get(),
90 height: height.get(),
91 })
92}
93
94fn into_yuv_bi_planar_image_mut<'a, T>(dst: &'a mut MappedPlanes, width: NonZeroU32, height: NonZeroU32) -> Result<YuvBiPlanarImageMut<'a, T>>
95where
96 T: Debug + Pod,
97{
98 if dst.planes.len() != 2 {
99 return Err(Error::Invalid("invalid plane count".to_string()));
100 }
101
102 let planes = dst.planes.as_mut_slice();
103 let size = size_of::<T>();
104
105 let y_stride = (planes[0].stride().unwrap() / size) as u32;
106 let uv_stride = (planes[1].stride().unwrap() / size) as u32;
107
108 let (y_plane, uv_plane) = planes.split_at_mut(1);
109
110 Ok(YuvBiPlanarImageMut::<T> {
111 y_plane: BufferStoreMut::Borrowed(bytemuck::cast_slice_mut(y_plane[0].data_mut().unwrap())),
112 y_stride,
113 uv_plane: BufferStoreMut::Borrowed(bytemuck::cast_slice_mut(uv_plane[0].data_mut().unwrap())),
114 uv_stride,
115 width: width.get(),
116 height: height.get(),
117 })
118}
119
120fn into_yuv_packed_image<'a, T>(src: &'a MappedPlanes, width: NonZeroU32, height: NonZeroU32) -> Result<YuvPackedImage<'a, T>>
121where
122 T: Debug + Pod,
123{
124 if src.planes.len() != 1 {
125 return Err(Error::Invalid("invalid plane count".to_string()));
126 }
127
128 let planes = &src.planes;
129 let size = size_of::<T>();
130
131 Ok(YuvPackedImage::<T> {
132 yuy: bytemuck::cast_slice(planes[0].data().unwrap()),
133 yuy_stride: (planes[0].stride().unwrap() / size) as u32,
134 width: width.get(),
135 height: height.get(),
136 })
137}
138
139fn into_yuv_packed_image_mut<'a, T>(dst: &'a mut MappedPlanes, width: NonZeroU32, height: NonZeroU32) -> Result<YuvPackedImageMut<'a, T>>
140where
141 T: Debug + Pod,
142{
143 if dst.planes.len() != 1 {
144 return Err(Error::Invalid("invalid plane count".to_string()));
145 }
146
147 let planes = dst.planes.as_mut();
148 let size = size_of::<T>();
149
150 let yuy_stride = (planes[0].stride().unwrap() / size) as u32;
151
152 Ok(YuvPackedImageMut::<T> {
153 yuy: BufferStoreMut::Borrowed(bytemuck::cast_slice_mut(planes[0].data_mut().unwrap())),
154 yuy_stride,
155 width: width.get(),
156 height: height.get(),
157 })
158}
159
160impl From<ColorRange> for YuvRange {
161 fn from(range: ColorRange) -> Self {
162 match range {
163 ColorRange::Video => YuvRange::Limited,
164 ColorRange::Full => YuvRange::Full,
165 _ => YuvRange::Limited,
166 }
167 }
168}
169
170impl From<ColorMatrix> for YuvStandardMatrix {
171 fn from(color_matrix: ColorMatrix) -> Self {
172 match color_matrix {
173 ColorMatrix::BT709 => YuvStandardMatrix::Bt709,
174 ColorMatrix::FCC => YuvStandardMatrix::Fcc,
175 ColorMatrix::BT470BG | ColorMatrix::SMPTE170M => YuvStandardMatrix::Bt601,
176 ColorMatrix::SMPTE240M => YuvStandardMatrix::Custom(0.212, 0.087),
177 ColorMatrix::YCgCo => YuvStandardMatrix::Custom(0.25, 0.25),
178 ColorMatrix::BT2020NCL | ColorMatrix::BT2020CL => YuvStandardMatrix::Bt2020,
179 _ => YuvStandardMatrix::Bt601,
180 }
181 }
182}
183
184macro_rules! impl_rgb_to_rgb {
185 ($func_name:ident, $convert_func:ident) => {
186 fn $func_name(
187 src: &MappedPlanes,
188 dst: &mut MappedPlanes,
189 _color_range: ColorRange,
190 _color_matrix: ColorMatrix,
191 width: NonZeroU32,
192 height: NonZeroU32,
193 ) -> Result<()> {
194 let dst_stride = dst.plane_stride(0).unwrap() as u32;
195
196 yuv::$convert_func(
197 src.plane_data(0).unwrap(),
198 src.plane_stride(0).unwrap() as u32,
199 dst.plane_data_mut(0).unwrap(),
200 dst_stride,
201 width.get(),
202 height.get(),
203 )
204 .map_err(|e| Error::Invalid(e.to_string()))?;
205
206 Ok(())
207 }
208 };
209}
210
211macro_rules! impl_rgb_to_yuv {
212 ($func_name:ident, $convert_func:ident, $into_image_func:ident) => {
213 fn $func_name(
214 src: &MappedPlanes,
215 dst: &mut MappedPlanes,
216 color_range: ColorRange,
217 color_matrix: ColorMatrix,
218 width: NonZeroU32,
219 height: NonZeroU32,
220 ) -> Result<()> {
221 let mut yuv_image = $into_image_func(dst, width, height)?;
222
223 yuv::$convert_func(
224 &mut yuv_image,
225 src.plane_data(0).unwrap(),
226 src.plane_stride(0).unwrap() as u32,
227 color_range.into(),
228 color_matrix.into(),
229 YuvConversionMode::Fast,
230 )
231 .map_err(|e| Error::Invalid(e.to_string()))?;
232
233 Ok(())
234 }
235 };
236}
237
238macro_rules! impl_yuv_to_rgb {
239 ($func_name:ident, $convert_func:ident, $into_image_func:ident) => {
240 fn $func_name(
241 src: &MappedPlanes,
242 dst: &mut MappedPlanes,
243 color_range: ColorRange,
244 color_matrix: ColorMatrix,
245 width: NonZeroU32,
246 height: NonZeroU32,
247 ) -> Result<()> {
248 let yuv_image = $into_image_func(src, width, height)?;
249 let dst_stride = dst.plane_stride(0).unwrap() as u32;
250
251 yuv::$convert_func(&yuv_image, dst.plane_data_mut(0).unwrap(), dst_stride, color_range.into(), color_matrix.into())
252 .map_err(|e| Error::Invalid(e.to_string()))?;
253
254 Ok(())
255 }
256 };
257}
258
259macro_rules! impl_yuv_to_rgb_with_conversion_mode {
260 ($func_name:ident, $convert_func:ident, $into_image_func:ident, $conversion_mode:ident) => {
261 fn $func_name(
262 src: &MappedPlanes,
263 dst: &mut MappedPlanes,
264 color_range: ColorRange,
265 color_matrix: ColorMatrix,
266 width: NonZeroU32,
267 height: NonZeroU32,
268 ) -> Result<()> {
269 let yuv_image = $into_image_func(src, width, height)?;
270 let dst_stride = dst.plane_stride(0).unwrap() as u32;
271
272 yuv::$convert_func(&yuv_image, dst.plane_data_mut(0).unwrap(), dst_stride, color_range.into(), color_matrix.into(), $conversion_mode)
273 .map_err(|e| Error::Invalid(e.to_string()))?;
274
275 Ok(())
276 }
277 };
278}
279
280macro_rules! impl_yuv_to_rgb_with_byte_order {
281 ($func_name:ident, $convert_func:ident, $into_image_func:ident, $byte_order:ident) => {
282 fn $func_name(
283 src: &MappedPlanes,
284 dst: &mut MappedPlanes,
285 color_range: ColorRange,
286 color_matrix: ColorMatrix,
287 width: NonZeroU32,
288 height: NonZeroU32,
289 ) -> Result<()> {
290 let yuv_image = $into_image_func(src, width, height)?;
291 let dst_stride = dst.plane_stride(0).unwrap() as u32;
292
293 yuv::$convert_func(&yuv_image, dst.plane_data_mut(0).unwrap(), dst_stride, $byte_order, color_range.into(), color_matrix.into())
294 .map_err(|e| Error::Invalid(e.to_string()))?;
295
296 Ok(())
297 }
298 };
299}
300
301macro_rules! impl_yuv_to_yuv {
302 ($func_name:ident, $convert_func:ident, $into_src_image_func:ident, $into_dst_image_func:ident) => {
303 fn $func_name(
304 src: &MappedPlanes,
305 dst: &mut MappedPlanes,
306 _color_range: ColorRange,
307 _color_matrix: ColorMatrix,
308 width: NonZeroU32,
309 height: NonZeroU32,
310 ) -> Result<()> {
311 let src_image = $into_src_image_func(src, width, height)?;
312 let mut dst_image = $into_dst_image_func(dst, width, height)?;
313
314 yuv::$convert_func(&mut dst_image, &src_image).map_err(|e| Error::Invalid(e.to_string()))?;
315
316 Ok(())
317 }
318 };
319}
320
321impl_rgb_to_rgb!(bgra32_to_rgba32, bgra_to_rgba);
322
323impl_rgb_to_yuv!(bgra32_to_i420, bgra_to_yuv420, into_yuv_planar_image_mut);
324impl_rgb_to_yuv!(bgra32_to_i422, bgra_to_yuv422, into_yuv_planar_image_mut);
325impl_rgb_to_yuv!(bgra32_to_i444, bgra_to_yuv444, into_yuv_planar_image_mut);
326impl_rgb_to_yuv!(bgra32_to_nv12, bgra_to_yuv_nv12, into_yuv_bi_planar_image_mut);
327impl_rgb_to_yuv!(bgra32_to_nv16, bgra_to_yuv_nv16, into_yuv_bi_planar_image_mut);
328impl_rgb_to_yuv!(bgra32_to_nv24, bgra_to_yuv_nv24, into_yuv_bi_planar_image_mut);
329impl_rgb_to_yuv!(bgra32_to_nv21, bgra_to_yuv_nv21, into_yuv_bi_planar_image_mut);
330impl_rgb_to_yuv!(bgra32_to_nv61, bgra_to_yuv_nv61, into_yuv_bi_planar_image_mut);
331impl_rgb_to_yuv!(bgra32_to_nv42, bgra_to_yuv_nv42, into_yuv_bi_planar_image_mut);
332
333impl_rgb_to_rgb!(rgba32_to_bgra32, rgba_to_bgra);
334
335impl_rgb_to_yuv!(rgba32_to_i420, rgba_to_yuv420, into_yuv_planar_image_mut);
336impl_rgb_to_yuv!(rgba32_to_i422, rgba_to_yuv422, into_yuv_planar_image_mut);
337impl_rgb_to_yuv!(rgba32_to_i444, rgba_to_yuv444, into_yuv_planar_image_mut);
338impl_rgb_to_yuv!(rgba32_to_nv12, rgba_to_yuv_nv12, into_yuv_bi_planar_image_mut);
339impl_rgb_to_yuv!(rgba32_to_nv16, rgba_to_yuv_nv16, into_yuv_bi_planar_image_mut);
340impl_rgb_to_yuv!(rgba32_to_nv24, rgba_to_yuv_nv24, into_yuv_bi_planar_image_mut);
341impl_rgb_to_yuv!(rgba32_to_nv21, rgba_to_yuv_nv21, into_yuv_bi_planar_image_mut);
342impl_rgb_to_yuv!(rgba32_to_nv61, rgba_to_yuv_nv61, into_yuv_bi_planar_image_mut);
343impl_rgb_to_yuv!(rgba32_to_nv42, rgba_to_yuv_nv42, into_yuv_bi_planar_image_mut);
344
345impl_yuv_to_rgb!(i420_to_bgra32, yuv420_to_bgra, into_yuv_planar_image);
346impl_yuv_to_rgb!(i420_to_rgba32, yuv420_to_rgba, into_yuv_planar_image);
347impl_yuv_to_rgb!(i420_to_bgr24, yuv420_to_bgr, into_yuv_planar_image);
348impl_yuv_to_rgb!(i420_to_rgb24, yuv420_to_rgb, into_yuv_planar_image);
349
350impl_yuv_to_yuv!(i420_to_yuyv, yuv420_to_yuyv422, into_yuv_planar_image, into_yuv_packed_image_mut);
351impl_yuv_to_yuv!(i420_to_yvyu, yuv420_to_yvyu422, into_yuv_planar_image, into_yuv_packed_image_mut);
352impl_yuv_to_yuv!(i420_to_uyvy, yuv420_to_uyvy422, into_yuv_planar_image, into_yuv_packed_image_mut);
353impl_yuv_to_yuv!(i420_to_vyuy, yuv420_to_vyuy422, into_yuv_planar_image, into_yuv_packed_image_mut);
354
355impl_yuv_to_rgb!(i422_to_bgra32, yuv422_to_bgra, into_yuv_planar_image);
356impl_yuv_to_rgb!(i422_to_rgba32, yuv422_to_rgba, into_yuv_planar_image);
357impl_yuv_to_rgb!(i422_to_bgr24, yuv422_to_bgr, into_yuv_planar_image);
358impl_yuv_to_rgb!(i422_to_rgb24, yuv422_to_rgb, into_yuv_planar_image);
359
360impl_yuv_to_yuv!(i422_to_yuyv, yuv422_to_yuyv422, into_yuv_planar_image, into_yuv_packed_image_mut);
361impl_yuv_to_yuv!(i422_to_yvyu, yuv422_to_yvyu422, into_yuv_planar_image, into_yuv_packed_image_mut);
362impl_yuv_to_yuv!(i422_to_uyvy, yuv422_to_uyvy422, into_yuv_planar_image, into_yuv_packed_image_mut);
363impl_yuv_to_yuv!(i422_to_vyuy, yuv422_to_vyuy422, into_yuv_planar_image, into_yuv_packed_image_mut);
364
365impl_yuv_to_rgb!(i444_to_bgra32, yuv444_to_bgra, into_yuv_planar_image);
366impl_yuv_to_rgb!(i444_to_rgba32, yuv444_to_rgba, into_yuv_planar_image);
367impl_yuv_to_rgb!(i444_to_bgr24, yuv444_to_bgr, into_yuv_planar_image);
368impl_yuv_to_rgb!(i444_to_rgb24, yuv444_to_rgb, into_yuv_planar_image);
369
370impl_yuv_to_yuv!(i444_to_yuyv, yuv444_to_yuyv422, into_yuv_planar_image, into_yuv_packed_image_mut);
371impl_yuv_to_yuv!(i444_to_yvyu, yuv444_to_yvyu422, into_yuv_planar_image, into_yuv_packed_image_mut);
372impl_yuv_to_yuv!(i444_to_uyvy, yuv444_to_uyvy422, into_yuv_planar_image, into_yuv_packed_image_mut);
373impl_yuv_to_yuv!(i444_to_vyuy, yuv444_to_vyuy422, into_yuv_planar_image, into_yuv_packed_image_mut);
374
375impl_yuv_to_rgb_with_conversion_mode!(nv12_to_bgra32, yuv_nv12_to_bgra, into_yuv_bi_planar_image, Fast);
376impl_yuv_to_rgb_with_conversion_mode!(nv12_to_rgba32, yuv_nv12_to_rgba, into_yuv_bi_planar_image, Fast);
377impl_yuv_to_rgb_with_conversion_mode!(nv12_to_bgr24, yuv_nv12_to_bgr, into_yuv_bi_planar_image, Fast);
378impl_yuv_to_rgb_with_conversion_mode!(nv12_to_rgb24, yuv_nv12_to_rgb, into_yuv_bi_planar_image, Fast);
379
380impl_yuv_to_rgb_with_conversion_mode!(nv16_to_bgra32, yuv_nv16_to_bgra, into_yuv_bi_planar_image, Fast);
381impl_yuv_to_rgb_with_conversion_mode!(nv16_to_rgba32, yuv_nv16_to_rgba, into_yuv_bi_planar_image, Fast);
382impl_yuv_to_rgb_with_conversion_mode!(nv16_to_bgr24, yuv_nv16_to_bgr, into_yuv_bi_planar_image, Fast);
383impl_yuv_to_rgb_with_conversion_mode!(nv16_to_rgb24, yuv_nv16_to_rgb, into_yuv_bi_planar_image, Fast);
384
385impl_yuv_to_rgb_with_conversion_mode!(nv24_to_bgra32, yuv_nv24_to_bgra, into_yuv_bi_planar_image, Fast);
386impl_yuv_to_rgb_with_conversion_mode!(nv24_to_rgba32, yuv_nv24_to_rgba, into_yuv_bi_planar_image, Fast);
387impl_yuv_to_rgb_with_conversion_mode!(nv24_to_bgr24, yuv_nv24_to_bgr, into_yuv_bi_planar_image, Fast);
388impl_yuv_to_rgb_with_conversion_mode!(nv24_to_rgb24, yuv_nv24_to_rgb, into_yuv_bi_planar_image, Fast);
389
390impl_yuv_to_rgb_with_conversion_mode!(nv21_to_bgra32, yuv_nv21_to_bgra, into_yuv_bi_planar_image, Fast);
391impl_yuv_to_rgb_with_conversion_mode!(nv21_to_rgba32, yuv_nv21_to_rgba, into_yuv_bi_planar_image, Fast);
392impl_yuv_to_rgb_with_conversion_mode!(nv21_to_bgr24, yuv_nv21_to_bgr, into_yuv_bi_planar_image, Fast);
393impl_yuv_to_rgb_with_conversion_mode!(nv21_to_rgb24, yuv_nv21_to_rgb, into_yuv_bi_planar_image, Fast);
394
395impl_yuv_to_rgb_with_conversion_mode!(nv61_to_bgra32, yuv_nv61_to_bgra, into_yuv_bi_planar_image, Fast);
396impl_yuv_to_rgb_with_conversion_mode!(nv61_to_rgba32, yuv_nv61_to_rgba, into_yuv_bi_planar_image, Fast);
397impl_yuv_to_rgb_with_conversion_mode!(nv61_to_bgr24, yuv_nv61_to_bgr, into_yuv_bi_planar_image, Fast);
398impl_yuv_to_rgb_with_conversion_mode!(nv61_to_rgb24, yuv_nv61_to_rgb, into_yuv_bi_planar_image, Fast);
399
400impl_yuv_to_rgb_with_conversion_mode!(nv42_to_bgra32, yuv_nv42_to_bgra, into_yuv_bi_planar_image, Fast);
401impl_yuv_to_rgb_with_conversion_mode!(nv42_to_rgba32, yuv_nv42_to_rgba, into_yuv_bi_planar_image, Fast);
402impl_yuv_to_rgb_with_conversion_mode!(nv42_to_bgr24, yuv_nv42_to_bgr, into_yuv_bi_planar_image, Fast);
403impl_yuv_to_rgb_with_conversion_mode!(nv42_to_rgb24, yuv_nv42_to_rgb, into_yuv_bi_planar_image, Fast);
404
405impl_yuv_to_rgb!(yuyv_to_bgra32, yuyv422_to_bgra, into_yuv_packed_image);
406impl_yuv_to_rgb!(yuyv_to_rgba32, yuyv422_to_rgba, into_yuv_packed_image);
407impl_yuv_to_rgb!(yuyv_to_bgr24, yuyv422_to_bgr, into_yuv_packed_image);
408impl_yuv_to_rgb!(yuyv_to_rgb24, yuyv422_to_rgb, into_yuv_packed_image);
409
410impl_yuv_to_yuv!(yuyv_to_i420, yuyv422_to_yuv420, into_yuv_packed_image, into_yuv_planar_image_mut);
411impl_yuv_to_yuv!(yuyv_to_i422, yuyv422_to_yuv422, into_yuv_packed_image, into_yuv_planar_image_mut);
412impl_yuv_to_yuv!(yuyv_to_i444, yuyv422_to_yuv444, into_yuv_packed_image, into_yuv_planar_image_mut);
413
414impl_yuv_to_rgb!(yvyu_to_bgra32, yvyu422_to_bgra, into_yuv_packed_image);
415impl_yuv_to_rgb!(yvyu_to_rgba32, yvyu422_to_rgba, into_yuv_packed_image);
416impl_yuv_to_rgb!(yvyu_to_bgr24, yvyu422_to_bgr, into_yuv_packed_image);
417impl_yuv_to_rgb!(yvyu_to_rgb24, yvyu422_to_rgb, into_yuv_packed_image);
418
419impl_yuv_to_yuv!(yvyu_to_i420, yvyu422_to_yuv420, into_yuv_packed_image, into_yuv_planar_image_mut);
420impl_yuv_to_yuv!(yvyu_to_i422, yvyu422_to_yuv422, into_yuv_packed_image, into_yuv_planar_image_mut);
421impl_yuv_to_yuv!(yvyu_to_i444, yvyu422_to_yuv444, into_yuv_packed_image, into_yuv_planar_image_mut);
422
423impl_yuv_to_rgb!(uyvy_to_bgra32, uyvy422_to_bgra, into_yuv_packed_image);
424impl_yuv_to_rgb!(uyvy_to_rgba32, uyvy422_to_rgba, into_yuv_packed_image);
425impl_yuv_to_rgb!(uyvy_to_bgr24, uyvy422_to_bgr, into_yuv_packed_image);
426impl_yuv_to_rgb!(uyvy_to_rgb24, uyvy422_to_rgb, into_yuv_packed_image);
427
428impl_yuv_to_yuv!(uyvy_to_i420, uyvy422_to_yuv420, into_yuv_packed_image, into_yuv_planar_image_mut);
429impl_yuv_to_yuv!(uyvy_to_i422, uyvy422_to_yuv422, into_yuv_packed_image, into_yuv_planar_image_mut);
430impl_yuv_to_yuv!(uyvy_to_i444, uyvy422_to_yuv444, into_yuv_packed_image, into_yuv_planar_image_mut);
431
432impl_yuv_to_rgb!(vyuy_to_bgra32, vyuy422_to_bgra, into_yuv_packed_image);
433impl_yuv_to_rgb!(vyuy_to_rgba32, vyuy422_to_rgba, into_yuv_packed_image);
434impl_yuv_to_rgb!(vyuy_to_bgr24, vyuy422_to_bgr, into_yuv_packed_image);
435impl_yuv_to_rgb!(vyuy_to_rgb24, vyuy422_to_rgb, into_yuv_packed_image);
436
437impl_yuv_to_yuv!(vyuy_to_i420, vyuy422_to_yuv420, into_yuv_packed_image, into_yuv_planar_image_mut);
438impl_yuv_to_yuv!(vyuy_to_i422, vyuy422_to_yuv422, into_yuv_packed_image, into_yuv_planar_image_mut);
439impl_yuv_to_yuv!(vyuy_to_i444, vyuy422_to_yuv444, into_yuv_packed_image, into_yuv_planar_image_mut);
440
441impl_yuv_to_rgb_with_byte_order!(i010_to_rgb30, i010_to_ra30, into_yuv_planar_image, Network);
442impl_yuv_to_rgb_with_byte_order!(i210_to_rgb30, i210_to_ra30, into_yuv_planar_image, Network);
443impl_yuv_to_rgb_with_byte_order!(i410_to_rgb30, i410_to_ra30, into_yuv_planar_image, Network);
444
445impl_yuv_to_rgb_with_byte_order!(p010_to_rgb30, p010_to_ra30, into_yuv_bi_planar_image, Network);
446impl_yuv_to_rgb_with_byte_order!(p210_to_rgb30, p210_to_ra30, into_yuv_bi_planar_image, Network);
447
448type VideoFormatConvertFunc = fn(&MappedPlanes, &mut MappedPlanes, ColorRange, ColorMatrix, NonZeroU32, NonZeroU32) -> Result<()>;
449
450const PIXEL_FORMAT_MAX: usize = PixelFormat::COUNT;
451
452static VIDEO_FORMAT_CONVERT_FUNCS: LazyLock<[[Option<VideoFormatConvertFunc>; PIXEL_FORMAT_MAX]; PIXEL_FORMAT_MAX]> = LazyLock::new(|| {
453 let mut funcs: [[Option<VideoFormatConvertFunc>; PIXEL_FORMAT_MAX]; PIXEL_FORMAT_MAX] = [[None; PIXEL_FORMAT_MAX]; PIXEL_FORMAT_MAX];
454 funcs[PixelFormat::BGRA32 as usize][PixelFormat::RGBA32 as usize] = Some(bgra32_to_rgba32);
455 funcs[PixelFormat::BGRA32 as usize][PixelFormat::I420 as usize] = Some(bgra32_to_i420);
456 funcs[PixelFormat::BGRA32 as usize][PixelFormat::I422 as usize] = Some(bgra32_to_i422);
457 funcs[PixelFormat::BGRA32 as usize][PixelFormat::I444 as usize] = Some(bgra32_to_i444);
458 funcs[PixelFormat::BGRA32 as usize][PixelFormat::NV12 as usize] = Some(bgra32_to_nv12);
459 funcs[PixelFormat::BGRA32 as usize][PixelFormat::NV16 as usize] = Some(bgra32_to_nv16);
460 funcs[PixelFormat::BGRA32 as usize][PixelFormat::NV24 as usize] = Some(bgra32_to_nv24);
461 funcs[PixelFormat::BGRA32 as usize][PixelFormat::NV21 as usize] = Some(bgra32_to_nv21);
462 funcs[PixelFormat::BGRA32 as usize][PixelFormat::NV61 as usize] = Some(bgra32_to_nv61);
463 funcs[PixelFormat::BGRA32 as usize][PixelFormat::NV42 as usize] = Some(bgra32_to_nv42);
464 funcs[PixelFormat::RGBA32 as usize][PixelFormat::BGRA32 as usize] = Some(rgba32_to_bgra32);
465 funcs[PixelFormat::RGBA32 as usize][PixelFormat::I420 as usize] = Some(rgba32_to_i420);
466 funcs[PixelFormat::RGBA32 as usize][PixelFormat::I422 as usize] = Some(rgba32_to_i422);
467 funcs[PixelFormat::RGBA32 as usize][PixelFormat::I444 as usize] = Some(rgba32_to_i444);
468 funcs[PixelFormat::RGBA32 as usize][PixelFormat::NV12 as usize] = Some(rgba32_to_nv12);
469 funcs[PixelFormat::RGBA32 as usize][PixelFormat::NV16 as usize] = Some(rgba32_to_nv16);
470 funcs[PixelFormat::RGBA32 as usize][PixelFormat::NV24 as usize] = Some(rgba32_to_nv24);
471 funcs[PixelFormat::RGBA32 as usize][PixelFormat::NV21 as usize] = Some(rgba32_to_nv21);
472 funcs[PixelFormat::RGBA32 as usize][PixelFormat::NV61 as usize] = Some(rgba32_to_nv61);
473 funcs[PixelFormat::RGBA32 as usize][PixelFormat::NV42 as usize] = Some(rgba32_to_nv42);
474 funcs[PixelFormat::I420 as usize][PixelFormat::BGRA32 as usize] = Some(i420_to_bgra32);
475 funcs[PixelFormat::I420 as usize][PixelFormat::RGBA32 as usize] = Some(i420_to_rgba32);
476 funcs[PixelFormat::I420 as usize][PixelFormat::BGR24 as usize] = Some(i420_to_bgr24);
477 funcs[PixelFormat::I420 as usize][PixelFormat::RGB24 as usize] = Some(i420_to_rgb24);
478 funcs[PixelFormat::I420 as usize][PixelFormat::YUYV as usize] = Some(i420_to_yuyv);
479 funcs[PixelFormat::I420 as usize][PixelFormat::YVYU as usize] = Some(i420_to_yvyu);
480 funcs[PixelFormat::I420 as usize][PixelFormat::UYVY as usize] = Some(i420_to_uyvy);
481 funcs[PixelFormat::I420 as usize][PixelFormat::VYUY as usize] = Some(i420_to_vyuy);
482 funcs[PixelFormat::I422 as usize][PixelFormat::BGRA32 as usize] = Some(i422_to_bgra32);
483 funcs[PixelFormat::I422 as usize][PixelFormat::RGBA32 as usize] = Some(i422_to_rgba32);
484 funcs[PixelFormat::I422 as usize][PixelFormat::BGR24 as usize] = Some(i422_to_bgr24);
485 funcs[PixelFormat::I422 as usize][PixelFormat::RGB24 as usize] = Some(i422_to_rgb24);
486 funcs[PixelFormat::I422 as usize][PixelFormat::YUYV as usize] = Some(i422_to_yuyv);
487 funcs[PixelFormat::I422 as usize][PixelFormat::YVYU as usize] = Some(i422_to_yvyu);
488 funcs[PixelFormat::I422 as usize][PixelFormat::UYVY as usize] = Some(i422_to_uyvy);
489 funcs[PixelFormat::I422 as usize][PixelFormat::VYUY as usize] = Some(i422_to_vyuy);
490 funcs[PixelFormat::I444 as usize][PixelFormat::BGRA32 as usize] = Some(i444_to_bgra32);
491 funcs[PixelFormat::I444 as usize][PixelFormat::RGBA32 as usize] = Some(i444_to_rgba32);
492 funcs[PixelFormat::I444 as usize][PixelFormat::BGR24 as usize] = Some(i444_to_bgr24);
493 funcs[PixelFormat::I444 as usize][PixelFormat::RGB24 as usize] = Some(i444_to_rgb24);
494 funcs[PixelFormat::I444 as usize][PixelFormat::YUYV as usize] = Some(i444_to_yuyv);
495 funcs[PixelFormat::I444 as usize][PixelFormat::YVYU as usize] = Some(i444_to_yvyu);
496 funcs[PixelFormat::I444 as usize][PixelFormat::UYVY as usize] = Some(i444_to_uyvy);
497 funcs[PixelFormat::I444 as usize][PixelFormat::VYUY as usize] = Some(i444_to_vyuy);
498 funcs[PixelFormat::NV12 as usize][PixelFormat::BGRA32 as usize] = Some(nv12_to_bgra32);
499 funcs[PixelFormat::NV12 as usize][PixelFormat::RGBA32 as usize] = Some(nv12_to_rgba32);
500 funcs[PixelFormat::NV12 as usize][PixelFormat::BGR24 as usize] = Some(nv12_to_bgr24);
501 funcs[PixelFormat::NV12 as usize][PixelFormat::RGB24 as usize] = Some(nv12_to_rgb24);
502 funcs[PixelFormat::NV16 as usize][PixelFormat::BGRA32 as usize] = Some(nv16_to_bgra32);
503 funcs[PixelFormat::NV16 as usize][PixelFormat::RGBA32 as usize] = Some(nv16_to_rgba32);
504 funcs[PixelFormat::NV16 as usize][PixelFormat::BGR24 as usize] = Some(nv16_to_bgr24);
505 funcs[PixelFormat::NV16 as usize][PixelFormat::RGB24 as usize] = Some(nv16_to_rgb24);
506 funcs[PixelFormat::NV24 as usize][PixelFormat::BGRA32 as usize] = Some(nv24_to_bgra32);
507 funcs[PixelFormat::NV24 as usize][PixelFormat::RGBA32 as usize] = Some(nv24_to_rgba32);
508 funcs[PixelFormat::NV24 as usize][PixelFormat::BGR24 as usize] = Some(nv24_to_bgr24);
509 funcs[PixelFormat::NV24 as usize][PixelFormat::RGB24 as usize] = Some(nv24_to_rgb24);
510 funcs[PixelFormat::NV21 as usize][PixelFormat::BGRA32 as usize] = Some(nv21_to_bgra32);
511 funcs[PixelFormat::NV21 as usize][PixelFormat::RGBA32 as usize] = Some(nv21_to_rgba32);
512 funcs[PixelFormat::NV21 as usize][PixelFormat::BGR24 as usize] = Some(nv21_to_bgr24);
513 funcs[PixelFormat::NV21 as usize][PixelFormat::RGB24 as usize] = Some(nv21_to_rgb24);
514 funcs[PixelFormat::NV61 as usize][PixelFormat::BGRA32 as usize] = Some(nv61_to_bgra32);
515 funcs[PixelFormat::NV61 as usize][PixelFormat::RGBA32 as usize] = Some(nv61_to_rgba32);
516 funcs[PixelFormat::NV61 as usize][PixelFormat::BGR24 as usize] = Some(nv61_to_bgr24);
517 funcs[PixelFormat::NV61 as usize][PixelFormat::RGB24 as usize] = Some(nv61_to_rgb24);
518 funcs[PixelFormat::NV42 as usize][PixelFormat::BGRA32 as usize] = Some(nv42_to_bgra32);
519 funcs[PixelFormat::NV42 as usize][PixelFormat::RGBA32 as usize] = Some(nv42_to_rgba32);
520 funcs[PixelFormat::NV42 as usize][PixelFormat::BGR24 as usize] = Some(nv42_to_bgr24);
521 funcs[PixelFormat::NV42 as usize][PixelFormat::RGB24 as usize] = Some(nv42_to_rgb24);
522 funcs[PixelFormat::YUYV as usize][PixelFormat::BGRA32 as usize] = Some(yuyv_to_bgra32);
523 funcs[PixelFormat::YUYV as usize][PixelFormat::RGBA32 as usize] = Some(yuyv_to_rgba32);
524 funcs[PixelFormat::YUYV as usize][PixelFormat::BGR24 as usize] = Some(yuyv_to_bgr24);
525 funcs[PixelFormat::YUYV as usize][PixelFormat::RGB24 as usize] = Some(yuyv_to_rgb24);
526 funcs[PixelFormat::YUYV as usize][PixelFormat::I420 as usize] = Some(yuyv_to_i420);
527 funcs[PixelFormat::YUYV as usize][PixelFormat::I422 as usize] = Some(yuyv_to_i422);
528 funcs[PixelFormat::YUYV as usize][PixelFormat::I444 as usize] = Some(yuyv_to_i444);
529 funcs[PixelFormat::YVYU as usize][PixelFormat::BGRA32 as usize] = Some(yvyu_to_bgra32);
530 funcs[PixelFormat::YVYU as usize][PixelFormat::RGBA32 as usize] = Some(yvyu_to_rgba32);
531 funcs[PixelFormat::YVYU as usize][PixelFormat::BGR24 as usize] = Some(yvyu_to_bgr24);
532 funcs[PixelFormat::YVYU as usize][PixelFormat::RGB24 as usize] = Some(yvyu_to_rgb24);
533 funcs[PixelFormat::YVYU as usize][PixelFormat::I420 as usize] = Some(yvyu_to_i420);
534 funcs[PixelFormat::YVYU as usize][PixelFormat::I422 as usize] = Some(yvyu_to_i422);
535 funcs[PixelFormat::YVYU as usize][PixelFormat::I444 as usize] = Some(yvyu_to_i444);
536 funcs[PixelFormat::UYVY as usize][PixelFormat::BGRA32 as usize] = Some(uyvy_to_bgra32);
537 funcs[PixelFormat::UYVY as usize][PixelFormat::RGBA32 as usize] = Some(uyvy_to_rgba32);
538 funcs[PixelFormat::UYVY as usize][PixelFormat::BGR24 as usize] = Some(uyvy_to_bgr24);
539 funcs[PixelFormat::UYVY as usize][PixelFormat::RGB24 as usize] = Some(uyvy_to_rgb24);
540 funcs[PixelFormat::UYVY as usize][PixelFormat::I420 as usize] = Some(uyvy_to_i420);
541 funcs[PixelFormat::UYVY as usize][PixelFormat::I422 as usize] = Some(uyvy_to_i422);
542 funcs[PixelFormat::UYVY as usize][PixelFormat::I444 as usize] = Some(uyvy_to_i444);
543 funcs[PixelFormat::VYUY as usize][PixelFormat::BGRA32 as usize] = Some(vyuy_to_bgra32);
544 funcs[PixelFormat::VYUY as usize][PixelFormat::RGBA32 as usize] = Some(vyuy_to_rgba32);
545 funcs[PixelFormat::VYUY as usize][PixelFormat::BGR24 as usize] = Some(vyuy_to_bgr24);
546 funcs[PixelFormat::VYUY as usize][PixelFormat::RGB24 as usize] = Some(vyuy_to_rgb24);
547 funcs[PixelFormat::VYUY as usize][PixelFormat::I420 as usize] = Some(vyuy_to_i420);
548 funcs[PixelFormat::VYUY as usize][PixelFormat::I422 as usize] = Some(vyuy_to_i422);
549 funcs[PixelFormat::VYUY as usize][PixelFormat::I444 as usize] = Some(vyuy_to_i444);
550 funcs[PixelFormat::I010 as usize][PixelFormat::RGB30 as usize] = Some(i010_to_rgb30);
551 funcs[PixelFormat::I210 as usize][PixelFormat::RGB30 as usize] = Some(i210_to_rgb30);
552 funcs[PixelFormat::I410 as usize][PixelFormat::RGB30 as usize] = Some(i410_to_rgb30);
553 funcs[PixelFormat::P010 as usize][PixelFormat::RGB30 as usize] = Some(p010_to_rgb30);
554 funcs[PixelFormat::P210 as usize][PixelFormat::RGB30 as usize] = Some(p210_to_rgb30);
555 funcs
556});
557
558fn data_copy(src: &MappedPlanes, dst: &mut MappedPlanes, format: PixelFormat, width: NonZeroU32, height: NonZeroU32) -> Result<()> {
559 if src.planes.len() != dst.planes.len() {
560 return Err(Error::Invalid("planes size mismatch".to_string()));
561 }
562
563 for (plane_index, (src_plane, dst_plane)) in src.planes.iter().zip(&mut dst.planes).enumerate() {
564 let plane_row_bytes = format.calc_plane_row_bytes(plane_index, width.get()) as usize;
565 let plane_height = format.calc_plane_height(plane_index, height.get());
566 if let (Some(src_stride), Some(dst_stride)) = (src_plane.stride(), dst_plane.stride()) {
567 if let (Some(src_data), Some(dst_data)) = (src_plane.data(), dst_plane.data_mut()) {
568 for row in 0..plane_height {
569 let src_start = row as usize * src_stride;
570 let dst_start = row as usize * dst_stride;
571 dst_data[dst_start..dst_start + plane_row_bytes].copy_from_slice(&src_data[src_start..src_start + plane_row_bytes]);
572 }
573 }
574 }
575 }
576
577 Ok(())
578}
579
580impl Frame<'_> {
581 pub fn convert_video_to(&self, dst: &mut Frame) -> Result<()> {
582 let (FrameDescriptor::Video(src_desc), FrameDescriptor::Video(dst_desc)) = (&self.desc, &dst.desc) else {
583 return Err(Error::Invalid("not video frame".to_string()));
584 };
585
586 VideoFrame::convert_video_to_internal(src_desc, &self.data, dst_desc, &mut dst.data)
587 }
588}
589
590impl VideoFrame<'_> {
591 fn convert_video_to_internal(
592 src_desc: &VideoFrameDescriptor,
593 src_data: &FrameData,
594 dst_desc: &VideoFrameDescriptor,
595 dst_data: &mut FrameData,
596 ) -> Result<()> {
597 if src_desc.dimensions != dst_desc.dimensions {
598 return Err(Error::Invalid("video frame dimensions mismatch".to_string()));
599 }
600
601 let guard = src_data.map().map_err(|_| Error::Invalid("not readable".into()))?;
602 let mut dst_guard = dst_data.map_mut().map_err(|_| Error::Invalid("not writable".into()))?;
603 let src_planes = guard.planes().unwrap();
604 let mut dst_planes = dst_guard.planes_mut().unwrap();
605
606 if src_desc.format == dst_desc.format {
607 return data_copy(&src_planes, &mut dst_planes, src_desc.format, src_desc.width(), src_desc.height());
608 }
609
610 let convert = VIDEO_FORMAT_CONVERT_FUNCS[src_desc.format as usize][dst_desc.format as usize]
611 .ok_or_else(|| Error::Unsupported("video format conversion".to_string()))?;
612
613 convert(&src_planes, &mut dst_planes, src_desc.color_range, src_desc.color_matrix, src_desc.width(), src_desc.height())
614 }
615
616 pub fn convert_to(&self, dst: &mut VideoFrame) -> Result<()> {
617 Self::convert_video_to_internal(&self.desc, &self.data, &dst.desc, &mut dst.data)
618 }
619}