我试图在Rust中创建一个简单的屏幕捕获库,但它在最大化窗口方面存在问题(监视器捕获和非最大化窗口捕获工作正常).
我从obs studio winrt-capture.cpp复制了我的大部分代码
但我不知道为什么除了最大化的应用程序之外,所有东西都能正常工作
// Create Frame Pool
let frame_pool = Direct3D11CaptureFramePool::Create(
&direct3d_device,
DirectXPixelFormat::R8G8B8A8UIntNormalized,
1,
item.Size()?,
)?;
// Create Capture Session
let session = frame_pool.CreateCaptureSession(&item)?;
// On Frame Arrived
frame_pool.FrameArrived(
&TypedEventHandler::<Direct3D11CaptureFramePool, IInspectable>::new({
move |frame, _| {
// Get Texture Stuff
let frame = frame.as_ref().unwrap().TryGetNextFrame()?;
let frame_content_size = frame.ContentSize()?;
let frame_surface = frame.Surface()?;
let frame_surface = frame_surface.cast::<IDirect3DDxgiInterfaceAccess>()?
let frame_surface = unsafe { frame_surface.GetInterface::<ID3D11Texture2D>()? };
let mut desc = D3D11_TEXTURE2D_DESC::default();
unsafe { frame_surface.GetDesc(&mut desc) }
if desc.Format == DXGI_FORMAT_R8G8B8A8_UNORM {
if frame_content_size.Width != last_size.Width
|| frame_content_size.Height != last_size.Height
{
let direct3d_device_recreate = &direct3d_device_recreate;
frame_pool_recreate
.Recreate(
&direct3d_device_recreate.inner,
DirectXPixelFormat::R8G8B8A8UIntNormalized,
1,
frame_content_size,
)
.unwrap();
last_size = frame_content_size;
return Ok(());
}
let texture_width = desc.Width;
let texture_height = desc.Height;
// Copy Texture Settings
let texture_desc = D3D11_TEXTURE2D_DESC {
Width: texture_width,
Height: texture_height,
MipLevels: 1,
ArraySize: 1,
Format: DXGI_FORMAT_R8G8B8A8_UNORM,
SampleDesc: DXGI_SAMPLE_DESC {
Count: 1,
Quality: 0,
},
Usage: D3D11_USAGE_STAGING,
BindFlags: 0,
CPUAccessFlags: D3D11_CPU_ACCESS_READ.0 as u32,
MiscFlags: 0,
};
// Create A Texture That CPU Can Read
let mut texture = None;
unsafe {
d3d_device_frame_pool.CreateTexture2D(
&texture_desc,
None,
Some(&mut texture),
)?
};
let texture = texture.unwrap();
// Copy The Real Texture To Copy Texture
unsafe { context.CopyResource(&texture, &frame_surface) };
// Map The Texture To Enable CPU Access
let mut mapped_resource = D3D11_MAPPED_SUBRESOURCE::default();
unsafe {
context.Map(
&texture,
0,
D3D11_MAP_READ,
0,
Some(&mut mapped_resource),
)?
};
// Create A Slice From The Bits
let slice: &[Rgba] = unsafe {
std::slice::from_raw_parts(
mapped_resource.pData as *const Rgba,
(texture_desc.Height * mapped_resource.RowPitch) as usize
/ std::mem::size_of::<Rgba>(),
)
};
// Send The Frame To Callback Struct
trigger_frame_pool.lock().on_frame_arrived(
slice,
texture_width,
texture_height,
);
// Unmap Copy Texture
unsafe { context.Unmap(&texture, 0) };
}
Result::Ok(())
}
}),
)?;
我注意到一件奇怪的事情是,帧池的大小是1922x1033,我的显示器是1920x1080,所以它的宽度甚至比我的显示器的宽度更高,但我不确定这是否是问题所在,因为我判断过,obs项目大小也是1922x1033,我不知道它是否在某个地方处理过.
Edit:在进一步调查之后,我想我发现了问题所在,我判断了像素,发现有时alpha值不是255,这意味着我错误地读取了映射的资源.
基于d3d11_mapped_subresource docs:
对于D3D_FEATURE_LEVEL_10_0及更高版本,指针与16个字节对齐. 对于低于D3D_FEATURE_LEVEL_10_0的值,指针对齐到4个字节.
我认为它是在我创建切片时自动处理的,因为它是有效的,而RGBA struct 对齐恰好是这样的.
但有一张纸条:The runtime might assign values to RowPitch and DepthPitch that are larger than anticipated because there might be padding between rows and depth.
我认为如果我处理这个问题就会解决.
Edit 2: I was right because:
window size: 1922x1033
Anticipated Byte Size 7941704 (1922 * 1033 * 4)
Byte Size: 8065664 (Height * mapped_resource.RowPitch)
现在的问题是,填充物在哪里?
Edit 3:已使用以下代码修复:
let mut vec = Vec::new();
let slice = if texture_desc.Width * texture_desc.Height * 4
== texture_desc.Height * mapped_resource.RowPitch
{
// Means There Is No Padding And We Can Do Our Work
unsafe {
std::slice::from_raw_parts(
mapped_resource.pData as *const Rgba,
(texture_desc.Height * mapped_resource.RowPitch) as usize
/ std::mem::size_of::<Rgba>(),
)
}
} else {
for i in 0..texture_desc.Height {
println!("{i}");
let slice = unsafe {
std::slice::from_raw_parts(
mapped_resource
.pData
.add((i * mapped_resource.RowPitch) as usize)
as *mut Rgba,
texture_desc.Width as usize,
)
};
vec.extend_from_slice(slice);
}
vec.as_slice()
};
我不知道这是不是最优化的方法(如果你知道的话,请提供更快的方法),但它确实有效.