2016-07-07 7 views
3

Я использую navigator.webkitGetUserMedia захватить скриншот окна один раз каждую секунду, назначив возвращаемый stream к <video> и скопировать его в <canvas> и сохранение буфера в файл.Сокращение использования ЦП navigator.webkitGetUserMedia (Electron: DesktopCapturer)

Использование процессора в моем приложении постоянно высокое, и я точно указал его на эту область.

Код

// Initialize the video, canvas, and ctx 
var localStream, 
    _video = document.querySelector('#video'), 
    _canvas = document.querySelector('#canvas'), 
    _ctx = _canvas.getContext('2d'), 
    sourceName = 'my-window-id'; 

// Load the stream from navigator.webkitGetUserMedia 
navigator.webkitGetUserMedia({ 
    audio: false, 
    video: { 
    mandatory: { 
     chromeMediaSource: 'desktop', 
     chromeMediaSourceId: sourceName, 
     minWidth: 1920, 
     maxWidth: 1920, 
     minHeight: 1080, 
     maxHeight: 1080 
    } 
    } 
}, gotStream, getUserMediaError); 

function gotStream(stream) { 
    // Use the stream in our <video> 
    _video.src = window.URL.createObjectURL(stream); 

    // Reference the stream locally 
    localStream = stream; 
} 

function captureState() { 
    var buffer, 
    dataURL; 

    // Draw <video> to <canvas> and convert to buffer (image data) 
    _ctx.drawImage(_video, 0, 0); 
    dataURL = _canvas.toDataURL('image/png'); 
    buffer = new Buffer(dataURL.split(",")[1], 'base64'); 

    // Create an image from the data 
    fs.writeFileSync('screenshot.png', buffer); 
} 

// Capture state every second 
setInterval(function() { 
    captureState(); 
}, 1000); 

Этот код не мой бежать, это упрощенная версия того, что у меня есть в моем коде, чтобы сделать его StackOverflow читаемым.

Вещи я Пробовал

  1. _video.pause() и _video.play() при необходимости. Кажется, не изменилось использование ЦП.
  2. _video.stop(). Это означает, что мне придется снова получить поток, который вызывает всплеск использования ЦП хуже, чем держать его открытым.

Мой лучший ведущий прямо сейчас, чтобы изменить частоту кадров, добавив:

optional: [ 
    { minFrameRate: 1 }, 
    { frameRate: 1 } 
    ] 

Крайне низкая частота кадров будет в порядке. Однако я не смог определить, работает ли в этом случае настройка frameRate. The docs не указаны в списке и у меня нет новых mediaDevices.getUserMedia.

Возможно ли установить очень низкие частоты кадров (или вообще любые) для navigator.webkitGetUserMedia?

Кто-нибудь мог уменьшить использование ЦП в потоке любым другим способом?

Любые альтернативные способы достижения одной и той же цели (захват состояния по интервалу) также будут полезны.

Спасибо!

Side Примечание

Это в приложение Electron на Windows, используя DesktopCapturer, чтобы получить chromeMediaSourceId.


Обновление на Использование CPU

  1. Стоимость работы stream: 6% загрузки процессора
  2. Вызов captureState каждые 1000 мс: 5% Использование CPU

Всего ток: 11 %

Cu редко работая над сокращением № 2 на основе рекомендаций Csaba Toth до сих пор. Я должен был бы уменьшить captureState, изменив, как захвачен холст. Будет обновляться, когда это будет сделано.

Для # 1, если я не могу избежать захвата видеопотока, мне нужно просто попытаться ограничить общее использование ЦП чуть более 6%, оптимизируя # 2.

+0

Какой у вас процессор? 6% + 5% не обязательно слишком плохо ИМХО. Если это 6-8 ядер, это все равно поглотит одно ядро. Не ожидайте полного устранения №2. Может быть, это хорошая идея, чтобы увидеть, можно ли скриншот без видеопотока. –

+0

i5-6500. Нет, это не страшно, похоже, сейчас все в порядке. Я использовал несколько потоков по другой причине, но нашел обходной путь и теперь нужен только один поток. Если я смогу заставить его остаться ниже 10%, я буду счастлив, я думаю, что ваши суффиксы ниже должны получить меня там. – Matt

ответ

1

Там какая-то ненужное кодирование base64 и операции здесь происходит, это странно, как вы раздобыть данные:

dataURL = _canvas.toDataURL('image/png'); 
buffer = new Buffer(dataURL.split(",")[1], 'base64'); 

Взгляните на то, как доступ декодер QR изображение вместо: https://github.com/bulldogearthday/booths/blob/master/scripts/qrdecoder.js#L1991

var canvas_qr = document.getElementById("qr-canvas"); 
var context = canvas_qr.getContext('2d'); 
qrcode.width = canvas_qr.width; 
qrcode.height = canvas_qr.height; 
qrcode.imagedata = context.getImageData(0, 0, qrcode.width, qrcode.height); 

