UNPKG

chrome-video-capturestream-fix

Version:

Fixes possible crashes caused by calling captureStream on a HTMLVideoElement with its src being a MediaSource in chrome.

97 lines (74 loc) 3.19 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (factory((global.VideoCaptureStreamFix = {}))); }(this, (function (exports) { 'use strict'; const DEFAULT_FRAMERATE = 24; function captureStream(videoEl, streamOptions) { const cvResult = createVideoCanvas(videoEl, streamOptions); const framerate = streamOptions.framerate || DEFAULT_FRAMERATE; const v_stream = cvResult.canvas.captureStream(framerate); const a_stream = getAudioStream(videoEl); const a_track = a_stream.getAudioTracks()[0]; if (a_track) { v_stream.addTrack(a_track); } // Optimization methods but also provides feature to pause the video frame. // It's necessary to call stopVideo when finished to clean up or it will // otherwise continue rendering (to canvas) in the bg and prevent gc. v_stream.startVideo = cvResult.startUpdating; v_stream.stopVideo = cvResult.stopUpdating; return v_stream; } function createVideoCanvas(videoEl, options = {}) { const width = options.width || videoEl.videoWidth; const height = options.height || videoEl.videoHeight; const canvas = document.createElement("canvas"); canvas.width = width; canvas.height = height; const ctx = canvas.getContext("2d"); const framerate = options.framerate || DEFAULT_FRAMERATE; function update() { ctx.drawImage(videoEl, 0, 0, width, height); } let stopId; function stopUpdating() { clearInterval(stopId); } function startUpdating() { stopId = setInterval(update, 1000 / framerate); } // Start automatically startUpdating(); return { videoEl, canvas, stopUpdating, startUpdating }; } // Audio streams need to be cached because createMediaElementSource() can only be called once per videoEl const audioStreamRefs = new WeakMap(); // Issue/downside that volume depends on video element function getAudioStream(videoEl) { let stream = audioStreamRefs.get(videoEl); if (stream) return stream.clone(); const audioCtx = new AudioContext(); const source = audioCtx.createMediaElementSource(videoEl); const destination = audioCtx.createMediaStreamDestination(); // Pipe video sounds to stream source.connect(destination); // Reconnect to audio output (so client hears sound) source.connect(audioCtx.destination); stream = destination.stream; audioStreamRefs.set(videoEl, stream); return stream.clone(); } function polyfill() { HTMLVideoElement.prototype.captureStream = function(streamOptions = {}) { return captureStream(this, streamOptions); }; } exports.polyfill = polyfill; exports.captureStream = captureStream; exports.DEFAULT_FRAMERATE = DEFAULT_FRAMERATE; exports.createVideoCanvas = createVideoCanvas; exports.getAudioStream = getAudioStream; Object.defineProperty(exports, '__esModule', { value: true }); }))); //# sourceMappingURL=videoCaptureStreamFix.umd.js.map