Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion crates/recording/src/capture_pipeline.rs
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ impl MakeCapturePipeline for screen_capture::Direct3DCapture {
}
} else {
let d3d_device = screen_capture.d3d_device.clone();
let frame_rate = screen_capture.capture_frame_rate();
let bitrate_multiplier = if ultra { 0.3f32 } else { 0.15f32 };

OutputPipeline::builder(output_path.clone())
Expand All @@ -335,7 +336,7 @@ impl MakeCapturePipeline for screen_capture::Direct3DCapture {
pixel_format: screen_capture::Direct3DCapture::PIXEL_FORMAT.as_dxgi(),
d3d_device,
bitrate_multiplier,
frame_rate: 30u32,
frame_rate,
output_size: output_size.map(|(w, h)| windows::Graphics::SizeInt32 {
Width: w as i32,
Height: h as i32,
Expand Down
87 changes: 24 additions & 63 deletions crates/recording/src/output_pipeline/win.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Comment thread
AatmanAJ marked this conversation as resolved.
Resolved
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,
Expand All @@ -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,
Expand All @@ -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 };
Expand All @@ -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,
Comment thread
AatmanAJ marked this conversation as resolved.
Resolved
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,
);
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion crates/recording/src/output_pipeline/win_fragmented_m4s.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,7 +775,7 @@ impl WindowsFragmentedM4SCameraMuxer {
let mut updated = false;

let desired_pixel_format = match frame.pixel_format {
cap_camera_windows::PixelFormat::UYVY422 => Pixel::YUYV422,
cap_camera_windows::PixelFormat::UYVY422 => Pixel::UYVY422,
_ => self.video_config.pixel_format,
};

Expand Down
7 changes: 7 additions & 0 deletions crates/recording/src/sources/screen_capture/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,13 @@ pub struct VideoSourceConfig {
settings: scap_direct3d::Settings,
pub d3d_device: ID3D11Device,
}

impl VideoSourceConfig {
pub(crate) fn capture_frame_rate(&self) -> u32 {
self.video_info.fps().max(1)
}
}

pub struct VideoSource {
video_info: VideoInfo,
ctrl_tx: std::sync::mpsc::SyncSender<VideoControl>,
Expand Down
2 changes: 1 addition & 1 deletion crates/scap-direct3d/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -582,7 +582,7 @@ impl Capturer {

let frame_pool_size = settings
.fps
.map(|fps| ((fps as f32 / 30.0 * 2.0).ceil() as i32).clamp(2, 4))
.map(|fps| ((fps as f32 / 30.0 * 2.0).ceil() as i32).clamp(4, 6))
.unwrap_or(2);

let item_size = item.Size().map_err(NewCapturerError::ItemSize)?;
Expand Down
6 changes: 5 additions & 1 deletion crates/verify/tests/codec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,11 @@ fn video_code_survives_compositing_scaling_rounding_bubble_and_blur() {
let dir = tempfile::tempdir().unwrap();
// 640x360 is a typical verification output; 320x180 puts the screen at
// a quarter of its source size and the camera code cells at ~6 px.
for (out_w, out_h, bitrate) in [(640, 360, 150_000), (320, 180, 80_000)] {
// 320x180 is encoded at the Web export preset (0.08 bpp): at 80 kbps the
// paravirtualised VideoToolbox HEVC encoder on the CI macOS VMs
// occasionally left a blurred camera cell at margin 0.17, below the
// reader's 0.18, while this host's hardware encoder read it at 0.31.
for (out_w, out_h, bitrate) in [(640, 360, 150_000), (320, 180, 138_240)] {
for blur in [false, true] {
let pairs: Vec<(u32, u32)> = (0..40).map(|i| (i * 3 + 100, i / 2 + 7)).collect();
let frames: Vec<Rgba> = pairs
Expand Down
8 changes: 4 additions & 4 deletions crates/video-decode/src/media_foundation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -775,11 +775,11 @@ impl MediaFoundationDecoder {
let uv_size = (y_stride * uv_height) as usize;
let total_size = y_size + uv_size;

let mut data = vec![0u8; total_size];
unsafe {
std::ptr::copy_nonoverlapping(mapped.pData as *const u8, data.as_mut_ptr(), total_size);
let data = unsafe {
let data = std::slice::from_raw_parts(mapped.pData as *const u8, total_size).to_vec();
self.d3d11_context.Unmap(staging, 0);
}
data
};

Ok(NV12Data {
data: Arc::new(data),
Expand Down
30 changes: 25 additions & 5 deletions scripts/linux-desktop-harness/scenario-export.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,32 @@ gif_frames_and_seconds() {
import sys

data = open(sys.argv[1], "rb").read()


def skip_sub_blocks(at):
while data[at]:
at += data[at] + 1
return at + 1


if data[:6] in (b"GIF87a", b"GIF89a"):
frames, centiseconds, at = 0, 0, data.find(b"\x21\xf9\x04")
while at != -1 and at + 6 <= len(data):
frames += 1
centiseconds += data[at + 4] | data[at + 5] << 8
at = data.find(b"\x21\xf9\x04", at + 8)
frames, centiseconds, delay = 0, 0, 0
flags = data[10]
at = 13 + (3 << ((flags & 7) + 1) if flags & 0x80 else 0)
while at < len(data) and data[at] != 0x3B:
if data[at] == 0x21:
if data[at + 1] == 0xF9:
delay = data[at + 4] | data[at + 5] << 8
at = skip_sub_blocks(at + 2)
elif data[at] == 0x2C:
frames += 1
centiseconds += delay
delay = 0
local = data[at + 9]
at += 10 + (3 << ((local & 7) + 1) if local & 0x80 else 0)
at = skip_sub_blocks(at + 1)
else:
break
print(frames, f"{centiseconds / 100:.2f}")
PY
}
Expand Down
Loading