dxgi返回黑色位图,桌面区域捕获

发布于 2025-02-09 02:23:48 字数 3621 浏览 2 评论 0原文

当使用涡流捕获桌面时,它可以正常工作。虽然,当我尝试使用矩形的x或y位置以外的任何其他值时,它仅作为黑色位图返回框架。我用于参考的代码位于此处: https:// github。 com/diogotr7/desktopduplicationsamples/blob/master/vorticecore/program.cs ,但它遇到了相同的问题。我尝试更改var框架中的大多数值,而var mapdest却没有很多运气。

public void Start()
{
    _isCapturing = true;
    DXGI.CreateDXGIFactory1<IDXGIFactory1>(out var factory);
    if (factory == null)
    {
        return;
    }
    var adapter = factory.GetAdapter(0);
    var output = adapter.GetOutput(0);
    var output1 = output.QueryInterface<IDXGIOutput1>();
    //D3D12.D3D12CreateDevice(adapter, FeatureLevel.Level_12_0, out ID3D12Device? device);
    D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, _featureLevels, out var device);
    if (device == null)
        throw new Exception("Unable to Locate Device.");

    // Width/Height of desktop to capture
    Rectangle rectangle = new Rectangle(0, 0,
            output.Description.DesktopCoordinates.Right,
            output.Description.DesktopCoordinates.Bottom);

    // Create Staging texture CPU-accessible
    var texture2dDescription = new Texture2DDescription
    {
        ArraySize = 1,
        BindFlags = BindFlags.None,
        CPUAccessFlags = CpuAccessFlags.Read | CpuAccessFlags.Write,
        Format = Format.B8G8R8A8_UNorm,
        Height = rectangle.Bottom,
        MipLevels = 1,
        SampleDescription = { Count = 1, Quality = 0 },
        Usage = ResourceUsage.Staging,
        Width = rectangle.Right
    };
    
    Task.Factory.StartNew(() =>
    {
        // Duplicate the output
        using var duplicatedOutput = output1.DuplicateOutput(device);
        while (_isCapturing)
        {
            try
            {
                var currentFrame = device.CreateTexture2D(texture2dDescription);

                Thread.Sleep(50);
                rectangle.X = 0;
                duplicatedOutput.AcquireNextFrame(100, out var frameInfo, out var desktopResource);
                if (desktopResource == null)
                    continue;
                var tempTexture = desktopResource.QueryInterface<ID3D11Texture2D>();
                device.ImmediateContext.CopyResource(currentFrame, tempTexture);
                var dataBox = device.ImmediateContext.Map(currentFrame, 0);
                var frame = new Bitmap(rectangle.Right, rectangle.Bottom, PixelFormat.Format32bppRgb);
                var mapDest = frame.LockBits(rectangle, ImageLockMode.WriteOnly, frame.PixelFormat);
                for (int y = rectangle.Y, sizeInBytesToCopy = rectangle.Width * 4; y < rectangle.Height; y++)
                {
                    MemoryHelpers.CopyMemory(mapDest.Scan0 + y * rectangle.Right * 4,
                        dataBox.DataPointer + y * dataBox.RowPitch, sizeInBytesToCopy);
                }

                frame.UnlockBits(mapDest);
                ScreenRefreshed?.Invoke(this, frame);
                desktopResource.Dispose();
                frame.Dispose();
                tempTexture.Dispose();
                currentFrame.Dispose();
            }
            catch (Exception e)
            {
                if (e.HResult != Vortice.DXGI.ResultCode.WaitTimeout.Code)
                {
                    Trace.TraceError(e.Message);
                    Trace.TraceError(e.StackTrace);
                }
            }

            duplicatedOutput.ReleaseFrame();
        }
    });
}

When capturing the desktop using Vortice with the code below it works just fine. Although, when I try to use any other value than 0 for the X or Y location of the rectangle it only returns frame as a black bitmap. The code I used for reference is located here: https://github.com/diogotr7/DesktopDuplicationSamples/blob/master/VorticeCore/Program.cs, but it suffers from the same problem. I have tried altering most values in the var frame and var mapDest with not much luck.

