2016-07-07 3 views
3

J'utilise navigator.webkitGetUserMedia pour capturer capture d'écran d'une fenêtre une fois par seconde en attribuant le retour stream à un <video> et la copie à un <canvas> et sauver le tampon de fichier.Réduction de l'utilisation du processeur de navigator.webkitGetUserMedia (Electron: DesktopCapturer)

L'utilisation du processeur dans mon application est toujours élevée et je l'ai identifié dans cette zone.

code

// 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); 

Ce code mon ne fonctionne pas, il est une version simplifiée de ce que j'ai dans mon code pour le rendre lisible StackOverflow.

choses que j'ai essayé

  1. _video.pause() et _video.play() en cas de besoin. Ne semble pas changer l'utilisation du processeur.
  2. _video.stop(). Cela signifie que je devrais récupérer le flux, ce qui provoque une augmentation de l'utilisation du processeur pire que de le garder ouvert.

Mon meilleur plomb est de changer en ce moment le taux de trame en ajoutant:

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

extrêmement faible taux d'image serait bien. Cependant, je n'ai pas pu déterminer si le réglage frameRate fonctionne dans ce cas. The docs ne l'ai pas énuméré et je n'ai pas le plus récent mediaDevices.getUserMedia disponible.

Est-il possible de définir des fréquences d'images extrêmement basses (ou pas du tout) pour navigator.webkitGetUserMedia?

Est-ce que quelqu'un a été capable de réduire l'utilisation du processeur du flux d'une autre manière?

Toute autre méthode permettant d'atteindre le même objectif (capture d'état sur intervalle) serait également utile.

Merci!

Side Note

Ceci est une application électronique sur Windows en utilisant DesktopCapturer pour obtenir le chromeMediaSourceId.


Mise à jour sur l'utilisation du processeur

  1. coût de fonctionnement stream: 6% CPU Utilisation
  2. Appel captureState tous les 1000ms: 5% CPU Utilisation

total actuel: 11 %

Cu travaille actuellement à réduire la # 2 sur la base des recommandations de Csaba Toth jusqu'à présent. Je devrais pouvoir réduire captureState en changeant comment la toile est capturée. Mettra à jour quand c'est fait.Pour le n ° 1, si je ne peux pas éviter de capturer le flux vidéo, je vais juste essayer de limiter l'utilisation totale du processeur à un peu plus de 6% en optimisant le # 2.

+0

Quel type de CPU avez-vous? 6% + 5% n'est pas nécessairement trop mauvais à mon humble avis. Si c'est 6-8 cœurs, cela consomme un noyau complètement cependant. Ne vous attendez pas à éliminer complètement # 2. Peut-être que c'est une bonne idée de voir s'il est possible de faire une capture d'écran sans flux vidéo. –

+0

i5-6500. Non ce n'est pas terrible, ça semble fonctionner correctement maintenant. J'utilisais plusieurs flux avant pour une autre raison mais j'ai trouvé une solution de contournement et maintenant je n'ai besoin que d'un flux. Si je peux l'obtenir pour rester en dessous de 10%, je serai heureux, je pense que vos sugestions ci-dessous devraient m'y amener. – Matt

Répondre

1

Il y a un codage base64 inutile et les opérations en cours ici, il est bizarre comment vous mettre la main sur les données:

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

Jetez un oeil à la façon dont l'accès décodeur QR l'image au lieu: 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); 

