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


159

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

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

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

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

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


画面の内容をグラフィックで記録するのではなく、再生システムを使用して検討しましたか?
ベンジャミンリンドレー

2
フックする必要はありません。入力イベントを記述して、ゲームが直接制御されないようにするだけでよく、代わりに他の関数を呼び出します。たとえば、プレーヤーが左キーを押した場合、単純にプレーヤーのx位置をデクリメントするわけではありません。代わりに、のような関数を呼び出しますMovePlayerLeft()。また、キーを押す時間やその他の入力時間も記録します。次に、再生モードでは、入力を無視して、代わりに記録されたデータを読み取ります。データで、左キーが押されたのを見つけたら、を呼び出しますMovePlayerLeft()
ベンジャミンリンドレー

1
@PigBenこれはゲーム映像を記録するための一般的なアプリケーションになります。特定のゲームのためではありません。誰かが左キーを押すと、右に移動することになるかもしれません。また、ユーザーの影響を受けないイベントは考慮していません。「レンダリングについてはどうですか?
someguy

1
@someguy OK何かもっと激しいことをしていると思います。上記の方法を使用して、問題なく30fpsでゲームのリプレイAVIを節約するルーチンを追加しました。「労働力の最適化」のためにWindows APIを使用してマルチモニター画面レコーダーを作成しましたが、ターゲットの4 fpsでもパフォーマンスが低下しました。
AJG85、2011

1
ここのUltraVNCのリポジトリサイト上でWindows用のオープンソースのミラードライバがありultravnc.svn.sourceforge.net/viewvc/ultravnc/...は
海岸に

回答:


62

これは私が単一フレームを収集するために使用するものですが、これを変更して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();
}

ありがとう。この方法については、フロントバッファーから読み取るよりも速いと言われていました。あなたは正直にその方法でそれを行い、それは適切に機能しますか?
誰か2016

フロントバッファの問題はアクセスの1つです。つまり、現在レンダリングされているプレーンをコピーしようとすると、コピーが「中断」されます。それは私にとって十分に機能し、私のハードドライブを食べます!
Brandrew、2011

@bobobobo正確にどのように機能するかはわかりませんが、私はHuffyuvのようなものを使用することを考えていました。編集:または、ユーザーが利用可能なdirectshowフィルターから選択できるようにします。
someguy

1
私はそれをうまく機能させることができません... DirectXはGetRenderTargetDataパーツで無効な呼び出しを吐き出します。明らかに、デバイスの作成方法には多くの重要性が必要です。
LightStriker 2013

12
反対投票、それはあなた自身のアプリケーションでのみ機能するので、一般的なプログラムの記録には使用できません
user3125280

32

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

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

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」 メソッドの一部としてこのようなものを実装hDestCreateCompatibleDCていますがIDXGISurface1、DirectX 10+で動作するを使用して宛先コンテキストを作成するのではなく、これに対するサポートがない場合、それらはにフォールバックしCreateCompatibleDCます。

特定のアプリケーションを使用することを変更するには、最初の行に変更する必要がGetDC(game)どこgameのゲームのウィンドウのハンドルがあり、その後、右に設定heightし、widthあまりにもゲームのウィンドウのを。

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


3
msdn.microsoft.com/en-us/library/dd183370%28VS.85%29.aspx 抜粋:ソースと宛先のデバイスコンテキストのカラー形式が一致しない場合、BitBlt関数はソースのカラー形式を宛先に一致するように変換しますフォーマット。

2
それを立証するいくつかの証拠は良いでしょう。どこかでパフォーマンス比較を公開したり、正確な比較を見たりしましたか?

2
それをプロファイリングしてみてください。投稿で述べたように、私はGDIの経験を持つ誰かを参照しています。メモリリークが発生し、修正方法がわかっている場合は、投稿を編集してリークを解消します。

1
この方法で約5 fpsを得ました。これは、グラフィックスが統合されたラップトップ上にあったので、実際のグラフィックスカードを備えたデスクトップに期待した方がいいですが、それでも非常に遅いです。
Timmmm

1
@Timmmm OBSがこれを実装する方法への参照を追加しました。うまくいけば、あなたのために少しスピードアップするかもしれません。

19

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は独自のロスレスビデオコーデックを使用しています。ラガリスとHUFFYUVは、リアルタイムアプリケーション用に設計された一般的なロスレスビデオコーデックです。ビデオファイルを出力する場合は、これらを確認する必要があります。

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


