ol-ext
Version:
A set of cool extensions for OpenLayers (ol) in node modules structure
233 lines (224 loc) • 7.07 kB
JavaScript
/*
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