Método mais rápido de captura de canvas

Eu quero escrever um programa de screencasting para a plataforma Windows, mas não tenho certeza de como capturar a canvas. O único método que estou ciente é o uso de GDI, mas estou curioso para saber se existem outras maneiras de fazer isso e, se houver, que incorre menos sobrecarga? A velocidade é uma prioridade.

O programa de screencasting será para gravação de imagens do jogo, embora, se isso limitar as opções, eu ainda estou aberto para quaisquer outras sugestões que caiam fora deste escopo. O conhecimento não é ruim, afinal.

Edit : Eu me deparei com este artigo: Vários methods para capturar a canvas . Ele me apresentou a maneira da API do Windows Media e a maneira de fazer isso com o DirectX. Ele menciona na Conclusão que a desativação da aceleração de hardware poderia melhorar drasticamente o desempenho do aplicativo de captura. Estou curioso para saber por que isso acontece. Alguém poderia preencher os espaços em falta para mim?

Edit : Eu li que programas de screencasting como o Camtasia usam seu próprio driver de captura. Alguém poderia me dar uma explicação detalhada sobre como funciona e por que é mais rápido? Eu também posso precisar de orientação sobre a implementação de algo assim, mas tenho certeza de que existe documentação de qualquer maneira.

Além disso, agora sei como o FRAPS grava a canvas. Ele conecta a API gráfica subjacente para ler o buffer de fundo. Pelo que entendi, isso é mais rápido do que ler a partir do buffer frontal, porque você está lendo a RAM do sistema, em vez de RAM de vídeo. Você pode ler o artigo aqui .

Isso é o que eu uso para coletar frameworks únicos, mas se você modificar isso e manter os dois destinos abertos o tempo todo, você poderá “fazer o stream” dele para o disco usando um contador estático para o nome do arquivo. – Não me lembro de onde encontrei isso, mas foi modificado, graças a quem quer que seja!

 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(); } 

EDIT: Eu posso ver que isso está listado em seu primeiro link de edição como “o caminho GDI”. Esta ainda é uma maneira decente de ir, mesmo com o alerta de desempenho nesse site, você pode chegar a 30fps facilmente eu acho.

A partir deste comentário (não tenho experiência em fazer isso, estou apenas referenciando alguém que faz):

 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); // ..and delete the context you created DeleteDC(hDest); 

Eu não estou dizendo que isso é o mais rápido, mas a operação BitBlt é geralmente muito rápida se você estiver copiando entre contextos de dispositivos compatíveis.

Para referência, o Open Broadcaster Software implementa algo assim como parte de seu método “dc_capture” , embora em vez de criar o contexto de destino hDest usando CreateCompatibleDC eles usam um IDXGISurface1 , que funciona com o DirectX 10+. Se não houver suporte para isso, eles retornarão ao CreateCompatibleDC .

Para alterá-lo para usar um aplicativo específico, você precisa alterar a primeira linha para GetDC(game) onde o game é o identificador da janela do jogo e, em seguida, defina a height e a width corretas da janela do jogo também.

Depois de ter os pixels em hDest / hbDesktop, você ainda precisa salvá-lo em um arquivo, mas se você estiver fazendo captura de canvas, então eu acho que você iria querer armazenar um certo número deles na memory e salvar no arquivo de vídeo em pedaços, por isso não vou apontar para o código para salvar uma imagem estática em disco.

Eu escrevi um software de captura de vídeo, semelhante ao FRAPS para aplicativos DirectX. O código fonte está disponível e meu artigo explica a técnica geral. Veja em http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/

Respeito às suas perguntas relacionadas ao desempenho,

  • O DirectX deve ser mais rápido que o GDI, exceto quando você estiver lendo o frontbuffer, que é muito lento. Minha abordagem é semelhante ao FRAPS (leitura do backbuffer). Eu interceptar um conjunto de methods de interfaces do Direct3D.

  • Para gravação de vídeo em tempo real (com impacto mínimo no aplicativo), um codec rápido é essencial. O FRAPS usa seu próprio codec de vídeo sem perdas. Lagarith e HUFFYUV são codecs de vídeo genéricos sem perda, projetados para aplicativos em tempo real. Você deve olhar para eles se quiser produzir arquivos de vídeo.

  • Outra abordagem para gravar screencasts poderia ser escrever um driver de espelhamento. De acordo com a Wikipedia: Quando o espelhamento de vídeo está ativo, cada vez que o sistema acessa o dispositivo de vídeo principal em um local dentro da área espelhada, uma cópia da operação de desenho é executada no dispositivo de vídeo espelhado em tempo real. Consulte drivers de espelho no MSDN: http://msdn.microsoft.com/en-us/library/windows/hardware/ff568315(v=vs.85).aspx .

Eu uso o d3d9 para obter o backbuffer e salve-o em um arquivo png usando a biblioteca d3dx:

     IDirect3DSurface9 * surface;

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

     // salve a superfície
     D3DXSaveSurfaceToFileA ("filename.png", D3DXIFF_PNG, superfície, NULL, NULL);

     SAFE_RELEASE (superfície);

Para fazer isso você deve criar seu swapbuffer com

 d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots. 

(Então você garante que o backbuffer não é destroçado antes de você tirar a screenshot).

Eu escrevi uma class que implementou o método GDI para captura de canvas. Eu também queria velocidade extra então, depois de descobrir o método DirectX (via GetFrontBuffer) eu tentei isso, esperando que fosse mais rápido.