16

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

    IDirect3DSurface9 * surface;

    // GetBackBuffer
    idirect3ddevice9-> GetBackBuffer(0、0、D3DBACKBUFFER_TYPE_MONO、&surface);

    //表面を保存します
    D3DXSaveSurfaceToFileA( "filename.png"、D3DXIFF_PNG、surface、NULL、NULL);

    SAFE_RELEASE(表面);

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

d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.

(そのため、スクリーンショットを撮る前に、バックバッファーが壊されないことを保証します)。


理にかなっている、ありがとう。これとの違いを知っていGetRenderTargetますか?
someguy

これは現在のレンダーターゲットを取得するだけです(呼び出し時に誰かがテクスチャーにレンダーしている場合、別のオフスクリーンサーフェスになる可能性があります)。
bobobobo 2011

13

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

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

本当に必要なのはリプレイシステムだと思います。


1
再生システムが実現できない理由については、説明を参照してください。スクリーンキャスティングプログラムは、特定のゲーム向けではありません。
someguy

10

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

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

BrandrewのコードをGetRenderTargetData経由でDirectXをテストするように機能させることができませんでした。画面のコピーは真っ黒になりました。ただし、その空白の画面を超高速でコピーできます。私はそれをいじくり続けて、それから実際の結果を見るために実用的なバージョンを得ることを望みます。


情報ありがとうございました。私はBrandrewのコードをテストしていませんが、このGetRenderTargetDataアプローチを取ることでうまくいくことを知っています。申請が終わったら、自分で答えを書くかもしれません。または、すべてが機能するようになったら更新することもできます。
someguy

スクリーンキャプチャあたり0.65 ?! 適切なGDI実装(デバイスの保持など)は、最新のコンピューターで1920x1200で30fpsを簡単に実行する必要があります。
クリストファーOezbek

私は、GDIによってレンダリングされたイメージ品質がDXよりも
明らかに

私はこのテストをC#でSlimDXを使用して実行しましたが、驚くべきことに、同じ結果が見つかりました。おそらく、これは、SlimDXを使用して、一度作成して巻き戻し、同じ場所に上書きし続けるのではなく、フレームの更新ごとに新しいストリームと新しいビットマップを作成する必要があるという事実に関係している可能性があります。
Cesar

明確化:実際には、「同じ結果」はGDIが高速であることを示していました-@Christopherが述べたように、30fps +は非常に実行可能であり、それでも十分な予備のCPUを残しました。
Cesar

8

私が集めることができたいくつかのこと:OSSのドライバーを知らないのに、明らかに「ミラードライバー」を使用すると高速です。

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

また、どうやらStretchRectのいくつかの畳み込みを使用すると、BitBltよりも高速になります。

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

そして、あなたが言及したもの(D3D DLLにフックするフラップ)は、おそらくD3Dアプリケーションの唯一の方法ですが、Windows XPデスクトップキャプチャでは機能しません。だから今私は通常のデスクトップウィンドウと同等の速度でフラップがあったらいいのに...誰か?

(私はaeroでフラップのようなフックを使用できるかもしれないと思いますが、XPユーザーは運が悪いでしょう)。

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

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

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


「スクリーンキャプチャレコーダー」を使用するか、自分でBitBltを使用する方が速くなりますか?プロジェクトに最適化はありますか?
blez 2015年

Bitbltを自分で使用する場合、余分なmemcpyを回避することができます(memcpyは通常、最大のボトルネックではありませんが、少し時間がかかります-ベンチマークユーティリティのためにここで言及しただけですが、何かまたはdirectshowが必要な場合は、 )
rogerdpack、2015年

8

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を使用して画面をビデオにキャプチャする方法は?


2
ああ、私はC(おそらくC ++)でプログラミングしていることを忘れており、.NETを使用する予定はありません。ひどく申し訳ありません:/。
someguy

私はすでにBitBlt(GDI)を知っていました。ただし、Direct3Dを調べます。ありがとう!
だれか

私はこれを数週間前に調べていましたが、まだ実装するまでには至っていません。Direct3Dは!! way !! GDI +を使用しているC#組み込みメソッドよりも高速です。
Tedd Hansen

元の投稿を更新しました。リンクによると、GetFrontBufferData()を呼び出す必要がある場合、DirectXは低速です。ゲームの映像を記録するときに考慮すべきことはありますか?これを私に文脈化してくれませんか?
someguy

