web-dev-qa-db-ja.com

Windowsでの画面キャプチャの最速の方法

Windowsプラットフォーム用のスクリーンキャストプログラムを作成したいのですが、画面をキャプチャする方法がわかりません。私が知っている唯一の方法はGDIを使用することですが、これについて他の方法があるかどうか、そしてもしあれば、最もオーバーヘッドが少ないかどうかに興味がありますか?速度が優先されます。

スクリーンキャストプログラムは、ゲームの映像を記録するためのものですが、これによりオプションが絞り込まれた場合は、この範囲外のその他の提案を受け付けています。結局のところ、知識は悪くありません。

Edit:この記事に出くわしました: 画面をキャプチャするためのさまざまな方法 。 Windows Media APIの方法とDirectXの方法を紹介してくれました。結論では、ハードウェアアクセラレーションを無効にすると、キャプチャアプリケーションのパフォーマンスが大幅に向上する可能性があることに言及しています。これがなぜなのか興味があります。誰かが私のために不足している空白を埋めることができますか?

Edit:Camtasiaなどのスクリーンキャストプログラムが独自のキャプチャドライバを使用することを読みました。誰かが私にそれがどのように機能し、なぜそれが速いのかについて詳細な説明をしてもらえますか?そのようなものを実装するためのガイダンスも必要かもしれませんが、とにかく既存のドキュメントがあると確信しています。

また、FRAPSが画面を記録する方法もわかりました。基礎となるグラフィックスAPIをフックして、バックバッファーから読み取ります。私の理解では、これはビデオRAMではなくシステムRAMから読み取るため、フロントバッファーから読み取るよりも高速です。記事を読むことができます こちら

146
someguy

これは私が単一のフレームを収集するために使用するものですが、これを変更して常に2つのターゲットを開いたままにすると、ファイル名の静的カウンターを使用してディスクに「ストリーミング」できます。 -これを見つけた場所を思い出せませんが、誰にでも感謝します!

void dump_buffer()
{
   IDirect3DSurface9* pRenderTarget=NULL;
   IDirect3DSurface9* pDestTarget=NULL;
     const char file[] = "Pickture.bmp";
   // sanity checks.
   if (Device == NULL)
      return;

   // get the render target surface.
   HRESULT hr = Device->GetRenderTarget(0, &pRenderTarget);
   // get the current adapter display mode.
   //hr = pDirect3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT,&d3ddisplaymode);

   // create a destination surface.
   hr = Device->CreateOffscreenPlainSurface(DisplayMde.Width,
                         DisplayMde.Height,
                         DisplayMde.Format,
                         D3DPOOL_SYSTEMMEM,
                         &pDestTarget,
                         NULL);
   //copy the render target to the destination surface.
   hr = Device->GetRenderTargetData(pRenderTarget, pDestTarget);
   //save its contents to a bitmap file.
   hr = D3DXSaveSurfaceToFile(file,
                              D3DXIFF_BMP,
                              pDestTarget,
                              NULL,
                              NULL);

   // clean up.
   pRenderTarget->Release();
   pDestTarget->Release();
}
55
Brandrew

編集:これは、最初の編集リンクの下に「GDI方法」としてリストされていることがわかります。これは、そのサイトのパフォーマンスアドバイザリを使用しても適切な方法であり、30fpsに簡単に到達できると思います。

this コメントから(これを行った経験はありませんが、そうする人を参照しているだけです):

HDC hdc = GetDC(NULL); // get the desktop device context
HDC hDest = CreateCompatibleDC(hdc); // create a device context to use yourself

// get the height and width of the screen
int height = GetSystemMetrics(SM_CYVIRTUALSCREEN);
int width = GetSystemMetrics(SM_CXVIRTUALSCREEN);

// create a bitmap
HBITMAP hbDesktop = CreateCompatibleBitmap( hdc, width, height);

// use the previously created device context with the bitmap
SelectObject(hDest, hbDesktop);

// copy from the desktop device context to the bitmap device context
// call this once per 'frame'
BitBlt(hDest, 0,0, width, height, hdc, 0, 0, SRCCOPY);

// after the recording is done, release the desktop context you got..
ReleaseDC(NULL, hdc);

// ..delete the bitmap you were using to capture frames..
DeleteObject(hbDesktop);

// ..and delete the context you created
DeleteDC(hDest);

これが最速だとは言いませんが、互換性のあるデバイスコンテキスト間でコピーしている場合、BitBlt操作は一般に非常に高速です。

参考のため、 Open Broadcaster Software"dc_capture" メソッドの一部としてこのようなものを実装していますが、hDestを使用して宛先コンテキストCreateCompatibleDCを作成するのではなく、 IDXGISurface1 、DirectX 10以降で動作します。これに対するサポートがない場合は、CreateCompatibleDCにフォールバックします。

