Самый быстрый способ захвата экрана в Windows - PullRequest
146 голосов
/ 21 февраля 2011

Я хочу написать программу для скринкастинга для платформы Windows, но не знаю, как сделать снимок экрана.Единственный известный мне метод - это использовать GDI, но мне любопытно, есть ли другие способы сделать это, и, если есть, то какие издержки наименьшие?Скорость является приоритетом.

Программа скринкастинга будет предназначена для записи игрового материала, хотя, если это сузит варианты, я все еще открыт для любых других предложений, которые выпадают из этой области.В конце концов, знания неплохие.

Редактировать : я сталкивался с этой статьей: Различные способы захвата экрана .Он познакомил меня со способом Windows Media API и способом DirectX.В заключении упоминается, что отключение аппаратного ускорения может значительно повысить производительность приложения захвата.Мне любопытно, почему это так.Может ли кто-нибудь заполнить пропущенные для меня пробелы?

Редактировать : Я читал, что программы для создания скриншотов, такие как Camtasia, используют свой собственный драйвер захвата.Может ли кто-нибудь дать мне подробное объяснение того, как это работает, и почему это быстрее?Мне также может понадобиться руководство по реализации чего-то подобного, но я уверен, что в любом случае существует документация.

Кроме того, теперь я знаю, как FRAPS записывает экран.Он перехватывает базовый графический API для чтения из заднего буфера.Из того, что я понимаю, это быстрее, чем чтение из переднего буфера, потому что вы читаете из системной памяти, а не видео-памяти.Вы можете прочитать статью здесь .

Ответы [ 12 ]

55 голосов
/ 28 февраля 2011

Это то, что я использую, чтобы собирать отдельные кадры, но если вы измените это и сохраните две цели открытыми, то вы можете «передать» их на диск, используя статический счетчик для имени файла. - Я не могу вспомнить, где я нашел это, но это было изменено, спасибо кому бы то ни было!

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();
}
27 голосов
/ 02 марта 2011

РЕДАКТИРОВАТЬ: я вижу, что это указано в вашей первой ссылке на редактирование как «путь GDI».Это по-прежнему достойный способ, даже если использовать рекомендации по производительности на этом сайте, я думаю, вы можете легко получить 30 кадров в секунду.

Из этого комментария (у меня нет опыта в этом,Я просто ссылаюсь на того, кто это делает):

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, вам все равно нужно сохранить его в файл, но если вытогда вы делаете снимок экрана, и я думаю, что вы захотите сохранить в памяти определенное их количество и сохранить его в видеофайле порциями, поэтому я не буду указывать на код для сохранения статического изображения на диск.

17 голосов
/ 24 июля 2013

Я написал программное обеспечение для захвата видео, похожее на FRAPS для приложений DirectX.Исходный код доступен, и моя статья объясняет общую технику.Посмотрите на 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.

15 голосов
/ 20 июля 2011

Я использую d3d9 для получения резервного буфера и сохраняю его в png-файл, используя библиотеку d3dx:

    IDirect3DSurface9 *surface ;

    // <a href="http://msdn.microsoft.com/en-us/library/bb174379(VS.85).aspx" rel="noreferrer">GetBackBuffer</a>
    idirect3ddevice9->GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO, &surface ) ;

    // save the surface
    D3DXSaveSurfaceToFileA( "filename.png", D3DXIFF_PNG, surface, NULL, NULL ) ;

    SAFE_RELEASE( surface ) ;

Для этого вы должны создать свой свопбуфер с

d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.

(Таким образом, вы гарантируете, что резервный буфер не будет поврежден до того, как вы сделаете скриншот).

10 голосов
/ 05 июня 2012