public void Start()
{
    _isCapturing = true;
    DXGI.CreateDXGIFactory1<IDXGIFactory1>(out var factory);
    if (factory == null)
    {
        return;
    }
    var adapter = factory.GetAdapter(0);
    var output = adapter.GetOutput(0);
    var output1 = output.QueryInterface<IDXGIOutput1>();
    //D3D12.D3D12CreateDevice(adapter, FeatureLevel.Level_12_0, out ID3D12Device? device);
    D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, _featureLevels, out var device);
    if (device == null)
        throw new Exception("Unable to Locate Device.");

    // Width/Height of desktop to capture
    Rectangle rectangle = new Rectangle(0, 0,
            output.Description.DesktopCoordinates.Right,
            output.Description.DesktopCoordinates.Bottom);

    // Create Staging texture CPU-accessible
    var texture2dDescription = new Texture2DDescription
    {
        ArraySize = 1,
        BindFlags = BindFlags.None,
        CPUAccessFlags = CpuAccessFlags.Read | CpuAccessFlags.Write,
        Format = Format.B8G8R8A8_UNorm,
        Height = rectangle.Bottom,
        MipLevels = 1,
        SampleDescription = { Count = 1, Quality = 0 },
        Usage = ResourceUsage.Staging,
        Width = rectangle.Right
    };
    
    Task.Factory.StartNew(() =>
    {
        // Duplicate the output
        using var duplicatedOutput = output1.DuplicateOutput(device);
        while (_isCapturing)
        {
            try
            {
                var currentFrame = device.CreateTexture2D(texture2dDescription);

                Thread.Sleep(50);
                rectangle.X = 0;
                duplicatedOutput.AcquireNextFrame(100, out var frameInfo, out var desktopResource);
                if (desktopResource == null)
                    continue;
                var tempTexture = desktopResource.QueryInterface<ID3D11Texture2D>();
                device.ImmediateContext.CopyResource(currentFrame, tempTexture);
                var dataBox = device.ImmediateContext.Map(currentFrame, 0);
                var frame = new Bitmap(rectangle.Right, rectangle.Bottom, PixelFormat.Format32bppRgb);
                var mapDest = frame.LockBits(rectangle, ImageLockMode.WriteOnly, frame.PixelFormat);
                for (int y = rectangle.Y, sizeInBytesToCopy = rectangle.Width * 4; y < rectangle.Height; y++)
                {
                    MemoryHelpers.CopyMemory(mapDest.Scan0 + y * rectangle.Right * 4,
                        dataBox.DataPointer + y * dataBox.RowPitch, sizeInBytesToCopy);
                }

                frame.UnlockBits(mapDest);
                ScreenRefreshed?.Invoke(this, frame);
                desktopResource.Dispose();
                frame.Dispose();
                tempTexture.Dispose();
                currentFrame.Dispose();
            }
            catch (Exception e)
            {
                if (e.HResult != Vortice.DXGI.ResultCode.WaitTimeout.Code)
                {
                    Trace.TraceError(e.Message);
                    Trace.TraceError(e.StackTrace);
                }
            }

            duplicatedOutput.ReleaseFrame();
        }
    });
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清晨说晚安 2025-02-16 02:23:48

您的代码存在问题。以下是说明为什么它不超过一次的问题,需要一个奇怪的睡眠呼叫:

  • 您不处理所有内容(您应该,我想这是在释放基础com接口)
  • 。 't
  • 您不会获得的帧。

但是,还有其他问题

  • 可以使用D3D11 CreatedEvice创建的设备上下文,您无需使用此即时Ectext属性。
  • 如果您想最大程度地提高重复性能,则不应直接在获取帧循环中使用CPU映射的纹理,您必须保持非常快。您应该将它们复制(就像您一样),然后使用另一个线程将它们传递到CPU,例如
  • 重复API是 屏幕捕获API,它是屏幕重复API(!),这意味着它将将渲染框架复制到喜欢的“邮箱”,您可以在可能的情况下查询。超时可以保持相对重要。当您暂停时,这只是意味着屏幕上没有任何更改。 frameinfo参数具有有关所有这些的有用信息。

这是一个似乎可以使用的版本(获取10帧并将其保存为位图文件):

class Program
{
    private static readonly FeatureLevel[] _featureLevels = new[]
    {
        FeatureLevel.Level_11_0,
        FeatureLevel.Level_10_1,
        FeatureLevel.Level_10_0,
        FeatureLevel.Level_9_3,
        FeatureLevel.Level_9_2,
        FeatureLevel.Level_9_1,
    };

    static void Main()
    {
        DXGI.CreateDXGIFactory1<IDXGIFactory1>(out var factory);
        if (factory == null)
            return;

        using (factory)
        {
            using var adapter = factory.GetAdapter(0);
            using var output = adapter.GetOutput(0);
            using var output1 = output.QueryInterface<IDXGIOutput1>();
            D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, _featureLevels, out var device, out var deviceContext);
            if (device == null)
                return;

            using (device)
            {
                var rectangle = new Rectangle(0, 0, output.Description.DesktopCoordinates.Right, output.Description.DesktopCoordinates.Bottom);
                var texture2dDescription = new Texture2DDescription
                {
                    ArraySize = 1,
                    CPUAccessFlags = CpuAccessFlags.Read | CpuAccessFlags.Write,
                    Format = Format.B8G8R8A8_UNorm,
                    MipLevels = 1,
                    SampleDescription = { Count = 1, Quality = 0 },
                    Usage = ResourceUsage.Staging,
                    Height = rectangle.Bottom,
                    Width = rectangle.Right
                };

                using var currentFrame = device.CreateTexture2D(texture2dDescription);
                using var duplicatedOutput = output1.DuplicateOutput(device);
                using var frame = new Bitmap(rectangle.Right, rectangle.Bottom, PixelFormat.Format32bppRgb);
                var index = 0;
                rectangle.X = 0;
                do
                {
                    duplicatedOutput.AcquireNextFrame(500, out var frameInfo, out var desktopResource);
                    if (desktopResource != null)
                    {
                        using (desktopResource)
                        {
                            using var tempTexture = desktopResource.QueryInterface<ID3D11Texture2D>();
                            deviceContext.CopyResource(currentFrame, tempTexture);
                            var dataBox = deviceContext.Map(currentFrame, 0, MapMode.Read);
                            var mapDest = frame.LockBits(rectangle, ImageLockMode.WriteOnly, frame.PixelFormat);
                            for (int y = rectangle.Y, sizeInBytesToCopy = rectangle.Width * 4; y < rectangle.Height; y++)
                            {
                                MemoryHelpers.CopyMemory(mapDest.Scan0 + y * rectangle.Right * 4, dataBox.DataPointer + y * dataBox.RowPitch, sizeInBytesToCopy);
                            }
                            deviceContext.Unmap(currentFrame, 0);
                            frame.UnlockBits(mapDest);
                            frame.Save("bitmap" + index++ + ".png", ImageFormat.Png);
                        }
                    }

                    duplicatedOutput.ReleaseFrame();
                }
                while (index < 10);
            }
        }
    }
}