特定のアプリケーションを使用するように変更するには、最初の行をGetDC(game)に変更する必要があります。ここでgameはゲームのウィンドウのハンドルであり、ゲームのウィンドウのheightおよびwidthも正しく設定します.

HDest/hbDesktopにピクセルを取得したら、それをファイルに保存する必要がありますが、スクリーンキャプチャを実行している場合は、特定の数のピクセルをメモリにバッファリングし、ビデオファイルに保存すると思いますチャンクなので、静的イメージをディスクに保存するためのコードは指しません。

27
darvids0n

DirectXアプリケーション用のFRAPSに似たビデオキャプチャソフトウェアを作成しました。ソースコードが利用可能であり、私の記事では一般的なテクニックについて説明しています。 http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/ を見てください

パフォーマンスに関する質問を尊重し、

  • DirectXはGDIよりも高速である必要がありますが、フロントバッファからの読み取りが非常に遅い場合は例外です。私のアプローチは、FRAPS(バックバッファから読み取る)に似ています。 Direct3Dインターフェイスから一連のメソッドをインターセプトします。

  • (アプリケーションへの影響を最小限に抑えた)リアルタイムでのビデオ録画には、高速コーデックが不可欠です。 FRAPSは、独自のロスレスビデオコーデックを使用します。 LagarithとHUFFYUVは、リアルタイムアプリケーション用に設計された一般的なロスレスビデオコーデックです。ビデオファイルを出力する場合は、それらを確認する必要があります。

  • スクリーンキャストを記録する別の方法は、ミラードライバーを作成することです。ウィキペディアによると:ビデオミラーリングがアクティブな場合、システムがミラー領域内の場所にあるプライマリビデオデバイスに描画するたびに、描画操作のコピーがミラービデオデバイスで実行されますMSDNのミラードライバーを参照してください: http://msdn.Microsoft.com/en-us/library/windows/hardware/ff568315(v = vs。 85).aspx .

17
Hernán

D3d9を使用してバックバッファーを取得し、d3dxライブラリを使用してpngファイルに保存します。

 IDirect3DSurface9 * surface; 
 
 //  GetBackBuffer  
 idirect3ddevice9-> GetBackBuffer(0、0、D3DBACKBUFFER_TYPE_MONO、&surface); 
 
 //サーフェスを保存します
 D3DXSaveSurfaceToFileA( "filename.png"、D3DXIFF_PNG、surface、NULL、NULL); 
 
 SAFE_RELEASE(surface); 

これを行うには、スワップバッファを作成する必要があります

d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.

(したがって、スクリーンショットを撮る前にバックバッファが破損していないことを保証します)。

15
bobobobo

私の印象では、GDIアプローチとDXアプローチは性質が異なります。 GDIを使用したペイントはFLUSHメソッドを適用し、FLUSHアプローチはフレームを描画してからクリアし、同じバッファーに別のフレームを再描画します。これにより、高フレームレートが必要なゲームでちらつきが発生します。

  1. なぜDXが速いのですか? DX(またはグラフィックスワールド)では、ダブルバッファーレンダリングと呼ばれるより成熟したメソッドが適用されます。2つのバッファーが存在し、フロントバッファーがハードウェアに存在する場合、他のバッファーにもレンダリングできます。レンダリングが終了すると、システムは他のバッファーにスワップし(ハードウェアに提示するためにロックし、以前のバッファーを解放します)、この方法でレンダリングの非効率性が大幅に改善されます。
  2. ハードウェアアクセラレーションをより速く停止する理由ダブルバッファーレンダリングでは、FPSは改善されますが、レンダリング時間は依然として制限されています。現代のグラフィックハードウェアは通常、アンチエイリアスなどのレンダリング中に多くの最適化を伴います。これは非常に計算集約的です。高品質のグラフィックが必要ない場合はもちろん、このオプションを無効にできます。これにより時間を節約できます。

本当に必要なのはリプレイシステムだと思いますが、それは人々が議論したこととまったく同感です。

10
zinking

画面キャプチャ用にGDIメソッドを実装したクラスを作成しました。私も余分な速度が欲しかったので、DirectXメソッドを(GetFrontBufferを介して)発見した後、より高速になると期待して、それを試しました。

GDIのパフォーマンスが約2.5倍高速であることを知ってがっかりしました。デュアルモニターディスプレイをキャプチャした100回の試行後、GDI実装はスクリーンキャプチャあたり平均0.65秒でしたが、DirectXメソッドは平均1.72秒でした。私のテストによると、GDIはGetFrontBufferよりも確実に高速です。

