самый быстрый способ захвата экрана в windows

<p>я хочу написать программу для скринкастинга для платформы windows, но не знаю, как сделать снимок экрана. единственный известный мне метод - это использовать gdi, но мне любопытно, есть ли другие способы сделать это, и, если есть, то какие издержки наименьшие? скорость является приоритетом.</p><p>программа для создания скринкастинга будет предназначена для записи игровых материалов, хотя, если это сузит варианты, я по-прежнему открыт для любых других предложений, которые выпадают из этой области. знание не плохо, в конце концов.</p><p><strong> редактировать </strong>: я наткнулся на эту статью: <a href="http://www.codeproject.com/kb/dialog/screencap.aspx">различные способы захвата экрана</a> . он познакомил меня со способом windows media api и способом directx. в заключении упоминается, что отключение аппаратного ускорения может значительно повысить производительность приложения захвата. мне любопытно, почему это так. может ли кто-нибудь заполнить пропущенные для меня пробелы?</p><p><strong> редактировать </strong>: я читал, что программы для создания скриншотов, такие как camtasia, используют собственный драйвер захвата. может ли кто-нибудь дать мне подробное объяснение того, как это работает, и почему это быстрее? мне также может понадобиться руководство по реализации чего-то подобного, но я уверен, что в любом случае существует документация.</p><p>также теперь я знаю, как fraps записывает экран. он перехватывает базовый графический api для чтения из заднего буфера. из того, что я понимаю, это быстрее, чем чтение из переднего буфера, потому что вы читаете из системной памяти, а не видео-памяти. вы можете прочитать статью <a href="https://web.archive.org/web/20121205062922/http://www.ring3circus.com/blog/2007/11/22/case-study-fraps/">здесь</a> .</p>

<p>это то, что я использую, чтобы собирать отдельные кадры, но если вы измените это и будете постоянно держать две цели открытыми, то вы можете "передать" их на диск, используя статический счетчик для имени файла. - я не могу вспомнить, где я нашел это, но это было изменено, спасибо кому бы то ни было!</p><pre><code>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(); } </code></pre>

<p>правка: я вижу, что это указано в вашей первой ссылке для редактирования как "gdi way". это по-прежнему достойный способ, даже если учесть рекомендации по производительности на этом сайте, я думаю, вы можете легко получить 30fps.</p><p>из <a href="http://www.gamedev.net/topic/585156-copy-screen-to-sdl\_surface/page\_\_view\_\_findpost\_\_p\_\_4722880">этого</a> комментария (у меня нет такого опыта, я просто ссылаюсь на того, кто это делает):</p><pre><code>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); </code></pre><p>я не говорю, что это самый быстрый способ, но операция <code>bitblt</code> обычно выполняется очень быстро, если вы копируете между контекстами совместимого устройства.</p><p>для справки, <a href="http://obsproject.com">open broadcaster software</a> реализует нечто подобное как часть их метода <a href="https://github.com/jp9000/obs-studio/blob/master/plugins/win-capture/dc-capture.c">"dc\_capture"</a> , хотя вместо создания контекста назначения <code>hdest</code> с использованием <code>createcompatibledc</code> они используют <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ff471343(v=vs.85).aspx"><code>idxgisurface1</code></a> , который работает с directx 10+. если это не поддерживается, они возвращаются к <code>createcompatibledc</code>.</p><p>чтобы изменить его на использование определенного приложения, вам нужно изменить первую строку на <code>getdc(game)</code>, где <code>game</code> - дескриптор окна игры, а затем установить правильные <code>height</code> и <code>width</code> окна игры.</p><p>как только у вас есть пиксели в hdest/hbdesktop, вам все равно нужно сохранить их в файл, но если вы делаете снимок экрана, то я думаю, что вы захотите зарезервировать определенное количество их в памяти и сохранить в видеофайл. в кусках, поэтому я не буду указывать на код для сохранения статического образа на диск.</p>

<p>я написал программное обеспечение для захвата видео, похожее на fraps для приложений directx. исходный код доступен, и моя статья объясняет общую технику. смотрите <a href="http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/">http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/</a> </p><p>уважение к вашим вопросам, связанным с производительностью,</p><ul> <li> <p>directx должен быть быстрее, чем gdi, за исключением случаев, когда вы читаете с фронт-буфера, который очень медленный. мой подход похож на fraps (чтение из буфера). я перехватываю множество методов из интерфейсов direct3d.</p> </li> <li> <p>для записи видео в реальном времени (с минимальным воздействием на приложение) необходим быстрый кодек. fraps использует собственный видеокодек без потерь. lagarith и huffyuv - это универсальные видеокодеки без потерь, разработанные для приложений реального времени. вы должны посмотреть на них, если вы хотите выводить видео файлы.</p> </li> <li> <p>другим подходом к записи скринкастов может быть написание зеркального драйвера. согласно википедии: <em> когда видео зеркалирование активно, каждый раз, когда система обращается к первичному видеоустройству в месте внутри зеркальной области, копия операции рисования выполняется на зеркальном видеоустройстве. в режиме реального времени. </em> см. драйверы зеркал в msdn: <a href="http://msdn.microsoft.com/en-us/library/windows/hardware/ff568315(v=vs.85).aspx">http://msdn.microsoft.com/en-us/library/windows/hardware/ff568315 (v = vs. 85) .aspx</a> .</p> </li> </ul>