(l'autre côté du logiciel a fait un drawImage à la toile plus tôt). Maintenant, la tâche serait de trouver une méthode qui ne convertira pas inutilement les données PNG en base64, puis le décoder. Je vois que partout cet encodage URI est conseillé car c'est moins de lignes. Mais en ce qui concerne les performances, une phase de codage/décodage inutile est indésirable. Les PNG 1920x1080 sont grands, pas destinés à la ligne de base64. Puisque vous êtes dans nodejs de toute façon, essayez d'utiliser https://github.com/niegowski/node-pngjs ou une bibliothèque similaire pour enregistrer les données d'image.

Il y a toujours un compromis entre l'espace et le temps, donc si le temps est vraiment important avec une compression plus faible vous pouvez avoir des performances plus élevées: https://github.com/wheany/js-png-encoder

Il y a un compromis entre ici, puisque les exemples URI base64 de codage profitent de la le codage png natif du navigateur (C++, rapide), mais ensuite l'encodage base64 inutile + le décodage. Le nœud-pngjs effectuerait un codage PNG en JS land, ce qui peut être peut-être moins performant que le codage interne du navigateur. Le mieux serait de trouver un moyen de tirer parti de l'encodage du navigateur sans avoir le base64.


conseils Plus tôt

Selon ce que vous montrez Je pense que votre problème principal est que vous effectuez _ctx.drawImage(_video, 0, 0); et d'autres opérations dans votre gotStream.

Voici une application Web progressive de la mine, il effectue un balayage de code QR aussi: https://github.com/bulldogearthday/booths/blob/master/scripts/app.js Notez que dans le « gotStream » (ce qui est anonyme dans mon cas https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L67) câbler que le cours d'eau sur la toile. Ma situation est plus facile parce que je n'ai pas besoin de forcer la taille (j'espère que vous ne câblez pas ces nombres de pixels), mais j'effectue également des traitements (tentative d'analyse de code QR, toutes les 500ms) périodiquement. À l'origine, j'utilisais la minuterie pour cela, mais cela a cessé de fonctionner après quelques itérations/ticks, donc techniquement, j'émets un seul timeout, et chaque fois que ça arrive, j'en re-édite un nouveau. Voir délai d'attente initial https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L209 et réédition périodique: https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L231

Comme vous pouvez le voir le seul endroit que je fais « soulever des objets lourds » est dans le app.scanQRCode qui ne se produit que deux fois par seconde. Là, je traite le contenu de la toile: https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L218

Je vous conseille de restructurer votre code de cette façon. Donc, installez soit une minuterie à chaque seconde ou ré-émettre des délais comme moi. Faites ensuite la capture + enregistrer dans cette section. Espérons que cela va alléger la charge du processeur, bien que l'encodage 1920x1080 PNG une fois par seconde peut stresser un CPU (il y aura PNG encodage).

(C'est utile si vous voulez opter pour des images individuelles.Si vous voulez finir avec une vidéo à la fin, alors j'essayerais d'appliquer la vidéo 1PS FPS comme vous l'avez suggéré et de capturer le flux vidéo directement au lieu d'images individuelles. Mais pour la CPU charger ma suggestion devrait aider à mon humble avis)


Dans le README (https://github.com/bulldogearthday/booths) vous pouvez voir l'une des sources principales que j'examinés sur getUserMedia. https://github.com/samdutton/simpl/blob/gh-pages/getusermedia/sources/js/main.js

Je ne tripote pas émission .play() ou .pause() ou quoi que ce soit. En fait mon code attend jusqu'à ce qu'il reçoive le signal que le jeu a commencé (commence par lui-même par défaut au moins pour les caméras): document.getElementById('qrVideo').addEventListener('playing', app.saveVideoSize, false);https://github.com/bulldogearthday/booths/blob/master/scripts/app.js#L67 Mon intention était de ne pas déranger le processus naturel avec quoi que ce soit si possible. Dans mon cas, je détecte la taille de la vidéo de cette façon douce. En regardant DesktopCapturer, ils n'effectuent pas non plus d'extra dans le gotStream dans leur fichier README https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md, et comme illustré idéalement, il vous suffit de câbler le flux vidéo avec le canevas.

+0

Merci pour le poste complet! Mon code est réellement séparé comme vous l'avez suggéré - saisit le flux et l'enregistre, appelant un 'captureState' distinct sur l'intervalle. J'ai mis à jour mon code pour refléter cela. Il suffit de lire le reste de votre code maintenant. – Matt

+0

Donc la seule différence que je vois est que vous saisissez le contexte de toile chaque fois que vous en avez besoin plutôt que de garder une référence au contexte comme je l'ai dans '_ctx'. Je vais essayer et voir s'il y a un changement. – Matt

+0

Quand vous parlez de capturer le flux vidéo directement, comment pourrais-je faire cela? J'ai besoin d'être capable de faire des comparaisons d'images et j'ai supposé que le 'canvas' rendu de cette manière était la seule façon d'accomplir cela. – Matt