There are issues with your code. Here are the issue that explain why it doesn't work more than once and requires a strange Sleep call:

  • You don't dispose everything (you should, I guess this is releasing underlying COM interfaces)
  • You don't Unmap what you've mapped.
  • You don't ReleaseFrame acquired frames.

But there are other issues

  • You can use the device context created by D3D11CreateDevice, you don't need to use this ImmediateContext property.
  • If you want to maximize duplication performance, you shouldn't use CPU mapped textures directly in the acquiring frames loop which you must keep very fast. You should copy them (like you do) and pass them to the CPU using another thread for example
  • The Duplication API is not a screen-capture API, it's a screen duplication API (!) which means it will copy rendered frames to like a "mailbox" you query when you can and want. The timeout can be kept relatively important. When you get a timeout, it just means nothing has changed on the screen. The frameInfo parameter has useful information about all this.

Here is a version that seems to work (acquires 10 frames and saves them as bitmap files):

class Program
{
    private static readonly FeatureLevel[] _featureLevels = new[]
    {
        FeatureLevel.Level_11_0,
        FeatureLevel.Level_10_1,
        FeatureLevel.Level_10_0,
        FeatureLevel.Level_9_3,
        FeatureLevel.Level_9_2,
        FeatureLevel.Level_9_1,
    };

    static void Main()
    {
        DXGI.CreateDXGIFactory1<IDXGIFactory1>(out var factory);
        if (factory == null)
            return;

        using (factory)
        {
            using var adapter = factory.GetAdapter(0);
            using var output = adapter.GetOutput(0);
            using var output1 = output.QueryInterface<IDXGIOutput1>();
            D3D11.D3D11CreateDevice(adapter, DriverType.Unknown, DeviceCreationFlags.None, _featureLevels, out var device, out var deviceContext);
            if (device == null)
                return;

            using (device)
            {
                var rectangle = new Rectangle(0, 0, output.Description.DesktopCoordinates.Right, output.Description.DesktopCoordinates.Bottom);
                var texture2dDescription = new Texture2DDescription
                {
                    ArraySize = 1,
                    CPUAccessFlags = CpuAccessFlags.Read | CpuAccessFlags.Write,
                    Format = Format.B8G8R8A8_UNorm,
                    MipLevels = 1,
                    SampleDescription = { Count = 1, Quality = 0 },
                    Usage = ResourceUsage.Staging,
                    Height = rectangle.Bottom,
                    Width = rectangle.Right
                };

                using var currentFrame = device.CreateTexture2D(texture2dDescription);
                using var duplicatedOutput = output1.DuplicateOutput(device);
                using var frame = new Bitmap(rectangle.Right, rectangle.Bottom, PixelFormat.Format32bppRgb);
                var index = 0;
                rectangle.X = 0;
                do
                {
                    duplicatedOutput.AcquireNextFrame(500, out var frameInfo, out var desktopResource);
                    if (desktopResource != null)
                    {
                        using (desktopResource)
                        {
                            using var tempTexture = desktopResource.QueryInterface<ID3D11Texture2D>();
                            deviceContext.CopyResource(currentFrame, tempTexture);
                            var dataBox = deviceContext.Map(currentFrame, 0, MapMode.Read);
                            var mapDest = frame.LockBits(rectangle, ImageLockMode.WriteOnly, frame.PixelFormat);
                            for (int y = rectangle.Y, sizeInBytesToCopy = rectangle.Width * 4; y < rectangle.Height; y++)
                            {
                                MemoryHelpers.CopyMemory(mapDest.Scan0 + y * rectangle.Right * 4, dataBox.DataPointer + y * dataBox.RowPitch, sizeInBytesToCopy);
                            }
                            deviceContext.Unmap(currentFrame, 0);
                            frame.UnlockBits(mapDest);
                            frame.Save("bitmap" + index++ + ".png", ImageFormat.Png);
                        }
                    }

                    duplicatedOutput.ReleaseFrame();
                }
                while (index < 10);
            }
        }
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文