<p>я использую d3d9, чтобы получить буфер, и сохраняю его в файл png, используя библиотеку d3dx:</p><pre> idirect3dsurface9 * surface; // <a href="http://msdn.microsoft.com/en-us/library/bb174379(vs.85).aspx">getbackbuffer</a> idirect3ddevice9-> getbackbuffer (0, 0, d3dbackbuffer\_type\_mono, & surface); // сохранить поверхность d3dxsavesurfacetofilea ("filename.png", d3dxiff\_png, поверхность, null, null); safe\_release (поверхность); </pre><p>для этого вы должны создать свой swapbuffer с</p><pre><code>d3dpps.swapeffect = d3dswapeffect\_copy ; // for screenshots. </code></pre><p>(таким образом, вы гарантируете, что задний буфер не будет поврежден до того, как вы сделаете скриншот).</p>

<p>в моем впечатлении подход gdi и подход dx различны по своей природе. при рисовании с использованием gdi применяется метод flush, подход flush рисует кадр, затем очищает его и перерисовывает другой кадр в том же буфере, это приведет к мерцанию в играх, требующих высокой частоты кадров.</p><ol> <li>почему dx быстрее? в dx (или графическом мире) применяется более зрелый метод, называемый рендерингом с двойным буфером, где присутствуют два буфера, когда при представлении переднего буфера аппаратному обеспечению вы можете также выполнить рендеринг в другой буфер, а затем после кадра 1 после завершения рендеринга система переходит в другой буфер (блокируя его для представления на аппаратном уровне и освобождая предыдущий буфер), таким образом, эффективность неэффективности рендеринга значительно улучшается.</li> <li>почему отказ от аппаратного ускорения? хотя при рендеринге с двойным буфером fps улучшается, но время рендеринга все еще ограничено. современное графическое оборудование обычно требует значительных оптимизаций при рендеринге, как правило, таких как сглаживание, это требует больших вычислительных ресурсов, если вам не требуется высококачественная графика, конечно, вы можете просто отключить эту опцию. и это сэкономит вам время.</li> </ol><p>я думаю, что вам действительно нужна система воспроизведения, я полностью согласен с тем, что обсуждали люди.</p>

<p>я написал класс, в котором реализован метод gdi для захвата экрана. я тоже хотел получить дополнительную скорость, поэтому, обнаружив метод directx (через getfrontbuffer), я попробовал это, ожидая, что он будет быстрее.</p><p>я с ужасом обнаружил, что gdi работает примерно в 2,5 раза быстрее. после 100 попыток захвата моего дисплея с двумя мониторами реализация gdi в среднем составляла 0,65 с на каждый снимок экрана, а метод directx - 1,72 с. так что, согласно моим тестам, gdi определенно быстрее, чем getfrontbuffer.</p><p>мне не удалось заставить код brandrew работать для тестирования directx через getrendertargetdata. экранная копия вышла чисто черной. тем не менее, он может скопировать этот пустой экран очень быстро! я продолжу возиться с этим и надеюсь получить рабочую версию, чтобы увидеть реальные результаты.</p>

