减少navigator.webkitGetUserMedia(电子:DesktopCapturer)

时间:2016-07-07 10:46:33

标签: webkit cpu-usage electron getusermedia navigator

我使用navigator.webkitGetUserMedia每秒捕获一次窗口的屏幕截图,方法是将返回的stream分配给<video>并将其复制到<canvas>并且将缓冲区保存到文件。

我的应用程序中的CPU使用率一直很高,我已将其精确定位到此区域。

代码

// 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()。似乎没有改变CPU使用率。
  2. _video.stop()。这意味着我必须再次获取流,这会导致CPU使用率的飙升比保持打开状态更差。
  3. 我现在最好的方法是通过添加:

    来更改帧速率
      optional: [
        { minFrameRate: 1 },
        { frameRate: 1 }
      ]
    

    极低的帧速率会很好。但是,在这种情况下,我还无法确定frameRate设置是否有效。 The docs没有列出,我没有更新的mediaDevices.getUserMedia

    是否可以为navigator.webkitGetUserMedia设置极低的帧速率(或根本不设置)?

    有没有人能够以任何其他方式减少流的CPU使用率?

    实现相同目标的任何替代方法(间隔状态捕获)也会有所帮助。

    谢谢!

    旁注

    这是在使用DesktopCapturer的Windows上的Electron应用中获取chromeMediaSourceId

    CPU使用率更新

    1. 运行成本stream:6%CPU使用率
    2. 每1000毫秒调用captureState:5%CPU使用率
    3. 总电流:11%

      目前正在根据Csaba Toth的建议努力减少#2。我应该可以通过更改画布的捕获方式来减少captureState。完成后会更新。

      对于#1,如果我无法捕获视频流,我将不得不通过优化#2来尝试将总CPU使用率限制在6%以上。

1 个答案:

答案 0 :(得分: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编码+解码。 node-pngjs将在JS land中执行PNG编码,可能不像浏览器的内部编码那样高效。最好的方法是找到一种方法来利用浏览器的编码,而无需使用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码扫描尝试,每500ms)。我最初使用计时器,但是经过一些迭代/滴答后停止工作,所以从技术上讲,我发出一次超时,每次点击我重新发出一个新的。请参阅初始超时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

我建议你以这种方式重构你的代码。因此,设置计时器每秒钟滴答或重新发出超时。然后在该部分进行捕获+保存。希望这会减轻CPU负载,尽管每秒编码1920x1080 PNG可能会对CPU造成压力(将PNG编码)。

(如果您想要获取单个图像,那将非常有用。如果您最终想要最终获得视频,那么我会尝试按照您的建议继续执行1s FPS视频并捕获视频流直接而不是单个图像。但对于CPU负载我的建议应该有助于恕我直言。)

在自述文件(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,他们也不会在自述文件https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md中的gotStream中执行任何额外操作,如图所示,理想情况下,您只需将视频流与画布相连接即可。