media_core/video/
convert.rs

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}