Fiquei consternado ao descobrir que o GDI executa cerca de 2,5x mais rápido. Após 100 tentativas capturando meu monitor de monitor duplo, a implementação de GDI teve uma média de 0,65s por captura de canvas, enquanto o método DirectX obteve uma média de 1,72s. Então GDI é definitivamente mais rápido que GetFrontBuffer, de acordo com meus testes.

Eu não consegui fazer o código de Brandrew funcionar para testar o DirectX via GetRenderTargetData. A cópia da canvas saiu puramente preta. No entanto, poderia copiar essa canvas em branco super rápido! Eu vou continuar mexendo com isso e espero ter uma versão funcional para ver resultados reais disso.

Na minha impressão, a abordagem GDI e a abordagem DX são diferentes em sua natureza. pintura usando GDI aplica o método FLUSH, a abordagem FLUSH desenha o quadro, em seguida, desmarque-o e redesenhe outro quadro no mesmo buffer, isso resultará em tremulação em jogos que exigem alta taxa de frameworks.

  1. PORQUÊ DX mais rápido? em DX (ou mundo gráfico), um método mais maduro chamado renderização de buffer duplo é aplicado, onde dois buffers estão presentes, quando presente o buffer frontal para o hardware, você pode renderizar para o outro buffer também, então após o frame 1 é renderização terminada, o sistema troca para o outro buffer (bloqueando-o para apresentar ao hardware e libera o buffer anterior), desta forma a ineficiência de renderização é muito melhorada.
  2. POR QUE diminuir a aceleração de hardware mais rapidamente? embora com renderização de buffer duplo, o FPS é melhorado, mas o tempo de renderização ainda é limitado. O hardware gráfico moderno geralmente envolve muita otimização durante a renderização, normalmente como anti-aliasing, isso é muito intensivo em computação, se você não precisa de charts de alta qualidade, é claro que você pode simplesmente desabilitar esta opção. e isso vai lhe poupar algum tempo.

Eu acho que o que você realmente precisa é de um sistema de replay, que eu concordo totalmente com o que as pessoas discutiram.

Para C ++, você pode usar: http://www.pinvoke.net/default.aspx/gdi32/BitBlt.html
Isso pode não funcionar em todos os tipos de aplicativos 3D / aplicativos de vídeo. Então este link pode ser mais útil, pois descreve 3 methods diferentes que você pode usar.

Resposta antiga (c #):
Você pode usar System.Drawing.Graphics.Copy , mas não é muito rápido.

Um projeto de exemplo que escrevi fazendo exatamente isso: http://blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/

Estou planejando atualizar este exemplo usando um método mais rápido como o Direct3D: http://spazzarama.com/2009/02/07/screencapture-with-direct3d/

E aqui está um link para a captura de vídeo: Como capturar a canvas para ser vídeo usando c # .net?

Algumas coisas que eu pude entender: aparentemente, usar um “driver de espelho” é rápido, embora eu não esteja ciente de um OSS.

Por que o RDP é tão rápido comparado a outro software de controle remoto?

Também, aparentemente, usar algumas circunvoluções do StretchRect são mais rápidas que o BitBlt

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

E o que você mencionou (fraps enganchando na dll do D3D) é provavelmente o único caminho para aplicativos D3D, mas não funciona com a captura de desktop do Windows XP. Então, agora eu só queria que houvesse um erro de velocidade equivalente para janelas normais de desktop … alguém?

(Eu acho que com o aero você pode usar hooks parecidos com os fraps, mas os usuários do XP não teriam sorte).

Também aparentemente mudando as profundidades de bit da canvas e / ou desativando a aceleração de hardware. pode ajudar (e / ou desativar o aero).

O https://github.com/rdp/screen-capture-recorder-program inclui um utilitário de captura baseado em BitBlt razoavelmente rápido, e um benchmarker como parte de sua instalação, que permite comparar as velocidades do BitBlt para otimizá-las.

O VirtualDub também tem um módulo de captura de canvas “opengl” que é considerado rápido e faz coisas como a detecção de alterações http://www.virtualdub.org/blog/pivot/entry.php?id=290

Você pode experimentar o projeto de código aberto c ++ WinRobot @git , um poderoso capturador de canvas

 CComPtr pService; hr = pService.CoCreateInstance(__uuidof(ServiceHost) ); //get active console session CComPtr pUnk; hr = pService->GetActiveConsoleSession(&pUnk); CComQIPtr pSession = pUnk; // capture screen pUnk = 0; hr = pSession->CreateScreenCapture(0,0,1280,800,&pUnk); // get screen image data(with file mapping) CComQIPtr pBuffer = pUnk; 

Apoio, suporte :

  • Janela UAC
  • Winlogon
  • DirectShowOverlay

Eu mesmo faço isso com o DirectX e acho que é tão rápido quanto você gostaria que fosse. Eu não tenho um exemplo de código rápido, mas eu achei isso que deve ser útil. a versão do directx11 não deve diferir muito, directx9 talvez um pouco mais, mas esse é o caminho a percorrer

Percebo que a seguinte sugestão não responde à sua pergunta, mas o método mais simples que encontrei para capturar uma visão do DirectX que muda rapidamente é conectar uma câmera de vídeo à porta S-video da placa de vídeo e gravar as imagens como um filme. Em seguida, transfira o vídeo da câmera de volta para um arquivo MPG, WMV, AVI etc. no computador.