From e27797cdcdc60b304161d38547ec58738a820dc1 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Mon, 13 Apr 2020 18:47:28 +0200 Subject: [PATCH] Add recording support --- assets/3rdparty/MediaStreamRecorder.min.js | 16 ++++++++ assets/css/components/calls/window.css | 2 +- assets/js/components/calls/window.js | 46 ++++++++++++++++++++++ system/config/dev.config.php | 3 ++ 4 files changed, 66 insertions(+), 1 deletion(-) create mode 100644 assets/3rdparty/MediaStreamRecorder.min.js diff --git a/assets/3rdparty/MediaStreamRecorder.min.js b/assets/3rdparty/MediaStreamRecorder.min.js new file mode 100644 index 00000000..ba3fbf1d --- /dev/null +++ b/assets/3rdparty/MediaStreamRecorder.min.js @@ -0,0 +1,16 @@ +'use strict'; + +// Last time updated: 2017-08-31 4:03:23 AM UTC + +// __________________________ +// MediaStreamRecorder v1.3.4 + +// Open-Sourced: https://github.com/streamproc/MediaStreamRecorder + +// -------------------------------------------------- +// Muaz Khan - www.MuazKhan.com +// MIT License - www.WebRTC-Experiment.com/licence +// -------------------------------------------------- + +"use strict";function MediaStreamRecorder(mediaStream){if(!mediaStream)throw"MediaStream is mandatory.";this.start=function(timeSlice){var Recorder;"undefined"!=typeof MediaRecorder?Recorder=MediaRecorderWrapper:(IsChrome||IsOpera||IsEdge)&&(this.mimeType.indexOf("video")!==-1?Recorder=WhammyRecorder:this.mimeType.indexOf("audio")!==-1&&(Recorder=StereoAudioRecorder)),"image/gif"===this.mimeType&&(Recorder=GifRecorder),"audio/wav"!==this.mimeType&&"audio/pcm"!==this.mimeType||(Recorder=StereoAudioRecorder),this.recorderType&&(Recorder=this.recorderType),mediaRecorder=new Recorder(mediaStream),mediaRecorder.blobs=[];var self=this;mediaRecorder.ondataavailable=function(data){mediaRecorder.blobs.push(data),self.ondataavailable(data)},mediaRecorder.onstop=this.onstop,mediaRecorder.onStartedDrawingNonBlankFrames=this.onStartedDrawingNonBlankFrames,mediaRecorder=mergeProps(mediaRecorder,this),mediaRecorder.start(timeSlice)},this.onStartedDrawingNonBlankFrames=function(){},this.clearOldRecordedFrames=function(){mediaRecorder&&mediaRecorder.clearOldRecordedFrames()},this.stop=function(){mediaRecorder&&mediaRecorder.stop()},this.ondataavailable=function(blob){this.disableLogs||console.log("ondataavailable..",blob)},this.onstop=function(error){console.warn("stopped..",error)},this.save=function(file,fileName){if(!file){if(!mediaRecorder)return;return void ConcatenateBlobs(mediaRecorder.blobs,mediaRecorder.blobs[0].type,function(concatenatedBlob){invokeSaveAsDialog(concatenatedBlob)})}invokeSaveAsDialog(file,fileName)},this.pause=function(){mediaRecorder&&(mediaRecorder.pause(),this.disableLogs||console.log("Paused recording.",this.mimeType||mediaRecorder.mimeType))},this.resume=function(){mediaRecorder&&(mediaRecorder.resume(),this.disableLogs||console.log("Resumed recording.",this.mimeType||mediaRecorder.mimeType))},this.recorderType=null,this.mimeType="video/webm",this.disableLogs=!1;var mediaRecorder}function MultiStreamRecorder(arrayOfMediaStreams,options){function getVideoTracks(){var tracks=[];return arrayOfMediaStreams.forEach(function(stream){stream.getVideoTracks().forEach(function(track){tracks.push(track)})}),tracks}arrayOfMediaStreams=arrayOfMediaStreams||[],arrayOfMediaStreams instanceof MediaStream&&(arrayOfMediaStreams=[arrayOfMediaStreams]);var mixer,mediaRecorder,self=this;options=options||{mimeType:"video/webm",video:{width:360,height:240}},options.frameInterval||(options.frameInterval=10),options.video||(options.video={}),options.video.width||(options.video.width=360),options.video.height||(options.video.height=240),this.start=function(timeSlice){mixer=new MultiStreamsMixer(arrayOfMediaStreams),getVideoTracks().length&&(mixer.frameInterval=options.frameInterval||10,mixer.width=options.video.width||360,mixer.height=options.video.height||240,mixer.startDrawingFrames()),"function"==typeof self.previewStream&&self.previewStream(mixer.getMixedStream()),mediaRecorder=new MediaStreamRecorder(mixer.getMixedStream());for(var prop in self)"function"!=typeof self[prop]&&(mediaRecorder[prop]=self[prop]);mediaRecorder.ondataavailable=function(blob){self.ondataavailable(blob)},mediaRecorder.onstop=self.onstop,mediaRecorder.start(timeSlice)},this.stop=function(callback){mediaRecorder&&mediaRecorder.stop(function(blob){callback(blob)})},this.pause=function(){mediaRecorder&&mediaRecorder.pause()},this.resume=function(){mediaRecorder&&mediaRecorder.resume()},this.clearRecordedData=function(){mediaRecorder&&(mediaRecorder.clearRecordedData(),mediaRecorder=null),mixer&&(mixer.releaseStreams(),mixer=null)},this.addStreams=this.addStream=function(streams){if(!streams)throw"First parameter is required.";streams instanceof Array||(streams=[streams]),arrayOfMediaStreams.concat(streams),mediaRecorder&&mixer&&mixer.appendStreams(streams)},this.resetVideoStreams=function(streams){mixer&&(!streams||streams instanceof Array||(streams=[streams]),mixer.resetVideoStreams(streams))},this.ondataavailable=function(blob){self.disableLogs||console.log("ondataavailable",blob)},this.onstop=function(){},this.name="MultiStreamRecorder",this.toString=function(){return this.name}}function MultiStreamsMixer(arrayOfMediaStreams){function drawVideosToCanvas(){if(!isStopDrawingFrames){var videosLength=videos.length,fullcanvas=!1,remaining=[];videos.forEach(function(video){video.stream||(video.stream={}),video.stream.fullcanvas?fullcanvas=video:remaining.push(video)}),fullcanvas?(canvas.width=fullcanvas.stream.width,canvas.height=fullcanvas.stream.height):remaining.length?(canvas.width=videosLength>1?2*remaining[0].width:remaining[0].width,canvas.height=videosLength>2?2*remaining[0].height:remaining[0].height):(canvas.width=self.width||360,canvas.height=self.height||240),fullcanvas&&fullcanvas instanceof HTMLVideoElement&&drawImage(fullcanvas),remaining.forEach(function(video,idx){drawImage(video,idx)}),setTimeout(drawVideosToCanvas,self.frameInterval)}}function drawImage(video,idx){if(!isStopDrawingFrames){var x=0,y=0,width=video.width,height=video.height;1===idx&&(x=video.width),2===idx&&(y=video.height),3===idx&&(x=video.width,y=video.height),"undefined"!=typeof video.stream.left&&(x=video.stream.left),"undefined"!=typeof video.stream.top&&(y=video.stream.top),"undefined"!=typeof video.stream.width&&(width=video.stream.width),"undefined"!=typeof video.stream.height&&(height=video.stream.height),context.drawImage(video,x,y,width,height),"function"==typeof video.stream.onRender&&video.stream.onRender(context,x,y,width,height,idx)}}function getMixedStream(){isStopDrawingFrames=!1;var mixedVideoStream=getMixedVideoStream(),mixedAudioStream=getMixedAudioStream();mixedAudioStream&&mixedAudioStream.getAudioTracks().forEach(function(track){mixedVideoStream.addTrack(track)});var fullcanvas;return arrayOfMediaStreams.forEach(function(stream){stream.fullcanvas&&(fullcanvas=!0)}),mixedVideoStream}function getMixedVideoStream(){resetVideoStreams();var capturedStream;"captureStream"in canvas?capturedStream=canvas.captureStream():"mozCaptureStream"in canvas?capturedStream=canvas.mozCaptureStream():self.disableLogs||console.error("Upgrade to latest Chrome or otherwise enable this flag: chrome://flags/#enable-experimental-web-platform-features");var videoStream=new MediaStream;return capturedStream.getVideoTracks().forEach(function(track){videoStream.addTrack(track)}),canvas.stream=videoStream,videoStream}function getMixedAudioStream(){Storage.AudioContextConstructor||(Storage.AudioContextConstructor=new Storage.AudioContext),self.audioContext=Storage.AudioContextConstructor,self.audioSources=[],self.useGainNode===!0&&(self.gainNode=self.audioContext.createGain(),self.gainNode.connect(self.audioContext.destination),self.gainNode.gain.value=0);var audioTracksLength=0;if(arrayOfMediaStreams.forEach(function(stream){if(stream.getAudioTracks().length){audioTracksLength++;var audioSource=self.audioContext.createMediaStreamSource(stream);self.useGainNode===!0&&audioSource.connect(self.gainNode),self.audioSources.push(audioSource)}}),audioTracksLength)return self.audioDestination=self.audioContext.createMediaStreamDestination(),self.audioSources.forEach(function(audioSource){audioSource.connect(self.audioDestination)}),self.audioDestination.stream}function getVideo(stream){var video=document.createElement("video");return"srcObject"in video?video.srcObject=stream:video.src=URL.createObjectURL(stream),video.muted=!0,video.volume=0,video.width=stream.width||self.width||360,video.height=stream.height||self.height||240,video.play(),video}function resetVideoStreams(streams){videos=[],streams=streams||arrayOfMediaStreams,streams.forEach(function(stream){if(stream.getVideoTracks().length){var video=getVideo(stream);video.stream=stream,videos.push(video)}})}var videos=[],isStopDrawingFrames=!1,canvas=document.createElement("canvas"),context=canvas.getContext("2d");canvas.style="opacity:0;position:absolute;z-index:-1;top: -100000000;left:-1000000000; margin-top:-1000000000;margin-left:-1000000000;",(document.body||document.documentElement).appendChild(canvas),this.disableLogs=!1,this.frameInterval=10,this.width=360,this.height=240,this.useGainNode=!0;var self=this,AudioContext=window.AudioContext;"undefined"==typeof AudioContext&&("undefined"!=typeof webkitAudioContext&&(AudioContext=webkitAudioContext),"undefined"!=typeof mozAudioContext&&(AudioContext=mozAudioContext));var URL=window.URL;"undefined"==typeof URL&&"undefined"!=typeof webkitURL&&(URL=webkitURL),"undefined"!=typeof navigator&&"undefined"==typeof navigator.getUserMedia&&("undefined"!=typeof navigator.webkitGetUserMedia&&(navigator.getUserMedia=navigator.webkitGetUserMedia),"undefined"!=typeof navigator.mozGetUserMedia&&(navigator.getUserMedia=navigator.mozGetUserMedia));var MediaStream=window.MediaStream;"undefined"==typeof MediaStream&&"undefined"!=typeof webkitMediaStream&&(MediaStream=webkitMediaStream),"undefined"!=typeof MediaStream&&("getVideoTracks"in MediaStream.prototype||(MediaStream.prototype.getVideoTracks=function(){if(!this.getTracks)return[];var tracks=[];return this.getTracks.forEach(function(track){track.kind.toString().indexOf("video")!==-1&&tracks.push(track)}),tracks},MediaStream.prototype.getAudioTracks=function(){if(!this.getTracks)return[];var tracks=[];return this.getTracks.forEach(function(track){track.kind.toString().indexOf("audio")!==-1&&tracks.push(track)}),tracks}),"undefined"==typeof MediaStream.prototype.stop&&(MediaStream.prototype.stop=function(){this.getTracks().forEach(function(track){track.stop()})}));var Storage={};"undefined"!=typeof AudioContext?Storage.AudioContext=AudioContext:"undefined"!=typeof webkitAudioContext&&(Storage.AudioContext=webkitAudioContext),this.startDrawingFrames=function(){drawVideosToCanvas()},this.appendStreams=function(streams){if(!streams)throw"First parameter is required.";streams instanceof Array||(streams=[streams]),arrayOfMediaStreams.concat(streams),streams.forEach(function(stream){if(stream.getVideoTracks().length){var video=getVideo(stream);video.stream=stream,videos.push(video)}if(stream.getAudioTracks().length&&self.audioContext){var audioSource=self.audioContext.createMediaStreamSource(stream);audioSource.connect(self.audioDestination),self.audioSources.push(audioSource)}})},this.releaseStreams=function(){videos=[],isStopDrawingFrames=!0,self.gainNode&&(self.gainNode.disconnect(),self.gainNode=null),self.audioSources.length&&(self.audioSources.forEach(function(source){source.disconnect()}),self.audioSources=[]),self.audioDestination&&(self.audioDestination.disconnect(),self.audioDestination=null),self.audioContext=null,context.clearRect(0,0,canvas.width,canvas.height),canvas.stream&&(canvas.stream.stop(),canvas.stream=null)},this.resetVideoStreams=function(streams){!streams||streams instanceof Array||(streams=[streams]),resetVideoStreams(streams)},this.name="MultiStreamsMixer",this.toString=function(){return this.name},this.getMixedStream=getMixedStream}function mergeProps(mergein,mergeto){for(var t in mergeto)"function"!=typeof mergeto[t]&&(mergein[t]=mergeto[t]);return mergein}function dropFirstFrame(arr){return arr.shift(),arr}function invokeSaveAsDialog(file,fileName){if(!file)throw"Blob object is required.";if(!file.type)try{file.type="video/webm"}catch(e){}var fileExtension=(file.type||"video/webm").split("/")[1];if(fileName&&fileName.indexOf(".")!==-1){var splitted=fileName.split(".");fileName=splitted[0],fileExtension=splitted[1]}var fileFullName=(fileName||Math.round(9999999999*Math.random())+888888888)+"."+fileExtension;if("undefined"!=typeof navigator.msSaveOrOpenBlob)return navigator.msSaveOrOpenBlob(file,fileFullName);if("undefined"!=typeof navigator.msSaveBlob)return navigator.msSaveBlob(file,fileFullName);var hyperlink=document.createElement("a");hyperlink.href=URL.createObjectURL(file),hyperlink.target="_blank",hyperlink.download=fileFullName,navigator.mozGetUserMedia&&(hyperlink.onclick=function(){(document.body||document.documentElement).removeChild(hyperlink)},(document.body||document.documentElement).appendChild(hyperlink));var evt=new MouseEvent("click",{view:window,bubbles:!0,cancelable:!0});hyperlink.dispatchEvent(evt),navigator.mozGetUserMedia||URL.revokeObjectURL(hyperlink.href)}function bytesToSize(bytes){var k=1e3,sizes=["Bytes","KB","MB","GB","TB"];if(0===bytes)return"0 Bytes";var i=parseInt(Math.floor(Math.log(bytes)/Math.log(k)),10);return(bytes/Math.pow(k,i)).toPrecision(3)+" "+sizes[i]}function isMediaRecorderCompatible(){var isOpera=!!window.opera||navigator.userAgent.indexOf(" OPR/")>=0,isChrome=!!window.chrome&&!isOpera,isFirefox="undefined"!=typeof window.InstallTrigger;if(isFirefox)return!0;if(!isChrome)return!1;var verOffset,ix,nAgt=(navigator.appVersion,navigator.userAgent),fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10);return isChrome&&(verOffset=nAgt.indexOf("Chrome"),fullVersion=nAgt.substring(verOffset+7)),(ix=fullVersion.indexOf(";"))!==-1&&(fullVersion=fullVersion.substring(0,ix)),(ix=fullVersion.indexOf(" "))!==-1&&(fullVersion=fullVersion.substring(0,ix)),majorVersion=parseInt(""+fullVersion,10),isNaN(majorVersion)&&(fullVersion=""+parseFloat(navigator.appVersion),majorVersion=parseInt(navigator.appVersion,10)),majorVersion>=49}function MediaRecorderWrapper(mediaStream){function isMediaStreamActive(){if("active"in mediaStream){if(!mediaStream.active)return!1}else if("ended"in mediaStream&&mediaStream.ended)return!1;return!0}var self=this;this.start=function(timeSlice,__disableLogs){if(this.timeSlice=timeSlice||5e3,self.mimeType||(self.mimeType="video/webm"),self.mimeType.indexOf("audio")!==-1&&mediaStream.getVideoTracks().length&&mediaStream.getAudioTracks().length){var stream;navigator.mozGetUserMedia?(stream=new MediaStream,stream.addTrack(mediaStream.getAudioTracks()[0])):stream=new MediaStream(mediaStream.getAudioTracks()),mediaStream=stream}self.mimeType.indexOf("audio")!==-1&&(self.mimeType=IsChrome?"audio/webm":"audio/ogg"),self.dontFireOnDataAvailableEvent=!1;var recorderHints={mimeType:self.mimeType};self.disableLogs||__disableLogs||console.log("Passing following params over MediaRecorder API.",recorderHints),mediaRecorder&&(mediaRecorder=null),IsChrome&&!isMediaRecorderCompatible()&&(recorderHints="video/vp8");try{mediaRecorder=new MediaRecorder(mediaStream,recorderHints)}catch(e){mediaRecorder=new MediaRecorder(mediaStream)}"canRecordMimeType"in mediaRecorder&&mediaRecorder.canRecordMimeType(self.mimeType)===!1&&(self.disableLogs||console.warn("MediaRecorder API seems unable to record mimeType:",self.mimeType)),self.ignoreMutedMedia===!0&&(mediaRecorder.ignoreMutedMedia=!0);var firedOnDataAvailableOnce=!1;mediaRecorder.ondataavailable=function(e){if(e.data&&e.data.size&&!(e.data.size<26800)&&!firedOnDataAvailableOnce){firedOnDataAvailableOnce=!0;var blob=self.getNativeBlob?e.data:new Blob([e.data],{type:self.mimeType||"video/webm"});self.ondataavailable(blob),mediaRecorder&&"recording"===mediaRecorder.state&&mediaRecorder.stop(),mediaRecorder=null,self.dontFireOnDataAvailableEvent||self.start(timeSlice,"__disableLogs")}},mediaRecorder.onerror=function(error){self.disableLogs||("InvalidState"===error.name?console.error("The MediaRecorder is not in a state in which the proposed operation is allowed to be executed."):"OutOfMemory"===error.name?console.error("The UA has exhaused the available memory. User agents SHOULD provide as much additional information as possible in the message attribute."):"IllegalStreamModification"===error.name?console.error("A modification to the stream has occurred that makes it impossible to continue recording. An example would be the addition of a Track while recording is occurring. User agents SHOULD provide as much additional information as possible in the message attribute."):"OtherRecordingError"===error.name?console.error("Used for an fatal error other than those listed above. User agents SHOULD provide as much additional information as possible in the message attribute."):"GenericError"===error.name?console.error("The UA cannot provide the codec or recording option that has been requested.",error):console.error("MediaRecorder Error",error)),mediaRecorder&&"inactive"!==mediaRecorder.state&&"stopped"!==mediaRecorder.state&&mediaRecorder.stop()};try{mediaRecorder.start(36e5)}catch(e){mediaRecorder=null}setTimeout(function(){mediaRecorder&&"recording"===mediaRecorder.state&&mediaRecorder.requestData()},timeSlice)},this.stop=function(callback){mediaRecorder&&"recording"===mediaRecorder.state&&(mediaRecorder.requestData(),setTimeout(function(){self.dontFireOnDataAvailableEvent=!0,mediaRecorder&&"recording"===mediaRecorder.state&&mediaRecorder.stop(),mediaRecorder=null,self.onstop()},2e3))},this.pause=function(){mediaRecorder&&("recording"===mediaRecorder.state&&mediaRecorder.pause(),this.dontFireOnDataAvailableEvent=!0)},this.ondataavailable=function(blob){console.log("recorded-blob",blob)},this.resume=function(){if(this.dontFireOnDataAvailableEvent){this.dontFireOnDataAvailableEvent=!1;var disableLogs=self.disableLogs;return self.disableLogs=!0,this.start(this.timeslice||5e3),void(self.disableLogs=disableLogs)}mediaRecorder&&"paused"===mediaRecorder.state&&mediaRecorder.resume()},this.clearRecordedData=function(){mediaRecorder&&(this.pause(),this.dontFireOnDataAvailableEvent=!0,this.stop())},this.onstop=function(){};var mediaRecorder;!function looper(){if(mediaRecorder)return isMediaStreamActive()===!1?void self.stop():void setTimeout(looper,1e3)}()}function StereoAudioRecorder(mediaStream){this.start=function(timeSlice){timeSlice=timeSlice||1e3,mediaRecorder=new StereoAudioRecorderHelper(mediaStream,this),mediaRecorder.record(),timeout=setInterval(function(){mediaRecorder.requestData()},timeSlice)},this.stop=function(){mediaRecorder&&(mediaRecorder.stop(),clearTimeout(timeout),this.onstop())},this.pause=function(){mediaRecorder&&mediaRecorder.pause()},this.resume=function(){mediaRecorder&&mediaRecorder.resume()},this.ondataavailable=function(){},this.onstop=function(){};var mediaRecorder,timeout}function StereoAudioRecorderHelper(mediaStream,root){function interleave(leftChannel,rightChannel){for(var length=leftChannel.length+rightChannel.length,result=new Float32Array(length),inputIndex=0,index=0;index-1,numChannels=root.audioChannels||2;this.record=function(){recording=!0,leftchannel.length=rightchannel.length=0,recordingLength=0},this.requestData=function(){if(!isPaused){if(0===recordingLength)return void(requestDataInvoked=!1);requestDataInvoked=!0;var internalLeftChannel=leftchannel.slice(0),internalRightChannel=rightchannel.slice(0),internalRecordingLength=recordingLength;leftchannel.length=rightchannel.length=[],recordingLength=0,requestDataInvoked=!1;var leftBuffer=mergeBuffers(internalLeftChannel,internalRecordingLength),interleaved=leftBuffer;if(2===numChannels){var rightBuffer=mergeBuffers(internalRightChannel,internalRecordingLength);interleaved=interleave(leftBuffer,rightBuffer)}if(isPCM){var blob=new Blob([convertoFloat32ToInt16(interleaved)],{type:"audio/pcm"});return console.debug("audio recorded blob size:",bytesToSize(blob.size)),void root.ondataavailable(blob)}var buffer=new ArrayBuffer(44+2*interleaved.length),view=new DataView(buffer);writeUTFBytes(view,0,"RIFF"),view.setUint32(4,44+2*interleaved.length-8,!0),writeUTFBytes(view,8,"WAVE"),writeUTFBytes(view,12,"fmt "),view.setUint32(16,16,!0),view.setUint16(20,1,!0),view.setUint16(22,numChannels,!0),view.setUint32(24,sampleRate,!0),view.setUint32(28,sampleRate*numChannels*2,!0),view.setUint16(32,2*numChannels,!0),view.setUint16(34,16,!0),writeUTFBytes(view,36,"data"),view.setUint32(40,2*interleaved.length,!0);for(var lng=interleaved.length,index=44,volume=1,i=0;i=0&&_pixTolerance<=1?_pixTolerance:0,frameTolerance=_frameTolerance&&_frameTolerance>=0&&_frameTolerance<=1?_frameTolerance:0,image=new Image;image.src=frame.image,context2d.drawImage(image,0,0,canvas.width,canvas.height);var imageData=context2d.getImageData(0,0,canvas.width,canvas.height);matchPixCount=0,endPixCheck=imageData.data.length,maxPixCount=imageData.data.length/4;for(var pix=0;pix0&&_framesToCheck<=_frames.length?_framesToCheck:_frames.length,sampleColor={r:0,g:0,b:0},maxColorDifference=Math.sqrt(Math.pow(255,2)+Math.pow(255,2)+Math.pow(255,2)),pixTolerance=_pixTolerance&&_pixTolerance>=0&&_pixTolerance<=1?_pixTolerance:0,frameTolerance=_frameTolerance&&_frameTolerance>=0&&_frameTolerance<=1?_frameTolerance:0,doNotCheckNext=!1,f=0;f0;)parts.push(255&num),num>>=8;return new Uint8Array(parts.reverse())}function strToBuffer(str){return new Uint8Array(str.split("").map(function(e){return e.charCodeAt(0)}))}function bitsToBuffer(bits){var data=[],pad=bits.length%8?new Array(9-bits.length%8).join("0"):"";bits=pad+bits;for(var i=0;i127)throw"TrackNumber > 127 not supported";var out=[128|data.trackNum,data.timecode>>8,255&data.timecode,flags].map(function(e){return String.fromCharCode(e)}).join("")+data.frame;return out}function parseWebP(riff){for(var VP8=riff.RIFF[0].WEBP[0],frameStart=VP8.indexOf("*"),i=0,c=[];i<4;i++)c[i]=VP8.charCodeAt(frameStart+3+i);var width,height,tmp;return tmp=c[1]<<8|c[0],width=16383&tmp,tmp=c[3]<<8|c[2],height=16383&tmp,{width:width,height:height,data:VP8,riff:riff}}function getStrLength(string,offset){return parseInt(string.substr(offset+4,4).split("").map(function(i){var unpadded=i.charCodeAt(0).toString(2);return new Array(8-unpadded.length+1).join("0")+unpadded}).join(""),2)}function parseRIFF(string){for(var offset=0,chunks={};offset { + this.startRecording() + } + }, + ] //Add buttons @@ -885,4 +894,41 @@ class CallWindow extends CustomEvents { } } + + /** + * Start / stop recording the streams + */ + startRecording() { + + const onDataAvailable = blob => { + console.info("New record available", blob) + + // = GET URL = const url = URL.createObjectURL(blob) + + // Save file + saveAs(blob, new Date().getTime() + ".webm") + } + + // Start recording + if(!this.recorder) { + // Determine the list of streams to save + const streams = [] + + if(this.mainStream) + streams.push(this.mainStream) + this.streamsEls.forEach(v => streams.push(v)) + + // Create & start recorder + this.recorder = new MultiStreamRecorder(streams); + this.recorder.ondataavailable = onDataAvailable + this.recorder.start(30*60*1000); // Ask for save every 30min + } + + + // Stop recording + else { + this.recorder.stop(onDataAvailable) + delete this.recorder + } + } } \ No newline at end of file diff --git a/system/config/dev.config.php b/system/config/dev.config.php index b4acaa6d..f2c087c9 100644 --- a/system/config/dev.config.php +++ b/system/config/dev.config.php @@ -165,6 +165,9 @@ class Dev { // Share screen "3rdparty/getScreenId.js", + + // Record MediaStream + "3rdparty/MediaStreamRecorder.min.js" ); /**