- commit
- 9c6f4331f8c76505ef21ad6e4fcca8efc03e5f64
- parent
- 42c987a04937250b2af411bd2d2208c36da772ce
- author
- dami etoile <dami.etoile@gmail.com>
| M | .github/ | 3 | +- |
| M | src-tauri/ | 69 | ++++++++++++++++++++---------- |
@@ -1231,35 +1231,57 @@ async fn stream_frames_xcap(
12311231 recv: &mut iroh::endpoint::RecvStream,12321232 app: &AppHandle,12331233 ) -> anyhow::Result<()> {1234- let monitors = xcap::Monitor::all().context("failed to enumerate monitors")?;1235- let monitor = monitors1236- .into_iter()1237- .next()1238- .ok_or_else(|| anyhow::anyhow!("no monitors found"))?;1234+ use std::sync::mpsc;12391235 1240- let mut encoder = Encoder::new().context("failed to create H.264 encoder")?;1236+ // xcap::Monitor and openh264::Encoder are !Send, so run them on a dedicated thread1237+ let (tx, rx) = mpsc::channel::<anyhow::Result<(usize, usize, Vec<u8>, bool, f64, f64)>>();1238+1239+ let capture_thread = std::thread::spawn(move || -> anyhow::Result<()> {1240+ let monitors = xcap::Monitor::all().context("failed to enumerate monitors")?;1241+ let monitor = monitors1242+ .into_iter()1243+ .next()1244+ .ok_or_else(|| anyhow::anyhow!("no monitors found"))?;1245+ let mut encoder = Encoder::new().context("failed to create H.264 encoder")?;1246+1247+ loop {1248+ if tx.send(Ok({1249+ let capture_start = Instant::now();1250+ let image = monitor.capture_image()?;1251+ let capture_ms = capture_start.elapsed().as_secs_f64() * 1000.0;1252+1253+ let rgb_image = image::DynamicImage::ImageRgba8(image).to_rgb8();1254+ let (w, h) = (rgb_image.width() as usize, rgb_image.height() as usize);1255+1256+ let encode_start = Instant::now();1257+ let rgb_source = RgbSliceU8::new(rgb_image.as_raw(), (w, h));1258+ let yuv = YUVBuffer::from_rgb8_source(rgb_source);1259+ let (h264_data, is_keyframe) = {1260+ let bitstream = encoder.encode(&yuv).context("H.264 encode failed")?;1261+ let kf = matches!(bitstream.frame_type(), openh264::encoder::FrameType::I | openh264::encoder::FrameType::IDR);1262+ (bitstream.to_vec(), kf)1263+ };1264+ let encode_ms = encode_start.elapsed().as_secs_f64() * 1000.0;1265+ (w, h, h264_data, is_keyframe, capture_ms, encode_ms)1266+ })).is_err() {1267+ break; // receiver dropped — stop1268+ }1269+ }1270+ Ok(())1271+ });12411272 12421273 let mut frame_count = 0u32;12431274 let start = Instant::now();12441275 12451276 loop {1246- let capture_start = Instant::now();1247- let image = monitor.capture_image()?;1248- let capture_ms = capture_start.elapsed().as_secs_f64() * 1000.0;1249-1250- let rgb_image = image::DynamicImage::ImageRgba8(image).to_rgb8();1251- let (w, h) = (rgb_image.width() as usize, rgb_image.height() as usize);1252-1253- // RGB8 → YUV → H.2641254- let encode_start = Instant::now();1255- let rgb_source = RgbSliceU8::new(rgb_image.as_raw(), (w, h));1256- let yuv = YUVBuffer::from_rgb8_source(rgb_source);1257- let (h264_data, is_keyframe) = {1258- let bitstream = encoder.encode(&yuv).context("H.264 encode failed")?;1259- let kf = matches!(bitstream.frame_type(), openh264::encoder::FrameType::I | openh264::encoder::FrameType::IDR);1260- (bitstream.to_vec(), kf)1277+ let (w, h, h264_data, is_keyframe, capture_ms, encode_ms) = match rx.recv() {1278+ Ok(Ok(frame)) => frame,1279+ Ok(Err(e)) => return Err(e),1280+ Err(_) => {1281+ eprintln!("[host] capture thread ended");1282+ break;1283+ }12611284 };1262- let encode_ms = encode_start.elapsed().as_secs_f64() * 1000.0;12631285 let h264_size = h264_data.len();12641286 12651287 // Header: width(4) + height(4) + size(4) + keyframe(1) + codec(1) = 14 bytes@@ -1306,6 +1328,9 @@ async fn stream_frames_xcap(
13061328 tokio::task::yield_now().await;13071329 }13081330 1331+ // Drop rx to signal the capture thread to stop1332+ drop(rx);1333+ let _ = capture_thread.join();13091334 Ok(())13101335 }13111336