1#![cfg(windows)]
4
5extern crate winapi;
6extern crate wio;
7
8use std::mem::zeroed;
9use std::{mem, ptr, slice};
10use winapi::shared::dxgi::{
11 CreateDXGIFactory1, IDXGIAdapter, IDXGIAdapter1, IDXGIFactory1, IDXGIOutput, IDXGISurface1,
12 IID_IDXGIFactory1, DXGI_MAP_READ, DXGI_OUTPUT_DESC, DXGI_RESOURCE_PRIORITY_MAXIMUM,
13};
14use winapi::shared::dxgi1_2::{IDXGIOutput1, IDXGIOutputDuplication};
15use winapi::shared::dxgitype::*;
16use winapi::shared::windef::*;
18use winapi::shared::winerror::*;
19use winapi::um::d3d11::*;
20use winapi::um::d3dcommon::*;
21use winapi::um::unknwnbase::*;
22use winapi::um::winuser::*;
23use wio::com::ComPtr;
24
25#[derive(Copy, Clone, Debug, PartialOrd, PartialEq, Eq, Ord)]
27pub struct BGRA8 {
28 pub b: u8,
29 pub g: u8,
30 pub r: u8,
31 pub a: u8,
32}
33
34#[derive(Debug)]
36pub enum CaptureError {
37 AccessDenied,
39 AccessLost,
41 RefreshFailure,
43 Timeout,
45 Fail(&'static str),
47}
48
49pub fn hr_failed(hr: HRESULT) -> bool {
51 hr < 0
52}
53
54fn create_dxgi_factory_1() -> ComPtr<IDXGIFactory1> {
55 unsafe {
56 let mut factory = ptr::null_mut();
57 let hr = CreateDXGIFactory1(&IID_IDXGIFactory1, &mut factory);
58 if hr_failed(hr) {
59 panic!("Failed to create DXGIFactory1, {:x}", hr)
60 } else {
61 ComPtr::from_raw(factory as *mut IDXGIFactory1)
62 }
63 }
64}
65
66fn d3d11_create_device(
67 adapter: *mut IDXGIAdapter,
68) -> (ComPtr<ID3D11Device>, ComPtr<ID3D11DeviceContext>) {
69 unsafe {
70 let (mut d3d11_device, mut device_context) = (ptr::null_mut(), ptr::null_mut());
71 let hr = D3D11CreateDevice(
72 adapter,
73 D3D_DRIVER_TYPE_UNKNOWN,
74 ptr::null_mut(),
75 0,
76 ptr::null_mut(),
77 0,
78 D3D11_SDK_VERSION,
79 &mut d3d11_device,
80 &mut D3D_FEATURE_LEVEL_9_1,
81 &mut device_context,
82 );
83 if hr_failed(hr) {
84 panic!("Failed to create d3d11 device and device context, {:x}", hr)
85 } else {
86 (
87 ComPtr::from_raw(d3d11_device as *mut ID3D11Device),
88 ComPtr::from_raw(device_context),
89 )
90 }
91 }
92}
93
94fn get_adapter_outputs(adapter: &IDXGIAdapter1) -> Vec<ComPtr<IDXGIOutput>> {
95 let mut outputs = Vec::new();
96 for i in 0.. {
97 unsafe {
98 let mut output = ptr::null_mut();
99 if hr_failed(adapter.EnumOutputs(i, &mut output)) {
100 break;
101 } else {
102 let mut out_desc = zeroed();
103 (*output).GetDesc(&mut out_desc);
104 if out_desc.AttachedToDesktop != 0 {
105 outputs.push(ComPtr::from_raw(output))
106 } else {
107 break;
108 }
109 }
110 }
111 }
112 outputs
113}
114
115fn output_is_primary(output: &ComPtr<IDXGIOutput1>) -> bool {
116 unsafe {
117 let mut output_desc = zeroed();
118 output.GetDesc(&mut output_desc);
119 let mut monitor_info: MONITORINFO = zeroed();
120 monitor_info.cbSize = mem::size_of::<MONITORINFO>() as u32;
121 GetMonitorInfoW(output_desc.Monitor, &mut monitor_info);
122 (monitor_info.dwFlags & 1) != 0
123 }
124}
125
126fn get_capture_source(
127 output_dups: Vec<(ComPtr<IDXGIOutputDuplication>, ComPtr<IDXGIOutput1>)>,
128 cs_index: usize,
129) -> Option<(ComPtr<IDXGIOutputDuplication>, ComPtr<IDXGIOutput1>)> {
130 if cs_index == 0 {
131 output_dups
132 .into_iter()
133 .find(|&(_, ref out)| output_is_primary(out))
134 } else {
135 output_dups
136 .into_iter()
137 .filter(|&(_, ref out)| !output_is_primary(out))
138 .nth(cs_index - 1)
139 }
140}
141
142fn duplicate_outputs(
143 mut device: ComPtr<ID3D11Device>,
144 outputs: Vec<ComPtr<IDXGIOutput>>,
145) -> Result<
146 (
147 ComPtr<ID3D11Device>,
148 Vec<(ComPtr<IDXGIOutputDuplication>, ComPtr<IDXGIOutput1>)>,
149 ),
150 HRESULT,
151> {
152 let mut out_dups = Vec::new();
153 for output in outputs
154 .into_iter()
155 .map(|out| out.cast::<IDXGIOutput1>().unwrap())
156 {
157 let dxgi_device = device.up::<IUnknown>();
158 let output_duplication = unsafe {
159 let mut output_duplication = ptr::null_mut();
160 let hr = output.DuplicateOutput(dxgi_device.as_raw(), &mut output_duplication);
161 if hr_failed(hr) {
162 return Err(hr);
163 }
164 ComPtr::from_raw(output_duplication)
165 };
166 device = dxgi_device.cast().unwrap();
167 out_dups.push((output_duplication, output));
168 }
169 Ok((device, out_dups))
170}
171
172struct DuplicatedOutput {
173 device: ComPtr<ID3D11Device>,
174 device_context: ComPtr<ID3D11DeviceContext>,
175 output: ComPtr<IDXGIOutput1>,
176 output_duplication: ComPtr<IDXGIOutputDuplication>,
177}
178impl DuplicatedOutput {
179 fn get_desc(&self) -> DXGI_OUTPUT_DESC {
180 unsafe {
181 let mut desc = zeroed();
182 self.output.GetDesc(&mut desc);
183 desc
184 }
185 }
186
187 fn capture_frame_to_surface(
188 &mut self,
189 timeout_ms: u32,
190 ) -> Result<ComPtr<IDXGISurface1>, HRESULT> {
191 let frame_resource = unsafe {
192 let mut frame_resource = ptr::null_mut();
193 let mut frame_info = zeroed();
194 let hr = self.output_duplication.AcquireNextFrame(
195 timeout_ms,
196 &mut frame_info,
197 &mut frame_resource,
198 );
199 if hr_failed(hr) {
200 return Err(hr);
201 }
202 ComPtr::from_raw(frame_resource)
203 };
204 let frame_texture = frame_resource.cast::<ID3D11Texture2D>().unwrap();
205 let mut texture_desc = unsafe {
206 let mut texture_desc = zeroed();
207 frame_texture.GetDesc(&mut texture_desc);
208 texture_desc
209 };
210 texture_desc.Usage = D3D11_USAGE_STAGING;
212 texture_desc.BindFlags = 0;
213 texture_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
214 texture_desc.MiscFlags = 0;
215 let readable_texture = unsafe {
216 let mut readable_texture = ptr::null_mut();
217 let hr =
218 self.device
219 .CreateTexture2D(&mut texture_desc, ptr::null(), &mut readable_texture);
220 if hr_failed(hr) {
221 return Err(hr);
222 }
223 ComPtr::from_raw(readable_texture)
224 };
225 unsafe { readable_texture.SetEvictionPriority(DXGI_RESOURCE_PRIORITY_MAXIMUM) };
228 let readable_surface = readable_texture.up::<ID3D11Resource>();
229 unsafe {
230 self.device_context.CopyResource(
231 readable_surface.as_raw(),
232 frame_texture.up::<ID3D11Resource>().as_raw(),
233 );
234 self.output_duplication.ReleaseFrame();
235 }
236 readable_surface.cast()
237 }
238}
239
240pub struct DXGIManager {
242 duplicated_output: Option<DuplicatedOutput>,
243 capture_source_index: usize,
244 timeout_ms: u32,
245}
246
247struct SharedPtr<T>(*const T);
248
249unsafe impl<T> Send for SharedPtr<T> {}
250
251unsafe impl<T> Sync for SharedPtr<T> {}
252
253impl DXGIManager {
254 pub fn new(timeout_ms: u32) -> Result<DXGIManager, &'static str> {
256 let mut manager = DXGIManager {
257 duplicated_output: None,
258 capture_source_index: 0,
259 timeout_ms: timeout_ms,
260 };
261
262 match manager.acquire_output_duplication() {
263 Ok(_) => Ok(manager),
264 Err(_) => Err("Failed to acquire output duplication"),
265 }
266 }
267
268 pub fn geometry(&self) -> (usize, usize) {
269 let output_desc = self.duplicated_output.as_ref().unwrap().get_desc();
270 let RECT {
271 left,
272 top,
273 right,
274 bottom,
275 } = output_desc.DesktopCoordinates;
276 ((right - left) as usize, (bottom - top) as usize)
277 }
278
279 pub fn set_capture_source_index(&mut self, cs: usize) {
281 self.capture_source_index = cs;
282 self.acquire_output_duplication().unwrap()
283 }
284
285 pub fn get_capture_source_index(&self) -> usize {
286 self.capture_source_index
287 }
288
289 pub fn set_timeout_ms(&mut self, timeout_ms: u32) {
291 self.timeout_ms = timeout_ms
292 }
293
294 pub fn acquire_output_duplication(&mut self) -> Result<(), ()> {
296 self.duplicated_output = None;
297 let factory = create_dxgi_factory_1();
298 for (outputs, adapter) in (0..)
299 .map(|i| {
300 let mut adapter = ptr::null_mut();
301 unsafe {
302 if factory.EnumAdapters1(i, &mut adapter) != DXGI_ERROR_NOT_FOUND {
303 Some(ComPtr::from_raw(adapter))
304 } else {
305 None
306 }
307 }
308 })
309 .take_while(Option::is_some)
310 .map(Option::unwrap)
311 .map(|mut adapter| (get_adapter_outputs(&mut adapter), adapter))
312 .filter(|&(ref outs, _)| !outs.is_empty())
313 {
314 let (d3d11_device, device_context) = d3d11_create_device(adapter.up().as_raw());
316 let (d3d11_device, output_duplications) =
317 duplicate_outputs(d3d11_device, outputs).map_err(|_| ())?;
318 if let Some((output_duplication, output)) =
319 get_capture_source(output_duplications, self.capture_source_index)
320 {
321 self.duplicated_output = Some(DuplicatedOutput {
322 device: d3d11_device,
323 device_context: device_context,
324 output: output,
325 output_duplication: output_duplication,
326 });
327 return Ok(());
328 }
329 }
330 Err(())
331 }
332
333 fn capture_frame_to_surface(&mut self) -> Result<ComPtr<IDXGISurface1>, CaptureError> {
334 if let None = self.duplicated_output {
335 if let Ok(_) = self.acquire_output_duplication() {
336 return Err(CaptureError::Fail("No valid duplicated output"));
337 } else {
338 return Err(CaptureError::RefreshFailure);
339 }
340 }
341 let timeout_ms = self.timeout_ms;
342 match self
343 .duplicated_output
344 .as_mut()
345 .unwrap()
346 .capture_frame_to_surface(timeout_ms)
347 {
348 Ok(surface) => Ok(surface),
349 Err(DXGI_ERROR_ACCESS_LOST) => {
350 if let Ok(_) = self.acquire_output_duplication() {
351 Err(CaptureError::AccessLost)
352 } else {
353 Err(CaptureError::RefreshFailure)
354 }
355 }
356 Err(E_ACCESSDENIED) => Err(CaptureError::AccessDenied),
357 Err(DXGI_ERROR_WAIT_TIMEOUT) => Err(CaptureError::Timeout),
358 Err(_) => {
359 if let Ok(_) = self.acquire_output_duplication() {
360 Err(CaptureError::Fail("Failure when acquiring frame"))
361 } else {
362 Err(CaptureError::RefreshFailure)
363 }
364 }
365 }
366 }
367
368 fn capture_frame_t<T: Copy + Send + Sync + Sized>(&mut self) -> Result<(Vec<T>, (usize, usize)), CaptureError> {
369 let frame_surface = match self.capture_frame_to_surface() {
370 Ok(surface) => surface,
371 Err(e) => return Err(e),
372 };
373 let mapped_surface = unsafe {
374 let mut mapped_surface = zeroed();
375 if hr_failed(frame_surface.Map(&mut mapped_surface, DXGI_MAP_READ)) {
376 frame_surface.Release();
377 return Err(CaptureError::Fail("Failed to map surface"));
378 }
379 mapped_surface
380 };
381 let byte_size = |x| x * mem::size_of::<BGRA8>() / mem::size_of::<T>();
382 let output_desc = self.duplicated_output.as_mut().unwrap().get_desc();
383 let stride = mapped_surface.Pitch as usize / mem::size_of::<BGRA8>();
384 let byte_stride = byte_size(stride);
385 let (output_width, output_height) = {
386 let RECT {
387 left,
388 top,
389 right,
390 bottom,
391 } = output_desc.DesktopCoordinates;
392 ((right - left) as usize, (bottom - top) as usize)
393 };
394 let mut pixel_buf = Vec::with_capacity(byte_size(output_width * output_height));
395
396 let scan_lines =
397 match output_desc.Rotation {
398 DXGI_MODE_ROTATION_ROTATE90 | DXGI_MODE_ROTATION_ROTATE270 => output_width,
399 _ => output_height,
400 };
401
402 let mapped_pixels = unsafe {
403 slice::from_raw_parts(
404 mapped_surface.pBits as *const T,
405 byte_stride * scan_lines,
406 )
407 };
408
409 match output_desc.Rotation {
410 DXGI_MODE_ROTATION_IDENTITY | DXGI_MODE_ROTATION_UNSPECIFIED =>
411 pixel_buf.extend_from_slice(mapped_pixels),
412 DXGI_MODE_ROTATION_ROTATE90 => {
413 unsafe {
414 let ptr = SharedPtr(pixel_buf.as_ptr() as *const BGRA8);
415 mapped_pixels.chunks(byte_stride).rev().enumerate().for_each(|(column, chunk)| {
416 let mut src = chunk.as_ptr() as *const BGRA8;
417 let mut dst = ptr.0 as *mut BGRA8;
418 dst = dst.add(column);
419 let stop = src.add(output_height);
420 while src != stop {
421 dst.write(*src);
422 src = src.add(1);
423 dst = dst.add(output_width);
424 }
425 });
426 pixel_buf.set_len(pixel_buf.capacity());
427 }
428 }
429 DXGI_MODE_ROTATION_ROTATE180 => {
430 unsafe {
431 let ptr = SharedPtr(pixel_buf.as_ptr() as *const BGRA8);
432 mapped_pixels.chunks(byte_stride).rev().enumerate().for_each(|(scan_line, chunk)| {
433 let mut src = chunk.as_ptr() as *const BGRA8;
434 let mut dst = ptr.0 as *mut BGRA8;
435 dst = dst.add(scan_line * output_width);
436 let stop = src;
437 src = src.add(output_width);
438 while src != stop {
439 src = src.sub(1);
440 dst.write(*src);
441 dst = dst.add(1);
442 }
443 });
444 pixel_buf.set_len(pixel_buf.capacity());
445 }
446 }
447 DXGI_MODE_ROTATION_ROTATE270 => {
448 unsafe {
449 let ptr = SharedPtr(pixel_buf.as_ptr() as *const BGRA8);
450 mapped_pixels.chunks(byte_stride).enumerate().for_each(|(column, chunk)| {
451 let mut src = chunk.as_ptr() as *const BGRA8;
452 let mut dst = ptr.0 as *mut BGRA8;
453 dst = dst.add(column);
454 let stop = src;
455 src = src.add(output_height);
456 while src != stop {
457 src = src.sub(1);
458 dst.write(*src);
459 dst = dst.add(output_width);
460 }
461 });
462 pixel_buf.set_len(pixel_buf.capacity());
463 }
464 }
465 n => unreachable!("Undefined DXGI_MODE_ROTATION: {}", n),
466 }
467 unsafe { frame_surface.Unmap() };
468 Ok((pixel_buf, (output_width, output_height)))
469 }
470
471 pub fn capture_frame(&mut self) -> Result<(Vec<BGRA8>, (usize, usize)), CaptureError> {
476 self.capture_frame_t()
477 }
478
479 pub fn capture_frame_components(&mut self) -> Result<(Vec<u8>, (usize, usize)), CaptureError> {
484 self.capture_frame_t()
485 }
486}
487
488#[test]
489fn test() {
490 let mut manager = DXGIManager::new(300).unwrap();
491 for _ in 0..100 {
492 match manager.capture_frame() {
493 Ok((pixels, (_, _))) => {
494 let len = pixels.len() as u64;
495 let (r, g, b) = pixels.into_iter().fold((0u64, 0u64, 0u64), |(r, g, b), p| {
496 (r + p.r as u64, g + p.g as u64, b + p.b as u64)
497 });
498 println!("avg: {} {} {}", r / len, g / len, b / len)
499 }
500 Err(e) => println!("error: {:?}", e),
501 }
502 }
503}
504
505#[test]
506fn compare_frame_dims() {
507 let mut manager = DXGIManager::new(300).unwrap();
508 let (frame, (fw, fh)) = manager.capture_frame().unwrap();
509 let (frame_u8, (fu8w, fu8h)) = manager.capture_frame_components().unwrap();
510 assert_eq!(fw, fu8w);
511 assert_eq!(fh, fu8h);
512 assert_eq!(4 * frame.len(), frame_u8.len());
513}