Skip to content

Commit 442f4fc

Browse files
committed
perf(desktop): optimize legacy camera preview with frame reuse and draining
Made-with: Cursor
1 parent 81c6313 commit 442f4fc

1 file changed

Lines changed: 58 additions & 29 deletions

File tree

apps/desktop/src-tauri/src/camera_legacy.rs

Lines changed: 58 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@ use tokio_util::sync::CancellationToken;
66

77
use crate::frame_ws::{WSFrame, create_frame_ws};
88

9+
const WS_PREVIEW_MAX_WIDTH: u32 = 640;
10+
const WS_PREVIEW_MAX_HEIGHT: u32 = 360;
11+
912
pub async fn create_camera_preview_ws() -> (Sender<FFmpegVideoFrame>, u16, CancellationToken) {
1013
let (camera_tx, camera_rx) = flume::bounded::<FFmpegVideoFrame>(4);
1114
let (frame_tx, _) = tokio::sync::broadcast::channel::<WSFrame>(4);
@@ -14,64 +17,90 @@ pub async fn create_camera_preview_ws() -> (Sender<FFmpegVideoFrame>, u16, Cance
1417
use ffmpeg::format::Pixel;
1518

1619
let mut converter: Option<(Pixel, ffmpeg::software::scaling::Context)> = None;
20+
let mut reusable_frame: Option<ffmpeg::util::frame::Video> = None;
1721

1822
while let Ok(raw_frame) = camera_rx.recv() {
1923
let mut frame = raw_frame.inner;
2024

21-
if frame.format() != Pixel::RGBA || frame.width() > 1280 || frame.height() > 720 {
22-
let converter = match &mut converter {
23-
Some((format, converter))
25+
while let Ok(newer) = camera_rx.try_recv() {
26+
frame = newer.inner;
27+
}
28+
29+
let needs_convert = frame.format() != Pixel::RGBA
30+
|| frame.width() > WS_PREVIEW_MAX_WIDTH
31+
|| frame.height() > WS_PREVIEW_MAX_HEIGHT;
32+
33+
if needs_convert {
34+
let target_width = WS_PREVIEW_MAX_WIDTH.min(frame.width());
35+
let target_height =
36+
(target_width as f64 / (frame.width() as f64 / frame.height() as f64)) as u32;
37+
38+
let ctx = match &mut converter {
39+
Some((format, ctx))
2440
if *format == frame.format()
25-
&& converter.input().width == frame.width()
26-
&& converter.input().height == frame.height() =>
41+
&& ctx.input().width == frame.width()
42+
&& ctx.input().height == frame.height() =>
2743
{
28-
converter
44+
ctx
2945
}
3046
_ => {
3147
let Ok(new_converter) = ffmpeg::software::scaling::Context::get(
3248
frame.format(),
3349
frame.width(),
3450
frame.height(),
3551
Pixel::RGBA,
36-
1280,
37-
(1280.0 / (frame.width() as f64 / frame.height() as f64)) as u32,
52+
target_width,
53+
target_height,
3854
ffmpeg::software::scaling::flag::Flags::FAST_BILINEAR,
3955
) else {
4056
continue;
4157
};
4258

59+
reusable_frame = None;
4360
&mut converter.insert((frame.format(), new_converter)).1
4461
}
4562
};
4663

47-
let mut new_frame = ffmpeg::util::frame::Video::new(
48-
Pixel::RGBA,
49-
converter.output().width,
50-
converter.output().height,
51-
);
64+
let out_frame = reusable_frame.get_or_insert_with(|| {
65+
ffmpeg::util::frame::Video::new(
66+
Pixel::RGBA,
67+
ctx.output().width,
68+
ctx.output().height,
69+
)
70+
});
5271

53-
if converter.run(&frame, &mut new_frame).is_err() {
72+
if ctx.run(&frame, out_frame).is_err() {
5473
continue;
5574
}
5675

57-
frame = new_frame;
76+
frame_tx_clone
77+
.send(WSFrame {
78+
data: std::sync::Arc::new(out_frame.data(0).to_vec()),
79+
width: out_frame.width(),
80+
height: out_frame.height(),
81+
stride: out_frame.stride(0) as u32,
82+
frame_number: 0,
83+
target_time_ns: 0,
84+
format: crate::frame_ws::WSFrameFormat::Rgba,
85+
created_at: Instant::now(),
86+
})
87+
.ok();
88+
} else {
89+
frame_tx_clone
90+
.send(WSFrame {
91+
data: std::sync::Arc::new(frame.data(0).to_vec()),
92+
width: frame.width(),
93+
height: frame.height(),
94+
stride: frame.stride(0) as u32,
95+
frame_number: 0,
96+
target_time_ns: 0,
97+
format: crate::frame_ws::WSFrameFormat::Rgba,
98+
created_at: Instant::now(),
99+
})
100+
.ok();
58101
}
59-
60-
frame_tx_clone
61-
.send(WSFrame {
62-
data: std::sync::Arc::new(frame.data(0).to_vec()),
63-
width: frame.width(),
64-
height: frame.height(),
65-
stride: frame.stride(0) as u32,
66-
frame_number: 0,
67-
target_time_ns: 0,
68-
format: crate::frame_ws::WSFrameFormat::Rgba,
69-
created_at: Instant::now(),
70-
})
71-
.ok();
72102
}
73103
});
74-
// _shutdown needs to be kept alive to keep the camera ws running
75104
let (camera_ws_port, _shutdown) = create_frame_ws(frame_tx).await;
76105

77106
(camera_tx, camera_ws_port, _shutdown)

0 commit comments

Comments
 (0)