Skip to content

Commit 92b587f

Browse files
authored
Fixes Direct3D capture issues on Windows (#1144)
Addresses several issues related to Direct3D capture on Windows: - Ensures the GraphicsCaptureItem is created on the capture thread to avoid COM threading problems. - Propagates encoder setup errors back to the main thread. - Improves error handling and logging in the capture thread. - Shares the D3D device with the encoder to avoid device mismatch.
1 parent a4ab3bb commit 92b587f

File tree

2 files changed

+51
-22
lines changed

2 files changed

+51
-22
lines changed

crates/recording/src/output_pipeline/win.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,13 @@ impl Muxer for WindowsMuxer {
113113
let encoder = match encoder {
114114
Ok(encoder) => {
115115
if ready_tx.send(Ok(())).is_err() {
116-
info!("Failed to send ready signal");
116+
error!("Failed to send ready signal - receiver dropped");
117117
return;
118118
}
119119
encoder
120120
}
121121
Err(e) => {
122+
error!("Encoder setup failed: {}", e);
122123
let _ = ready_tx.send(Err(e));
123124
return;
124125
}
@@ -185,7 +186,9 @@ impl Muxer for WindowsMuxer {
185186
});
186187
}
187188

188-
let _ = ready_rx.await;
189+
ready_rx
190+
.await
191+
.map_err(|_| anyhow!("Encoder thread ended unexpectedly"))??;
189192

190193
output.lock().unwrap().write_header()?;
191194

crates/recording/src/sources/screen_capture/windows.rs

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,7 @@ use crate::{
33
output_pipeline,
44
screen_capture::{ScreenCaptureConfig, ScreenCaptureFormat},
55
};
6-
use ::windows::{
7-
Graphics::Capture::GraphicsCaptureItem,
8-
Win32::Graphics::Direct3D11::{D3D11_BOX, ID3D11Device},
9-
};
6+
use ::windows::Win32::Graphics::Direct3D11::{D3D11_BOX, ID3D11Device};
107
use anyhow::anyhow;
118
use cap_fail::fail_err;
129
use cap_media_info::{AudioInfo, VideoInfo};
@@ -24,7 +21,7 @@ use std::{
2421
collections::VecDeque,
2522
time::{Duration, Instant},
2623
};
27-
use tracing::{info, trace};
24+
use tracing::{error, info, trace};
2825

2926
const WINDOW_DURATION: Duration = Duration::from_secs(3);
3027
const LOG_INTERVAL: Duration = Duration::from_secs(5);
@@ -121,18 +118,12 @@ impl ScreenCaptureConfig<Direct3DCapture> {
121118
Some(Duration::from_secs_f64(1.0 / self.config.fps as f64));
122119
}
123120

124-
let display = Display::from_id(&self.config.display)
125-
.ok_or_else(|| SourceError::NoDisplay(self.config.display.clone()))?;
126-
127-
let capture_item = display
128-
.raw_handle()
129-
.try_as_capture_item()
130-
.map_err(SourceError::AsCaptureItem)?;
131-
121+
// Store the display ID instead of GraphicsCaptureItem to avoid COM threading issues
122+
// The GraphicsCaptureItem will be created on the capture thread
132123
Ok((
133124
VideoSourceConfig {
134125
video_info: self.video_info,
135-
capture_item,
126+
display_id: self.config.display.clone(),
136127
settings,
137128
d3d_device: self.d3d_device.clone(),
138129
},
@@ -149,7 +140,7 @@ pub enum VideoSourceError {
149140

150141
pub struct VideoSourceConfig {
151142
video_info: VideoInfo,
152-
capture_item: GraphicsCaptureItem,
143+
display_id: DisplayId,
153144
settings: scap_direct3d::Settings,
154145
pub d3d_device: ID3D11Device,
155146
}
@@ -170,9 +161,9 @@ impl output_pipeline::VideoSource for VideoSource {
170161
async fn setup(
171162
VideoSourceConfig {
172163
video_info,
173-
capture_item,
164+
display_id,
174165
settings,
175-
d3d_device,
166+
d3d_device, // Share the D3D device with the encoder to avoid device mismatch
176167
}: Self::Config,
177168
mut video_tx: mpsc::Sender<Self::Frame>,
178169
ctx: &mut output_pipeline::SetupCtx,
@@ -186,6 +177,28 @@ impl output_pipeline::VideoSource for VideoSource {
186177
ctx.tasks().spawn_thread("d3d-capture-thread", move || {
187178
cap_mediafoundation_utils::thread_init();
188179

180+
// Look up the display and create the GraphicsCaptureItem on this thread to avoid COM threading issues
181+
let capture_item = match Display::from_id(&display_id) {
182+
Some(display) => {
183+
match display.raw_handle().try_as_capture_item() {
184+
Ok(item) => {
185+
trace!("GraphicsCaptureItem created successfully on capture thread");
186+
item
187+
}
188+
Err(e) => {
189+
error!("Failed to create GraphicsCaptureItem on capture thread: {}", e);
190+
let _ = error_tx.send(anyhow!("Failed to create GraphicsCaptureItem: {}", e));
191+
return;
192+
}
193+
}
194+
}
195+
None => {
196+
error!("Display not found for ID: {:?}", display_id);
197+
let _ = error_tx.send(anyhow!("Display not found for ID: {:?}", display_id));
198+
return;
199+
}
200+
};
201+
189202
let res = scap_direct3d::Capturer::new(
190203
capture_item,
191204
settings,
@@ -206,26 +219,39 @@ impl output_pipeline::VideoSource for VideoSource {
206219
Ok(())
207220
}
208221
},
209-
Some(d3d_device),
222+
Some(d3d_device), // Use the same D3D device as the encoder
210223
);
211224

212225
let mut capturer = match res {
213-
Ok(capturer) => capturer,
226+
Ok(capturer) => {
227+
trace!("D3D capturer created successfully");
228+
capturer
229+
}
214230
Err(e) => {
231+
error!("Failed to create D3D capturer: {}", e);
215232
let _ = error_tx.send(e.into());
216233
return;
217234
}
218235
};
219236

220237
let Ok(VideoControl::Start(reply)) = ctrl_rx.recv() else {
238+
error!("Failed to receive Start control message - channel disconnected");
239+
let _ = error_tx.send(anyhow!("Control channel disconnected before Start"));
221240
return;
222241
};
223242

224-
if reply.send(capturer.start().map_err(Into::into)).is_err() {
243+
trace!("Starting D3D capturer");
244+
let start_result = capturer.start().map_err(Into::into);
245+
if let Err(ref e) = start_result {
246+
error!("Failed to start D3D capturer: {}", e);
247+
}
248+
if reply.send(start_result).is_err() {
249+
error!("Failed to send start result - receiver dropped");
225250
return;
226251
}
227252

228253
let Ok(VideoControl::Stop(reply)) = ctrl_rx.recv() else {
254+
trace!("Failed to receive Stop control message - channel disconnected (expected during shutdown)");
229255
return;
230256
};
231257

0 commit comments

Comments
 (0)