/* Copyright (c) 2019 Jean-Marc VIGLINO, released under the CeCILL-B license (French BSD license) (http://www.cecill.info/licences/Licence_CeCILL-B_V1-en.txt). */ import ol_control_Control from 'ol/control/Control.js' import ol_ext_element from '../util/element.js' import ol_control_Print from './Print.js' import ol_control_Dialog from './Dialog.js' /** Record map canvas as video * @constructor * @fire start * @fire error * @fire stop * @fire pause * @fire resume * @extends {ol.control.Control} * @param {Object=} options Control options. * @param {String} options.className class of the control * @param {number} [options.framerate=30] framerate for the video * @param {number} [options.videoBitsPerSecond=5000000] bitrate for the video * @param {DOMElement|string} [options.videoTarget] video element or the container to add the video when finished or 'DIALOG' to show it in a dialog, default none */ var ol_control_VideoRecorder = class olcontrolVideoRecorder extends ol_control_Control { constructor(options) { options = options || {} var element = ol_ext_element.create('DIV', { className: (options.className || 'ol-videorec') + ' ol-unselectable ol-control' }) super({ element: element, target: options.target }) // buttons ol_ext_element.create('BUTTON', { type: 'button', className: 'ol-start', title: ol_control_VideoRecorder.prototype.tips.start, click: function () { this.start() }.bind(this), parent: element }) ol_ext_element.create('BUTTON', { type: 'button', className: 'ol-stop', title: ol_control_VideoRecorder.prototype.tips.stop, click: function () { this.stop() }.bind(this), parent: element }) ol_ext_element.create('BUTTON', { type: 'button', className: 'ol-pause', title: ol_control_VideoRecorder.prototype.tips.pause, click: function () { this.pause() }.bind(this), parent: element }) ol_ext_element.create('BUTTON', { type: 'button', className: 'ol-resume', title: ol_control_VideoRecorder.prototype.tips.resume, click: function () { this.resume() }.bind(this), parent: element }) // Start this.set('framerate', 30) this.set('videoBitsPerSecond', 5000000) if (options.videoTarget === 'DIALOG') { this._dialog = new ol_control_Dialog({ className: 'ol-fullscreen-dialog', target: document.body, closeBox: true }) this._videoTarget = this._dialog.getContentElement() } else { this._videoTarget = options.videoTarget } // Print control this._printCtrl = new ol_control_Print({ target: ol_ext_element.create('DIV') }) } /** Set button title * @param {string} button button name (start, stop, pause or resume) * @param {string} title */ setTooltip(button, title) { var elt = this.element.querySelector('button.ol-'+button) if (elt) { elt.title = title; } } /** * Remove the control from its current map and attach it to the new map. * Subclasses may set up event handlers to get notified about changes to * the map here. * @param {ol.Map} map Map. * @api stable */ setMap(map) { if (this.getMap()) { this.getMap().removeControl(this._printCtrl) if (this._dialog) this.getMap().removeControl(this._dialog) } super.setMap(map) if (this.getMap()) { this.getMap().addControl(this._printCtrl) if (this._dialog) this.getMap().addControl(this._dialog) } } /** Start recording */ start() { var print = this._printCtrl var stop = false function capture(canvas) { // Stop recording if (stop) return // New frame print.fastPrint({ canvas: canvas }, capture) } print.fastPrint({}, function (canvas) { var videoStream try { videoStream = canvas.captureStream(this.get('framerate') || 30) } catch (e) { this.dispatchEvent({ type: 'error', error: e }) // console.warn(e); return } this._mediaRecorder = new MediaRecorder(videoStream, { videoBitsPerSecond: this.get('videoBitsPerSecond') || 5000000 }) var chunks = [] this._mediaRecorder.ondataavailable = function (e) { chunks.push(e.data) } this._mediaRecorder.onstop = function () { stop = true var blob = new Blob(chunks, { 'type': 'video/mp4' }) // other types are available such as 'video/webm' for instance, see the doc for more info chunks = [] if (this._videoTarget instanceof Element) { var video if (this._videoTarget.tagName === 'VIDEO') { video = this._videoTarget } else { video = this._videoTarget.querySelector('video') if (!video) { video = ol_ext_element.create('VIDEO', { controls: '', parent: this._videoTarget }) } } if (this._dialog) this._dialog.show() video.src = URL.createObjectURL(blob) this.dispatchEvent({ type: 'stop', videoURL: video.src }) } else { this.dispatchEvent({ type: 'stop', videoURL: URL.createObjectURL(blob) }) } }.bind(this) this._mediaRecorder.onpause = function () { stop = true this.dispatchEvent({ type: 'pause' }) }.bind(this) this._mediaRecorder.onresume = function () { stop = false capture(canvas) this.dispatchEvent({ type: 'resume' }) }.bind(this) this._mediaRecorder.onerror = function (e) { this.dispatchEvent({ type: 'error', error: e }) }.bind(this) stop = false capture(canvas) this._mediaRecorder.start() this.dispatchEvent({ type: 'start', canvas: canvas }) this.element.setAttribute('data-state', 'rec') }.bind(this)) } /** Stop recording */ stop() { if (this._mediaRecorder) { this._mediaRecorder.stop() this._mediaRecorder = null this.element.setAttribute('data-state', 'inactive') } } /** Pause recording */ pause() { if (this._mediaRecorder) { this._mediaRecorder.pause() this.element.setAttribute('data-state', 'pause') } } /** Resume recording after pause */ resume() { if (this._mediaRecorder) { this._mediaRecorder.resume() this.element.setAttribute('data-state', 'rec') } } } /** Default button tips */ ol_control_VideoRecorder.prototype.tips = { start: 'start video', stop: 'stop', pause: 'pause', resume: 'resume' } export default ol_control_VideoRecorder