media_core/video/
scale.rs

1use std::{borrow::Cow, fmt::Debug};
2
3use bytemuck::{self, Pod};
4use pic_scale::{BufferStore, ImageStore, ImageStoreMut, LinearScaler, ResamplingFunction, Scaling, ScalingU16};
5
6use super::{
7    frame::VideoFrame,
8    video::{PixelFormat, ScaleFilter, VideoFrameDescriptor},
9};
10use crate::{
11    error::Error,
12    frame::{DataMappable, Frame, FrameData, MappedPlane},
13    FrameDescriptor, Result,
14};
15
16impl From<ScaleFilter> for ResamplingFunction {
17    fn from(filter: ScaleFilter) -> Self {
18        match filter {
19            ScaleFilter::Nearest => ResamplingFunction::Nearest,
20            ScaleFilter::Bilinear => ResamplingFunction::Bilinear,
21            ScaleFilter::Bicubic => ResamplingFunction::Bicubic,
22        }
23    }
24}
25
26fn into_image_store<'a, T, const N: usize>(src: &'a MappedPlane, width: u32, height: u32) -> Result<ImageStore<'a, T, N>>
27where
28    T: Debug + Pod,
29{
30    Ok(ImageStore::<T, N> {
31        buffer: Cow::Borrowed(bytemuck::cast_slice(src.data().unwrap())),
32        channels: N,
33        width: width as usize,
34        height: height as usize,
35        stride: src.stride().unwrap() / size_of::<T>(),
36        bit_depth: 0,
37    })
38}
39
40fn into_image_store_mut<'a, T, const N: usize>(dst: &'a mut MappedPlane, width: u32, height: u32) -> Result<ImageStoreMut<'a, T, N>>
41where
42    T: Debug + Pod,
43{
44    let stride = dst.stride().unwrap() / size_of::<T>();
45
46    Ok(ImageStoreMut::<T, N> {
47        buffer: BufferStore::Borrowed(bytemuck::cast_slice_mut(dst.data_mut().unwrap())),
48        channels: N,
49        width: width as usize,
50        height: height as usize,
51        stride,
52        bit_depth: 0,
53    })
54}
55
56impl Frame<'_> {
57    pub fn scale_to(&self, dst: &mut Frame<'_>, scale_filter: ScaleFilter) -> Result<()> {
58        let (FrameDescriptor::Video(src_desc), FrameDescriptor::Video(dst_desc)) = (&self.desc, &dst.desc) else {
59            return Err(Error::Invalid("not video frame".to_string()));
60        };
61
62        VideoFrame::scale_to_internal(src_desc, &self.data, dst_desc, &mut dst.data, scale_filter)
63    }
64}
65
66impl VideoFrame<'_> {
67    fn scale_to_internal(
68        src_desc: &VideoFrameDescriptor,
69        src_data: &FrameData,
70        dst_desc: &VideoFrameDescriptor,
71        dst_data: &mut FrameData,
72        scale_filter: ScaleFilter,
73    ) -> Result<()> {
74        if src_desc.format != dst_desc.format {
75            return Err(Error::Invalid("pixel format mismatch".to_string()));
76        }
77
78        let guard = src_data.map().map_err(|_| Error::Invalid("not readable".into()))?;
79        let mut dst_guard = dst_data.map_mut().map_err(|_| Error::Invalid("not writable".into()))?;
80        let src_planes = guard.planes().unwrap();
81        let mut dst_planes = dst_guard.planes_mut().unwrap();
82
83        let resampling_function: ResamplingFunction = scale_filter.into();
84        let scaler = LinearScaler::new(resampling_function);
85
86        let format = src_desc.format;
87        match format {
88            PixelFormat::ARGB32 | PixelFormat::BGRA32 | PixelFormat::ABGR32 | PixelFormat::RGBA32 => {
89                let src = into_image_store::<u8, 4>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
90                let mut dst = into_image_store_mut::<u8, 4>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
91                scaler.resize_rgba(&src, &mut dst, true).map_err(|e| Error::Invalid(e.to_string()))
92            }
93            PixelFormat::RGB24 | PixelFormat::BGR24 => {
94                let src = into_image_store::<u8, 3>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
95                let mut dst = into_image_store_mut::<u8, 3>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
96                scaler.resize_rgb(&src, &mut dst).map_err(|e| Error::Invalid(e.to_string()))
97            }
98            PixelFormat::I420 |
99            PixelFormat::I422 |
100            PixelFormat::I444 |
101            PixelFormat::I440 |
102            PixelFormat::YV12 |
103            PixelFormat::YV16 |
104            PixelFormat::YV24 => {
105                let (src_chroma_width, src_chroma_height) = format.calc_chroma_dimensions(src_desc.width().get(), src_desc.height().get());
106                let (dst_chroma_width, dst_chroma_height) = format.calc_chroma_dimensions(dst_desc.width().get(), dst_desc.height().get());
107                let src_y = into_image_store::<u8, 1>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
108                let src_u = into_image_store::<u8, 1>(&src_planes.planes[1], src_chroma_width, src_chroma_height)?;
109                let src_v = into_image_store::<u8, 1>(&src_planes.planes[2], src_chroma_width, src_chroma_height)?;
110                let mut dst_y = into_image_store_mut::<u8, 1>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
111                scaler.resize_plane(&src_y, &mut dst_y).map_err(|e| Error::Invalid(e.to_string()))?;
112                let mut dst_u = into_image_store_mut::<u8, 1>(&mut dst_planes.planes[1], dst_chroma_width, dst_chroma_height)?;
113                scaler.resize_plane(&src_u, &mut dst_u).map_err(|e| Error::Invalid(e.to_string()))?;
114                let mut dst_v = into_image_store_mut::<u8, 1>(&mut dst_planes.planes[2], dst_chroma_width, dst_chroma_height)?;
115                scaler.resize_plane(&src_v, &mut dst_v).map_err(|e| Error::Invalid(e.to_string()))?;
116                Ok(())
117            }
118            PixelFormat::NV12 | PixelFormat::NV21 | PixelFormat::NV16 | PixelFormat::NV61 | PixelFormat::NV24 | PixelFormat::NV42 => {
119                let (src_chroma_width, src_chroma_height) = format.calc_chroma_dimensions(src_desc.width().get(), src_desc.height().get());
120                let (dst_chroma_width, dst_chroma_height) = format.calc_chroma_dimensions(dst_desc.width().get(), dst_desc.height().get());
121                let src_y = into_image_store::<u8, 1>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
122                let src_uv = into_image_store::<u8, 2>(&src_planes.planes[1], src_chroma_width, src_chroma_height)?;
123                let mut dst_y = into_image_store_mut::<u8, 1>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
124                scaler.resize_plane(&src_y, &mut dst_y).map_err(|e| Error::Invalid(e.to_string()))?;
125                let mut dst_uv = into_image_store_mut::<u8, 2>(&mut dst_planes.planes[1], dst_chroma_width, dst_chroma_height)?;
126                scaler.resize_cbcr8(&src_uv, &mut dst_uv).map_err(|e| Error::Invalid(e.to_string()))?;
127                Ok(())
128            }
129            PixelFormat::ARGB64 | PixelFormat::BGRA64 | PixelFormat::ABGR64 | PixelFormat::RGBA64 => {
130                let src = into_image_store::<u16, 4>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
131                let mut dst = into_image_store_mut::<u16, 4>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
132                scaler.resize_rgba_u16(&src, &mut dst, true).map_err(|e| Error::Invalid(e.to_string()))
133            }
134            PixelFormat::I010 |
135            PixelFormat::I210 |
136            PixelFormat::I410 |
137            PixelFormat::I44010 |
138            PixelFormat::I012 |
139            PixelFormat::I212 |
140            PixelFormat::I412 |
141            PixelFormat::I44012 |
142            PixelFormat::I016 |
143            PixelFormat::I216 |
144            PixelFormat::I416 |
145            PixelFormat::I44016 => {
146                let (src_chroma_width, src_chroma_height) = format.calc_chroma_dimensions(src_desc.width().get(), src_desc.height().get());
147                let (dst_chroma_width, dst_chroma_height) = format.calc_chroma_dimensions(dst_desc.width().get(), dst_desc.height().get());
148                let src_y = into_image_store::<u16, 1>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
149                let src_u = into_image_store::<u16, 1>(&src_planes.planes[1], src_chroma_width, src_chroma_height)?;
150                let src_v = into_image_store::<u16, 1>(&src_planes.planes[2], src_chroma_width, src_chroma_height)?;
151                let mut dst_y = into_image_store_mut::<u16, 1>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
152                scaler.resize_plane_u16(&src_y, &mut dst_y).map_err(|e| Error::Invalid(e.to_string()))?;
153                let mut dst_u = into_image_store_mut::<u16, 1>(&mut dst_planes.planes[1], dst_chroma_width, dst_chroma_height)?;
154                scaler.resize_plane_u16(&src_u, &mut dst_u).map_err(|e| Error::Invalid(e.to_string()))?;
155                let mut dst_v = into_image_store_mut::<u16, 1>(&mut dst_planes.planes[2], dst_chroma_width, dst_chroma_height)?;
156                scaler.resize_plane_u16(&src_v, &mut dst_v).map_err(|e| Error::Invalid(e.to_string()))?;
157                Ok(())
158            }
159            PixelFormat::P010 |
160            PixelFormat::P210 |
161            PixelFormat::P410 |
162            PixelFormat::P012 |
163            PixelFormat::P212 |
164            PixelFormat::P412 |
165            PixelFormat::P016 |
166            PixelFormat::P216 |
167            PixelFormat::P416 => {
168                let (src_chroma_width, src_chroma_height) = format.calc_chroma_dimensions(src_desc.width().get(), src_desc.height().get());
169                let (dst_chroma_width, dst_chroma_height) = format.calc_chroma_dimensions(dst_desc.width().get(), dst_desc.height().get());
170                let src_y = into_image_store::<u16, 1>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
171                let src_uv = into_image_store::<u16, 2>(&src_planes.planes[1], src_chroma_width, src_chroma_height)?;
172                let mut dst_y = into_image_store_mut::<u16, 1>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
173                scaler.resize_plane_u16(&src_y, &mut dst_y).map_err(|e| Error::Invalid(e.to_string()))?;
174                let mut dst_uv = into_image_store_mut::<u16, 2>(&mut dst_planes.planes[1], dst_chroma_width, dst_chroma_height)?;
175                scaler.resize_cbcr_u16(&src_uv, &mut dst_uv).map_err(|e| Error::Invalid(e.to_string()))?;
176                Ok(())
177            }
178            PixelFormat::Y8 => {
179                let src = into_image_store::<u8, 1>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
180                let mut dst = into_image_store_mut::<u8, 1>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
181                scaler.resize_plane(&src, &mut dst).map_err(|e| Error::Invalid(e.to_string()))
182            }
183            PixelFormat::YA8 => {
184                let src = into_image_store::<u8, 2>(&src_planes.planes[0], src_desc.width().get(), src_desc.height().get())?;
185                let mut dst = into_image_store_mut::<u8, 2>(&mut dst_planes.planes[0], dst_desc.width().get(), dst_desc.height().get())?;
186                // similar to UV component interleaving
187                scaler.resize_cbcr8(&src, &mut dst).map_err(|e| Error::Invalid(e.to_string()))
188            }
189            _ => Err(Error::Invalid("unsupported pixel format".to_string())),
190        }
191    }
192
193    pub fn scale_to(&self, dst: &mut VideoFrame<'_>, scale_filter: ScaleFilter) -> Result<()> {
194        Self::scale_to_internal(&self.desc, &self.data, &dst.desc, &mut dst.data, scale_filter)
195    }
196}