<p>несколько вещей, которые я смог найти: очевидно, "зеркальный драйвер" работает быстро, хотя я не знаю об oss.</p><p> <a href="https://stackoverflow.com/questions/1284055/why-is-rdp-so-fast-compared-to-other-remote-control-software">почему rdp такой быстрый по сравнению с другими программами дистанционного управления?</a> </p><p>также очевидно, что использование некоторых сверток stretchrect быстрее, чем bitblt</p><p> <a href="http://betterlogic.com/roger/2010/07/fast-screen-capture/comment-page-1/#comment-5193">http://betterlogic.com/roger/2010/07/fast-screen-capture/comment-page-1/#comment-519</a> </p><p>и тот, который вы упомянули (подключение fraps к d3d dll), вероятно, является единственным способом для приложений d3d, но он не будет работать с захватом рабочего стола windows xp. так что теперь я просто хотел бы, чтобы по обычным окнам рабочего стола был эквивалентный fraps. кто-нибудь?</p><p>(я думаю, что с помощью aero вы могли бы использовать фрейп-подобные хуки, но xp пользователям не повезет).</p><p>также очевидно изменение битовой глубины экрана и/или отключение аппаратного ускорения. может помочь (и/или отключить аэро).</p><p> <a href="https://github.com/rdp/screen-capture-recorder-program">https://github.com/rdp/screen-capture-recorder-program</a> включает в себя достаточно быструю утилиту захвата на основе bitblt и тестовый модуль в составе своей установки, который может позволить вам измерять скорости bitblt до оптимизировать их.</p><p>virtualdub также имеет модуль захвата экрана "opengl", который, как говорят, быстр и делает такие вещи, как обнаружение изменений <a href="http://www.virtualdub.org/blog/pivot/entry.php?id=290">http://www.virtualdub.org/blog/pivot/entry.php?id=29</a> </p>

<p>для c++ вы можете использовать: <a href="http://www.pinvoke.net/default.aspx/gdi32/bitblt.html">http://www.pinvoke.net/default.aspx/gdi32/bitblt.html</a> <br> это может не работать на всех типах 3d-приложений/видео приложений. тогда <a href="http://www.codeproject.com/kb/dialog/screencap.aspx">эта ссылка</a> может быть более полезным, так как описывает 3 различных метода, которые вы можете использовать.</p><p><strong> старый ответ (c #): </strong><br> вы можете использовать <a href="http://msdn.microsoft.com/en-us/library/system.drawing.graphics.copyfromscreen.aspx">system.drawing.graphics.copy</a> , но это не очень быстро.</p><p>пример проекта, который я написал, делает именно это: <a href="http://blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/">http://blog.tedd.no/index.php/2010/08/16/c-image-analysis-auto-gaming-with-source/</a> </p><p>я планирую обновить этот образец, используя более быстрый метод, такой как direct3d: <a href="http://spazzarama.com/2009/02/07/screencapture-with-direct3d/">http://spazzarama.com/2009/02/07/screencapture-with-direct3d/</a> </p><p>а вот ссылка для захвата видео: <a href="https://stackoverflow.com/questions/4068414/how-to-capture-screen-to-be-video-using-c-net">как сделать снимок экрана для видео с помощью c # .net?</a> </p>

<p>вы можете попробовать проект с открытым исходным кодом c ++ <a href="https://github.com/caoym/winrobot">winrobot @git</a> , мощный захват экрана</p><pre><code>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; </code></pre><p>служба поддержки :</p><ul> <li>окно uac</li> <li>winlogon</li> <li>directshowoverlay</li> </ul>

<p>я понимаю, что следующее предложение не отвечает на ваш вопрос, но самый простой метод, который я нашел для захвата быстро меняющегося представления directx, это подключить <strong>видеокамера</strong> к порту s-video видео карту и запишите изображения в виде фильма. затем перенесите видео с камеры обратно в файл mpg, wmv, avi и т.д. на компьютере.</p>

<p>я сам делаю это с directx и думаю, что это так быстро, как вы бы этого хотели. у меня нет быстрого примера кода, но я нашел <a href="https://stackoverflow.com/questions/4120108/how-to-save-backbuffer-to-file-in-directx-10/4286971#4286971">это</a> , что должно быть полезно. версия для directx11 не должна сильно отличаться, возможно, directx9 немного больше, но это путь</p>

<p>в windows 8 microsoft представила api дублирования рабочего стола windows. это официально рекомендуемый способ сделать это. одна приятная особенность, которую он имеет для показа экрана, заключается в том, что он обнаруживает движение окна, поэтому вы можете передавать дельты блоков при перемещении окон вместо необработанных пикселей. кроме того, он сообщает вам, какие прямоугольники были изменены, от одного кадра к другому.</p><p>пример кода microsoft довольно сложный, но api на самом деле прост и удобен в использовании. я собрал пример проекта, который намного проще, чем официальный пример:</p><p> <a href="https://github.com/bmharper/windowsdesktopduplicationsample">https://github.com/bmharper/windowsdesktopduplicationsample</a> </p><p>документы: <a href="https://docs.microsoft.com/en-gb/windows/desktop/direct3ddxgi/desktop-dup-api">https://docs.microsoft.com/en-gb/windows/desktop/direct3ddxgi/desktop-dup-api</a> </p><p>официальный пример кода microsoft: <a href="https://code.msdn.microsoft.com/windowsdesktop/desktop-duplication-sample-da4c696a">https://code.msdn.microsoft.com/windowsdesktop/desktop-duplication-sample-da4c696a</a> </p>