我试图在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()
                        };

我不知道这是不是最优化的方法(如果你知道的话,请提供更快的方法),但它确实有效.

推荐答案

Image Stride

当视频图像存储在内存中时,内存缓冲区可能 在每行像素之后包含额外的填充字节.填充物 字节影响图像在内存中的存储方式,但不影响存储方式 此时将显示该图像.

跨度是从内存中的一行像素到 内存中的下一行像素.Stride is also called Pitch.如果 填充字节的情况下,跨度比 图像,如下图所示:

enter image description here

Rust相关问答推荐

移植带有可变borrow 的C代码-卸载期间错误(nappgui示例)

使用pyo3::Types::PyIterator的无限内存使用量

为什么我可以跟踪以前borrow 过的变量?房主在哪里?

`Pin`有没有不涉及不安全代码的目的?

变量需要parse()中的显式类型

零拷贝按步骤引用一段字节

正在将带有盒的异步特征迁移到新的异步_fn_in_特征功能

你能在Rust中弃用一个属性吗?

链表堆栈溢出

如何将生存期参数添加到框<>;具有dyn类型别名

用于实现获取 struct 体 id 的特征规范

为相同特征的特征对象使用 move 方法实现特征

如何在 Rust 中编写一个通用方法,它可以接受任何可以转换为另一个值的值?

从 rust 函数返回 &HashMap

如何在 Rust 中显式声明 std::str::Matches<'a, P> ?

按下 Ctrl + C 时优雅地停止命令并退出进程

改变不实现克隆的 dioxus UseState struct

在单独的线程上运行 actix web 服务器

为什么-x试图解析为文字并在声明性宏中失败?

在 Rust 中有条件地导入?