2016-07-07 130 views
3

我使用navigator.webkitGetUserMedia捕获窗口每秒一次被指派返回stream<video>并将其复制到<canvas>并保存在缓冲区中的文件的截图。降低CPU占用率(电子:DesktopCapturer)

我的应用程序中的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使用率的上升比保持开放更糟。

我最好的铅,现在是通过向更改帧速率:

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

极低的帧速率将被罚款。但是,我无法确定frameRate设置是否适用于这种情况。 The docs没有它列出,我没有新的mediaDevices.getUserMedia可用。

是否有可能为navigator.webkitGetUserMedia设置极低的帧率(或任何)?

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

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

谢谢!

旁注

这在使用DesktopCapturer获得chromeMediaSourceId Windows上的电子应用。


CPU使用率更新

  1. 运行stream成本:6%的CPU使用率
  2. 要求每1000毫秒captureState:5%的CPU使用率

总电流:11 %

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

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

+0

你有什么类型的CPU? 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编码+解码。 node-pngjs会在JS land中执行PNG编码,其中可能不是,而不是浏览器的内部编码。最好的办法是找到一种方法来利用浏览器的编码,而无需使用base64。


早些时候通知:

根据大家展示一下,我认为你的主要问题是,你执行你的gotStream_ctx.drawImage(_video, 0, 0);等操作。

这里是我的一个渐进的Web应用程序,它执行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

我建议你以这种方式重构你的代码。因此,请设置每秒计时器计时器或重新发布暂停计时器。然后执行捕获并保存在该部分。希望这会减轻CPU负载,虽然编码1920x1080 PNG每秒一次可能会强调一个CPU(将有PNG编码)。

(如果您想要拍摄个人照片,这将非常有用。如果您最终还是想以视频结尾,那么我会尝试按照您的建议继续执行1秒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,他们也不会在自己的README https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md中的gotStream中执行任何额外的操作,如图所示,理想情况下,您只需用画布连接视频流。

+0

感谢您的深入了解!我的代码实际上是按照你的建议分开的 - 抓住流并保存它,每隔一段时间调用一个单独的'captureState'。我更新了我的代码以反映这一点。现在阅读您的其他代码。 – Matt

+0

所以我看到的唯一区别就是你每次需要时都抓取画布上下文,而不是像'_ctx'那样保留对上下文的引用。我会试试看看是否有任何改变。 – Matt

+0

当你谈论直接捕获视频流时,我该如何去做这件事?我需要能够进行图像比较,并且我认为以这种方式渲染“画布”是实现这一点的唯一方法。 – Matt