2011-07-25 25 views
7

私は繰り返しスクリーンショットを撮り、2つの異なるショット間に汚れた矩形を出力する機能を実装しています。その後、スクリーンをウィンドウに再描画します。C#スクリーンショットを撮る最も速い方法はどれですか?

現在、20〜30FPSの間で動作させることができます。すでに受け入れられています。しかし、私はベンチマークを行い、パフォーマンスを測定しました。 Graphics.CopyFromScreen()が処理時間の50%を占めることが判明しました。 (ええ、最悪の場合でも、それはすべての汚れた矩形を見つけるよりも時間がかかります)次に、ネイティブAPI実装BitBlt()を使用し、改善が得られません。

この場合、30FPSよりも速くすることは実際的な理由がないことは知っています。私はちょうどスクリーンショットを取るための速い方法はありますか?

ありがとうございました。

答えて

6

これは、数年前の質問とよく似ています。Here。その質問は、より良いパフォーマンスを得るためにdirectx's capturingの能力を使用できるかどうかでした。

TightVNCはおそらくパフォーマンスを向上させることはないと考えていました。これは、おそらく.NETが使用しているAPIを使用する必要のないドライバを使用します。

ある時点で、私はCamstudioのソースコードを見ていて、directxのキャプチャ機能を使用していると思います。私は30fpsを過ぎてもそれほどプッシュできるとは思っていません。私はそれが、何かが変更されたときや実際のキャプチャメカニズムを理解するためにcamstudioが使うフックに問題があるかどうかはわかりません。このスレッドに来る人のために

+0

ありがとうございました。私はそれらの方向性を考慮しなかった。今私はもっと多くの選択肢があります。 – AKFish

0

CopyFromScreen()の代わりに、チェックthisGraphics.CopyFromScreen()自体がAPI BitBlt()を呼び出して画面からコピーすると、リフレクターを使用してソースコードを確認できます。

+2

それとも、今日、あなたもここに元のコードを見ることができます:http://referencesource.microsoft.com/#System.Drawing/commonui/System/Drawing/Graphics.cs,f76c9e39776eeb24 – Jens

+0

@ jensはい、マイクロソフト社がこれを利用できるようにすることでスマートになりました。 –

3

、私は、このソリューションに来た:

using SharpDX; 
using SharpDX.Direct3D11; 
using SharpDX.DXGI; 
using System; 
using System.Diagnostics; 
using System.Drawing; 
using System.Drawing.Imaging; 
using System.IO; 
using System.Threading.Tasks; 

あなたはこのコードは、可能な限り速くなります

public class ScreenStateLogger 
{ 
    private byte[] _previousScreen; 
    private bool _run, _init; 

    public int Size { get; private set; } 
    public ScreenStateLogger() 
    { 

    } 

    public void Start() 
    { 
     _run = true; 
     var factory = new Factory1(); 
     //Get first adapter 
     var adapter = factory.GetAdapter1(0); 
     //Get device from adapter 
     var device = new SharpDX.Direct3D11.Device(adapter); 
     //Get front buffer of the adapter 
     var output = adapter.GetOutput(0); 
     var output1 = output.QueryInterface<Output1>(); 

     // Width/Height of desktop to capture 
     int width = output.Description.DesktopBounds.Right; 
     int height = output.Description.DesktopBounds.Bottom; 

     // Create Staging texture CPU-accessible 
     var textureDesc = new Texture2DDescription 
     { 
      CpuAccessFlags = CpuAccessFlags.Read, 
      BindFlags = BindFlags.None, 
      Format = Format.B8G8R8A8_UNorm, 
      Width = width, 
      Height = height, 
      OptionFlags = ResourceOptionFlags.None, 
      MipLevels = 1, 
      ArraySize = 1, 
      SampleDescription = { Count = 1, Quality = 0 }, 
      Usage = ResourceUsage.Staging 
     }; 
     var screenTexture = new Texture2D(device, textureDesc); 

     Task.Factory.StartNew(() => 
     { 
      // Duplicate the output 
      using (var duplicatedOutput = output1.DuplicateOutput(device)) 
      { 
       while (_run) 
       { 
        try 
        { 
         SharpDX.DXGI.Resource screenResource; 
         OutputDuplicateFrameInformation duplicateFrameInformation; 

         // Try to get duplicated frame within given time is ms 
         duplicatedOutput.AcquireNextFrame(5, out duplicateFrameInformation, out screenResource); 

         // copy resource into memory that can be accessed by the CPU 
         using (var screenTexture2D = screenResource.QueryInterface<Texture2D>()) 
          device.ImmediateContext.CopyResource(screenTexture2D, screenTexture); 

         // Get the desktop capture texture 
         var mapSource = device.ImmediateContext.MapSubresource(screenTexture, 0, MapMode.Read, SharpDX.Direct3D11.MapFlags.None); 

         // Create Drawing.Bitmap 
         using (var bitmap = new Bitmap(width, height, PixelFormat.Format32bppArgb)) 
         { 
          var boundsRect = new 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 (int 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); 
          device.ImmediateContext.UnmapSubresource(screenTexture, 0); 

          using (var ms = new MemoryStream()) 
          { 
           bitmap.Save(ms, ImageFormat.Bmp); 
           ScreenRefreshed?.Invoke(this, ms.ToArray()); 
           _init = true; 
          } 
         } 
         screenResource.Dispose(); 
         duplicatedOutput.ReleaseFrame(); 
        } 
        catch (SharpDXException e) 
        { 
         if (e.ResultCode.Code != SharpDX.DXGI.ResultCode.WaitTimeout.Result.Code) 
         { 
          Trace.TraceError(e.Message); 
          Trace.TraceError(e.StackTrace); 
         } 
        } 
       } 
      } 
     }); 
     while (!_init) ; 
    } 

    public void Stop() 
    { 
     _run = false; 
    } 

    public EventHandler<byte[]> ScreenRefreshed; 
} 

パッケージSharpDXとSharpDX.Direct3D11が必要になりますフレームをグラフィックデバイスのフロントバッファから取り出し、作成したビットマップからbyte []を取り出します。 コードはメモリとプロセッサの使用状況(GPUとCPU)が安定しているようです。

用法:

var screenStateLogger = new ScreenStateLogger(); 
screenStateLogger.ScreenRefreshed += (sender, data) => 
{ 
    //New frame in data 
}; 
screenStateLogger.Start(); 
+0

私は3840x2160で17-18fps、1920x1080で~60fpsのFury Xとi7-3770を得ました。 –

関連する問題