233 lines
6.7 KiB
JavaScript
233 lines
6.7 KiB
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
|