По моему мнению, подход GDI и подход DX различны по своей природе. При рисовании с использованием GDI применяется метод FLUSH, подход FLUSH рисует кадр, затем очищает его и перерисовывает другой кадр в том же буфере, это приведет к мерцанию в играх, требующих высокой частоты кадров.

  1. ПОЧЕМУ DX быстрее? в DX (или мире графики) применяется более зрелый метод, называемый рендерингом с двойным буфером, где присутствуют два буфера, при представлении переднего буфера аппаратному обеспечению вы можете также выполнить рендеринг в другой буфер, а затем после кадра 1 Закончив рендеринг, система переключается на другой буфер (блокируя его для представления аппаратному обеспечению и освобождая предыдущий буфер), таким образом, неэффективность рендеринга значительно улучшается.
  2. ПОЧЕМУ отказ от аппаратного ускорения? хотя при рендеринге с двойным буфером FPS улучшается, но время рендеринга все еще ограничено. Современное графическое оборудование обычно требует значительных оптимизаций при рендеринге, как правило, таких как сглаживание, это требует значительных вычислений, если вам не нужна высококачественная графика, конечно, вы можете просто отключить эту опцию. и это сэкономит вам время.

Я думаю, что вам действительно нужна система воспроизведения, и я полностью согласен с тем, что обсуждали люди.

9 голосов
/ 11 марта 2011

Я написал класс, в котором реализован метод GDI для захвата экрана.Я тоже хотел получить дополнительную скорость, поэтому, обнаружив метод DirectX (через GetFrontBuffer), я попытался это сделать, ожидая, что он будет быстрее.

Я с ужасом обнаружил, что GDI работает примерно в 2,5 раза быстрее.После 100 испытаний захвата моего дисплея с двумя мониторами реализация GDI в среднем составляла 0,65 с на каждый снимок экрана, а метод DirectX - 1,72 с.Так что, согласно моим тестам, GDI определенно быстрее, чем GetFrontBuffer.

Мне не удалось заставить код Brandrew работать для тестирования DirectX через GetRenderTargetData.Экранная копия вышла чисто черной.Тем не менее, он может скопировать этот пустой экран очень быстро!Я продолжу возиться с этим и надеюсь получить рабочую версию, чтобы увидеть реальные результаты.

8 голосов
/ 20 июля 2011

Несколько вещей, которые я смог найти: очевидно, использование «зеркального драйвера» быстро, хотя я не знаю об OSS.

Почему RDP такой быстрый по сравнению сдругое программное обеспечение для дистанционного управления?

Также, очевидно, использование некоторых сверток StretchRect выполняется быстрее, чем BitBlt

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

И тот, который вы упомянули (фраппс подключается кD3D dll's), вероятно, единственный способ для приложений D3D, но не будет работать с захватом рабочего стола Windows XP.Так что теперь я просто хотел бы, чтобы по обычным окнам рабочего стола был бы эквивалент Fraps для любого рабочего стола ... кто-нибудь?

(Я думаю, что с помощью aero вы могли бы использовать ловушки, похожие на fraps, но пользователи XP были бы внеудачи).

Также, видимо, изменение битовой глубины экрана и / или отключение аппаратного ускорения.может помочь (и / или отключить аэро).

https://github.com/rdp/screen-capture-recorder-program включает в себя достаточно быструю утилиту захвата на основе BitBlt, а также тестовый модуль в составе его установки, который позволяет вам измерять скорости BitBlt для их оптимизации.

VirtualDub также имеетмодуль захвата экрана "opengl", который называется быстрым и выполняет такие вещи, как обнаружение изменений http://www.virtualdub.org/blog/pivot/entry.php?id=290

8 голосов
/ 21 февраля 2011

Для 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/

I 'Я планирую обновить этот образец, используя более быстрый метод, такой как Direct3D: http://spazzarama.com/2009/02/07/screencapture-with-direct3d/

А вот ссылка для захвата видео: Как сделать снимок экрана для видео с использованием C # .Net?

5 голосов
/ 07 декабря 2015

Вы можете попробовать проект с открытым исходным кодом 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
3 голосов
/ 06 марта 2013

Я понимаю, что следующее предложение не отвечает на ваш вопрос, но самый простой метод, который я нашел для захвата быстро меняющегося представления DirectX, - это подключить видеокамеру к порту S-video видеокарту и запишите изображения в виде фильма. Затем перенесите видео с камеры обратно в файл MPG, WMV, AVI и т. Д. На компьютере.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...