GetRenderTargetDataを介してDirectXをテストするためにBrandrewのコードを動作させることができませんでした。画面コピーは真っ黒になりました。ただし、その空白の画面を超高速でコピーできます。私はそれをいじり続け、実際の結果を見るために実際のバージョンを手に入れたいと思っています。

9
rotanimod

私が収集できたいくつかのこと:明らかに「ミラードライバー」の使用は高速ですが、OSSを認識していません。

他のリモートコントロールソフトウェアと比較してRDPが非常に高速なのはなぜですか

また、明らかにStretchRectのいくつかの畳み込みを使用すると、BitBltよりも高速です

http://betterlogic.com/roger/2010/07/fast-screen-capture/comment-page-1/#comment-519

そして、あなたが言及したもの(D3D dllにフラップする)はおそらくD3Dアプリケーションの唯一の方法ですが、Windows XPデスクトップキャプチャでは動作しません。だから今、私はちょうど通常のデスクトップウィンドウにスピード的に同等のフラップがあればいいのに...誰か?

(エアロではフラップのようなフックを使用できるかもしれませんが、XPユーザーは運が悪いでしょう)。

また、明らかに画面のビット深度を変更したり、ハードウェアアクセラレーションを無効にしたりします。 (および/またはエアロを無効にする)役立つかもしれません。

https://github.com/rdp/screen-capture-recorder-program には、適度に高速なBitBltベースのキャプチャユーティリティと、そのインストールの一部としてのベンチマーク機能が含まれています。それらを最適化します。

VirtualDubには、高速で変更検出などの処理を行うと言われる「opengl」画面キャプチャモジュールもあります http://www.virtualdub.org/blog/pivot/entry.php?id=29

8
rogerdpack

C++の場合: http://www.pinvoke.net/default.aspx/gdi32/BitBlt.html
これは、すべてのタイプの3Dアプリケーション/ビデオアプリで動作しない場合があります。次に、 このリンク は、使用できる3つの異なる方法を説明しているため、より便利です。

古い回答(C#):
System.Drawing.Graphics.Copy を使用できますが、高速ではありません。

私がまさにこれをやって書いたサンプルプロジェクト: http://blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/

Direct3Dなどの高速な方法を使用してこのサンプルを更新する予定です。 http://spazzarama.com/2009/02/07/screencapture-with-direct3d/

そして、ビデオへのキャプチャのリンクは次のとおりです。 C#.Netを使用して画面をビデオにキャプチャする方法

8
Tedd Hansen

C++オープンソースプロジェクト WinRobot @git 、強力なスクリーンキャプチャーを試すことができます。

CComPtr<IWinRobotService> pService;
hr = pService.CoCreateInstance(__uuidof(ServiceHost) );

//get active console session
CComPtr<IUnknown> pUnk;
hr = pService->GetActiveConsoleSession(&pUnk);
CComQIPtr<IWinRobotSession> pSession = pUnk;

// capture screen
pUnk = 0;
hr = pSession->CreateScreenCapture(0,0,1280,800,&pUnk);

// get screen image data(with file mapping)
CComQIPtr<IScreenBufferStream> pBuffer = pUnk;

サポート :

  • UACウィンドウ
  • Winlogon
  • DirectShowOverlay
5
Cayman

次の提案はあなたの質問には答えないことを理解していますが、急速に変化するDirectXビューをキャプチャするために見つけた最も簡単な方法は、ビデオのSビデオポートにビデオカメラを差し込むことですカード、および画像をムービーとして記録します。次に、ビデオをカメラからMPG、WMV、AVIなどのコンピューター上のファイルに転送します。

3
Pierre

私自身はdirectxでそれを行い、あなたが望むように速くなると思います。簡単なコードサンプルはありませんが、 this が役立つはずです。 directx11のバージョンはそれほど違わないはずで、directx9のバージョンはもう少し違いますが、

3
cppanda

Windows 8では、MicrosoftはWindows Desktop Duplication APIを導入しました。それが公式に推奨されている方法です。スクリーンキャストの優れた機能の1つは、ウィンドウの動きを検出することです。そのため、生のピクセルではなく、ウィンドウが動き回ったときにブロックデルタを送信できます。また、1つのフレームから次のフレームに変更された長方形を示します。

Microsoftのサンプルコードはかなり複雑ですが、APIは実際にはシンプルで使いやすいです。公式の例よりもはるかに簡単なサンプルプロジェクトをまとめました。

https://github.com/bmharper/WindowsDesktopDuplicationSample

ドキュメント: https://docs.Microsoft.com/en-gb/windows/desktop/direct3ddxgi/desktop-dup-api

Microsoftの公式サンプルコード: https://code.msdn.Microsoft.com/windowsdesktop/Desktop-Duplication-Sample-da4c696a

2
Ben Harper