From e47969ba1c70542e7665a29ba3c9680a98b83e3a Mon Sep 17 00:00:00 2001 From: Karishma Chadha Date: Thu, 26 Apr 2018 21:33:18 -0400 Subject: [PATCH 1/6] Refactor video io device to pull HTML DOM related functionality into gui, and for resuability of video device with camera modal. --- src/containers/stage.jsx | 2 + src/lib/camera.js | 377 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 379 insertions(+) create mode 100644 src/lib/camera.js diff --git a/src/containers/stage.jsx b/src/containers/stage.jsx index f3444af1a..7e543617a 100644 --- a/src/containers/stage.jsx +++ b/src/containers/stage.jsx @@ -6,6 +6,7 @@ import VM from 'scratch-vm'; import {connect} from 'react-redux'; import {getEventXY} from '../lib/touch-utils'; +import {VideoProvider} from '../lib/camera'; import StageComponent from '../components/stage/stage.jsx'; @@ -57,6 +58,7 @@ class Stage extends React.Component { this.renderer = new Renderer(this.canvas); this.props.vm.attachRenderer(this.renderer); this.props.vm.runtime.addListener('QUESTION', this.questionListener); + this.props.vm.setVideoProvider(new VideoProvider()); } shouldComponentUpdate (nextProps, nextState) { return this.props.width !== nextProps.width || diff --git a/src/lib/camera.js b/src/lib/camera.js new file mode 100644 index 000000000..339d59dc2 --- /dev/null +++ b/src/lib/camera.js @@ -0,0 +1,377 @@ +import getUserMedia from 'get-user-media-promise'; +import log from './log.js'; + +// Single Setup For All Video Streams used by the GUI +// While VideoProvider uses a private _singleSetup +// property to ensure that each instance of a VideoProvider +// use the same setup, this ensures that all instances +// of VideoProviders use a single stream. This way, closing a camera modal +// does not affect the video on the stage, and a program running and disabling +// video on the stage will not affect the camera modal's video. +const requestStack = []; +const requestVideoStream = videoDesc => { + let streamPromise; + if (requestStack.length === 0) { + streamPromise = getUserMedia({ + audio: false, + video: videoDesc + }); + requestStack.push(streamPromise); + } else if (requestStack.length > 0) { + streamPromise = requestStack[0]; + requestStack.push(true); + } + return streamPromise; +}; + +const requestDisableCheck = () => { + requestStack.pop(); + if (requestStack.length > 0) return false; + return true; +}; + +/** + * Video Manager for video extensions. + */ +class VideoProvider { + constructor () { + /** + * Default value for mirrored frames. + * @type boolean + */ + this.mirror = true; + + /** + * Cache frames for this many ms. + * @type number + */ + this._frameCacheTimeout = 16; + + /** + * DOM Video element + * @private + */ + this._video = null; + + /** + * Usermedia stream track + * @private + */ + this._track = null; + + /** + * Stores some canvas/frame data per resolution/mirror states + */ + this._workspace = []; + } + + static get FORMAT_IMAGE_DATA () { + return 'image-data'; + } + + static get FORMAT_CANVAS () { + return 'canvas'; + } + + /** + * Dimensions the video stream is analyzed at after its rendered to the + * sample canvas. + * @type {Array.} + */ + static get DIMENSIONS () { + return [480, 360]; + } + + /** + * Order preview drawable is inserted at in the renderer. + * @type {number} + */ + static get ORDER () { + return 1; + } + + /** + * Get the HTML video element containing the stream + */ + get video () { + return this._video; + } + + /** + * Request video be enabled. Sets up video, creates video skin and enables preview. + * + * ioDevices.video.requestVideo() + * + * @return {Promise.