[英]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.