11use cap_camera:: CapturedFrame ;
22use cap_camera_avfoundation:: ImageBufExt ;
33use cidre:: * ;
4+ use ffmpeg:: { format:: Pixel , software:: scaling} ;
5+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
46
57use crate :: CapturedFrameExt ;
68
@@ -14,10 +16,41 @@ pub enum AsFFmpegError {
1416 expected : usize ,
1517 found : usize ,
1618 } ,
19+ #[ error( "Swscale fallback failed for format '{format}': {reason}" ) ]
20+ SwscaleFallbackFailed { format : String , reason : String } ,
1721 #[ error( "{0}" ) ]
1822 Native ( #[ from] cidre:: os:: Error ) ,
1923}
2024
25+ struct FourccInfo {
26+ pixel : Pixel ,
27+ bytes_per_pixel : usize ,
28+ }
29+
30+ fn fourcc_to_pixel_format ( fourcc : & str ) -> Option < FourccInfo > {
31+ match fourcc {
32+ "ABGR" => Some ( FourccInfo {
33+ pixel : Pixel :: ABGR ,
34+ bytes_per_pixel : 4 ,
35+ } ) ,
36+ "b64a" => Some ( FourccInfo {
37+ pixel : Pixel :: RGBA64BE ,
38+ bytes_per_pixel : 8 ,
39+ } ) ,
40+ "b48r" => Some ( FourccInfo {
41+ pixel : Pixel :: RGB48BE ,
42+ bytes_per_pixel : 6 ,
43+ } ) ,
44+ "L016" => Some ( FourccInfo {
45+ pixel : Pixel :: GRAY16LE ,
46+ bytes_per_pixel : 2 ,
47+ } ) ,
48+ _ => None ,
49+ }
50+ }
51+
52+ static FALLBACK_WARNING_LOGGED : AtomicBool = AtomicBool :: new ( false ) ;
53+
2154impl CapturedFrameExt for CapturedFrame {
2255 fn as_ffmpeg ( & self ) -> Result < ffmpeg:: frame:: Video , AsFFmpegError > {
2356 let native = self . native ( ) . clone ( ) ;
@@ -162,6 +195,29 @@ impl CapturedFrameExt for CapturedFrame {
162195
163196 ff_frame
164197 }
198+ "RGBA" => {
199+ let mut ff_frame = ffmpeg:: frame:: Video :: new (
200+ ffmpeg:: format:: Pixel :: RGBA ,
201+ width as u32 ,
202+ height as u32 ,
203+ ) ;
204+
205+ let src_stride = native. image_buf ( ) . plane_bytes_per_row ( 0 ) ;
206+ let dest_stride = ff_frame. stride ( 0 ) ;
207+
208+ let src_bytes = bytes_lock. plane_data ( 0 ) ;
209+ let dest_bytes = & mut ff_frame. data_mut ( 0 ) ;
210+
211+ for y in 0 ..height {
212+ let row_width = width * 4 ;
213+ let src_row = & src_bytes[ y * src_stride..y * src_stride + row_width] ;
214+ let dest_row = & mut dest_bytes[ y * dest_stride..y * dest_stride + row_width] ;
215+
216+ dest_row. copy_from_slice ( src_row) ;
217+ }
218+
219+ ff_frame
220+ }
165221 "24BG" => {
166222 let mut ff_frame = ffmpeg:: frame:: Video :: new (
167223 ffmpeg:: format:: Pixel :: BGR24 ,
@@ -185,6 +241,29 @@ impl CapturedFrameExt for CapturedFrame {
185241
186242 ff_frame
187243 }
244+ "24RG" => {
245+ let mut ff_frame = ffmpeg:: frame:: Video :: new (
246+ ffmpeg:: format:: Pixel :: RGB24 ,
247+ width as u32 ,
248+ height as u32 ,
249+ ) ;
250+
251+ let src_stride = native. image_buf ( ) . plane_bytes_per_row ( 0 ) ;
252+ let dest_stride = ff_frame. stride ( 0 ) ;
253+
254+ let src_bytes = bytes_lock. plane_data ( 0 ) ;
255+ let dest_bytes = & mut ff_frame. data_mut ( 0 ) ;
256+
257+ for y in 0 ..height {
258+ let row_width = width * 3 ;
259+ let src_row = & src_bytes[ y * src_stride..y * src_stride + row_width] ;
260+ let dest_row = & mut dest_bytes[ y * dest_stride..y * dest_stride + row_width] ;
261+
262+ dest_row. copy_from_slice ( src_row) ;
263+ }
264+
265+ ff_frame
266+ }
188267 "y420" => {
189268 let plane_count = native. image_buf ( ) . plane_count ( ) ;
190269 if plane_count < 3 {
@@ -220,8 +299,82 @@ impl CapturedFrameExt for CapturedFrame {
220299
221300 ff_frame
222301 }
302+ "L008" | "GRAY" => {
303+ let mut ff_frame = ffmpeg:: frame:: Video :: new (
304+ ffmpeg:: format:: Pixel :: GRAY8 ,
305+ width as u32 ,
306+ height as u32 ,
307+ ) ;
308+
309+ let src_stride = native. image_buf ( ) . plane_bytes_per_row ( 0 ) ;
310+ let dest_stride = ff_frame. stride ( 0 ) ;
311+
312+ let src_bytes = bytes_lock. plane_data ( 0 ) ;
313+ let dest_bytes = & mut ff_frame. data_mut ( 0 ) ;
314+
315+ for y in 0 ..height {
316+ let row_width = width;
317+ let src_row = & src_bytes[ y * src_stride..y * src_stride + row_width] ;
318+ let dest_row = & mut dest_bytes[ y * dest_stride..y * dest_stride + row_width] ;
319+
320+ dest_row. copy_from_slice ( src_row) ;
321+ }
322+
323+ ff_frame
324+ }
223325 format => {
224- return Err ( AsFFmpegError :: UnsupportedSubType ( format. to_string ( ) ) ) ;
326+ if let Some ( info) = fourcc_to_pixel_format ( format) {
327+ if !FALLBACK_WARNING_LOGGED . swap ( true , Ordering :: Relaxed ) {
328+ tracing:: warn!(
329+ "Using swscale fallback for camera format '{}' - this may impact performance" ,
330+ format
331+ ) ;
332+ }
333+
334+ let mut src_frame =
335+ ffmpeg:: frame:: Video :: new ( info. pixel , width as u32 , height as u32 ) ;
336+
337+ let src_stride = native. image_buf ( ) . plane_bytes_per_row ( 0 ) ;
338+ let dest_stride = src_frame. stride ( 0 ) ;
339+ let src_bytes = bytes_lock. plane_data ( 0 ) ;
340+ let dest_bytes = & mut src_frame. data_mut ( 0 ) ;
341+
342+ let row_width = width * info. bytes_per_pixel ;
343+ for y in 0 ..height {
344+ let src_row = & src_bytes[ y * src_stride..y * src_stride + row_width] ;
345+ let dest_row =
346+ & mut dest_bytes[ y * dest_stride..y * dest_stride + row_width] ;
347+ dest_row. copy_from_slice ( src_row) ;
348+ }
349+
350+ let mut scaler = scaling:: Context :: get (
351+ info. pixel ,
352+ width as u32 ,
353+ height as u32 ,
354+ Pixel :: RGBA ,
355+ width as u32 ,
356+ height as u32 ,
357+ scaling:: flag:: Flags :: FAST_BILINEAR ,
358+ )
359+ . map_err ( |e| AsFFmpegError :: SwscaleFallbackFailed {
360+ format : format. to_string ( ) ,
361+ reason : format ! ( "Failed to create scaler: {e}" ) ,
362+ } ) ?;
363+
364+ let mut output_frame =
365+ ffmpeg:: frame:: Video :: new ( Pixel :: RGBA , width as u32 , height as u32 ) ;
366+
367+ scaler. run ( & src_frame, & mut output_frame) . map_err ( |e| {
368+ AsFFmpegError :: SwscaleFallbackFailed {
369+ format : format. to_string ( ) ,
370+ reason : format ! ( "Conversion failed: {e}" ) ,
371+ }
372+ } ) ?;
373+
374+ output_frame
375+ } else {
376+ return Err ( AsFFmpegError :: UnsupportedSubType ( format. to_string ( ) ) ) ;
377+ }
225378 }
226379 } ;
227380
0 commit comments