1
GDIは遅いため、問題のあるドメインには適していません。DirectX、またはOpenGLのみが賢明な推奨です。

6

Windows 8では、MicrosoftはWindowsデスクトップ複製APIを導入しました。それが公式に推奨される方法です。スクリーンキャストの優れた機能の1つは、ウィンドウの動きを検出することです。これにより、ウィンドウが移動したときに、生のピクセルの代わりにブロックデルタを送信できます。また、1つのフレームから次のフレームに変更された長方形もわかります。

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

https://github.com/bmharper/WindowsDesktopDuplicationSample

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

マイクロソフト公式サンプルコード:https : //code.msdn.microsoft.com/windowsdesktop/Desktop-Duplication-Sample-da4c696a


GithubプロジェクトはWindows 10で完全に動作し、WebビデオとResident Evil 7でテストされています。最も優れているのは、CPU負荷がグラフィックスのアップグレード率に比例して変化することです。
jw_

GPU-Zを起動するとすぐに、このプログラムは画面の取得を停止します。
MarinoŠimić

5

強力な画面キャプチャーである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

1
それは...多くの低レベルのフックです。あなたが管理者権限を持っている場合、キャプチャ速度は素晴らしいです。
toster-cx 2016

winrobotは非常に強力でスムーズであることがわかりました。
ashishgupta_mca 2017

WinRobotのコードを調べたところ、画期的なwrtスクリーンキャプチャはありませんでした。同じCreateCompatibleDC..BitBltを使用しています。これがサービスコンテキストで実行されるときに何らかの魔法がない限り?
シェフ

@shekh:あなたの研究は表面的すぎました。コードはIDirectDrawSurface7-> BltFast()を使用して画面を画面描画面からコピーDD面にコピーし、次にFilemappingを使用して画像をコピーする。コードは、デスクトップに簡単にアクセスできないサービスで実行されているため、非常に複雑です。
Elmue

2

私自身はdirectxでそれをやっていて、あなたがそれを望んでいるのと同じくらい速いと思っています。私は、迅速なコードサンプルを持っていませんが、私が見つかりました。これを便利であるべきです。directx11のバージョンはそれほど大きく異なるべきではありません。directx9のバージョンはもう少し違うかもしれませんが、それが方法です


2

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


2

画面記録は、VLC APIを使用してC#で実行できます。これを示すサンプルプログラムを実行しました。LibVLCSharpおよびVideoLAN.LibVLC.Windowsライブラリを使用します。このクロスプラットフォームAPIを使用して、ビデオレンダリングに関連するさらに多くの機能を実現できます。

APIドキュメントについては、LibVLCSharp API Githubを参照してください。

using System;
using System.IO;
using System.Reflection;
using System.Threading;
using LibVLCSharp.Shared;

namespace ScreenRecorderNetApp
{
    class Program
    {
        static void Main(string[] args)
        {
            Core.Initialize();

            using (var libVlc = new LibVLC())
            using (var mediaPlayer = new MediaPlayer(libVlc))
            {
                var media = new Media(libVlc, "screen://", FromType.FromLocation);
                media.AddOption(":screen-fps=24");
                media.AddOption(":sout=#transcode{vcodec=h264,vb=0,scale=0,acodec=mp4a,ab=128,channels=2,samplerate=44100}:file{dst=testvlc.mp4}");
                media.AddOption(":sout-keep");

                mediaPlayer.Play(media);
                Thread.Sleep(10*1000);
                mediaPlayer.Stop();
            }
        }
    }
}

1
関係者へ:libvlcはGPLの下でリリースされていることに注意してください。GPLでコードをリリースするつもりがない場合は、libvlcを使用しないでください。
セバスチャンキャボット

正確ではありません。LibVLCはLGPLです。2011年に再ライセンスされました。VLC
Andrew

0

DXGIデスクトップキャプチャ

DXGI複製でデスクトップイメージをキャプチャするプロジェクト。キャプチャした画像をさまざまな画像形式(* .bmp; * .jpg; * .tif)でファイルに保存します。

このサンプルはC ++で書かれています。また、DirectX(D3D11、D2D1)の経験も必要です。

アプリケーションでできること

  • デスクトップモニターが複数ある場合は、選択できます。
  • キャプチャしたデスクトップイメージのサイズを変更します。
  • 異なるスケーリングモードを選択します。
  • 出力画像でマウスアイコンを表示または非表示にできます。
  • 出力画像の画像を回転するか、デフォルトのままにすることができます。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.