Как захватить экран окна со скоростью 60 кадров в секунду с помощью api? [Дубликат]

Мы оказываемся во вселенной, которая, по-видимому, развивается по измерению, которое мы называем «временем». Мы не понимаем, какое время, но мы разработали абстракции и словарный запас, которые позволяют рассуждать и говорить об этом: «прошлое», «настоящее», «будущее», «до», «после».

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

Рассмотрим пример. Вы называете молочника и заказываете молоко. Когда это произойдет, вы хотите положить его в свой кофе. Вы не можете положить молоко в свой кофе прямо сейчас, потому что его еще нет. Вы должны подождать, пока это произойдет, прежде чем положить его в свой кофе. Другими словами, следующее не будет работать:

var milk = order_milk();
put_in_coffee(milk);

Поскольку JS не знает, что ему нужно дождаться окончания order_milk, прежде чем он выполнит put_in_coffee. Другими словами, он не знает, что order_milk является асинхронным - это то, что не приведет к молоку до некоторого будущего времени. JS и другие декларативные языки, выполняйте один оператор за другим, не ожидая.

Классический подход JS к этой проблеме, используя тот факт, что JS поддерживает функции как объекты первого класса, которые могут быть переданы, заключается в передаче функции в качестве параметра для асинхронного запроса, который затем будет вызываться, когда он будет выполнять свою задачу в будущем. Это подход «обратного вызова». Это выглядит так:

order_milk(put_in_coffee);

order_milk запускает, заказывает молоко, тогда, когда и только когда он прибывает, он вызывает put_in_coffee.

Проблема с этот подход обратного вызова состоит в том, что он загрязняет нормальную семантику функции, сообщающей свой результат с помощью return; вместо этого функции должны сообщать свои результаты, вызывая обратный вызов, заданный как параметр. Кроме того, этот подход может быстро стать громоздким при работе с более длинными последовательностями событий. Например, предположим, что я хочу дождаться, когда молоко будет помещено в кофе, а затем и только затем выполните третий шаг, а именно - выпить кофе. В конце концов мне нужно написать что-то вроде этого:

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

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

В этом случае мы могли бы переписать код в вопросе как:

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

Enter обещает

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

В случае нашего молока и кофе мы создаем order_milk, чтобы вернуть обещание о прибытии молока, затем укажите put_in_coffee как действие then следующим образом:

order_milk() . then(put_in_coffee)

. Одно из преимуществ этого заключается в том, что мы можем объединить их вместе для создания последовательностей будущие вхождения («цепочка»):

order_milk() . then(put_in_coffee) . then(drink_coffee)

Давайте применим обещания к вашей конкретной проблеме. Мы завершим нашу логику запроса внутри функции, которая возвращает обещание:

function get_data() {
  return $.ajax('/foo.json');
}

На самом деле, все, что мы сделали, добавлено к return к вызову $.ajax. Это работает, потому что jQuery $.ajax уже возвращает вид обетоподобной вещи. (На практике, не вдаваясь в подробности, мы предпочли бы обернуть этот вызов, чтобы вернуть реальное обещание, или использовать некоторую альтернативу $.ajax, которая делает это.) Теперь, если мы хотим загрузить файл и дождаться его завершите, а затем сделайте что-нибудь, мы можем просто сказать

get_data() . then(do_something)

, например,

get_data() . 
  then(function(data) { console.log(data); });

. При использовании обещаний мы заканчиваем передачу множества функций в then, поэтому часто полезно использовать более компактные функции стрелок в стиле ES6:

get_data() . 
  then(data => console.log(data));

Ключевое слово async

Но все еще есть что-то неопределенное в том, что нужно писать код одним способом, если синхронно и совершенно по-другому, если асинхронно. Для синхронного мы пишем

a();
b();

, но если a является асинхронным, с обещаниями мы должны написать

a() . then(b);

Выше, мы сказали: «JS не имеет никакого способа узнать что ему нужно дождаться завершения первого вызова, прежде чем он выполнит второй ». Было бы неплохо, если бы можно было сказать JS? Оказывается, существует ключевое слово await, используемое внутри специального типа функции, называемого функцией «async». Эта функция является частью предстоящей версии ES, но уже доступна в транспилерах, таких как Babel, с учетом правильных настроек. Это позволяет нам просто написать

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

. В вашем случае вы могли бы написать что-то вроде

async function foo() {
  data = await get_data();
  console.log(data);
}
134
задан BarnyardOwl 14 November 2014 в 05:01
поделиться

9 ответов

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

    IDirect3DSurface9 *surface ;

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

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

    SAFE_RELEASE( surface ) ;

Для этого вы должны создать свой swapbuffer с помощью

d3dpps.SwapEffect = D3DSWAPEFFECT_COPY ; // for screenshots.

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

12
ответ дан bobobobo 26 August 2018 в 10:03
поделиться

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

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();
}
52
ответ дан Brandrew 26 August 2018 в 10:03
поделиться

Вы можете попробовать проект с открытым исходным кодом 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
4
ответ дан Cayman 26 August 2018 в 10:03
поделиться

Я сам делаю это с directx и думаю, что это так быстро, как вы хотели бы этого. У меня нет быстрого кода, но я нашел этот , который должен быть полезен. версия directx11 не должна сильно отличаться, directx9 может быть немного больше, но это путь

7
ответ дан Community 26 August 2018 в 10:03
поделиться

EDIT: я вижу, что это указано в вашей первой ссылке редактирования как «способ 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);

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

26
ответ дан darvids0n 26 August 2018 в 10:03
поделиться

Я написал программное обеспечение для видеозахвата, похожее на FRAPS для приложений DirectX. Исходный код доступен, и моя статья объясняет общую технику. Посмотрите на http://blog.nektra.com/main/2013/07/23/instrumenting-direct3d-applications-to-capture-video-and-calculate-frames-per-second/

Уважение к вашим вопросам, связанным с производительностью,

  • DirectX должен быть быстрее, чем GDI, за исключением тех случаев, когда вы читаете из буфера, который очень медленный. Мой подход похож на FRAPS (чтение из backbuffer). Я перехватываю набор методов из Direct3D-интерфейсов.
  • Для видеозаписи в реальном времени (с минимальным воздействием приложения) необходим быстрый кодек. FRAPS использует собственный видеокодек без потерь. Lagarith и HUFFYUV - это общие видеокодеки без потерь, предназначенные для приложений реального времени. Вы должны смотреть на них, если хотите вывести видеофайлы.
  • Другой подход к записи скринкастов может заключаться в написании драйвера зеркального отображения. Согласно Википедии: Когда видеореклама активна, каждый раз, когда система обращается к основному видеоустройству в месте внутри зеркальной области, копия операции рисования выполняется на зеркальном видеоустройстве в режиме реального времени. См. драйверы зеркал в MSDN: http://msdn.microsoft.com/en-us/library/windows/hardware/ff568315 (v = vs.85) .aspx .
14
ответ дан Hernán 26 August 2018 в 10:03
поделиться

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

1
ответ дан Pierre 26 August 2018 в 10:03
поделиться

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

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

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

8
ответ дан rotanimod 26 August 2018 в 10:03
поделиться

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

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

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

8
ответ дан zinking 26 August 2018 в 10:03
поделиться
Другие вопросы по тегам:

Похожие вопросы: