use std::{
sync::{
Arc,
mpsc::{Receiver, SyncSender, sync_channel},
},
thread,
};
use windows::{
Win32::Graphics::{
Direct3D11::{
D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_SINGLETHREADED,
D3D11_TEXTURE2D_DESC, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D,
},
Dxgi::{
DXGI_ERROR_WAIT_TIMEOUT, DXGI_OUTDUPL_FRAME_INFO, IDXGIDevice, IDXGIOutput1,
IDXGIOutputDuplication, IDXGIResource,
},
Gdi::HMONITOR,
},
core::Interface,
};
use crate::{
XCapError, XCapResult,
video_recorder::{Frame, RecorderWaker},
};
use super::utils::{create_d3d_device, texture_to_frame};
#[derive(Debug, Clone)]
pub(crate) struct ImplVideoRecorder {
d3d_device: ID3D11Device,
d3d_context: ID3D11DeviceContext,
duplication: IDXGIOutputDuplication,
recorder_waker: Arc<RecorderWaker>,
tx: SyncSender<Frame>,
}
impl ImplVideoRecorder {
pub fn new(h_monitor: HMONITOR) -> XCapResult<(Self, Receiver<Frame>)> {
unsafe {
let d3d_device = create_d3d_device(
D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_SINGLETHREADED,
)?;
let dxgi_device = d3d_device.cast::<IDXGIDevice>()?;
let d3d_context = d3d_device.GetImmediateContext()?;
let adapter = dxgi_device.GetAdapter()?;
let mut output_index = 0;
loop {
let output = adapter.EnumOutputs(output_index)?;
output_index += 1;
let output_desc = output.GetDesc()?;
let output1 = output.cast::<IDXGIOutput1>()?;
let duplication = output1.DuplicateOutput(&dxgi_device)?;
if output_desc.Monitor == h_monitor {
let (tx, sx) = sync_channel(0);
let s = Self {
d3d_device,
d3d_context,
duplication,
recorder_waker: Arc::new(RecorderWaker::new()),
tx,
};
s.on_frame()?;
return Ok((s, sx));
}
}
}
}
pub fn on_frame(&self) -> XCapResult<()> {
let duplication = self.duplication.clone();
let d3d_device = self.d3d_device.clone();
let d3d_context = self.d3d_context.clone();
let recorder_waker = self.recorder_waker.clone();
let tx = self.tx.clone();
thread::spawn(move || {
loop {
recorder_waker.wait()?;
let mut frame_info = DXGI_OUTDUPL_FRAME_INFO::default();
let mut resource: Option<IDXGIResource> = None;
unsafe {
match duplication.AcquireNextFrame(200, &mut frame_info, &mut resource) {
Err(err) => {
let _ = duplication.ReleaseFrame();
if err.code() != DXGI_ERROR_WAIT_TIMEOUT {
break Err::<(), XCapError>(XCapError::new(
"DXGI_ERROR_UNSUPPORTED",
));
}
}
_ => {
if frame_info.LastPresentTime != 0 {
let resource =
resource.ok_or(XCapError::new("AcquireNextFrame failed"))?;
let source_texture = resource.cast::<ID3D11Texture2D>()?;
let mut source_texture_desc = D3D11_TEXTURE2D_DESC::default();
source_texture.GetDesc(&mut source_texture_desc);
let frame = texture_to_frame(
&d3d_device,
&d3d_context,
&source_texture,
0,
0,
source_texture_desc.Width,
source_texture_desc.Height,
)?;
let _ = tx.send(frame);
}
duplication.ReleaseFrame()?;
}
}
}
}
});
Ok(())
}
pub fn start(&self) -> XCapResult<()> {
self.recorder_waker.wake()?;
Ok(())
}
pub fn stop(&self) -> XCapResult<()> {
self.recorder_waker.sleep()?;
Ok(())
}
}