[英]fully real-time screen capture in window 8/10 technology without delay
我将用C#或C ++创建一个非常快速和实时的远程服务 ,无所谓,但目前我正在使用c#。
好吧,我正在寻找和要求的是一种以最快的形状实时捕捉Windows屏幕的方法。
我知道互联网已有的方法,但问题是延迟 ......
- 我使用C#
copyfromscreen
,1920x1080的延迟为92ms [视频中的5帧]- 我使用带有jpg编码器的C#
copyfromscreen
,对于1920x1080,延迟为36ms- 我使用带有jpg编码器的Unity3D
screenCapture
,对于1920x1080,延迟为38ms- 我使用C#Windows
Desktop Duplication API
,1920x1080的延迟为19ms [视频中的3帧]- 我使用带有jpg编码器的C#Windows
Desktop Duplication API
,1920x1080的延迟为12ms [视频中的2帧]- 我使用带有jpg编码器的C ++ Windows
Desktop Duplication API
,1920x1080的延迟为9ms [视频中的2帧]- 我使用带有jpg编码器的C#Windows
DirectXCapture
,1920x1080的延迟为16ms [视频中的2帧]
我认为这样的进展是正常的,直到我检查了Windows 10/8任务栏实时预览缩略图,这是完全实时的,延迟1ms,这意味着帧到帧!
我试图将1920 x 1080从所有方法调整为任务栏预览大小,但它没有做任何改变!
注意:此程序不通过互联网进行处理,它将在本地网络上运行。
我猜测延迟是因为位图处理或其他东西,但我还不知道! [已添加时间]以下是我在服务器上处理图片的方式:
private static byte[] capture()
{
Bitmap bmp = screenshot_DDAPI(); // Avarage Time : 29.6566ms
MemoryStream ms = new MemoryStream();
bmp.Save(ms, ImageFormat.Jpeg); // Avarage Time : 1.7101 ms
return ms.ToArray(); // Avarage Time : 3.032 ms
}
有谁知道窗口使用哪种技术和方法来处理屏幕截图到任务栏实时缩略图?
注意:如果这个问题与堆栈溢出无关,或者它是偏离主题请告诉我哪个堆栈类别我可以问我的问题?
谢谢
桌面复制API是最快的。 捕获延迟为零。
但是,在捕获之后,您将原始纹理数据下载到基于CPU的jpeg编码器的系统RAM中。 这是花时间的。 实时缩略图不需要这样做,它们在GPU中缩放窗口纹理并使用GPU渲染它们,当源数据已经在VRAM中时,两者都非常便宜。
如果您真的想要最小化延迟,请寻找可以从B8G8R8A8_UNORM
D3D11纹理获取源数据的基于GPU的JPEG编码器。 JPEG比RGBA占用更少的空间(因此带宽),即您可能会更快地获得编码结果。
可能最快的方法是使用DirectX。 每个DirectX应用程序都包含一个缓冲区或一个表面来保存与该应用程序相关的视频内存的内容。 这称为应用程序的后台缓冲区。 某些应用程序可能有多个后备缓冲区。 每个应用程序都可以默认访问另一个缓冲区 - 前端缓冲区。 这个前缓冲区保存与桌面内容相关的视频内存,因此基本上是屏幕图像。 通过从应用程序访问前缓冲区,您可以捕获当时屏幕的内容。
从应用程序访问前端缓冲区非常简单明了。 接口IDirect3DDevice9
提供GetFrontBufferData()
方法,该方法接收IDirect3DSurface9
对象指针并将前缓冲区的内容复制到该表面上。 可以使用方法IDirect3DDevice8::CreateOffscreenPlainSurface()
生成IDirect3DSurfce9
对象。 将屏幕捕获到曲面后,可以使用D3DXSaveSurfaceToFile()
函数将曲面直接以位图格式保存到磁盘。 因此,捕获屏幕的代码如下所示:
extern IDirect3DDevice9* g_pd3dDevice;
Void CaptureScreen()
{
IDirect3DSurface9* pSurface;
// g_pd3dDevice is an IDirect3DDevice9 object, and has been assumed to be properly initialized
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH, &pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DXSaveSurfaceToFile("Desktop.bmp",D3DXIFF_BMP,pSurface,NULL,NULL);
pSurface->Release();
}
如果你想要一个缓冲区到实际位,而不是直接将它保存到磁盘,你可以使用这样的代码:
extern void* pBits;
extern IDirect3DDevice9* g_pd3dDevice;
IDirect3DSurface9* pSurface;
// g_pd3dDevice is an IDirect3DDevice9 object, and has been assumed to be properly initialized
g_pd3dDevice->CreateOffscreenPlainSurface(ScreenWidth, ScreenHeight,
D3DFMT_A8R8G8B8, D3DPOOL_SCRATCH,
&pSurface, NULL);
g_pd3dDevice->GetFrontBufferData(0, pSurface);
D3DLOCKED_RECT lockedRect;
pSurface->LockRect(&lockedRect,NULL,
D3DLOCK_NO_DIRTY_UPDATE|
D3DLOCK_NOSYSLOCK|D3DLOCK_READONLY)));
for( int i=0 ; i < ScreenHeight ; i++)
{
memcpy( (BYTE*) pBits + i * ScreenWidth * BITSPERPIXEL / 8 ,
(BYTE*) lockedRect.pBits + i* lockedRect.Pitch ,
ScreenWidth * BITSPERPIXEL / 8);
}
g_pSurface->UnlockRect();
pSurface->Release();
在复制到pBits
之前,请确保我们已经分配了足够的内存。 BITSPERPIXEL
的典型值是每像素32
位。 但是,它可能会因您当前的显示器设置而异。
编辑
这是一个C ++程序,我把它放在一起计时。 Direct3D9TakeScreenshots
函数采用一个参数来确定拍摄屏幕截图的次数。 我现在把它设置为10,但你可以改变它。 在我的机器上运行,在发布模式下,我得到以下结果:
18:33:23.189
18:33:23.579
所以这需要390
毫秒来拍摄10个屏幕截图并将它们复制到缓冲区。 每个屏幕截图和缓冲区复制的平均值约为39毫秒。
#include "pch.h"
#include <iostream>
#include <d3d9.h> // DirectX 9 header
#pragma comment(lib, "d3d9.lib") // link to DirectX 9 library
#define WIDEN2(x) L ## x
#define WIDEN(x) WIDEN2(x)
#define __WFILE__ WIDEN(__FILE__)
#define HRCHECK(__expr) {hr=(__expr);if(FAILED(hr)){wprintf(L"FAILURE 0x%08X (%i)\n\tline: %u file: '%s'\n\texpr: '" WIDEN(#__expr) L"'\n",hr, hr, __LINE__,__WFILE__);goto cleanup;}}
#define RELEASE(__p) {if(__p!=nullptr){__p->Release();__p=nullptr;}}
HRESULT Direct3D9TakeScreenshots(UINT adapter, UINT count)
{
HRESULT hr = S_OK;
IDirect3D9 *d3d = nullptr;
IDirect3DDevice9 *device = nullptr;
IDirect3DSurface9 *surface = nullptr;
D3DPRESENT_PARAMETERS parameters = { 0 };
D3DDISPLAYMODE mode;
D3DLOCKED_RECT rc;
UINT pitch;
SYSTEMTIME st;
LPBYTE *shots = nullptr;
// init D3D and get screen size
d3d = Direct3DCreate9(D3D_SDK_VERSION);
HRCHECK(d3d->GetAdapterDisplayMode(adapter, &mode));
parameters.Windowed = TRUE;
parameters.BackBufferCount = 1;
parameters.BackBufferHeight = mode.Height;
parameters.BackBufferWidth = mode.Width;
parameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
parameters.hDeviceWindow = nullptr;
// create device & capture surface
HRCHECK(d3d->CreateDevice(adapter, D3DDEVTYPE_HAL, NULL, D3DCREATE_SOFTWARE_VERTEXPROCESSING, ¶meters, &device));
HRCHECK(device->CreateOffscreenPlainSurface(mode.Width, mode.Height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &surface, nullptr));
// compute the required buffer size
HRCHECK(surface->LockRect(&rc, NULL, 0));
pitch = rc.Pitch;
HRCHECK(surface->UnlockRect());
// allocate screenshots buffers
shots = new LPBYTE[count];
for (UINT i = 0; i < count; i++)
{
shots[i] = new BYTE[pitch * mode.Height];
}
GetSystemTime(&st); // measure the time we spend doing <count> captures
wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
for (UINT i = 0; i < count; i++)
{
// get the data
HRCHECK(device->GetFrontBufferData(0, surface));
// copy it into our buffers
HRCHECK(surface->LockRect(&rc, NULL, 0));
CopyMemory(shots[i], rc.pBits, rc.Pitch * mode.Height);
HRCHECK(surface->UnlockRect());
}
GetSystemTime(&st);
wprintf(L"%i:%i:%i.%i\n", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
cleanup:
if (shots != nullptr)
{
for (UINT i = 0; i < count; i++)
{
delete shots[i];
}
delete[] shots;
}
RELEASE(surface);
RELEASE(device);
RELEASE(d3d);
return hr;
}
int main()
{
Direct3D9TakeScreenshots(D3DADAPTER_DEFAULT, 10);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.