Repository navigation
perf(recording): reduce Windows CPU copies and fix studio fps cap #3581
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
800e058
cbf38f7
564cb04
a4c0fbf
7e6b3c3
c8eff37
131c402
18f0311
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1334,19 +1334,6 @@ fn convert_uyvy_to_yuyv_scalar(src: &[u8], dst: &mut [u8], len: usize) { | |
| } | ||
| } | ||
|
|
||
| fn convert_uyvy_to_yuyv( | ||
| src: &[u8], | ||
| width: u32, | ||
| height: u32, | ||
| src_stride: usize, | ||
| flip: bool, | ||
| ) -> Vec<u8> { | ||
| let total_bytes = (width * height * 2) as usize; | ||
| let mut dst = vec![0u8; total_bytes]; | ||
| convert_uyvy_to_yuyv_into(src, &mut dst, width, height, src_stride, flip); | ||
| dst | ||
| } | ||
|
|
||
| pub(crate) fn camera_frame_to_ffmpeg( | ||
| frame: &NativeCameraFrame, | ||
| mjpeg_decoder: &mut MjpegDecoder, | ||
|
|
@@ -1393,37 +1380,19 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| ) | ||
| })?; | ||
|
|
||
| let converted_data_storage; | ||
| let (final_data, final_format): (&[u8], ffmpeg::format::Pixel) = | ||
| if frame.pixel_format == cap_camera_windows::PixelFormat::UYVY422 { | ||
| converted_data_storage = convert_uyvy_to_yuyv( | ||
| data, | ||
| frame.width, | ||
| frame.height, | ||
| layout.primary_stride, | ||
| frame.is_bottom_up, | ||
| ); | ||
| ( | ||
| converted_data_storage.as_slice(), | ||
| ffmpeg::format::Pixel::YUYV422, | ||
| ) | ||
| } else { | ||
| (data, ffmpeg_format) | ||
| }; | ||
|
|
||
| let mut ffmpeg_frame = ffmpeg::frame::Video::new(final_format, frame.width, frame.height); | ||
| let flip = frame.is_bottom_up && frame.pixel_format != cap_camera_windows::PixelFormat::UYVY422; | ||
| let mut ffmpeg_frame = ffmpeg::frame::Video::new(ffmpeg_format, frame.width, frame.height); | ||
| let flip = frame.is_bottom_up; | ||
|
|
||
| match frame.pixel_format { | ||
| cap_camera_windows::PixelFormat::NV12 => { | ||
| let uv_height = height / 2; | ||
| let y_size = layout.primary_stride * height; | ||
| let uv_size = layout.secondary_stride * uv_height; | ||
| if final_data.len() >= y_size + uv_size { | ||
| if data.len() >= y_size + uv_size { | ||
| let stride_y = ffmpeg_frame.stride(0); | ||
| let stride_uv = ffmpeg_frame.stride(1); | ||
| copy_plane( | ||
| &final_data[..y_size], | ||
| &data[..y_size], | ||
| ffmpeg_frame.data_mut(0), | ||
| width, | ||
| height, | ||
|
|
@@ -1432,7 +1401,7 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| flip, | ||
| ); | ||
| copy_plane( | ||
| &final_data[y_size..y_size + uv_size], | ||
| &data[y_size..y_size + uv_size], | ||
| ffmpeg_frame.data_mut(1), | ||
| width, | ||
| uv_height, | ||
|
|
@@ -1446,19 +1415,19 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| let uv_height = height / 2; | ||
| let y_size = layout.primary_stride * height; | ||
| let uv_size = layout.secondary_stride * uv_height; | ||
| if final_data.len() >= y_size + uv_size { | ||
| if data.len() >= y_size + uv_size { | ||
| let stride_y = ffmpeg_frame.stride(0); | ||
| let stride_uv = ffmpeg_frame.stride(1); | ||
| copy_plane( | ||
| &final_data[..y_size], | ||
| &data[..y_size], | ||
| ffmpeg_frame.data_mut(0), | ||
| width, | ||
| height, | ||
| layout.primary_stride, | ||
| stride_y, | ||
| flip, | ||
| ); | ||
| let uv_data = &final_data[y_size..y_size + uv_size]; | ||
| let uv_data = &data[y_size..y_size + uv_size]; | ||
| let dest = ffmpeg_frame.data_mut(1); | ||
| for row in 0..uv_height { | ||
| let src_row = if flip { uv_height - 1 - row } else { row }; | ||
|
|
@@ -1475,23 +1444,15 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| } | ||
| cap_camera_windows::PixelFormat::YUYV422 | cap_camera_windows::PixelFormat::UYVY422 => { | ||
| let row_bytes = width * 2; | ||
| let size = if frame.pixel_format == cap_camera_windows::PixelFormat::UYVY422 { | ||
| row_bytes * height | ||
| } else { | ||
| layout.primary_stride * height | ||
| }; | ||
| if final_data.len() >= size { | ||
| let size = layout.primary_stride * height; | ||
| if data.len() >= size { | ||
| let stride = ffmpeg_frame.stride(0); | ||
| copy_plane( | ||
| final_data, | ||
| data, | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: crates/recording/src/output_pipeline/win.rs
Line: 1371
Comment:
**Padded camera rows are corrupted**
`camera_frame_to_ffmpeg` now copies the original UYVY buffer, but its UYVY branch still uses `width * 2` as the source stride. That was correct only after the removed conversion packed the rows. If a webcam adds padding between rows, each row after the first starts at the wrong byte, corrupting the recorded image. Use `layout.primary_stride` for the source stride and size check.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly. |
||
| ffmpeg_frame.data_mut(0), | ||
| row_bytes, | ||
| height, | ||
| if frame.pixel_format == cap_camera_windows::PixelFormat::UYVY422 { | ||
| row_bytes | ||
| } else { | ||
| layout.primary_stride | ||
| }, | ||
| layout.primary_stride, | ||
| stride, | ||
| flip, | ||
| ); | ||
|
|
@@ -1500,10 +1461,10 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| cap_camera_windows::PixelFormat::ARGB | cap_camera_windows::PixelFormat::RGB32 => { | ||
| let row_bytes = width * 4; | ||
| let size = layout.primary_stride * height; | ||
| if final_data.len() >= size { | ||
| if data.len() >= size { | ||
| let stride = ffmpeg_frame.stride(0); | ||
| copy_plane( | ||
| final_data, | ||
| data, | ||
| ffmpeg_frame.data_mut(0), | ||
| row_bytes, | ||
| height, | ||
|
|
@@ -1516,10 +1477,10 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| cap_camera_windows::PixelFormat::RGB24 | cap_camera_windows::PixelFormat::BGR24 => { | ||
| let row_bytes = width * 3; | ||
| let size = layout.primary_stride * height; | ||
| if final_data.len() >= size { | ||
| if data.len() >= size { | ||
| let stride = ffmpeg_frame.stride(0); | ||
| copy_plane( | ||
| final_data, | ||
| data, | ||
| ffmpeg_frame.data_mut(0), | ||
| row_bytes, | ||
| height, | ||
|
|
@@ -1533,12 +1494,12 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| let uv_height = height / 2; | ||
| let y_size = layout.primary_stride * height; | ||
| let uv_size = layout.secondary_stride * uv_height; | ||
| if final_data.len() >= y_size + uv_size * 2 { | ||
| if data.len() >= y_size + uv_size * 2 { | ||
| let stride_y = ffmpeg_frame.stride(0); | ||
| let stride_u = ffmpeg_frame.stride(1); | ||
| let stride_v = ffmpeg_frame.stride(2); | ||
| copy_plane( | ||
| &final_data[..y_size], | ||
| &data[..y_size], | ||
| ffmpeg_frame.data_mut(0), | ||
| width, | ||
| height, | ||
|
|
@@ -1547,7 +1508,7 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| flip, | ||
| ); | ||
| copy_plane( | ||
| &final_data[y_size..y_size + uv_size], | ||
| &data[y_size..y_size + uv_size], | ||
| ffmpeg_frame.data_mut(1), | ||
| width / 2, | ||
| uv_height, | ||
|
|
@@ -1556,7 +1517,7 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| flip, | ||
| ); | ||
| copy_plane( | ||
| &final_data[y_size + uv_size..y_size + uv_size * 2], | ||
| &data[y_size + uv_size..y_size + uv_size * 2], | ||
| ffmpeg_frame.data_mut(2), | ||
| width / 2, | ||
| uv_height, | ||
|
|
@@ -1570,12 +1531,12 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| let uv_height = height / 2; | ||
| let y_size = layout.primary_stride * height; | ||
| let uv_size = layout.secondary_stride * uv_height; | ||
| if final_data.len() >= y_size + uv_size * 2 { | ||
| if data.len() >= y_size + uv_size * 2 { | ||
| let stride_y = ffmpeg_frame.stride(0); | ||
| let stride_u = ffmpeg_frame.stride(1); | ||
| let stride_v = ffmpeg_frame.stride(2); | ||
| copy_plane( | ||
| &final_data[..y_size], | ||
| &data[..y_size], | ||
| ffmpeg_frame.data_mut(0), | ||
| width, | ||
| height, | ||
|
|
@@ -1584,7 +1545,7 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| flip, | ||
| ); | ||
| copy_plane( | ||
| &final_data[y_size + uv_size..y_size + uv_size * 2], | ||
| &data[y_size + uv_size..y_size + uv_size * 2], | ||
| ffmpeg_frame.data_mut(1), | ||
| width / 2, | ||
| uv_height, | ||
|
|
@@ -1593,7 +1554,7 @@ pub(crate) fn camera_frame_to_ffmpeg( | |
| flip, | ||
| ); | ||
| copy_plane( | ||
| &final_data[y_size..y_size + uv_size], | ||
| &data[y_size..y_size + uv_size], | ||
| ffmpeg_frame.data_mut(2), | ||
| width / 2, | ||
| uv_height, | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
camera_frame_to_ffmpegnow returnsUYVY422frames, butWindowsFragmentedM4SCameraMuxer::normalize_video_config_for_framestill configures UYVY cameras asYUYV422. The encoder builds its scaler from that configured format. The AVX2 converter hides the mismatch for same-size frames, but resizing or running without AVX2 falls through to a scaler that expects the wrong input, causing conversion to fail. Update the fragmented camera configuration to match the returned frame format.Prompt To Fix With AI