检测屏幕捕获期间的帧丢失

问题描述 投票:0回答:2

我正在使用SharpDx捕获屏幕(1到60fps)。一些框架都是透明的,并最终被代码处理和保存。

是否有任何简单/快速的方法来检测这些帧丢失而无需打开生成的位图并查找alpha值?

这是我正在使用的(将捕获保存为图像):

try
{
    //Try to get duplicated frame within given time.
    _duplicatedOutput.AcquireNextFrame(MinimumDelay, out var duplicateFrameInformation, out var screenResource);

    //Copy resource into memory that can be accessed by the CPU. 
    using (var screenTexture2D = screenResource.QueryInterface<Texture2D>())
        _device.ImmediateContext.CopySubresourceRegion(screenTexture2D, 0, new ResourceRegion(Left, Top, 0, Left + Width, Top + Height, 1), _screenTexture, 0);

    //Get the desktop capture texture.
    var mapSource = _device.ImmediateContext.MapSubresource(_screenTexture, 0, MapMode.Read, MapFlags.None); //, out var stream);

    #region Get image data

    var bitmap = new System.Drawing.Bitmap(Width, Height, PixelFormat.Format32bppArgb);
    var boundsRect = new System.Drawing.Rectangle(0, 0, Width, Height);

    //Copy pixels from screen capture Texture to GDI bitmap.
    var mapDest = bitmap.LockBits(boundsRect, ImageLockMode.WriteOnly, bitmap.PixelFormat);
    var sourcePtr = mapSource.DataPointer;
    var destPtr = mapDest.Scan0;

    for (var y = 0; y < Height; y++)
    {
        //Copy a single line 
        Utilities.CopyMemory(destPtr, sourcePtr, Width * 4);

        //Advance pointers
        sourcePtr = IntPtr.Add(sourcePtr, mapSource.RowPitch);
        destPtr = IntPtr.Add(destPtr, mapDest.Stride);
    }

    //Release source and dest locks
    bitmap.UnlockBits(mapDest);

    //Bitmap is saved in here!!!

    #endregion

    _device.ImmediateContext.UnmapSubresource(_screenTexture, 0);

    screenResource.Dispose();
    _duplicatedOutput.ReleaseFrame();
}
catch (SharpDXException e)
{
    if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code)
        throw;
}

这是modified version from this one

我也有这个版本(将捕获保存为像素数组:

//Get the desktop capture texture.
var data = _device.ImmediateContext.MapSubresource(_screenTexture, 0, MapMode.Read, MapFlags.None, out var stream);

var bytes = new byte[stream.Length];

//BGRA32 is 4 bytes.
for (var height = 0; height < Height; height++)
{
    stream.Position = height * data.RowPitch;
    Marshal.Copy(new IntPtr(stream.DataPointer.ToInt64() + height * data.RowPitch), bytes, height * Width * 4, Width * 4);
}

我不确定这是否是将屏幕捕获另存为图像和/或像素阵列的最佳方法,但是有些有用。

无论如何,问题在于捕获的某些帧是完全透明的,对我来说毫无用处。我需要以某种方式避免完全保存它们。

[捕获为像素数组时,我可以简单地检查bytes数组,以了解第4个项目是255还是0。保存为图像时,我可以使用bitmap.GetPixel(0,0).A来了解图像是否包含内容。

但是用两种方式,我都必须先完成捕获并获得完整的图像内容,然后才能知道帧是否掉落。

是否有办法知道是否正确捕获了帧?

c# screen-capture sharpdx
2个回答
0
投票

您的问题归结为尝试在计时器上执行此操作。没有办法保证每个guarantee嗒/帧的执行时间[[minimum。而且,如果用户选择的值太高,则会获得类似的效果。在最坏的情况下,您可能会使滴答声在EventQueue中排队,直到由于队列已满而遇到异常。

您需要做的是将速率限制为

最大

。如果它不能按用户期望的速度运行,那就是现实。我为这种情况编写了一些简单的限速代码:integer interval = 20; DateTime dueTime = DateTime.Now.AddMillisconds(interval); while(true){ if(DateTime.Now >= dueTime){ //insert code here //Update next dueTime dueTime = DateTime.Now.AddMillisconds(interval); } else{ //Just yield to not tax out the CPU Thread.Sleep(1); } }
仅两个注意事项:

    这被设计为在单独的线程中运行。您必须按原样运行它,或使Thread.Sleep()适应您选择的多任务处理选项。
  • DateTime.Now不适合这么小的时间范围(2位数毫秒)。通常,返回值仅每18毫秒左右更新一次。实际上,它会随着时间而变化。 60 FPS使您大约16毫秒。您应该使用秒表或类似的工具。

0
投票
为了忽略掉帧,我正在使用此代码(到目前为止,它按预期工作):

//Try to get the duplicated frame within given time. _duplicatedOutput.AcquireNextFrame(1000, out var duplicateFrameInformation, out var screenResource); //Somehow, it was not possible to retrieve the resource. if (screenResource == null || duplicateFrameInformation.AccumulatedFrames == 0) { //Mark the frame as dropped. frame.WasDropped = true; FrameList.Add(frame); screenResource?.Dispose(); _duplicatedOutput.ReleaseFrame(); return; }

我只是检查screenResource是否不为空以及是否累积了帧。
© www.soinside.com 2019 - 2024. All rights reserved.