(с другой стороны программное обеспечение сделало drawImage на холст раньше). Теперь задача заключалась бы в поиске метода, который не будет излишне конвертировать PNG-данные в base64 и затем декодировать его. Я вижу, что везде эта кодировка URI рекомендуется, поскольку количество строк меньше. Но производительность с ненужной фазой кодирования/декодирования нежелательна. 1920x1080 PNG являются большими, не предназначены для base64 в подкладке. Так как вы все равно находитесь в nodejs, попробуйте использовать https://github.com/niegowski/node-pngjs или аналогичную библиотеку для сохранения данных изображения.

Там всегда компромисс между пространством и временем, поэтому если время действительно имеет значение с меньшим сжатием вы можете иметь более высокую производительность: https://github.com/wheany/js-png-encoder

Там есть компромисс здесь, поскольку примеры base64 URI кодирования использовать в своих интересах (C++, быстрая) кодировка png, но затем сделать ненужное кодирование base64 + декодирование. Узел-pngjs выполнил бы PNG-кодирование на JS-земле, что может быть не так хорошо, как внутренняя кодировка браузера. Лучше всего было бы найти способ использовать кодировку браузера, не имея base64.


Ранее советы

Согласно тому, что вы показать, что я думаю, что ваша главная проблема в том, что вы выполняете _ctx.drawImage(_video, 0, 0); и другие операции в вашем gotStream.

Вот Progressive Web App шахты, он выполняет QR-код сканирования тоже: https://github.com/bulldogearthday/booths/blob/master/scripts/app.js Обратите внимание, что в «gotStream» (которое является анонимным в моем случае https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L67) Я только телеграфировать поток на холсте.

Мое положение проще, потому что мне не нужно обеспечивать размер (надеюсь, вы не будете жестко прокручивать эти пиксельные номера экрана), но я также периодически обрабатываю (попытку сканирования QR-кода каждые 500 мс). Я изначально использовал таймер для этого, но это прекратило работать после некоторых итераций/тиков, поэтому технически я выдаю один тайм-аут, и каждый раз, когда он попадает, я перевыпускаю новый. См начального интервал ожидание https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L209 и периодическое переиздание: https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L231

Как вы можете видеть, единственное место, где я «тяжелый» находится в app.scanQRCode, которое происходит только два раза в секунду. Там я обрабатываю содержимое холста: https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L218

Я советую вам перестроить код таким образом. Поэтому настройте либо таймер, указывающий каждую секунду, либо перевыпуск тайм-аутов, как и я. Затем сделайте захват + сохранить в этом разделе. Надеемся, что это облегчит загрузку процессора, хотя кодировка 1920x1080 PNG раз в секунду может вызвать нагрузку на CPU (будет кодировка PNG).

(Это полезно, если вы хотите перейти на отдельные изображения.Если вы хотите закончить видео в любом случае в конце, я бы попытался пойти по пути обеспечения 1s FPS видео, как вы предложили, и захватить видеопоток напрямую, а не отдельные изображения. Но для CPU загрузить мое предложение должно помочь ИМХО)


В README (https://github.com/bulldogearthday/booths) вы можете увидеть один из главных источников я посмотрел на для getUserMedia:. https://github.com/samdutton/simpl/blob/gh-pages/getusermedia/sources/js/main.js

Я не возиться с выдача .play() или .pause() или что-то еще. На самом деле мой код ждет, пока он не получит сигнал о начале игры (запускается по умолчанию по крайней мере для камер): document.getElementById('qrVideo').addEventListener('playing', app.saveVideoSize, false);https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L67 Мое намерение заключалось в том, чтобы не нарушать естественный процесс чем угодно, если это возможно. В моем случае я определяю размер видео таким нежным способом. Глядя на DesktopCapturer, они также не выполняют никаких дополнительных действий в gotStream в своем README https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md, и, как показано в идеальном случае, вы просто подключаете видеопоток к холсту.

+0

Спасибо за тщательный пост! Мой код фактически разделен, как вы сказали, - захватывает поток и сохраняет его, вызывая отдельный 'captureState' на интервале. Я обновил свой код, чтобы отразить это. Просто прочитайте остальную часть своего кода сейчас. – Matt

+0

Итак, единственное различие, которое я вижу, это то, что вы захватываете контекст canvas каждый раз, когда вам это нужно, вместо того, чтобы ссылаться на контекст, как у меня в '_ctx'. Я дам эту попытку и посмотрю, есть ли какие-либо изменения. – Matt

+0

Когда вы говорите о захвате видеопотока напрямую, как я могу это сделать? Мне нужно сделать сравнение изображений, и я предположил, что сделанный таким образом «холст» был единственным способом добиться этого. – Matt