{"version":3,"sources":["../node_modules/recordrtc/RecordRTC.js"],"names":["RecordRTC","mediaStream","config","RecordRTCConfiguration","type","self","this","initRecorder","initCallback","Recorder","GetRecorderType","mediaRecorder","record","setState","disableLogs","console","log","constructor","name","stopRecording","callback","state","resumeRecording","setTimeout","warn","stop","_callback","warningLog","__blob","Object","keys","forEach","key","blob","bytesToSize","size","url","URL","createObjectURL","e","call","autoWriteToDisk","getDataURL","dataURL","parameter","DiskStorage","Store","readFile","_blob","postMessage","FileReaderSync","readAsDataURL","_mediaRecorder","Worker","navigator","mozGetUserMedia","reader","FileReader","onload","event","target","result","webWorker","_function","Blob","toString","worker","revokeObjectURL","processInWebWorker","onmessage","data","handleRecordingDuration","counter","recordingDuration","onRecordingStopped","onStateChanged","WARNING","returnObject","startRecording","config2","version","clearRecordedData","pauseRecording","pause","resume","setRecordingDuration","getBlob","toURL","getInternalRecorder","save","fileName","invokeSaveAsDialog","getFromDisk","setAdvertisementArray","arrayOfWebPImages","advertisement","length","i","push","duration","image","bufferSize","sampleRate","buffer","reset","getState","destroy","disableLogsCache","Storage","AudioContextConstructor","close","prop","recorderType","audio","video","WhammyRecorder","CanvasRecorder","WebAssemblyRecorder","GifRecorder","StereoAudioRecorder","MediaStreamRecorder","getTracks","MediaRecorder","prototype","mimeType","split","bitsPerSecond","recorder","isChrome","isEdge","isOpera","ReadableStream","isMediaRecorderCompatible","isTypeSupported","Array","MultiStreamRecorder","isSafari","MRecordRTC","addStream","_mediaStream","mediaType","gif","audioRecorder","numberOfAudioChannels","timeSlice","onTimeStamp","newStream","videoTrack","isFirefox","MediaStream","addTrack","videoRecorder","canvas","frameInterval","workerPath","webAssemblyPath","frameRate","bitrate","isSingleRecorder","gifRecorder","quality","blobURL","output","callback00","webkitURL","_audioDataURL","_videoDataURL","writeToDisk","args","module","exports","Fetch","_type","replace","options","audioDataURL","videoDataURL","gifDataURL","audioBlob","videoBlob","gifBlob","that","global","window","userAgent","getUserMedia","error","arguments","document","documentElement","appendChild","createElement","captureStream","mozCaptureStream","obj","getContext","play","drawImage","toDataURL","style","HTMLVideoElement","location","protocol","href","hash","screen","width","height","requestAnimationFrame","webkitRequestAnimationFrame","mozRequestAnimationFrame","msRequestAnimationFrame","lastTime","element","currTime","Date","getTime","timeToCall","Math","max","id","cancelAnimationFrame","webkitCancelAnimationFrame","mozCancelAnimationFrame","msCancelAnimationFrame","clearTimeout","AudioContext","webkitAudioContext","mozAudioContext","webkitGetUserMedia","indexOf","msSaveBlob","msSaveOrOpenBlob","opera","toLowerCase","test","isElectron","bytes","parseInt","floor","pow","toPrecision","file","fileExtension","splitted","fileFullName","round","random","hyperlink","download","body","click","dispatchEvent","MouseEvent","view","bubbles","cancelable","process","versions","electron","stream","kind","filter","t","setSrcObject","srcObject","mozSrcObject","webkitMediaStream","track","getSeekableBlob","inputBlob","EBML","Error","Reader","decoder","Decoder","tools","fileReader","decode","read","refinedMetadataBuf","makeMetadataSeekable","metadatas","cues","slice","metadataSize","newBlob","readAsArrayBuffer","appVersion","verOffset","ix","nAgt","fullVersion","parseFloat","majorVersion","substring","isNaN","arrayOfBlobs","updateTimeStamp","timestamps","getMimeType","secondObject","clearRecordedDataCB","getArrayOfBlobs","allStates","recorderHints","canRecordMimeType","ondataavailable","recordingCallback","getNativeBlob","onstart","onpause","onresume","onstop","onerror","looper","manuallyStopped","timeslice","start","getAllStates","checkForInactiveTracks","active","ended","isMediaStreamActive","jsAudioNode","leftchannel","rightchannel","recording","recordingLength","desiredSampRate","mergeLeftRightBuffers","mergeAudioBuffers","cb","interleaved","leftBuffers","rightBuffers","internalInterleavedLength","interpolateArray","newSampleRate","oldSampleRate","fitCount","newData","springFactor","Number","tmp","before","toFixed","after","ceil","atPoint","linearInterpolate","mergeBuffers","channelBuffer","rLength","Float64Array","offset","lng","set","writeUTFBytes","string","setUint8","charCodeAt","leftChannel","rightChannel","inputIndex","index","interleave","interleavedLength","ArrayBuffer","DataView","setUint32","setUint16","setInt16","noWorker","workerURL","terminate","resetVariables","isAudioProcessStarted","isPaused","byteLength","context","audioInput","createMediaStreamSource","legalBufferValues","JSON","stringify","createJavaScriptNode","createScriptProcessor","connect","intervalsBasedBuffers","left","right","onaudioprocess","disconnect","onAudioProcessStarted","inputBuffer","getChannelData","chLeft","Float32Array","chRight","createMediaStreamDestination","destination","htmlElement","html2canvas","isCanvasSupportsStreamCapturing","item","globalCanvas","mediaStreamRecorder","isRecording","_isChrome","webkitRTCPeerConnection","chrome","chromeVersion","matchArray","match","useWhammyRecorder","HTMLCanvasElement","CanvasRenderingContext2D","canvasMediaStream","webkitCaptureStream","mdStream","whammy","frames","drawCanvasFrame","getWebPImages","nodeName","framesLength","frame","idx","framesRemaining","onEncodingCallback","webp","compile","isPausedRecording","cloneCanvas","newCanvas","grabMouse","showMousePointer","onrendered","Whammy","Video","drawFrames","paused","isStopDrawing","dropBlackFrames","_frames","_framesToCheck","_pixTolerance","_frameTolerance","localCanvas","context2d","resultFrames","checkUntilNotBlack","endCheckFrame","sampleColor","maxColorDifference","sqrt","pixTolerance","frameTolerance","doNotCheckNext","o","loop","functionToLoop","asyncLoop","f","matchPixCount","endPixCheck","maxPixCount","finishImage","Image","imageData","getImageData","pix","currentColor","r","g","b","src","concat","cloneNode","onloadedmetadata","muted","_this","WhammyVideo","whammyInWebWorker","getClusterData","clusterTimecode","clusterCounter","clusterFrames","map","block","flags","keyframe","invisible","lacing","discardable","trackNum","timecode","String","fromCharCode","join","makeSimpleBlock","numToBuffer","num","parts","Uint8Array","reverse","bitsToBuffer","bits","substr","generateEBML","json","ebml","len","zeroes","sizeToString","padded","getStrLength","unpadded","parseRIFF","chunks","webm","info","checkFrames","frameNumber","clusterDuration","cluster","riff","VP8","RIFF","WEBP","frameStart","c","parseWebP","atob","add","init","indexedDB","open","db","dbName","request","onError","onsuccess","setVersion","createObjectStore","putInDB","onupgradeneeded","dataBase","dataStoreName","transaction","getFromStore","portionName","objectStore","get","put","GIFEncoder","script","isHTMLObject","isLoadedMetaData","offsetWidth","offsetHeight","gifEncoder","setRepeat","setDelay","setQuality","onGifRecordingStarted","now","lastAnimationFrame","drawVideoFrame","time","clearedRecordedData","undefined","lastFrameTime","onGifPreview","addFrame","bin","autoplay","playsInline","MultiStreamsMixer","arrayOfMediaStreams","elementClass","videos","isStopDrawingFrames","opacity","position","zIndex","top","className","useGainNode","drawVideosToCanvas","videosLength","fullcanvas","remaining","x","y","onRender","getVideo","volume","resetVideoStreams","streams","startDrawingFrames","appendStreams","audioSource","audioContext","audioDestination","releaseStreams","gainNode","audioSources","source","clearRect","getMixedStream","mixedVideoStream","capturedStream","videoStream","getMixedVideoStream","mixedAudioStream","createGain","gain","value","audioTracksLength","getMixedAudioStream","mixer","tracks","getAllVideoTracks","previewStream","addStreams","getMixer","finished","cameraStream","controller","cvs","first","onplaying","ctx","frameTimeout","cameraTimer","setInterval","clearInterval","onVideoProcessStarted","_controlledReadableStream","enqueue","fetch","then","arrayBuffer","addEventListener","timebaseDen","realtime","pipeTo","WritableStream","write","arrayOfBuffers","RecordRTCPromisesHandler","recordRTC","Promise","resolve","reject"],"mappings":"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","file":"static/js/25.9f0f0239.chunk.js","sourcesContent":["'use strict';\r\n\r\n// Last time updated: 2020-05-17 5:04:38 PM UTC\r\n\r\n// ________________\r\n// RecordRTC v5.6.1\r\n\r\n// Open-Sourced: https://github.com/muaz-khan/RecordRTC\r\n\r\n// --------------------------------------------------\r\n// Muaz Khan - www.MuazKhan.com\r\n// MIT License - www.WebRTC-Experiment.com/licence\r\n// --------------------------------------------------\r\n\r\n// ____________\r\n// RecordRTC.js\r\n\r\n/**\r\n * {@link https://github.com/muaz-khan/RecordRTC|RecordRTC} is a WebRTC JavaScript library for audio/video as well as screen activity recording. It supports Chrome, Firefox, Opera, Android, and Microsoft Edge. Platforms: Linux, Mac and Windows. \r\n * @summary Record audio, video or screen inside the browser.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef RecordRTC\r\n * @class\r\n * @example\r\n * var recorder = RecordRTC(mediaStream or [arrayOfMediaStream], {\r\n * type: 'video', // audio or video or gif or canvas\r\n * recorderType: MediaStreamRecorder || CanvasRecorder || StereoAudioRecorder || Etc\r\n * });\r\n * recorder.startRecording();\r\n * @see For further information:\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - Single media-stream object, array of media-streams, html-canvas-element, etc.\r\n * @param {object} config - {type:\"video\", recorderType: MediaStreamRecorder, disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, desiredSampRate: 16000, video: HTMLVideoElement, etc.}\r\n */\r\n\r\nfunction RecordRTC(mediaStream, config) {\r\n if (!mediaStream) {\r\n throw 'First parameter is required.';\r\n }\r\n\r\n config = config || {\r\n type: 'video'\r\n };\r\n\r\n config = new RecordRTCConfiguration(mediaStream, config);\r\n\r\n // a reference to user's recordRTC object\r\n var self = this;\r\n\r\n function startRecording(config2) {\r\n if (!config.disableLogs) {\r\n console.log('RecordRTC version: ', self.version);\r\n }\r\n\r\n if (!!config2) {\r\n // allow users to set options using startRecording method\r\n // config2 is similar to main \"config\" object (second parameter over RecordRTC constructor)\r\n config = new RecordRTCConfiguration(mediaStream, config2);\r\n }\r\n\r\n if (!config.disableLogs) {\r\n console.log('started recording ' + config.type + ' stream.');\r\n }\r\n\r\n if (mediaRecorder) {\r\n mediaRecorder.clearRecordedData();\r\n mediaRecorder.record();\r\n\r\n setState('recording');\r\n\r\n if (self.recordingDuration) {\r\n handleRecordingDuration();\r\n }\r\n return self;\r\n }\r\n\r\n initRecorder(function() {\r\n if (self.recordingDuration) {\r\n handleRecordingDuration();\r\n }\r\n });\r\n\r\n return self;\r\n }\r\n\r\n function initRecorder(initCallback) {\r\n if (initCallback) {\r\n config.initCallback = function() {\r\n initCallback();\r\n initCallback = config.initCallback = null; // recorder.initRecorder should be call-backed once.\r\n };\r\n }\r\n\r\n var Recorder = new GetRecorderType(mediaStream, config);\r\n\r\n mediaRecorder = new Recorder(mediaStream, config);\r\n mediaRecorder.record();\r\n\r\n setState('recording');\r\n\r\n if (!config.disableLogs) {\r\n console.log('Initialized recorderType:', mediaRecorder.constructor.name, 'for output-type:', config.type);\r\n }\r\n }\r\n\r\n function stopRecording(callback) {\r\n callback = callback || function() {};\r\n\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n if (self.state === 'paused') {\r\n self.resumeRecording();\r\n\r\n setTimeout(function() {\r\n stopRecording(callback);\r\n }, 1);\r\n return;\r\n }\r\n\r\n if (self.state !== 'recording' && !config.disableLogs) {\r\n console.warn('Recording state should be: \"recording\", however current state is: ', self.state);\r\n }\r\n\r\n if (!config.disableLogs) {\r\n console.log('Stopped recording ' + config.type + ' stream.');\r\n }\r\n\r\n if (config.type !== 'gif') {\r\n mediaRecorder.stop(_callback);\r\n } else {\r\n mediaRecorder.stop();\r\n _callback();\r\n }\r\n\r\n setState('stopped');\r\n\r\n function _callback(__blob) {\r\n if (!mediaRecorder) {\r\n if (typeof callback.call === 'function') {\r\n callback.call(self, '');\r\n } else {\r\n callback('');\r\n }\r\n return;\r\n }\r\n\r\n Object.keys(mediaRecorder).forEach(function(key) {\r\n if (typeof mediaRecorder[key] === 'function') {\r\n return;\r\n }\r\n\r\n self[key] = mediaRecorder[key];\r\n });\r\n\r\n var blob = mediaRecorder.blob;\r\n\r\n if (!blob) {\r\n if (__blob) {\r\n mediaRecorder.blob = blob = __blob;\r\n } else {\r\n throw 'Recording failed.';\r\n }\r\n }\r\n\r\n if (blob && !config.disableLogs) {\r\n console.log(blob.type, '->', bytesToSize(blob.size));\r\n }\r\n\r\n if (callback) {\r\n var url;\r\n\r\n try {\r\n url = URL.createObjectURL(blob);\r\n } catch (e) {}\r\n\r\n if (typeof callback.call === 'function') {\r\n callback.call(self, url);\r\n } else {\r\n callback(url);\r\n }\r\n }\r\n\r\n if (!config.autoWriteToDisk) {\r\n return;\r\n }\r\n\r\n getDataURL(function(dataURL) {\r\n var parameter = {};\r\n parameter[config.type + 'Blob'] = dataURL;\r\n DiskStorage.Store(parameter);\r\n });\r\n }\r\n }\r\n\r\n function pauseRecording() {\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n if (self.state !== 'recording') {\r\n if (!config.disableLogs) {\r\n console.warn('Unable to pause the recording. Recording state: ', self.state);\r\n }\r\n return;\r\n }\r\n\r\n setState('paused');\r\n\r\n mediaRecorder.pause();\r\n\r\n if (!config.disableLogs) {\r\n console.log('Paused recording.');\r\n }\r\n }\r\n\r\n function resumeRecording() {\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n if (self.state !== 'paused') {\r\n if (!config.disableLogs) {\r\n console.warn('Unable to resume the recording. Recording state: ', self.state);\r\n }\r\n return;\r\n }\r\n\r\n setState('recording');\r\n\r\n // not all libs have this method yet\r\n mediaRecorder.resume();\r\n\r\n if (!config.disableLogs) {\r\n console.log('Resumed recording.');\r\n }\r\n }\r\n\r\n function readFile(_blob) {\r\n postMessage(new FileReaderSync().readAsDataURL(_blob));\r\n }\r\n\r\n function getDataURL(callback, _mediaRecorder) {\r\n if (!callback) {\r\n throw 'Pass a callback function over getDataURL.';\r\n }\r\n\r\n var blob = _mediaRecorder ? _mediaRecorder.blob : (mediaRecorder || {}).blob;\r\n\r\n if (!blob) {\r\n if (!config.disableLogs) {\r\n console.warn('Blob encoder did not finish its job yet.');\r\n }\r\n\r\n setTimeout(function() {\r\n getDataURL(callback, _mediaRecorder);\r\n }, 1000);\r\n return;\r\n }\r\n\r\n if (typeof Worker !== 'undefined' && !navigator.mozGetUserMedia) {\r\n var webWorker = processInWebWorker(readFile);\r\n\r\n webWorker.onmessage = function(event) {\r\n callback(event.data);\r\n };\r\n\r\n webWorker.postMessage(blob);\r\n } else {\r\n var reader = new FileReader();\r\n reader.readAsDataURL(blob);\r\n reader.onload = function(event) {\r\n callback(event.target.result);\r\n };\r\n }\r\n\r\n function processInWebWorker(_function) {\r\n try {\r\n var blob = URL.createObjectURL(new Blob([_function.toString(),\r\n 'this.onmessage = function (eee) {' + _function.name + '(eee.data);}'\r\n ], {\r\n type: 'application/javascript'\r\n }));\r\n\r\n var worker = new Worker(blob);\r\n URL.revokeObjectURL(blob);\r\n return worker;\r\n } catch (e) {}\r\n }\r\n }\r\n\r\n function handleRecordingDuration(counter) {\r\n counter = counter || 0;\r\n\r\n if (self.state === 'paused') {\r\n setTimeout(function() {\r\n handleRecordingDuration(counter);\r\n }, 1000);\r\n return;\r\n }\r\n\r\n if (self.state === 'stopped') {\r\n return;\r\n }\r\n\r\n if (counter >= self.recordingDuration) {\r\n stopRecording(self.onRecordingStopped);\r\n return;\r\n }\r\n\r\n counter += 1000; // 1-second\r\n\r\n setTimeout(function() {\r\n handleRecordingDuration(counter);\r\n }, 1000);\r\n }\r\n\r\n function setState(state) {\r\n if (!self) {\r\n return;\r\n }\r\n\r\n self.state = state;\r\n\r\n if (typeof self.onStateChanged.call === 'function') {\r\n self.onStateChanged.call(self, state);\r\n } else {\r\n self.onStateChanged(state);\r\n }\r\n }\r\n\r\n var WARNING = 'It seems that recorder is destroyed or \"startRecording\" is not invoked for ' + config.type + ' recorder.';\r\n\r\n function warningLog() {\r\n if (config.disableLogs === true) {\r\n return;\r\n }\r\n\r\n console.warn(WARNING);\r\n }\r\n\r\n var mediaRecorder;\r\n\r\n var returnObject = {\r\n /**\r\n * This method starts the recording.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * var recorder = RecordRTC(mediaStream, {\r\n * type: 'video'\r\n * });\r\n * recorder.startRecording();\r\n */\r\n startRecording: startRecording,\r\n\r\n /**\r\n * This method stops the recording. It is strongly recommended to get \"blob\" or \"URI\" inside the callback to make sure all recorders finished their job.\r\n * @param {function} callback - Callback to get the recorded blob.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * // use either \"this\" or \"recorder\" object; both are identical\r\n * video.src = this.toURL();\r\n * var blob = this.getBlob();\r\n * });\r\n */\r\n stopRecording: stopRecording,\r\n\r\n /**\r\n * This method pauses the recording. You can resume recording using \"resumeRecording\" method.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @todo Firefox is unable to pause the recording. Fix it.\r\n * @example\r\n * recorder.pauseRecording(); // pause the recording\r\n * recorder.resumeRecording(); // resume again\r\n */\r\n pauseRecording: pauseRecording,\r\n\r\n /**\r\n * This method resumes the recording.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.pauseRecording(); // first of all, pause the recording\r\n * recorder.resumeRecording(); // now resume it\r\n */\r\n resumeRecording: resumeRecording,\r\n\r\n /**\r\n * This method initializes the recording.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @todo This method should be deprecated.\r\n * @example\r\n * recorder.initRecorder();\r\n */\r\n initRecorder: initRecorder,\r\n\r\n /**\r\n * Ask RecordRTC to auto-stop the recording after 5 minutes.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * var fiveMinutes = 5 * 1000 * 60;\r\n * recorder.setRecordingDuration(fiveMinutes, function() {\r\n * var blob = this.getBlob();\r\n * video.src = this.toURL();\r\n * });\r\n * \r\n * // or otherwise\r\n * recorder.setRecordingDuration(fiveMinutes).onRecordingStopped(function() {\r\n * var blob = this.getBlob();\r\n * video.src = this.toURL();\r\n * });\r\n */\r\n setRecordingDuration: function(recordingDuration, callback) {\r\n if (typeof recordingDuration === 'undefined') {\r\n throw 'recordingDuration is required.';\r\n }\r\n\r\n if (typeof recordingDuration !== 'number') {\r\n throw 'recordingDuration must be a number.';\r\n }\r\n\r\n self.recordingDuration = recordingDuration;\r\n self.onRecordingStopped = callback || function() {};\r\n\r\n return {\r\n onRecordingStopped: function(callback) {\r\n self.onRecordingStopped = callback;\r\n }\r\n };\r\n },\r\n\r\n /**\r\n * This method can be used to clear/reset all the recorded data.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @todo Figure out the difference between \"reset\" and \"clearRecordedData\" methods.\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n clearRecordedData: function() {\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n mediaRecorder.clearRecordedData();\r\n\r\n if (!config.disableLogs) {\r\n console.log('Cleared old recorded data.');\r\n }\r\n },\r\n\r\n /**\r\n * Get the recorded blob. Use this method inside the \"stopRecording\" callback.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * var blob = this.getBlob();\r\n *\r\n * var file = new File([blob], 'filename.webm', {\r\n * type: 'video/webm'\r\n * });\r\n *\r\n * var formData = new FormData();\r\n * formData.append('file', file); // upload \"File\" object rather than a \"Blob\"\r\n * uploadToServer(formData);\r\n * });\r\n * @returns {Blob} Returns recorded data as \"Blob\" object.\r\n */\r\n getBlob: function() {\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n return mediaRecorder.blob;\r\n },\r\n\r\n /**\r\n * Get data-URI instead of Blob.\r\n * @param {function} callback - Callback to get the Data-URI.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * recorder.getDataURL(function(dataURI) {\r\n * video.src = dataURI;\r\n * });\r\n * });\r\n */\r\n getDataURL: getDataURL,\r\n\r\n /**\r\n * Get virtual/temporary URL. Usage of this URL is limited to current tab.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * video.src = this.toURL();\r\n * });\r\n * @returns {String} Returns a virtual/temporary URL for the recorded \"Blob\".\r\n */\r\n toURL: function() {\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n return URL.createObjectURL(mediaRecorder.blob);\r\n },\r\n\r\n /**\r\n * Get internal recording object (i.e. internal module) e.g. MutliStreamRecorder, MediaStreamRecorder, StereoAudioRecorder or WhammyRecorder etc.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * var internalRecorder = recorder.getInternalRecorder();\r\n * if(internalRecorder instanceof MultiStreamRecorder) {\r\n * internalRecorder.addStreams([newAudioStream]);\r\n * internalRecorder.resetVideoStreams([screenStream]);\r\n * }\r\n * @returns {Object} Returns internal recording object.\r\n */\r\n getInternalRecorder: function() {\r\n return mediaRecorder;\r\n },\r\n\r\n /**\r\n * Invoke save-as dialog to save the recorded blob into your disk.\r\n * @param {string} fileName - Set your own file name.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * this.save('file-name');\r\n *\r\n * // or manually:\r\n * invokeSaveAsDialog(this.getBlob(), 'filename.webm');\r\n * });\r\n */\r\n save: function(fileName) {\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n invokeSaveAsDialog(mediaRecorder.blob, fileName);\r\n },\r\n\r\n /**\r\n * This method gets a blob from indexed-DB storage.\r\n * @param {function} callback - Callback to get the recorded blob.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.getFromDisk(function(dataURL) {\r\n * video.src = dataURL;\r\n * });\r\n */\r\n getFromDisk: function(callback) {\r\n if (!mediaRecorder) {\r\n warningLog();\r\n return;\r\n }\r\n\r\n RecordRTC.getFromDisk(config.type, callback);\r\n },\r\n\r\n /**\r\n * This method appends an array of webp images to the recorded video-blob. It takes an \"array\" object.\r\n * @type {Array.}\r\n * @param {Array} arrayOfWebPImages - Array of webp images.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @todo This method should be deprecated.\r\n * @example\r\n * var arrayOfWebPImages = [];\r\n * arrayOfWebPImages.push({\r\n * duration: index,\r\n * image: 'data:image/webp;base64,...'\r\n * });\r\n * recorder.setAdvertisementArray(arrayOfWebPImages);\r\n */\r\n setAdvertisementArray: function(arrayOfWebPImages) {\r\n config.advertisement = [];\r\n\r\n var length = arrayOfWebPImages.length;\r\n for (var i = 0; i < length; i++) {\r\n config.advertisement.push({\r\n duration: i,\r\n image: arrayOfWebPImages[i]\r\n });\r\n }\r\n },\r\n\r\n /**\r\n * It is equivalent to \"recorder.getBlob()\" method. Usage of \"getBlob\" is recommended, though.\r\n * @property {Blob} blob - Recorded Blob can be accessed using this property.\r\n * @memberof RecordRTC\r\n * @instance\r\n * @readonly\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * var blob = this.blob;\r\n *\r\n * // below one is recommended\r\n * var blob = this.getBlob();\r\n * });\r\n */\r\n blob: null,\r\n\r\n /**\r\n * This works only with {recorderType:StereoAudioRecorder}. Use this property on \"stopRecording\" to verify the encoder's sample-rates.\r\n * @property {number} bufferSize - Buffer-size used to encode the WAV container\r\n * @memberof RecordRTC\r\n * @instance\r\n * @readonly\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * alert('Recorder used this buffer-size: ' + this.bufferSize);\r\n * });\r\n */\r\n bufferSize: 0,\r\n\r\n /**\r\n * This works only with {recorderType:StereoAudioRecorder}. Use this property on \"stopRecording\" to verify the encoder's sample-rates.\r\n * @property {number} sampleRate - Sample-rates used to encode the WAV container\r\n * @memberof RecordRTC\r\n * @instance\r\n * @readonly\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * alert('Recorder used these sample-rates: ' + this.sampleRate);\r\n * });\r\n */\r\n sampleRate: 0,\r\n\r\n /**\r\n * {recorderType:StereoAudioRecorder} returns ArrayBuffer object.\r\n * @property {ArrayBuffer} buffer - Audio ArrayBuffer, supported only in Chrome.\r\n * @memberof RecordRTC\r\n * @instance\r\n * @readonly\r\n * @example\r\n * recorder.stopRecording(function() {\r\n * var arrayBuffer = this.buffer;\r\n * alert(arrayBuffer.byteLength);\r\n * });\r\n */\r\n buffer: null,\r\n\r\n /**\r\n * This method resets the recorder. So that you can reuse single recorder instance many times.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.reset();\r\n * recorder.startRecording();\r\n */\r\n reset: function() {\r\n if (self.state === 'recording' && !config.disableLogs) {\r\n console.warn('Stop an active recorder.');\r\n }\r\n\r\n if (mediaRecorder && typeof mediaRecorder.clearRecordedData === 'function') {\r\n mediaRecorder.clearRecordedData();\r\n }\r\n mediaRecorder = null;\r\n setState('inactive');\r\n self.blob = null;\r\n },\r\n\r\n /**\r\n * This method is called whenever recorder's state changes. Use this as an \"event\".\r\n * @property {String} state - A recorder's state can be: recording, paused, stopped or inactive.\r\n * @method\r\n * @memberof RecordRTC\r\n * @instance\r\n * @example\r\n * recorder.onStateChanged = function(state) {\r\n * console.log('Recorder state: ', state);\r\n * };\r\n */\r\n onStateChanged: function(state) {\r\n if (!config.disableLogs) {\r\n console.log('Recorder state changed:', state);\r\n }\r\n },\r\n\r\n /**\r\n * A recorder can have inactive, recording, paused or stopped states.\r\n * @property {String} state - A recorder's state can be: recording, paused, stopped or inactive.\r\n * @memberof RecordRTC\r\n * @static\r\n * @readonly\r\n * @example\r\n * // this looper function will keep you updated about the recorder's states.\r\n * (function looper() {\r\n * document.querySelector('h1').innerHTML = 'Recorder\\'s state is: ' + recorder.state;\r\n * if(recorder.state === 'stopped') return; // ignore+stop\r\n * setTimeout(looper, 1000); // update after every 3-seconds\r\n * })();\r\n * recorder.startRecording();\r\n */\r\n state: 'inactive',\r\n\r\n /**\r\n * Get recorder's readonly state.\r\n * @method\r\n * @memberof RecordRTC\r\n * @example\r\n * var state = recorder.getState();\r\n * @returns {String} Returns recording state.\r\n */\r\n getState: function() {\r\n return self.state;\r\n },\r\n\r\n /**\r\n * Destroy RecordRTC instance. Clear all recorders and objects.\r\n * @method\r\n * @memberof RecordRTC\r\n * @example\r\n * recorder.destroy();\r\n */\r\n destroy: function() {\r\n var disableLogsCache = config.disableLogs;\r\n\r\n config = {\r\n disableLogs: true\r\n };\r\n self.reset();\r\n setState('destroyed');\r\n returnObject = self = null;\r\n\r\n if (Storage.AudioContextConstructor) {\r\n Storage.AudioContextConstructor.close();\r\n Storage.AudioContextConstructor = null;\r\n }\r\n\r\n config.disableLogs = disableLogsCache;\r\n\r\n if (!config.disableLogs) {\r\n console.log('RecordRTC is destroyed.');\r\n }\r\n },\r\n\r\n /**\r\n * RecordRTC version number\r\n * @property {String} version - Release version number.\r\n * @memberof RecordRTC\r\n * @static\r\n * @readonly\r\n * @example\r\n * alert(recorder.version);\r\n */\r\n version: '5.6.1'\r\n };\r\n\r\n if (!this) {\r\n self = returnObject;\r\n return returnObject;\r\n }\r\n\r\n // if someone wants to use RecordRTC with the \"new\" keyword.\r\n for (var prop in returnObject) {\r\n this[prop] = returnObject[prop];\r\n }\r\n\r\n self = this;\r\n\r\n return returnObject;\r\n}\r\n\r\nRecordRTC.version = '5.6.1';\r\n\r\nif (typeof module !== 'undefined' /* && !!module.exports*/ ) {\r\n module.exports = RecordRTC;\r\n}\r\n\r\nif (typeof define === 'function' && define.amd) {\r\n define('RecordRTC', [], function() {\r\n return RecordRTC;\r\n });\r\n}\n\r\nRecordRTC.getFromDisk = function(type, callback) {\r\n if (!callback) {\r\n throw 'callback is mandatory.';\r\n }\r\n\r\n console.log('Getting recorded ' + (type === 'all' ? 'blobs' : type + ' blob ') + ' from disk!');\r\n DiskStorage.Fetch(function(dataURL, _type) {\r\n if (type !== 'all' && _type === type + 'Blob' && callback) {\r\n callback(dataURL);\r\n }\r\n\r\n if (type === 'all' && callback) {\r\n callback(dataURL, _type.replace('Blob', ''));\r\n }\r\n });\r\n};\r\n\r\n/**\r\n * This method can be used to store recorded blobs into IndexedDB storage.\r\n * @param {object} options - {audio: Blob, video: Blob, gif: Blob}\r\n * @method\r\n * @memberof RecordRTC\r\n * @example\r\n * RecordRTC.writeToDisk({\r\n * audio: audioBlob,\r\n * video: videoBlob,\r\n * gif : gifBlob\r\n * });\r\n */\r\nRecordRTC.writeToDisk = function(options) {\r\n console.log('Writing recorded blob(s) to disk!');\r\n options = options || {};\r\n if (options.audio && options.video && options.gif) {\r\n options.audio.getDataURL(function(audioDataURL) {\r\n options.video.getDataURL(function(videoDataURL) {\r\n options.gif.getDataURL(function(gifDataURL) {\r\n DiskStorage.Store({\r\n audioBlob: audioDataURL,\r\n videoBlob: videoDataURL,\r\n gifBlob: gifDataURL\r\n });\r\n });\r\n });\r\n });\r\n } else if (options.audio && options.video) {\r\n options.audio.getDataURL(function(audioDataURL) {\r\n options.video.getDataURL(function(videoDataURL) {\r\n DiskStorage.Store({\r\n audioBlob: audioDataURL,\r\n videoBlob: videoDataURL\r\n });\r\n });\r\n });\r\n } else if (options.audio && options.gif) {\r\n options.audio.getDataURL(function(audioDataURL) {\r\n options.gif.getDataURL(function(gifDataURL) {\r\n DiskStorage.Store({\r\n audioBlob: audioDataURL,\r\n gifBlob: gifDataURL\r\n });\r\n });\r\n });\r\n } else if (options.video && options.gif) {\r\n options.video.getDataURL(function(videoDataURL) {\r\n options.gif.getDataURL(function(gifDataURL) {\r\n DiskStorage.Store({\r\n videoBlob: videoDataURL,\r\n gifBlob: gifDataURL\r\n });\r\n });\r\n });\r\n } else if (options.audio) {\r\n options.audio.getDataURL(function(audioDataURL) {\r\n DiskStorage.Store({\r\n audioBlob: audioDataURL\r\n });\r\n });\r\n } else if (options.video) {\r\n options.video.getDataURL(function(videoDataURL) {\r\n DiskStorage.Store({\r\n videoBlob: videoDataURL\r\n });\r\n });\r\n } else if (options.gif) {\r\n options.gif.getDataURL(function(gifDataURL) {\r\n DiskStorage.Store({\r\n gifBlob: gifDataURL\r\n });\r\n });\r\n }\r\n};\n\r\n// __________________________\r\n// RecordRTC-Configuration.js\r\n\r\n/**\r\n * {@link RecordRTCConfiguration} is an inner/private helper for {@link RecordRTC}.\r\n * @summary It configures the 2nd parameter passed over {@link RecordRTC} and returns a valid \"config\" object.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef RecordRTCConfiguration\r\n * @class\r\n * @example\r\n * var options = RecordRTCConfiguration(mediaStream, options);\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.\r\n * @param {object} config - {type:\"video\", disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, getNativeBlob:true, etc.}\r\n */\r\n\r\nfunction RecordRTCConfiguration(mediaStream, config) {\r\n if (!config.recorderType && !config.type) {\r\n if (!!config.audio && !!config.video) {\r\n config.type = 'video';\r\n } else if (!!config.audio && !config.video) {\r\n config.type = 'audio';\r\n }\r\n }\r\n\r\n if (config.recorderType && !config.type) {\r\n if (config.recorderType === WhammyRecorder || config.recorderType === CanvasRecorder || (typeof WebAssemblyRecorder !== 'undefined' && config.recorderType === WebAssemblyRecorder)) {\r\n config.type = 'video';\r\n } else if (config.recorderType === GifRecorder) {\r\n config.type = 'gif';\r\n } else if (config.recorderType === StereoAudioRecorder) {\r\n config.type = 'audio';\r\n } else if (config.recorderType === MediaStreamRecorder) {\r\n if (getTracks(mediaStream, 'audio').length && getTracks(mediaStream, 'video').length) {\r\n config.type = 'video';\r\n } else if (!getTracks(mediaStream, 'audio').length && getTracks(mediaStream, 'video').length) {\r\n config.type = 'video';\r\n } else if (getTracks(mediaStream, 'audio').length && !getTracks(mediaStream, 'video').length) {\r\n config.type = 'audio';\r\n } else {\r\n // config.type = 'UnKnown';\r\n }\r\n }\r\n }\r\n\r\n if (typeof MediaStreamRecorder !== 'undefined' && typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {\r\n if (!config.mimeType) {\r\n config.mimeType = 'video/webm';\r\n }\r\n\r\n if (!config.type) {\r\n config.type = config.mimeType.split('/')[0];\r\n }\r\n\r\n if (!config.bitsPerSecond) {\r\n // config.bitsPerSecond = 128000;\r\n }\r\n }\r\n\r\n // consider default type=audio\r\n if (!config.type) {\r\n if (config.mimeType) {\r\n config.type = config.mimeType.split('/')[0];\r\n }\r\n if (!config.type) {\r\n config.type = 'audio';\r\n }\r\n }\r\n\r\n return config;\r\n}\n\r\n// __________________\r\n// GetRecorderType.js\r\n\r\n/**\r\n * {@link GetRecorderType} is an inner/private helper for {@link RecordRTC}.\r\n * @summary It returns best recorder-type available for your browser.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef GetRecorderType\r\n * @class\r\n * @example\r\n * var RecorderType = GetRecorderType(options);\r\n * var recorder = new RecorderType(options);\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.\r\n * @param {object} config - {type:\"video\", disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, etc.}\r\n */\r\n\r\nfunction GetRecorderType(mediaStream, config) {\r\n var recorder;\r\n\r\n // StereoAudioRecorder can work with all three: Edge, Firefox and Chrome\r\n // todo: detect if it is Edge, then auto use: StereoAudioRecorder\r\n if (isChrome || isEdge || isOpera) {\r\n // Media Stream Recording API has not been implemented in chrome yet;\r\n // That's why using WebAudio API to record stereo audio in WAV format\r\n recorder = StereoAudioRecorder;\r\n }\r\n\r\n if (typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype && !isChrome) {\r\n recorder = MediaStreamRecorder;\r\n }\r\n\r\n // video recorder (in WebM format)\r\n if (config.type === 'video' && (isChrome || isOpera)) {\r\n recorder = WhammyRecorder;\r\n\r\n if (typeof WebAssemblyRecorder !== 'undefined' && typeof ReadableStream !== 'undefined') {\r\n recorder = WebAssemblyRecorder;\r\n }\r\n }\r\n\r\n // video recorder (in Gif format)\r\n if (config.type === 'gif') {\r\n recorder = GifRecorder;\r\n }\r\n\r\n // html2canvas recording!\r\n if (config.type === 'canvas') {\r\n recorder = CanvasRecorder;\r\n }\r\n\r\n if (isMediaRecorderCompatible() && recorder !== CanvasRecorder && recorder !== GifRecorder && typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {\r\n if (getTracks(mediaStream, 'video').length || getTracks(mediaStream, 'audio').length) {\r\n // audio-only recording\r\n if (config.type === 'audio') {\r\n if (typeof MediaRecorder.isTypeSupported === 'function' && MediaRecorder.isTypeSupported('audio/webm')) {\r\n recorder = MediaStreamRecorder;\r\n }\r\n // else recorder = StereoAudioRecorder;\r\n } else {\r\n // video or screen tracks\r\n if (typeof MediaRecorder.isTypeSupported === 'function' && MediaRecorder.isTypeSupported('video/webm')) {\r\n recorder = MediaStreamRecorder;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (mediaStream instanceof Array && mediaStream.length) {\r\n recorder = MultiStreamRecorder;\r\n }\r\n\r\n if (config.recorderType) {\r\n recorder = config.recorderType;\r\n }\r\n\r\n if (!config.disableLogs && !!recorder && !!recorder.name) {\r\n console.log('Using recorderType:', recorder.name || recorder.constructor.name);\r\n }\r\n\r\n if (!recorder && isSafari) {\r\n recorder = MediaStreamRecorder;\r\n }\r\n\r\n return recorder;\r\n}\n\r\n// _____________\r\n// MRecordRTC.js\r\n\r\n/**\r\n * MRecordRTC runs on top of {@link RecordRTC} to bring multiple recordings in a single place, by providing simple API.\r\n * @summary MRecordRTC stands for \"Multiple-RecordRTC\".\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef MRecordRTC\r\n * @class\r\n * @example\r\n * var recorder = new MRecordRTC();\r\n * recorder.addStream(MediaStream);\r\n * recorder.mediaType = {\r\n * audio: true, // or StereoAudioRecorder or MediaStreamRecorder\r\n * video: true, // or WhammyRecorder or MediaStreamRecorder or WebAssemblyRecorder or CanvasRecorder\r\n * gif: true // or GifRecorder\r\n * };\r\n * // mimeType is optional and should be set only in advance cases.\r\n * recorder.mimeType = {\r\n * audio: 'audio/wav',\r\n * video: 'video/webm',\r\n * gif: 'image/gif'\r\n * };\r\n * recorder.startRecording();\r\n * @see For further information:\r\n * @see {@link https://github.com/muaz-khan/RecordRTC/tree/master/MRecordRTC|MRecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.\r\n * @requires {@link RecordRTC}\r\n */\r\n\r\nfunction MRecordRTC(mediaStream) {\r\n\r\n /**\r\n * This method attaches MediaStream object to {@link MRecordRTC}.\r\n * @param {MediaStream} mediaStream - A MediaStream object, either fetched using getUserMedia API, or generated using captureStreamUntilEnded or WebAudio API.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.addStream(MediaStream);\r\n */\r\n this.addStream = function(_mediaStream) {\r\n if (_mediaStream) {\r\n mediaStream = _mediaStream;\r\n }\r\n };\r\n\r\n /**\r\n * This property can be used to set the recording type e.g. audio, or video, or gif, or canvas.\r\n * @property {object} mediaType - {audio: true, video: true, gif: true}\r\n * @memberof MRecordRTC\r\n * @example\r\n * var recorder = new MRecordRTC();\r\n * recorder.mediaType = {\r\n * audio: true, // TRUE or StereoAudioRecorder or MediaStreamRecorder\r\n * video: true, // TRUE or WhammyRecorder or MediaStreamRecorder or WebAssemblyRecorder or CanvasRecorder\r\n * gif : true // TRUE or GifRecorder\r\n * };\r\n */\r\n this.mediaType = {\r\n audio: true,\r\n video: true\r\n };\r\n\r\n /**\r\n * This method starts recording.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.startRecording();\r\n */\r\n this.startRecording = function() {\r\n var mediaType = this.mediaType;\r\n var recorderType;\r\n var mimeType = this.mimeType || {\r\n audio: null,\r\n video: null,\r\n gif: null\r\n };\r\n\r\n if (typeof mediaType.audio !== 'function' && isMediaRecorderCompatible() && !getTracks(mediaStream, 'audio').length) {\r\n mediaType.audio = false;\r\n }\r\n\r\n if (typeof mediaType.video !== 'function' && isMediaRecorderCompatible() && !getTracks(mediaStream, 'video').length) {\r\n mediaType.video = false;\r\n }\r\n\r\n if (typeof mediaType.gif !== 'function' && isMediaRecorderCompatible() && !getTracks(mediaStream, 'video').length) {\r\n mediaType.gif = false;\r\n }\r\n\r\n if (!mediaType.audio && !mediaType.video && !mediaType.gif) {\r\n throw 'MediaStream must have either audio or video tracks.';\r\n }\r\n\r\n if (!!mediaType.audio) {\r\n recorderType = null;\r\n if (typeof mediaType.audio === 'function') {\r\n recorderType = mediaType.audio;\r\n }\r\n\r\n this.audioRecorder = new RecordRTC(mediaStream, {\r\n type: 'audio',\r\n bufferSize: this.bufferSize,\r\n sampleRate: this.sampleRate,\r\n numberOfAudioChannels: this.numberOfAudioChannels || 2,\r\n disableLogs: this.disableLogs,\r\n recorderType: recorderType,\r\n mimeType: mimeType.audio,\r\n timeSlice: this.timeSlice,\r\n onTimeStamp: this.onTimeStamp\r\n });\r\n\r\n if (!mediaType.video) {\r\n this.audioRecorder.startRecording();\r\n }\r\n }\r\n\r\n if (!!mediaType.video) {\r\n recorderType = null;\r\n if (typeof mediaType.video === 'function') {\r\n recorderType = mediaType.video;\r\n }\r\n\r\n var newStream = mediaStream;\r\n\r\n if (isMediaRecorderCompatible() && !!mediaType.audio && typeof mediaType.audio === 'function') {\r\n var videoTrack = getTracks(mediaStream, 'video')[0];\r\n\r\n if (isFirefox) {\r\n newStream = new MediaStream();\r\n newStream.addTrack(videoTrack);\r\n\r\n if (recorderType && recorderType === WhammyRecorder) {\r\n // Firefox does NOT supports webp-encoding yet\r\n // But Firefox do supports WebAssemblyRecorder\r\n recorderType = MediaStreamRecorder;\r\n }\r\n } else {\r\n newStream = new MediaStream();\r\n newStream.addTrack(videoTrack);\r\n }\r\n }\r\n\r\n this.videoRecorder = new RecordRTC(newStream, {\r\n type: 'video',\r\n video: this.video,\r\n canvas: this.canvas,\r\n frameInterval: this.frameInterval || 10,\r\n disableLogs: this.disableLogs,\r\n recorderType: recorderType,\r\n mimeType: mimeType.video,\r\n timeSlice: this.timeSlice,\r\n onTimeStamp: this.onTimeStamp,\r\n workerPath: this.workerPath,\r\n webAssemblyPath: this.webAssemblyPath,\r\n frameRate: this.frameRate, // used by WebAssemblyRecorder; values: usually 30; accepts any.\r\n bitrate: this.bitrate // used by WebAssemblyRecorder; values: 0 to 1000+\r\n });\r\n\r\n if (!mediaType.audio) {\r\n this.videoRecorder.startRecording();\r\n }\r\n }\r\n\r\n if (!!mediaType.audio && !!mediaType.video) {\r\n var self = this;\r\n\r\n var isSingleRecorder = isMediaRecorderCompatible() === true;\r\n\r\n if (mediaType.audio instanceof StereoAudioRecorder && !!mediaType.video) {\r\n isSingleRecorder = false;\r\n } else if (mediaType.audio !== true && mediaType.video !== true && mediaType.audio !== mediaType.video) {\r\n isSingleRecorder = false;\r\n }\r\n\r\n if (isSingleRecorder === true) {\r\n self.audioRecorder = null;\r\n self.videoRecorder.startRecording();\r\n } else {\r\n self.videoRecorder.initRecorder(function() {\r\n self.audioRecorder.initRecorder(function() {\r\n // Both recorders are ready to record things accurately\r\n self.videoRecorder.startRecording();\r\n self.audioRecorder.startRecording();\r\n });\r\n });\r\n }\r\n }\r\n\r\n if (!!mediaType.gif) {\r\n recorderType = null;\r\n if (typeof mediaType.gif === 'function') {\r\n recorderType = mediaType.gif;\r\n }\r\n this.gifRecorder = new RecordRTC(mediaStream, {\r\n type: 'gif',\r\n frameRate: this.frameRate || 200,\r\n quality: this.quality || 10,\r\n disableLogs: this.disableLogs,\r\n recorderType: recorderType,\r\n mimeType: mimeType.gif\r\n });\r\n this.gifRecorder.startRecording();\r\n }\r\n };\r\n\r\n /**\r\n * This method stops recording.\r\n * @param {function} callback - Callback function is invoked when all encoders finished their jobs.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.stopRecording(function(recording){\r\n * var audioBlob = recording.audio;\r\n * var videoBlob = recording.video;\r\n * var gifBlob = recording.gif;\r\n * });\r\n */\r\n this.stopRecording = function(callback) {\r\n callback = callback || function() {};\r\n\r\n if (this.audioRecorder) {\r\n this.audioRecorder.stopRecording(function(blobURL) {\r\n callback(blobURL, 'audio');\r\n });\r\n }\r\n\r\n if (this.videoRecorder) {\r\n this.videoRecorder.stopRecording(function(blobURL) {\r\n callback(blobURL, 'video');\r\n });\r\n }\r\n\r\n if (this.gifRecorder) {\r\n this.gifRecorder.stopRecording(function(blobURL) {\r\n callback(blobURL, 'gif');\r\n });\r\n }\r\n };\r\n\r\n /**\r\n * This method pauses recording.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.pauseRecording();\r\n */\r\n this.pauseRecording = function() {\r\n if (this.audioRecorder) {\r\n this.audioRecorder.pauseRecording();\r\n }\r\n\r\n if (this.videoRecorder) {\r\n this.videoRecorder.pauseRecording();\r\n }\r\n\r\n if (this.gifRecorder) {\r\n this.gifRecorder.pauseRecording();\r\n }\r\n };\r\n\r\n /**\r\n * This method resumes recording.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.resumeRecording();\r\n */\r\n this.resumeRecording = function() {\r\n if (this.audioRecorder) {\r\n this.audioRecorder.resumeRecording();\r\n }\r\n\r\n if (this.videoRecorder) {\r\n this.videoRecorder.resumeRecording();\r\n }\r\n\r\n if (this.gifRecorder) {\r\n this.gifRecorder.resumeRecording();\r\n }\r\n };\r\n\r\n /**\r\n * This method can be used to manually get all recorded blobs.\r\n * @param {function} callback - All recorded blobs are passed back to the \"callback\" function.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.getBlob(function(recording){\r\n * var audioBlob = recording.audio;\r\n * var videoBlob = recording.video;\r\n * var gifBlob = recording.gif;\r\n * });\r\n * // or\r\n * var audioBlob = recorder.getBlob().audio;\r\n * var videoBlob = recorder.getBlob().video;\r\n */\r\n this.getBlob = function(callback) {\r\n var output = {};\r\n\r\n if (this.audioRecorder) {\r\n output.audio = this.audioRecorder.getBlob();\r\n }\r\n\r\n if (this.videoRecorder) {\r\n output.video = this.videoRecorder.getBlob();\r\n }\r\n\r\n if (this.gifRecorder) {\r\n output.gif = this.gifRecorder.getBlob();\r\n }\r\n\r\n if (callback) {\r\n callback(output);\r\n }\r\n\r\n return output;\r\n };\r\n\r\n /**\r\n * Destroy all recorder instances.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.destroy();\r\n */\r\n this.destroy = function() {\r\n if (this.audioRecorder) {\r\n this.audioRecorder.destroy();\r\n this.audioRecorder = null;\r\n }\r\n\r\n if (this.videoRecorder) {\r\n this.videoRecorder.destroy();\r\n this.videoRecorder = null;\r\n }\r\n\r\n if (this.gifRecorder) {\r\n this.gifRecorder.destroy();\r\n this.gifRecorder = null;\r\n }\r\n };\r\n\r\n /**\r\n * This method can be used to manually get all recorded blobs' DataURLs.\r\n * @param {function} callback - All recorded blobs' DataURLs are passed back to the \"callback\" function.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.getDataURL(function(recording){\r\n * var audioDataURL = recording.audio;\r\n * var videoDataURL = recording.video;\r\n * var gifDataURL = recording.gif;\r\n * });\r\n */\r\n this.getDataURL = function(callback) {\r\n this.getBlob(function(blob) {\r\n if (blob.audio && blob.video) {\r\n getDataURL(blob.audio, function(_audioDataURL) {\r\n getDataURL(blob.video, function(_videoDataURL) {\r\n callback({\r\n audio: _audioDataURL,\r\n video: _videoDataURL\r\n });\r\n });\r\n });\r\n } else if (blob.audio) {\r\n getDataURL(blob.audio, function(_audioDataURL) {\r\n callback({\r\n audio: _audioDataURL\r\n });\r\n });\r\n } else if (blob.video) {\r\n getDataURL(blob.video, function(_videoDataURL) {\r\n callback({\r\n video: _videoDataURL\r\n });\r\n });\r\n }\r\n });\r\n\r\n function getDataURL(blob, callback00) {\r\n if (typeof Worker !== 'undefined') {\r\n var webWorker = processInWebWorker(function readFile(_blob) {\r\n postMessage(new FileReaderSync().readAsDataURL(_blob));\r\n });\r\n\r\n webWorker.onmessage = function(event) {\r\n callback00(event.data);\r\n };\r\n\r\n webWorker.postMessage(blob);\r\n } else {\r\n var reader = new FileReader();\r\n reader.readAsDataURL(blob);\r\n reader.onload = function(event) {\r\n callback00(event.target.result);\r\n };\r\n }\r\n }\r\n\r\n function processInWebWorker(_function) {\r\n var blob = URL.createObjectURL(new Blob([_function.toString(),\r\n 'this.onmessage = function (eee) {' + _function.name + '(eee.data);}'\r\n ], {\r\n type: 'application/javascript'\r\n }));\r\n\r\n var worker = new Worker(blob);\r\n var url;\r\n if (typeof URL !== 'undefined') {\r\n url = URL;\r\n } else if (typeof webkitURL !== 'undefined') {\r\n url = webkitURL;\r\n } else {\r\n throw 'Neither URL nor webkitURL detected.';\r\n }\r\n url.revokeObjectURL(blob);\r\n return worker;\r\n }\r\n };\r\n\r\n /**\r\n * This method can be used to ask {@link MRecordRTC} to write all recorded blobs into IndexedDB storage.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.writeToDisk();\r\n */\r\n this.writeToDisk = function() {\r\n RecordRTC.writeToDisk({\r\n audio: this.audioRecorder,\r\n video: this.videoRecorder,\r\n gif: this.gifRecorder\r\n });\r\n };\r\n\r\n /**\r\n * This method can be used to invoke a save-as dialog for all recorded blobs.\r\n * @param {object} args - {audio: 'audio-name', video: 'video-name', gif: 'gif-name'}\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * recorder.save({\r\n * audio: 'audio-file-name',\r\n * video: 'video-file-name',\r\n * gif : 'gif-file-name'\r\n * });\r\n */\r\n this.save = function(args) {\r\n args = args || {\r\n audio: true,\r\n video: true,\r\n gif: true\r\n };\r\n\r\n if (!!args.audio && this.audioRecorder) {\r\n this.audioRecorder.save(typeof args.audio === 'string' ? args.audio : '');\r\n }\r\n\r\n if (!!args.video && this.videoRecorder) {\r\n this.videoRecorder.save(typeof args.video === 'string' ? args.video : '');\r\n }\r\n if (!!args.gif && this.gifRecorder) {\r\n this.gifRecorder.save(typeof args.gif === 'string' ? args.gif : '');\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * This method can be used to get all recorded blobs from IndexedDB storage.\r\n * @param {string} type - 'all' or 'audio' or 'video' or 'gif'\r\n * @param {function} callback - Callback function to get all stored blobs.\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * MRecordRTC.getFromDisk('all', function(dataURL, type){\r\n * if(type === 'audio') { }\r\n * if(type === 'video') { }\r\n * if(type === 'gif') { }\r\n * });\r\n */\r\nMRecordRTC.getFromDisk = RecordRTC.getFromDisk;\r\n\r\n/**\r\n * This method can be used to store recorded blobs into IndexedDB storage.\r\n * @param {object} options - {audio: Blob, video: Blob, gif: Blob}\r\n * @method\r\n * @memberof MRecordRTC\r\n * @example\r\n * MRecordRTC.writeToDisk({\r\n * audio: audioBlob,\r\n * video: videoBlob,\r\n * gif : gifBlob\r\n * });\r\n */\r\nMRecordRTC.writeToDisk = RecordRTC.writeToDisk;\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.MRecordRTC = MRecordRTC;\r\n}\n\r\nvar browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';\r\n\r\n(function(that) {\r\n if (!that) {\r\n return;\r\n }\r\n\r\n if (typeof window !== 'undefined') {\r\n return;\r\n }\r\n\r\n if (typeof global === 'undefined') {\r\n return;\r\n }\r\n\r\n global.navigator = {\r\n userAgent: browserFakeUserAgent,\r\n getUserMedia: function() {}\r\n };\r\n\r\n if (!global.console) {\r\n global.console = {};\r\n }\r\n\r\n if (typeof global.console.log === 'undefined' || typeof global.console.error === 'undefined') {\r\n global.console.error = global.console.log = global.console.log || function() {\r\n console.log(arguments);\r\n };\r\n }\r\n\r\n if (typeof document === 'undefined') {\r\n /*global document:true */\r\n that.document = {\r\n documentElement: {\r\n appendChild: function() {\r\n return '';\r\n }\r\n }\r\n };\r\n\r\n document.createElement = document.captureStream = document.mozCaptureStream = function() {\r\n var obj = {\r\n getContext: function() {\r\n return obj;\r\n },\r\n play: function() {},\r\n pause: function() {},\r\n drawImage: function() {},\r\n toDataURL: function() {\r\n return '';\r\n },\r\n style: {}\r\n };\r\n return obj;\r\n };\r\n\r\n that.HTMLVideoElement = function() {};\r\n }\r\n\r\n if (typeof location === 'undefined') {\r\n /*global location:true */\r\n that.location = {\r\n protocol: 'file:',\r\n href: '',\r\n hash: ''\r\n };\r\n }\r\n\r\n if (typeof screen === 'undefined') {\r\n /*global screen:true */\r\n that.screen = {\r\n width: 0,\r\n height: 0\r\n };\r\n }\r\n\r\n if (typeof URL === 'undefined') {\r\n /*global screen:true */\r\n that.URL = {\r\n createObjectURL: function() {\r\n return '';\r\n },\r\n revokeObjectURL: function() {\r\n return '';\r\n }\r\n };\r\n }\r\n\r\n /*global window:true */\r\n that.window = global;\r\n})(typeof global !== 'undefined' ? global : null);\n\r\n// _____________________________\r\n// Cross-Browser-Declarations.js\r\n\r\n// animation-frame used in WebM recording\r\n\r\n/*jshint -W079 */\r\nvar requestAnimationFrame = window.requestAnimationFrame;\r\nif (typeof requestAnimationFrame === 'undefined') {\r\n if (typeof webkitRequestAnimationFrame !== 'undefined') {\r\n /*global requestAnimationFrame:true */\r\n requestAnimationFrame = webkitRequestAnimationFrame;\r\n } else if (typeof mozRequestAnimationFrame !== 'undefined') {\r\n /*global requestAnimationFrame:true */\r\n requestAnimationFrame = mozRequestAnimationFrame;\r\n } else if (typeof msRequestAnimationFrame !== 'undefined') {\r\n /*global requestAnimationFrame:true */\r\n requestAnimationFrame = msRequestAnimationFrame;\r\n } else if (typeof requestAnimationFrame === 'undefined') {\r\n // via: https://gist.github.com/paulirish/1579671\r\n var lastTime = 0;\r\n\r\n /*global requestAnimationFrame:true */\r\n requestAnimationFrame = function(callback, element) {\r\n var currTime = new Date().getTime();\r\n var timeToCall = Math.max(0, 16 - (currTime - lastTime));\r\n var id = setTimeout(function() {\r\n callback(currTime + timeToCall);\r\n }, timeToCall);\r\n lastTime = currTime + timeToCall;\r\n return id;\r\n };\r\n }\r\n}\r\n\r\n/*jshint -W079 */\r\nvar cancelAnimationFrame = window.cancelAnimationFrame;\r\nif (typeof cancelAnimationFrame === 'undefined') {\r\n if (typeof webkitCancelAnimationFrame !== 'undefined') {\r\n /*global cancelAnimationFrame:true */\r\n cancelAnimationFrame = webkitCancelAnimationFrame;\r\n } else if (typeof mozCancelAnimationFrame !== 'undefined') {\r\n /*global cancelAnimationFrame:true */\r\n cancelAnimationFrame = mozCancelAnimationFrame;\r\n } else if (typeof msCancelAnimationFrame !== 'undefined') {\r\n /*global cancelAnimationFrame:true */\r\n cancelAnimationFrame = msCancelAnimationFrame;\r\n } else if (typeof cancelAnimationFrame === 'undefined') {\r\n /*global cancelAnimationFrame:true */\r\n cancelAnimationFrame = function(id) {\r\n clearTimeout(id);\r\n };\r\n }\r\n}\r\n\r\n// WebAudio API representer\r\nvar AudioContext = window.AudioContext;\r\n\r\nif (typeof AudioContext === 'undefined') {\r\n if (typeof webkitAudioContext !== 'undefined') {\r\n /*global AudioContext:true */\r\n AudioContext = webkitAudioContext;\r\n }\r\n\r\n if (typeof mozAudioContext !== 'undefined') {\r\n /*global AudioContext:true */\r\n AudioContext = mozAudioContext;\r\n }\r\n}\r\n\r\n/*jshint -W079 */\r\nvar URL = window.URL;\r\n\r\nif (typeof URL === 'undefined' && typeof webkitURL !== 'undefined') {\r\n /*global URL:true */\r\n URL = webkitURL;\r\n}\r\n\r\nif (typeof navigator !== 'undefined' && typeof navigator.getUserMedia === 'undefined') { // maybe window.navigator?\r\n if (typeof navigator.webkitGetUserMedia !== 'undefined') {\r\n navigator.getUserMedia = navigator.webkitGetUserMedia;\r\n }\r\n\r\n if (typeof navigator.mozGetUserMedia !== 'undefined') {\r\n navigator.getUserMedia = navigator.mozGetUserMedia;\r\n }\r\n}\r\n\r\nvar isEdge = navigator.userAgent.indexOf('Edge') !== -1 && (!!navigator.msSaveBlob || !!navigator.msSaveOrOpenBlob);\r\nvar isOpera = !!window.opera || navigator.userAgent.indexOf('OPR/') !== -1;\r\nvar isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 && ('netscape' in window) && / rv:/.test(navigator.userAgent);\r\nvar isChrome = (!isOpera && !isEdge && !!navigator.webkitGetUserMedia) || isElectron() || navigator.userAgent.toLowerCase().indexOf('chrome/') !== -1;\r\n\r\nvar isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);\r\n\r\nif (isSafari && !isChrome && navigator.userAgent.indexOf('CriOS') !== -1) {\r\n isSafari = false;\r\n isChrome = true;\r\n}\r\n\r\nvar MediaStream = window.MediaStream;\r\n\r\nif (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {\r\n MediaStream = webkitMediaStream;\r\n}\r\n\r\n/*global MediaStream:true */\r\nif (typeof MediaStream !== 'undefined') {\r\n // override \"stop\" method for all browsers\r\n if (typeof MediaStream.prototype.stop === 'undefined') {\r\n MediaStream.prototype.stop = function() {\r\n this.getTracks().forEach(function(track) {\r\n track.stop();\r\n });\r\n };\r\n }\r\n}\r\n\r\n// below function via: http://goo.gl/B3ae8c\r\n/**\r\n * Return human-readable file size.\r\n * @param {number} bytes - Pass bytes and get formatted string.\r\n * @returns {string} - formatted string\r\n * @example\r\n * bytesToSize(1024*1024*5) === '5 GB'\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n */\r\nfunction bytesToSize(bytes) {\r\n var k = 1000;\r\n var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];\r\n if (bytes === 0) {\r\n return '0 Bytes';\r\n }\r\n var i = parseInt(Math.floor(Math.log(bytes) / Math.log(k)), 10);\r\n return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];\r\n}\r\n\r\n/**\r\n * @param {Blob} file - File or Blob object. This parameter is required.\r\n * @param {string} fileName - Optional file name e.g. \"Recorded-Video.webm\"\r\n * @example\r\n * invokeSaveAsDialog(blob or file, [optional] fileName);\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n */\r\nfunction invokeSaveAsDialog(file, fileName) {\r\n if (!file) {\r\n throw 'Blob object is required.';\r\n }\r\n\r\n if (!file.type) {\r\n try {\r\n file.type = 'video/webm';\r\n } catch (e) {}\r\n }\r\n\r\n var fileExtension = (file.type || 'video/webm').split('/')[1];\r\n\r\n if (fileName && fileName.indexOf('.') !== -1) {\r\n var splitted = fileName.split('.');\r\n fileName = splitted[0];\r\n fileExtension = splitted[1];\r\n }\r\n\r\n var fileFullName = (fileName || (Math.round(Math.random() * 9999999999) + 888888888)) + '.' + fileExtension;\r\n\r\n if (typeof navigator.msSaveOrOpenBlob !== 'undefined') {\r\n return navigator.msSaveOrOpenBlob(file, fileFullName);\r\n } else if (typeof navigator.msSaveBlob !== 'undefined') {\r\n return navigator.msSaveBlob(file, fileFullName);\r\n }\r\n\r\n var hyperlink = document.createElement('a');\r\n hyperlink.href = URL.createObjectURL(file);\r\n hyperlink.download = fileFullName;\r\n\r\n hyperlink.style = 'display:none;opacity:0;color:transparent;';\r\n (document.body || document.documentElement).appendChild(hyperlink);\r\n\r\n if (typeof hyperlink.click === 'function') {\r\n hyperlink.click();\r\n } else {\r\n hyperlink.target = '_blank';\r\n hyperlink.dispatchEvent(new MouseEvent('click', {\r\n view: window,\r\n bubbles: true,\r\n cancelable: true\r\n }));\r\n }\r\n\r\n URL.revokeObjectURL(hyperlink.href);\r\n}\r\n\r\n/**\r\n * from: https://github.com/cheton/is-electron/blob/master/index.js\r\n **/\r\nfunction isElectron() {\r\n // Renderer process\r\n if (typeof window !== 'undefined' && typeof window.process === 'object' && window.process.type === 'renderer') {\r\n return true;\r\n }\r\n\r\n // Main process\r\n if (typeof process !== 'undefined' && typeof process.versions === 'object' && !!process.versions.electron) {\r\n return true;\r\n }\r\n\r\n // Detect the user agent when the `nodeIntegration` option is set to true\r\n if (typeof navigator === 'object' && typeof navigator.userAgent === 'string' && navigator.userAgent.indexOf('Electron') >= 0) {\r\n return true;\r\n }\r\n\r\n return false;\r\n}\r\n\r\nfunction getTracks(stream, kind) {\r\n if (!stream || !stream.getTracks) {\r\n return [];\r\n }\r\n\r\n return stream.getTracks().filter(function(t) {\r\n return t.kind === (kind || 'audio');\r\n });\r\n}\r\n\r\nfunction setSrcObject(stream, element) {\r\n if ('srcObject' in element) {\r\n element.srcObject = stream;\r\n } else if ('mozSrcObject' in element) {\r\n element.mozSrcObject = stream;\r\n } else {\r\n element.srcObject = stream;\r\n }\r\n}\r\n\r\n/**\r\n * @param {Blob} file - File or Blob object.\r\n * @param {function} callback - Callback function.\r\n * @example\r\n * getSeekableBlob(blob or file, callback);\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n */\r\nfunction getSeekableBlob(inputBlob, callback) {\r\n // EBML.js copyrights goes to: https://github.com/legokichi/ts-ebml\r\n if (typeof EBML === 'undefined') {\r\n throw new Error('Please link: https://www.webrtc-experiment.com/EBML.js');\r\n }\r\n\r\n var reader = new EBML.Reader();\r\n var decoder = new EBML.Decoder();\r\n var tools = EBML.tools;\r\n\r\n var fileReader = new FileReader();\r\n fileReader.onload = function(e) {\r\n var ebmlElms = decoder.decode(this.result);\r\n ebmlElms.forEach(function(element) {\r\n reader.read(element);\r\n });\r\n reader.stop();\r\n var refinedMetadataBuf = tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues);\r\n var body = this.result.slice(reader.metadataSize);\r\n var newBlob = new Blob([refinedMetadataBuf, body], {\r\n type: 'video/webm'\r\n });\r\n\r\n callback(newBlob);\r\n };\r\n fileReader.readAsArrayBuffer(inputBlob);\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.invokeSaveAsDialog = invokeSaveAsDialog;\r\n RecordRTC.getTracks = getTracks;\r\n RecordRTC.getSeekableBlob = getSeekableBlob;\r\n RecordRTC.bytesToSize = bytesToSize;\r\n RecordRTC.isElectron = isElectron;\r\n}\n\r\n// __________ (used to handle stuff like http://goo.gl/xmE5eg) issue #129\r\n// Storage.js\r\n\r\n/**\r\n * Storage is a standalone object used by {@link RecordRTC} to store reusable objects e.g. \"new AudioContext\".\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @example\r\n * Storage.AudioContext === webkitAudioContext\r\n * @property {webkitAudioContext} AudioContext - Keeps a reference to AudioContext object.\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n */\r\n\r\nvar Storage = {};\r\n\r\nif (typeof AudioContext !== 'undefined') {\r\n Storage.AudioContext = AudioContext;\r\n} else if (typeof webkitAudioContext !== 'undefined') {\r\n Storage.AudioContext = webkitAudioContext;\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.Storage = Storage;\r\n}\n\r\nfunction isMediaRecorderCompatible() {\r\n if (isFirefox || isSafari || isEdge) {\r\n return true;\r\n }\r\n\r\n var nVer = navigator.appVersion;\r\n var nAgt = navigator.userAgent;\r\n var fullVersion = '' + parseFloat(navigator.appVersion);\r\n var majorVersion = parseInt(navigator.appVersion, 10);\r\n var nameOffset, verOffset, ix;\r\n\r\n if (isChrome || isOpera) {\r\n verOffset = nAgt.indexOf('Chrome');\r\n fullVersion = nAgt.substring(verOffset + 7);\r\n }\r\n\r\n // trim the fullVersion string at semicolon/space if present\r\n if ((ix = fullVersion.indexOf(';')) !== -1) {\r\n fullVersion = fullVersion.substring(0, ix);\r\n }\r\n\r\n if ((ix = fullVersion.indexOf(' ')) !== -1) {\r\n fullVersion = fullVersion.substring(0, ix);\r\n }\r\n\r\n majorVersion = parseInt('' + fullVersion, 10);\r\n\r\n if (isNaN(majorVersion)) {\r\n fullVersion = '' + parseFloat(navigator.appVersion);\r\n majorVersion = parseInt(navigator.appVersion, 10);\r\n }\r\n\r\n return majorVersion >= 49;\r\n}\n\r\n// ______________________\r\n// MediaStreamRecorder.js\r\n\r\n/**\r\n * MediaStreamRecorder is an abstraction layer for {@link https://w3c.github.io/mediacapture-record/MediaRecorder.html|MediaRecorder API}. It is used by {@link RecordRTC} to record MediaStream(s) in both Chrome and Firefox.\r\n * @summary Runs top over {@link https://w3c.github.io/mediacapture-record/MediaRecorder.html|MediaRecorder API}.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://github.com/muaz-khan|Muaz Khan}\r\n * @typedef MediaStreamRecorder\r\n * @class\r\n * @example\r\n * var config = {\r\n * mimeType: 'video/webm', // vp8, vp9, h264, mkv, opus/vorbis\r\n * audioBitsPerSecond : 256 * 8 * 1024,\r\n * videoBitsPerSecond : 256 * 8 * 1024,\r\n * bitsPerSecond: 256 * 8 * 1024, // if this is provided, skip above two\r\n * checkForInactiveTracks: true,\r\n * timeSlice: 1000, // concatenate intervals based blobs\r\n * ondataavailable: function() {} // get intervals based blobs\r\n * }\r\n * var recorder = new MediaStreamRecorder(mediaStream, config);\r\n * recorder.record();\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n *\r\n * // or\r\n * var blob = recorder.blob;\r\n * });\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.\r\n * @param {object} config - {disableLogs:true, initCallback: function, mimeType: \"video/webm\", timeSlice: 1000}\r\n * @throws Will throw an error if first argument \"MediaStream\" is missing. Also throws error if \"MediaRecorder API\" are not supported by the browser.\r\n */\r\n\r\nfunction MediaStreamRecorder(mediaStream, config) {\r\n var self = this;\r\n\r\n if (typeof mediaStream === 'undefined') {\r\n throw 'First argument \"MediaStream\" is required.';\r\n }\r\n\r\n if (typeof MediaRecorder === 'undefined') {\r\n throw 'Your browser does not support the Media Recorder API. Please try other modules e.g. WhammyRecorder or StereoAudioRecorder.';\r\n }\r\n\r\n config = config || {\r\n // bitsPerSecond: 256 * 8 * 1024,\r\n mimeType: 'video/webm'\r\n };\r\n\r\n if (config.type === 'audio') {\r\n if (getTracks(mediaStream, 'video').length && getTracks(mediaStream, 'audio').length) {\r\n var stream;\r\n if (!!navigator.mozGetUserMedia) {\r\n stream = new MediaStream();\r\n stream.addTrack(getTracks(mediaStream, 'audio')[0]);\r\n } else {\r\n // webkitMediaStream\r\n stream = new MediaStream(getTracks(mediaStream, 'audio'));\r\n }\r\n mediaStream = stream;\r\n }\r\n\r\n if (!config.mimeType || config.mimeType.toString().toLowerCase().indexOf('audio') === -1) {\r\n config.mimeType = isChrome ? 'audio/webm' : 'audio/ogg';\r\n }\r\n\r\n if (config.mimeType && config.mimeType.toString().toLowerCase() !== 'audio/ogg' && !!navigator.mozGetUserMedia) {\r\n // forcing better codecs on Firefox (via #166)\r\n config.mimeType = 'audio/ogg';\r\n }\r\n }\r\n\r\n var arrayOfBlobs = [];\r\n\r\n /**\r\n * This method returns array of blobs. Use only with \"timeSlice\". Its useful to preview recording anytime, without using the \"stop\" method.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * var arrayOfBlobs = recorder.getArrayOfBlobs();\r\n * @returns {Array} Returns array of recorded blobs.\r\n */\r\n this.getArrayOfBlobs = function() {\r\n return arrayOfBlobs;\r\n };\r\n\r\n /**\r\n * This method records MediaStream.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * recorder.record();\r\n */\r\n this.record = function() {\r\n // set defaults\r\n self.blob = null;\r\n self.clearRecordedData();\r\n self.timestamps = [];\r\n allStates = [];\r\n arrayOfBlobs = [];\r\n\r\n var recorderHints = config;\r\n\r\n if (!config.disableLogs) {\r\n console.log('Passing following config over MediaRecorder API.', recorderHints);\r\n }\r\n\r\n if (mediaRecorder) {\r\n // mandatory to make sure Firefox doesn't fails to record streams 3-4 times without reloading the page.\r\n mediaRecorder = null;\r\n }\r\n\r\n if (isChrome && !isMediaRecorderCompatible()) {\r\n // to support video-only recording on stable\r\n recorderHints = 'video/vp8';\r\n }\r\n\r\n if (typeof MediaRecorder.isTypeSupported === 'function' && recorderHints.mimeType) {\r\n if (!MediaRecorder.isTypeSupported(recorderHints.mimeType)) {\r\n if (!config.disableLogs) {\r\n console.warn('MediaRecorder API seems unable to record mimeType:', recorderHints.mimeType);\r\n }\r\n\r\n recorderHints.mimeType = config.type === 'audio' ? 'audio/webm' : 'video/webm';\r\n }\r\n }\r\n\r\n // using MediaRecorder API here\r\n try {\r\n mediaRecorder = new MediaRecorder(mediaStream, recorderHints);\r\n\r\n // reset\r\n config.mimeType = recorderHints.mimeType;\r\n } catch (e) {\r\n // chrome-based fallback\r\n mediaRecorder = new MediaRecorder(mediaStream);\r\n }\r\n\r\n // old hack?\r\n if (recorderHints.mimeType && !MediaRecorder.isTypeSupported && 'canRecordMimeType' in mediaRecorder && mediaRecorder.canRecordMimeType(recorderHints.mimeType) === false) {\r\n if (!config.disableLogs) {\r\n console.warn('MediaRecorder API seems unable to record mimeType:', recorderHints.mimeType);\r\n }\r\n }\r\n\r\n // Dispatching OnDataAvailable Handler\r\n mediaRecorder.ondataavailable = function(e) {\r\n if (e.data) {\r\n allStates.push('ondataavailable: ' + bytesToSize(e.data.size));\r\n }\r\n\r\n if (typeof config.timeSlice === 'number') {\r\n if (e.data && e.data.size && e.data.size > 100) {\r\n arrayOfBlobs.push(e.data);\r\n updateTimeStamp();\r\n\r\n if (typeof config.ondataavailable === 'function') {\r\n // intervals based blobs\r\n var blob = config.getNativeBlob ? e.data : new Blob([e.data], {\r\n type: getMimeType(recorderHints)\r\n });\r\n config.ondataavailable(blob);\r\n }\r\n }\r\n return;\r\n }\r\n\r\n if (!e.data || !e.data.size || e.data.size < 100 || self.blob) {\r\n // make sure that stopRecording always getting fired\r\n // even if there is invalid data\r\n if (self.recordingCallback) {\r\n self.recordingCallback(new Blob([], {\r\n type: getMimeType(recorderHints)\r\n }));\r\n self.recordingCallback = null;\r\n }\r\n return;\r\n }\r\n\r\n self.blob = config.getNativeBlob ? e.data : new Blob([e.data], {\r\n type: getMimeType(recorderHints)\r\n });\r\n\r\n if (self.recordingCallback) {\r\n self.recordingCallback(self.blob);\r\n self.recordingCallback = null;\r\n }\r\n };\r\n\r\n mediaRecorder.onstart = function() {\r\n allStates.push('started');\r\n };\r\n\r\n mediaRecorder.onpause = function() {\r\n allStates.push('paused');\r\n };\r\n\r\n mediaRecorder.onresume = function() {\r\n allStates.push('resumed');\r\n };\r\n\r\n mediaRecorder.onstop = function() {\r\n allStates.push('stopped');\r\n };\r\n\r\n mediaRecorder.onerror = function(error) {\r\n if (!error) {\r\n return;\r\n }\r\n\r\n if (!error.name) {\r\n error.name = 'UnknownError';\r\n }\r\n\r\n allStates.push('error: ' + error);\r\n\r\n if (!config.disableLogs) {\r\n // via: https://w3c.github.io/mediacapture-record/MediaRecorder.html#exception-summary\r\n if (error.name.toString().toLowerCase().indexOf('invalidstate') !== -1) {\r\n console.error('The MediaRecorder is not in a state in which the proposed operation is allowed to be executed.', error);\r\n } else if (error.name.toString().toLowerCase().indexOf('notsupported') !== -1) {\r\n console.error('MIME type (', recorderHints.mimeType, ') is not supported.', error);\r\n } else if (error.name.toString().toLowerCase().indexOf('security') !== -1) {\r\n console.error('MediaRecorder security error', error);\r\n }\r\n\r\n // older code below\r\n else if (error.name === 'OutOfMemory') {\r\n console.error('The UA has exhaused the available memory. User agents SHOULD provide as much additional information as possible in the message attribute.', error);\r\n } else if (error.name === 'IllegalStreamModification') {\r\n 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.', error);\r\n } else if (error.name === 'OtherRecordingError') {\r\n 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.', error);\r\n } else if (error.name === 'GenericError') {\r\n console.error('The UA cannot provide the codec or recording option that has been requested.', error);\r\n } else {\r\n console.error('MediaRecorder Error', error);\r\n }\r\n }\r\n\r\n (function(looper) {\r\n if (!self.manuallyStopped && mediaRecorder && mediaRecorder.state === 'inactive') {\r\n delete config.timeslice;\r\n\r\n // 10 minutes, enough?\r\n mediaRecorder.start(10 * 60 * 1000);\r\n return;\r\n }\r\n\r\n setTimeout(looper, 1000);\r\n })();\r\n\r\n if (mediaRecorder.state !== 'inactive' && mediaRecorder.state !== 'stopped') {\r\n mediaRecorder.stop();\r\n }\r\n };\r\n\r\n if (typeof config.timeSlice === 'number') {\r\n updateTimeStamp();\r\n mediaRecorder.start(config.timeSlice);\r\n } else {\r\n // default is 60 minutes; enough?\r\n // use config => {timeSlice: 1000} otherwise\r\n\r\n mediaRecorder.start(3.6e+6);\r\n }\r\n\r\n if (config.initCallback) {\r\n config.initCallback(); // old code\r\n }\r\n };\r\n\r\n /**\r\n * @property {Array} timestamps - Array of time stamps\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * console.log(recorder.timestamps);\r\n */\r\n this.timestamps = [];\r\n\r\n function updateTimeStamp() {\r\n self.timestamps.push(new Date().getTime());\r\n\r\n if (typeof config.onTimeStamp === 'function') {\r\n config.onTimeStamp(self.timestamps[self.timestamps.length - 1], self.timestamps);\r\n }\r\n }\r\n\r\n function getMimeType(secondObject) {\r\n if (mediaRecorder && mediaRecorder.mimeType) {\r\n return mediaRecorder.mimeType;\r\n }\r\n\r\n return secondObject.mimeType || 'video/webm';\r\n }\r\n\r\n /**\r\n * This method stops recording MediaStream.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n */\r\n this.stop = function(callback) {\r\n callback = callback || function() {};\r\n\r\n self.manuallyStopped = true; // used inside the mediaRecorder.onerror\r\n\r\n if (!mediaRecorder) {\r\n return;\r\n }\r\n\r\n this.recordingCallback = callback;\r\n\r\n if (mediaRecorder.state === 'recording') {\r\n mediaRecorder.stop();\r\n }\r\n\r\n if (typeof config.timeSlice === 'number') {\r\n setTimeout(function() {\r\n self.blob = new Blob(arrayOfBlobs, {\r\n type: getMimeType(config)\r\n });\r\n\r\n self.recordingCallback(self.blob);\r\n }, 100);\r\n }\r\n };\r\n\r\n /**\r\n * This method pauses the recording process.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * recorder.pause();\r\n */\r\n this.pause = function() {\r\n if (!mediaRecorder) {\r\n return;\r\n }\r\n\r\n if (mediaRecorder.state === 'recording') {\r\n mediaRecorder.pause();\r\n }\r\n };\r\n\r\n /**\r\n * This method resumes the recording process.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * recorder.resume();\r\n */\r\n this.resume = function() {\r\n if (!mediaRecorder) {\r\n return;\r\n }\r\n\r\n if (mediaRecorder.state === 'paused') {\r\n mediaRecorder.resume();\r\n }\r\n };\r\n\r\n /**\r\n * This method resets currently recorded data.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n this.clearRecordedData = function() {\r\n if (mediaRecorder && mediaRecorder.state === 'recording') {\r\n self.stop(clearRecordedDataCB);\r\n }\r\n\r\n clearRecordedDataCB();\r\n };\r\n\r\n function clearRecordedDataCB() {\r\n arrayOfBlobs = [];\r\n mediaRecorder = null;\r\n self.timestamps = [];\r\n }\r\n\r\n // Reference to \"MediaRecorder\" object\r\n var mediaRecorder;\r\n\r\n /**\r\n * Access to native MediaRecorder API\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @instance\r\n * @example\r\n * var internal = recorder.getInternalRecorder();\r\n * internal.ondataavailable = function() {}; // override\r\n * internal.stream, internal.onpause, internal.onstop, etc.\r\n * @returns {Object} Returns internal recording object.\r\n */\r\n this.getInternalRecorder = function() {\r\n return mediaRecorder;\r\n };\r\n\r\n function isMediaStreamActive() {\r\n if ('active' in mediaStream) {\r\n if (!mediaStream.active) {\r\n return false;\r\n }\r\n } else if ('ended' in mediaStream) { // old hack\r\n if (mediaStream.ended) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * @property {Blob} blob - Recorded data as \"Blob\" object.\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * recorder.stop(function() {\r\n * var blob = recorder.blob;\r\n * });\r\n */\r\n this.blob = null;\r\n\r\n\r\n /**\r\n * Get MediaRecorder readonly state.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * var state = recorder.getState();\r\n * @returns {String} Returns recording state.\r\n */\r\n this.getState = function() {\r\n if (!mediaRecorder) {\r\n return 'inactive';\r\n }\r\n\r\n return mediaRecorder.state || 'inactive';\r\n };\r\n\r\n // list of all recording states\r\n var allStates = [];\r\n\r\n /**\r\n * Get MediaRecorder all recording states.\r\n * @method\r\n * @memberof MediaStreamRecorder\r\n * @example\r\n * var state = recorder.getAllStates();\r\n * @returns {Array} Returns all recording states\r\n */\r\n this.getAllStates = function() {\r\n return allStates;\r\n };\r\n\r\n // if any Track within the MediaStream is muted or not enabled at any time, \r\n // the browser will only record black frames \r\n // or silence since that is the content produced by the Track\r\n // so we need to stopRecording as soon as any single track ends.\r\n if (typeof config.checkForInactiveTracks === 'undefined') {\r\n config.checkForInactiveTracks = false; // disable to minimize CPU usage\r\n }\r\n\r\n var self = this;\r\n\r\n // this method checks if media stream is stopped\r\n // or if any track is ended.\r\n (function looper() {\r\n if (!mediaRecorder || config.checkForInactiveTracks === false) {\r\n return;\r\n }\r\n\r\n if (isMediaStreamActive() === false) {\r\n if (!config.disableLogs) {\r\n console.log('MediaStream seems stopped.');\r\n }\r\n self.stop();\r\n return;\r\n }\r\n\r\n setTimeout(looper, 1000); // check every second\r\n })();\r\n\r\n // for debugging\r\n this.name = 'MediaStreamRecorder';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.MediaStreamRecorder = MediaStreamRecorder;\r\n}\n\r\n// source code from: http://typedarray.org/wp-content/projects/WebAudioRecorder/script.js\r\n// https://github.com/mattdiamond/Recorderjs#license-mit\r\n// ______________________\r\n// StereoAudioRecorder.js\r\n\r\n/**\r\n * StereoAudioRecorder is a standalone class used by {@link RecordRTC} to bring \"stereo\" audio-recording in chrome.\r\n * @summary JavaScript standalone object for stereo audio recording.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef StereoAudioRecorder\r\n * @class\r\n * @example\r\n * var recorder = new StereoAudioRecorder(MediaStream, {\r\n * sampleRate: 44100,\r\n * bufferSize: 4096\r\n * });\r\n * recorder.record();\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.\r\n * @param {object} config - {sampleRate: 44100, bufferSize: 4096, numberOfAudioChannels: 1, etc.}\r\n */\r\n\r\nfunction StereoAudioRecorder(mediaStream, config) {\r\n if (!getTracks(mediaStream, 'audio').length) {\r\n throw 'Your stream has no audio tracks.';\r\n }\r\n\r\n config = config || {};\r\n\r\n var self = this;\r\n\r\n // variables\r\n var leftchannel = [];\r\n var rightchannel = [];\r\n var recording = false;\r\n var recordingLength = 0;\r\n var jsAudioNode;\r\n\r\n var numberOfAudioChannels = 2;\r\n\r\n /**\r\n * Set sample rates such as 8K or 16K. Reference: http://stackoverflow.com/a/28977136/552182\r\n * @property {number} desiredSampRate - Desired Bits per sample * 1000\r\n * @memberof StereoAudioRecorder\r\n * @instance\r\n * @example\r\n * var recorder = StereoAudioRecorder(mediaStream, {\r\n * desiredSampRate: 16 * 1000 // bits-per-sample * 1000\r\n * });\r\n */\r\n var desiredSampRate = config.desiredSampRate;\r\n\r\n // backward compatibility\r\n if (config.leftChannel === true) {\r\n numberOfAudioChannels = 1;\r\n }\r\n\r\n if (config.numberOfAudioChannels === 1) {\r\n numberOfAudioChannels = 1;\r\n }\r\n\r\n if (!numberOfAudioChannels || numberOfAudioChannels < 1) {\r\n numberOfAudioChannels = 2;\r\n }\r\n\r\n if (!config.disableLogs) {\r\n console.log('StereoAudioRecorder is set to record number of channels: ' + numberOfAudioChannels);\r\n }\r\n\r\n // if any Track within the MediaStream is muted or not enabled at any time, \r\n // the browser will only record black frames \r\n // or silence since that is the content produced by the Track\r\n // so we need to stopRecording as soon as any single track ends.\r\n if (typeof config.checkForInactiveTracks === 'undefined') {\r\n config.checkForInactiveTracks = true;\r\n }\r\n\r\n function isMediaStreamActive() {\r\n if (config.checkForInactiveTracks === false) {\r\n // always return \"true\"\r\n return true;\r\n }\r\n\r\n if ('active' in mediaStream) {\r\n if (!mediaStream.active) {\r\n return false;\r\n }\r\n } else if ('ended' in mediaStream) { // old hack\r\n if (mediaStream.ended) {\r\n return false;\r\n }\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * This method records MediaStream.\r\n * @method\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.record();\r\n */\r\n this.record = function() {\r\n if (isMediaStreamActive() === false) {\r\n throw 'Please make sure MediaStream is active.';\r\n }\r\n\r\n resetVariables();\r\n\r\n isAudioProcessStarted = isPaused = false;\r\n recording = true;\r\n\r\n if (typeof config.timeSlice !== 'undefined') {\r\n looper();\r\n }\r\n };\r\n\r\n function mergeLeftRightBuffers(config, callback) {\r\n function mergeAudioBuffers(config, cb) {\r\n var numberOfAudioChannels = config.numberOfAudioChannels;\r\n\r\n // todo: \"slice(0)\" --- is it causes loop? Should be removed?\r\n var leftBuffers = config.leftBuffers.slice(0);\r\n var rightBuffers = config.rightBuffers.slice(0);\r\n var sampleRate = config.sampleRate;\r\n var internalInterleavedLength = config.internalInterleavedLength;\r\n var desiredSampRate = config.desiredSampRate;\r\n\r\n if (numberOfAudioChannels === 2) {\r\n leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength);\r\n rightBuffers = mergeBuffers(rightBuffers, internalInterleavedLength);\r\n\r\n if (desiredSampRate) {\r\n leftBuffers = interpolateArray(leftBuffers, desiredSampRate, sampleRate);\r\n rightBuffers = interpolateArray(rightBuffers, desiredSampRate, sampleRate);\r\n }\r\n }\r\n\r\n if (numberOfAudioChannels === 1) {\r\n leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength);\r\n\r\n if (desiredSampRate) {\r\n leftBuffers = interpolateArray(leftBuffers, desiredSampRate, sampleRate);\r\n }\r\n }\r\n\r\n // set sample rate as desired sample rate\r\n if (desiredSampRate) {\r\n sampleRate = desiredSampRate;\r\n }\r\n\r\n // for changing the sampling rate, reference:\r\n // http://stackoverflow.com/a/28977136/552182\r\n function interpolateArray(data, newSampleRate, oldSampleRate) {\r\n var fitCount = Math.round(data.length * (newSampleRate / oldSampleRate));\r\n var newData = [];\r\n var springFactor = Number((data.length - 1) / (fitCount - 1));\r\n newData[0] = data[0];\r\n for (var i = 1; i < fitCount - 1; i++) {\r\n var tmp = i * springFactor;\r\n var before = Number(Math.floor(tmp)).toFixed();\r\n var after = Number(Math.ceil(tmp)).toFixed();\r\n var atPoint = tmp - before;\r\n newData[i] = linearInterpolate(data[before], data[after], atPoint);\r\n }\r\n newData[fitCount - 1] = data[data.length - 1];\r\n return newData;\r\n }\r\n\r\n function linearInterpolate(before, after, atPoint) {\r\n return before + (after - before) * atPoint;\r\n }\r\n\r\n function mergeBuffers(channelBuffer, rLength) {\r\n var result = new Float64Array(rLength);\r\n var offset = 0;\r\n var lng = channelBuffer.length;\r\n\r\n for (var i = 0; i < lng; i++) {\r\n var buffer = channelBuffer[i];\r\n result.set(buffer, offset);\r\n offset += buffer.length;\r\n }\r\n\r\n return result;\r\n }\r\n\r\n function interleave(leftChannel, rightChannel) {\r\n var length = leftChannel.length + rightChannel.length;\r\n\r\n var result = new Float64Array(length);\r\n\r\n var inputIndex = 0;\r\n\r\n for (var index = 0; index < length;) {\r\n result[index++] = leftChannel[inputIndex];\r\n result[index++] = rightChannel[inputIndex];\r\n inputIndex++;\r\n }\r\n return result;\r\n }\r\n\r\n function writeUTFBytes(view, offset, string) {\r\n var lng = string.length;\r\n for (var i = 0; i < lng; i++) {\r\n view.setUint8(offset + i, string.charCodeAt(i));\r\n }\r\n }\r\n\r\n // interleave both channels together\r\n var interleaved;\r\n\r\n if (numberOfAudioChannels === 2) {\r\n interleaved = interleave(leftBuffers, rightBuffers);\r\n }\r\n\r\n if (numberOfAudioChannels === 1) {\r\n interleaved = leftBuffers;\r\n }\r\n\r\n var interleavedLength = interleaved.length;\r\n\r\n // create wav file\r\n var resultingBufferLength = 44 + interleavedLength * 2;\r\n\r\n var buffer = new ArrayBuffer(resultingBufferLength);\r\n\r\n var view = new DataView(buffer);\r\n\r\n // RIFF chunk descriptor/identifier \r\n writeUTFBytes(view, 0, 'RIFF');\r\n\r\n // RIFF chunk length\r\n // changed \"44\" to \"36\" via #401\r\n view.setUint32(4, 36 + interleavedLength * 2, true);\r\n\r\n // RIFF type \r\n writeUTFBytes(view, 8, 'WAVE');\r\n\r\n // format chunk identifier \r\n // FMT sub-chunk\r\n writeUTFBytes(view, 12, 'fmt ');\r\n\r\n // format chunk length \r\n view.setUint32(16, 16, true);\r\n\r\n // sample format (raw)\r\n view.setUint16(20, 1, true);\r\n\r\n // stereo (2 channels)\r\n view.setUint16(22, numberOfAudioChannels, true);\r\n\r\n // sample rate \r\n view.setUint32(24, sampleRate, true);\r\n\r\n // byte rate (sample rate * block align)\r\n view.setUint32(28, sampleRate * 2, true);\r\n\r\n // block align (channel count * bytes per sample) \r\n view.setUint16(32, numberOfAudioChannels * 2, true);\r\n\r\n // bits per sample \r\n view.setUint16(34, 16, true);\r\n\r\n // data sub-chunk\r\n // data chunk identifier \r\n writeUTFBytes(view, 36, 'data');\r\n\r\n // data chunk length \r\n view.setUint32(40, interleavedLength * 2, true);\r\n\r\n // write the PCM samples\r\n var lng = interleavedLength;\r\n var index = 44;\r\n var volume = 1;\r\n for (var i = 0; i < lng; i++) {\r\n view.setInt16(index, interleaved[i] * (0x7FFF * volume), true);\r\n index += 2;\r\n }\r\n\r\n if (cb) {\r\n return cb({\r\n buffer: buffer,\r\n view: view\r\n });\r\n }\r\n\r\n postMessage({\r\n buffer: buffer,\r\n view: view\r\n });\r\n }\r\n\r\n if (config.noWorker) {\r\n mergeAudioBuffers(config, function(data) {\r\n callback(data.buffer, data.view);\r\n });\r\n return;\r\n }\r\n\r\n\r\n var webWorker = processInWebWorker(mergeAudioBuffers);\r\n\r\n webWorker.onmessage = function(event) {\r\n callback(event.data.buffer, event.data.view);\r\n\r\n // release memory\r\n URL.revokeObjectURL(webWorker.workerURL);\r\n\r\n // kill webworker (or Chrome will kill your page after ~25 calls)\r\n webWorker.terminate();\r\n };\r\n\r\n webWorker.postMessage(config);\r\n }\r\n\r\n function processInWebWorker(_function) {\r\n var workerURL = URL.createObjectURL(new Blob([_function.toString(),\r\n ';this.onmessage = function (eee) {' + _function.name + '(eee.data);}'\r\n ], {\r\n type: 'application/javascript'\r\n }));\r\n\r\n var worker = new Worker(workerURL);\r\n worker.workerURL = workerURL;\r\n return worker;\r\n }\r\n\r\n /**\r\n * This method stops recording MediaStream.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n */\r\n this.stop = function(callback) {\r\n callback = callback || function() {};\r\n\r\n // stop recording\r\n recording = false;\r\n\r\n mergeLeftRightBuffers({\r\n desiredSampRate: desiredSampRate,\r\n sampleRate: sampleRate,\r\n numberOfAudioChannels: numberOfAudioChannels,\r\n internalInterleavedLength: recordingLength,\r\n leftBuffers: leftchannel,\r\n rightBuffers: numberOfAudioChannels === 1 ? [] : rightchannel,\r\n noWorker: config.noWorker\r\n }, function(buffer, view) {\r\n /**\r\n * @property {Blob} blob - The recorded blob object.\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.stop(function(){\r\n * var blob = recorder.blob;\r\n * });\r\n */\r\n self.blob = new Blob([view], {\r\n type: 'audio/wav'\r\n });\r\n\r\n /**\r\n * @property {ArrayBuffer} buffer - The recorded buffer object.\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.stop(function(){\r\n * var buffer = recorder.buffer;\r\n * });\r\n */\r\n self.buffer = new ArrayBuffer(view.buffer.byteLength);\r\n\r\n /**\r\n * @property {DataView} view - The recorded data-view object.\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.stop(function(){\r\n * var view = recorder.view;\r\n * });\r\n */\r\n self.view = view;\r\n\r\n self.sampleRate = desiredSampRate || sampleRate;\r\n self.bufferSize = bufferSize;\r\n\r\n // recorded audio length\r\n self.length = recordingLength;\r\n\r\n isAudioProcessStarted = false;\r\n\r\n if (callback) {\r\n callback(self.blob);\r\n }\r\n });\r\n };\r\n\r\n if (typeof RecordRTC.Storage === 'undefined') {\r\n RecordRTC.Storage = {\r\n AudioContextConstructor: null,\r\n AudioContext: window.AudioContext || window.webkitAudioContext\r\n };\r\n }\r\n\r\n if (!RecordRTC.Storage.AudioContextConstructor || RecordRTC.Storage.AudioContextConstructor.state === 'closed') {\r\n RecordRTC.Storage.AudioContextConstructor = new RecordRTC.Storage.AudioContext();\r\n }\r\n\r\n var context = RecordRTC.Storage.AudioContextConstructor;\r\n\r\n // creates an audio node from the microphone incoming stream\r\n var audioInput = context.createMediaStreamSource(mediaStream);\r\n\r\n var legalBufferValues = [0, 256, 512, 1024, 2048, 4096, 8192, 16384];\r\n\r\n /**\r\n * From the spec: This value controls how frequently the audioprocess event is\r\n * dispatched and how many sample-frames need to be processed each call.\r\n * Lower values for buffer size will result in a lower (better) latency.\r\n * Higher values will be necessary to avoid audio breakup and glitches\r\n * The size of the buffer (in sample-frames) which needs to\r\n * be processed each time onprocessaudio is called.\r\n * Legal values are (256, 512, 1024, 2048, 4096, 8192, 16384).\r\n * @property {number} bufferSize - Buffer-size for how frequently the audioprocess event is dispatched.\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder = new StereoAudioRecorder(mediaStream, {\r\n * bufferSize: 4096\r\n * });\r\n */\r\n\r\n // \"0\" means, let chrome decide the most accurate buffer-size for current platform.\r\n var bufferSize = typeof config.bufferSize === 'undefined' ? 4096 : config.bufferSize;\r\n\r\n if (legalBufferValues.indexOf(bufferSize) === -1) {\r\n if (!config.disableLogs) {\r\n console.log('Legal values for buffer-size are ' + JSON.stringify(legalBufferValues, null, '\\t'));\r\n }\r\n }\r\n\r\n if (context.createJavaScriptNode) {\r\n jsAudioNode = context.createJavaScriptNode(bufferSize, numberOfAudioChannels, numberOfAudioChannels);\r\n } else if (context.createScriptProcessor) {\r\n jsAudioNode = context.createScriptProcessor(bufferSize, numberOfAudioChannels, numberOfAudioChannels);\r\n } else {\r\n throw 'WebAudio API has no support on this browser.';\r\n }\r\n\r\n // connect the stream to the script processor\r\n audioInput.connect(jsAudioNode);\r\n\r\n if (!config.bufferSize) {\r\n bufferSize = jsAudioNode.bufferSize; // device buffer-size\r\n }\r\n\r\n /**\r\n * The sample rate (in sample-frames per second) at which the\r\n * AudioContext handles audio. It is assumed that all AudioNodes\r\n * in the context run at this rate. In making this assumption,\r\n * sample-rate converters or \"varispeed\" processors are not supported\r\n * in real-time processing.\r\n * The sampleRate parameter describes the sample-rate of the\r\n * linear PCM audio data in the buffer in sample-frames per second.\r\n * An implementation must support sample-rates in at least\r\n * the range 22050 to 96000.\r\n * @property {number} sampleRate - Buffer-size for how frequently the audioprocess event is dispatched.\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder = new StereoAudioRecorder(mediaStream, {\r\n * sampleRate: 44100\r\n * });\r\n */\r\n var sampleRate = typeof config.sampleRate !== 'undefined' ? config.sampleRate : context.sampleRate || 44100;\r\n\r\n if (sampleRate < 22050 || sampleRate > 96000) {\r\n // Ref: http://stackoverflow.com/a/26303918/552182\r\n if (!config.disableLogs) {\r\n console.log('sample-rate must be under range 22050 and 96000.');\r\n }\r\n }\r\n\r\n if (!config.disableLogs) {\r\n if (config.desiredSampRate) {\r\n console.log('Desired sample-rate: ' + config.desiredSampRate);\r\n }\r\n }\r\n\r\n var isPaused = false;\r\n /**\r\n * This method pauses the recording process.\r\n * @method\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.pause();\r\n */\r\n this.pause = function() {\r\n isPaused = true;\r\n };\r\n\r\n /**\r\n * This method resumes the recording process.\r\n * @method\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.resume();\r\n */\r\n this.resume = function() {\r\n if (isMediaStreamActive() === false) {\r\n throw 'Please make sure MediaStream is active.';\r\n }\r\n\r\n if (!recording) {\r\n if (!config.disableLogs) {\r\n console.log('Seems recording has been restarted.');\r\n }\r\n this.record();\r\n return;\r\n }\r\n\r\n isPaused = false;\r\n };\r\n\r\n /**\r\n * This method resets currently recorded data.\r\n * @method\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n this.clearRecordedData = function() {\r\n config.checkForInactiveTracks = false;\r\n\r\n if (recording) {\r\n this.stop(clearRecordedDataCB);\r\n }\r\n\r\n clearRecordedDataCB();\r\n };\r\n\r\n function resetVariables() {\r\n leftchannel = [];\r\n rightchannel = [];\r\n recordingLength = 0;\r\n isAudioProcessStarted = false;\r\n recording = false;\r\n isPaused = false;\r\n context = null;\r\n\r\n self.leftchannel = leftchannel;\r\n self.rightchannel = rightchannel;\r\n self.numberOfAudioChannels = numberOfAudioChannels;\r\n self.desiredSampRate = desiredSampRate;\r\n self.sampleRate = sampleRate;\r\n self.recordingLength = recordingLength;\r\n\r\n intervalsBasedBuffers = {\r\n left: [],\r\n right: [],\r\n recordingLength: 0\r\n };\r\n }\r\n\r\n function clearRecordedDataCB() {\r\n if (jsAudioNode) {\r\n jsAudioNode.onaudioprocess = null;\r\n jsAudioNode.disconnect();\r\n jsAudioNode = null;\r\n }\r\n\r\n if (audioInput) {\r\n audioInput.disconnect();\r\n audioInput = null;\r\n }\r\n\r\n resetVariables();\r\n }\r\n\r\n // for debugging\r\n this.name = 'StereoAudioRecorder';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n\r\n var isAudioProcessStarted = false;\r\n\r\n function onAudioProcessDataAvailable(e) {\r\n if (isPaused) {\r\n return;\r\n }\r\n\r\n if (isMediaStreamActive() === false) {\r\n if (!config.disableLogs) {\r\n console.log('MediaStream seems stopped.');\r\n }\r\n jsAudioNode.disconnect();\r\n recording = false;\r\n }\r\n\r\n if (!recording) {\r\n if (audioInput) {\r\n audioInput.disconnect();\r\n audioInput = null;\r\n }\r\n return;\r\n }\r\n\r\n /**\r\n * This method is called on \"onaudioprocess\" event's first invocation.\r\n * @method {function} onAudioProcessStarted\r\n * @memberof StereoAudioRecorder\r\n * @example\r\n * recorder.onAudioProcessStarted: function() { };\r\n */\r\n if (!isAudioProcessStarted) {\r\n isAudioProcessStarted = true;\r\n if (config.onAudioProcessStarted) {\r\n config.onAudioProcessStarted();\r\n }\r\n\r\n if (config.initCallback) {\r\n config.initCallback();\r\n }\r\n }\r\n\r\n var left = e.inputBuffer.getChannelData(0);\r\n\r\n // we clone the samples\r\n var chLeft = new Float32Array(left);\r\n leftchannel.push(chLeft);\r\n\r\n if (numberOfAudioChannels === 2) {\r\n var right = e.inputBuffer.getChannelData(1);\r\n var chRight = new Float32Array(right);\r\n rightchannel.push(chRight);\r\n }\r\n\r\n recordingLength += bufferSize;\r\n\r\n // export raw PCM\r\n self.recordingLength = recordingLength;\r\n\r\n if (typeof config.timeSlice !== 'undefined') {\r\n intervalsBasedBuffers.recordingLength += bufferSize;\r\n intervalsBasedBuffers.left.push(chLeft);\r\n\r\n if (numberOfAudioChannels === 2) {\r\n intervalsBasedBuffers.right.push(chRight);\r\n }\r\n }\r\n }\r\n\r\n jsAudioNode.onaudioprocess = onAudioProcessDataAvailable;\r\n\r\n // to prevent self audio to be connected with speakers\r\n if (context.createMediaStreamDestination) {\r\n jsAudioNode.connect(context.createMediaStreamDestination());\r\n } else {\r\n jsAudioNode.connect(context.destination);\r\n }\r\n\r\n // export raw PCM\r\n this.leftchannel = leftchannel;\r\n this.rightchannel = rightchannel;\r\n this.numberOfAudioChannels = numberOfAudioChannels;\r\n this.desiredSampRate = desiredSampRate;\r\n this.sampleRate = sampleRate;\r\n self.recordingLength = recordingLength;\r\n\r\n // helper for intervals based blobs\r\n var intervalsBasedBuffers = {\r\n left: [],\r\n right: [],\r\n recordingLength: 0\r\n };\r\n\r\n // this looper is used to support intervals based blobs (via timeSlice+ondataavailable)\r\n function looper() {\r\n if (!recording || typeof config.ondataavailable !== 'function' || typeof config.timeSlice === 'undefined') {\r\n return;\r\n }\r\n\r\n if (intervalsBasedBuffers.left.length) {\r\n mergeLeftRightBuffers({\r\n desiredSampRate: desiredSampRate,\r\n sampleRate: sampleRate,\r\n numberOfAudioChannels: numberOfAudioChannels,\r\n internalInterleavedLength: intervalsBasedBuffers.recordingLength,\r\n leftBuffers: intervalsBasedBuffers.left,\r\n rightBuffers: numberOfAudioChannels === 1 ? [] : intervalsBasedBuffers.right\r\n }, function(buffer, view) {\r\n var blob = new Blob([view], {\r\n type: 'audio/wav'\r\n });\r\n config.ondataavailable(blob);\r\n\r\n setTimeout(looper, config.timeSlice);\r\n });\r\n\r\n intervalsBasedBuffers = {\r\n left: [],\r\n right: [],\r\n recordingLength: 0\r\n };\r\n } else {\r\n setTimeout(looper, config.timeSlice);\r\n }\r\n }\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.StereoAudioRecorder = StereoAudioRecorder;\r\n}\n\r\n// _________________\r\n// CanvasRecorder.js\r\n\r\n/**\r\n * CanvasRecorder is a standalone class used by {@link RecordRTC} to bring HTML5-Canvas recording into video WebM. It uses HTML2Canvas library and runs top over {@link Whammy}.\r\n * @summary HTML2Canvas recording into video WebM.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef CanvasRecorder\r\n * @class\r\n * @example\r\n * var recorder = new CanvasRecorder(htmlElement, { disableLogs: true, useWhammyRecorder: true });\r\n * recorder.record();\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {HTMLElement} htmlElement - querySelector/getElementById/getElementsByTagName[0]/etc.\r\n * @param {object} config - {disableLogs:true, initCallback: function}\r\n */\r\n\r\nfunction CanvasRecorder(htmlElement, config) {\r\n if (typeof html2canvas === 'undefined') {\r\n throw 'Please link: https://www.webrtc-experiment.com/screenshot.js';\r\n }\r\n\r\n config = config || {};\r\n if (!config.frameInterval) {\r\n config.frameInterval = 10;\r\n }\r\n\r\n // via DetectRTC.js\r\n var isCanvasSupportsStreamCapturing = false;\r\n ['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) {\r\n if (item in document.createElement('canvas')) {\r\n isCanvasSupportsStreamCapturing = true;\r\n }\r\n });\r\n\r\n var _isChrome = (!!window.webkitRTCPeerConnection || !!window.webkitGetUserMedia) && !!window.chrome;\r\n\r\n var chromeVersion = 50;\r\n var matchArray = navigator.userAgent.match(/Chrom(e|ium)\\/([0-9]+)\\./);\r\n if (_isChrome && matchArray && matchArray[2]) {\r\n chromeVersion = parseInt(matchArray[2], 10);\r\n }\r\n\r\n if (_isChrome && chromeVersion < 52) {\r\n isCanvasSupportsStreamCapturing = false;\r\n }\r\n\r\n if (config.useWhammyRecorder) {\r\n isCanvasSupportsStreamCapturing = false;\r\n }\r\n\r\n var globalCanvas, mediaStreamRecorder;\r\n\r\n if (isCanvasSupportsStreamCapturing) {\r\n if (!config.disableLogs) {\r\n console.log('Your browser supports both MediRecorder API and canvas.captureStream!');\r\n }\r\n\r\n if (htmlElement instanceof HTMLCanvasElement) {\r\n globalCanvas = htmlElement;\r\n } else if (htmlElement instanceof CanvasRenderingContext2D) {\r\n globalCanvas = htmlElement.canvas;\r\n } else {\r\n throw 'Please pass either HTMLCanvasElement or CanvasRenderingContext2D.';\r\n }\r\n } else if (!!navigator.mozGetUserMedia) {\r\n if (!config.disableLogs) {\r\n console.error('Canvas recording is NOT supported in Firefox.');\r\n }\r\n }\r\n\r\n var isRecording;\r\n\r\n /**\r\n * This method records Canvas.\r\n * @method\r\n * @memberof CanvasRecorder\r\n * @example\r\n * recorder.record();\r\n */\r\n this.record = function() {\r\n isRecording = true;\r\n\r\n if (isCanvasSupportsStreamCapturing && !config.useWhammyRecorder) {\r\n // CanvasCaptureMediaStream\r\n var canvasMediaStream;\r\n if ('captureStream' in globalCanvas) {\r\n canvasMediaStream = globalCanvas.captureStream(25); // 25 FPS\r\n } else if ('mozCaptureStream' in globalCanvas) {\r\n canvasMediaStream = globalCanvas.mozCaptureStream(25);\r\n } else if ('webkitCaptureStream' in globalCanvas) {\r\n canvasMediaStream = globalCanvas.webkitCaptureStream(25);\r\n }\r\n\r\n try {\r\n var mdStream = new MediaStream();\r\n mdStream.addTrack(getTracks(canvasMediaStream, 'video')[0]);\r\n canvasMediaStream = mdStream;\r\n } catch (e) {}\r\n\r\n if (!canvasMediaStream) {\r\n throw 'captureStream API are NOT available.';\r\n }\r\n\r\n // Note: Jan 18, 2016 status is that, \r\n // Firefox MediaRecorder API can't record CanvasCaptureMediaStream object.\r\n mediaStreamRecorder = new MediaStreamRecorder(canvasMediaStream, {\r\n mimeType: config.mimeType || 'video/webm'\r\n });\r\n mediaStreamRecorder.record();\r\n } else {\r\n whammy.frames = [];\r\n lastTime = new Date().getTime();\r\n drawCanvasFrame();\r\n }\r\n\r\n if (config.initCallback) {\r\n config.initCallback();\r\n }\r\n };\r\n\r\n this.getWebPImages = function(callback) {\r\n if (htmlElement.nodeName.toLowerCase() !== 'canvas') {\r\n callback();\r\n return;\r\n }\r\n\r\n var framesLength = whammy.frames.length;\r\n whammy.frames.forEach(function(frame, idx) {\r\n var framesRemaining = framesLength - idx;\r\n if (!config.disableLogs) {\r\n console.log(framesRemaining + '/' + framesLength + ' frames remaining');\r\n }\r\n\r\n if (config.onEncodingCallback) {\r\n config.onEncodingCallback(framesRemaining, framesLength);\r\n }\r\n\r\n var webp = frame.image.toDataURL('image/webp', 1);\r\n whammy.frames[idx].image = webp;\r\n });\r\n\r\n if (!config.disableLogs) {\r\n console.log('Generating WebM');\r\n }\r\n\r\n callback();\r\n };\r\n\r\n /**\r\n * This method stops recording Canvas.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof CanvasRecorder\r\n * @example\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n */\r\n this.stop = function(callback) {\r\n isRecording = false;\r\n\r\n var that = this;\r\n\r\n if (isCanvasSupportsStreamCapturing && mediaStreamRecorder) {\r\n mediaStreamRecorder.stop(callback);\r\n return;\r\n }\r\n\r\n this.getWebPImages(function() {\r\n /**\r\n * @property {Blob} blob - Recorded frames in video/webm blob.\r\n * @memberof CanvasRecorder\r\n * @example\r\n * recorder.stop(function() {\r\n * var blob = recorder.blob;\r\n * });\r\n */\r\n whammy.compile(function(blob) {\r\n if (!config.disableLogs) {\r\n console.log('Recording finished!');\r\n }\r\n\r\n that.blob = blob;\r\n\r\n if (that.blob.forEach) {\r\n that.blob = new Blob([], {\r\n type: 'video/webm'\r\n });\r\n }\r\n\r\n if (callback) {\r\n callback(that.blob);\r\n }\r\n\r\n whammy.frames = [];\r\n });\r\n });\r\n };\r\n\r\n var isPausedRecording = false;\r\n\r\n /**\r\n * This method pauses the recording process.\r\n * @method\r\n * @memberof CanvasRecorder\r\n * @example\r\n * recorder.pause();\r\n */\r\n this.pause = function() {\r\n isPausedRecording = true;\r\n\r\n if (mediaStreamRecorder instanceof MediaStreamRecorder) {\r\n mediaStreamRecorder.pause();\r\n return;\r\n }\r\n };\r\n\r\n /**\r\n * This method resumes the recording process.\r\n * @method\r\n * @memberof CanvasRecorder\r\n * @example\r\n * recorder.resume();\r\n */\r\n this.resume = function() {\r\n isPausedRecording = false;\r\n\r\n if (mediaStreamRecorder instanceof MediaStreamRecorder) {\r\n mediaStreamRecorder.resume();\r\n return;\r\n }\r\n\r\n if (!isRecording) {\r\n this.record();\r\n }\r\n };\r\n\r\n /**\r\n * This method resets currently recorded data.\r\n * @method\r\n * @memberof CanvasRecorder\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n this.clearRecordedData = function() {\r\n if (isRecording) {\r\n this.stop(clearRecordedDataCB);\r\n }\r\n clearRecordedDataCB();\r\n };\r\n\r\n function clearRecordedDataCB() {\r\n whammy.frames = [];\r\n isRecording = false;\r\n isPausedRecording = false;\r\n }\r\n\r\n // for debugging\r\n this.name = 'CanvasRecorder';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n\r\n function cloneCanvas() {\r\n //create a new canvas\r\n var newCanvas = document.createElement('canvas');\r\n var context = newCanvas.getContext('2d');\r\n\r\n //set dimensions\r\n newCanvas.width = htmlElement.width;\r\n newCanvas.height = htmlElement.height;\r\n\r\n //apply the old canvas to the new one\r\n context.drawImage(htmlElement, 0, 0);\r\n\r\n //return the new canvas\r\n return newCanvas;\r\n }\r\n\r\n function drawCanvasFrame() {\r\n if (isPausedRecording) {\r\n lastTime = new Date().getTime();\r\n return setTimeout(drawCanvasFrame, 500);\r\n }\r\n\r\n if (htmlElement.nodeName.toLowerCase() === 'canvas') {\r\n var duration = new Date().getTime() - lastTime;\r\n // via #206, by Jack i.e. @Seymourr\r\n lastTime = new Date().getTime();\r\n\r\n whammy.frames.push({\r\n image: cloneCanvas(),\r\n duration: duration\r\n });\r\n\r\n if (isRecording) {\r\n setTimeout(drawCanvasFrame, config.frameInterval);\r\n }\r\n return;\r\n }\r\n\r\n html2canvas(htmlElement, {\r\n grabMouse: typeof config.showMousePointer === 'undefined' || config.showMousePointer,\r\n onrendered: function(canvas) {\r\n var duration = new Date().getTime() - lastTime;\r\n if (!duration) {\r\n return setTimeout(drawCanvasFrame, config.frameInterval);\r\n }\r\n\r\n // via #206, by Jack i.e. @Seymourr\r\n lastTime = new Date().getTime();\r\n\r\n whammy.frames.push({\r\n image: canvas.toDataURL('image/webp', 1),\r\n duration: duration\r\n });\r\n\r\n if (isRecording) {\r\n setTimeout(drawCanvasFrame, config.frameInterval);\r\n }\r\n }\r\n });\r\n }\r\n\r\n var lastTime = new Date().getTime();\r\n\r\n var whammy = new Whammy.Video(100);\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.CanvasRecorder = CanvasRecorder;\r\n}\n\r\n// _________________\r\n// WhammyRecorder.js\r\n\r\n/**\r\n * WhammyRecorder is a standalone class used by {@link RecordRTC} to bring video recording in Chrome. It runs top over {@link Whammy}.\r\n * @summary Video recording feature in Chrome.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef WhammyRecorder\r\n * @class\r\n * @example\r\n * var recorder = new WhammyRecorder(mediaStream);\r\n * recorder.record();\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.\r\n * @param {object} config - {disableLogs: true, initCallback: function, video: HTMLVideoElement, etc.}\r\n */\r\n\r\nfunction WhammyRecorder(mediaStream, config) {\r\n\r\n config = config || {};\r\n\r\n if (!config.frameInterval) {\r\n config.frameInterval = 10;\r\n }\r\n\r\n if (!config.disableLogs) {\r\n console.log('Using frames-interval:', config.frameInterval);\r\n }\r\n\r\n /**\r\n * This method records video.\r\n * @method\r\n * @memberof WhammyRecorder\r\n * @example\r\n * recorder.record();\r\n */\r\n this.record = function() {\r\n if (!config.width) {\r\n config.width = 320;\r\n }\r\n\r\n if (!config.height) {\r\n config.height = 240;\r\n }\r\n\r\n if (!config.video) {\r\n config.video = {\r\n width: config.width,\r\n height: config.height\r\n };\r\n }\r\n\r\n if (!config.canvas) {\r\n config.canvas = {\r\n width: config.width,\r\n height: config.height\r\n };\r\n }\r\n\r\n canvas.width = config.canvas.width || 320;\r\n canvas.height = config.canvas.height || 240;\r\n\r\n context = canvas.getContext('2d');\r\n\r\n // setting defaults\r\n if (config.video && config.video instanceof HTMLVideoElement) {\r\n video = config.video.cloneNode();\r\n\r\n if (config.initCallback) {\r\n config.initCallback();\r\n }\r\n } else {\r\n video = document.createElement('video');\r\n\r\n setSrcObject(mediaStream, video);\r\n\r\n video.onloadedmetadata = function() { // \"onloadedmetadata\" may NOT work in FF?\r\n if (config.initCallback) {\r\n config.initCallback();\r\n }\r\n };\r\n\r\n video.width = config.video.width;\r\n video.height = config.video.height;\r\n }\r\n\r\n video.muted = true;\r\n video.play();\r\n\r\n lastTime = new Date().getTime();\r\n whammy = new Whammy.Video();\r\n\r\n if (!config.disableLogs) {\r\n console.log('canvas resolutions', canvas.width, '*', canvas.height);\r\n console.log('video width/height', video.width || canvas.width, '*', video.height || canvas.height);\r\n }\r\n\r\n drawFrames(config.frameInterval);\r\n };\r\n\r\n /**\r\n * Draw and push frames to Whammy\r\n * @param {integer} frameInterval - set minimum interval (in milliseconds) between each time we push a frame to Whammy\r\n */\r\n function drawFrames(frameInterval) {\r\n frameInterval = typeof frameInterval !== 'undefined' ? frameInterval : 10;\r\n\r\n var duration = new Date().getTime() - lastTime;\r\n if (!duration) {\r\n return setTimeout(drawFrames, frameInterval, frameInterval);\r\n }\r\n\r\n if (isPausedRecording) {\r\n lastTime = new Date().getTime();\r\n return setTimeout(drawFrames, 100);\r\n }\r\n\r\n // via #206, by Jack i.e. @Seymourr\r\n lastTime = new Date().getTime();\r\n\r\n if (video.paused) {\r\n // via: https://github.com/muaz-khan/WebRTC-Experiment/pull/316\r\n // Tweak for Android Chrome\r\n video.play();\r\n }\r\n\r\n context.drawImage(video, 0, 0, canvas.width, canvas.height);\r\n whammy.frames.push({\r\n duration: duration,\r\n image: canvas.toDataURL('image/webp')\r\n });\r\n\r\n if (!isStopDrawing) {\r\n setTimeout(drawFrames, frameInterval, frameInterval);\r\n }\r\n }\r\n\r\n function asyncLoop(o) {\r\n var i = -1,\r\n length = o.length;\r\n\r\n (function loop() {\r\n i++;\r\n if (i === length) {\r\n o.callback();\r\n return;\r\n }\r\n\r\n // \"setTimeout\" added by Jim McLeod\r\n setTimeout(function() {\r\n o.functionToLoop(loop, i);\r\n }, 1);\r\n })();\r\n }\r\n\r\n\r\n /**\r\n * remove black frames from the beginning to the specified frame\r\n * @param {Array} _frames - array of frames to be checked\r\n * @param {number} _framesToCheck - number of frame until check will be executed (-1 - will drop all frames until frame not matched will be found)\r\n * @param {number} _pixTolerance - 0 - very strict (only black pixel color) ; 1 - all\r\n * @param {number} _frameTolerance - 0 - very strict (only black frame color) ; 1 - all\r\n * @returns {Array} - array of frames\r\n */\r\n // pull#293 by @volodalexey\r\n function dropBlackFrames(_frames, _framesToCheck, _pixTolerance, _frameTolerance, callback) {\r\n var localCanvas = document.createElement('canvas');\r\n localCanvas.width = canvas.width;\r\n localCanvas.height = canvas.height;\r\n var context2d = localCanvas.getContext('2d');\r\n var resultFrames = [];\r\n\r\n var checkUntilNotBlack = _framesToCheck === -1;\r\n var endCheckFrame = (_framesToCheck && _framesToCheck > 0 && _framesToCheck <= _frames.length) ?\r\n _framesToCheck : _frames.length;\r\n var sampleColor = {\r\n r: 0,\r\n g: 0,\r\n b: 0\r\n };\r\n var maxColorDifference = Math.sqrt(\r\n Math.pow(255, 2) +\r\n Math.pow(255, 2) +\r\n Math.pow(255, 2)\r\n );\r\n var pixTolerance = _pixTolerance && _pixTolerance >= 0 && _pixTolerance <= 1 ? _pixTolerance : 0;\r\n var frameTolerance = _frameTolerance && _frameTolerance >= 0 && _frameTolerance <= 1 ? _frameTolerance : 0;\r\n var doNotCheckNext = false;\r\n\r\n asyncLoop({\r\n length: endCheckFrame,\r\n functionToLoop: function(loop, f) {\r\n var matchPixCount, endPixCheck, maxPixCount;\r\n\r\n var finishImage = function() {\r\n if (!doNotCheckNext && maxPixCount - matchPixCount <= maxPixCount * frameTolerance) {\r\n // console.log('removed black frame : ' + f + ' ; frame duration ' + _frames[f].duration);\r\n } else {\r\n // console.log('frame is passed : ' + f);\r\n if (checkUntilNotBlack) {\r\n doNotCheckNext = true;\r\n }\r\n resultFrames.push(_frames[f]);\r\n }\r\n loop();\r\n };\r\n\r\n if (!doNotCheckNext) {\r\n var image = new Image();\r\n image.onload = function() {\r\n context2d.drawImage(image, 0, 0, canvas.width, canvas.height);\r\n var imageData = context2d.getImageData(0, 0, canvas.width, canvas.height);\r\n matchPixCount = 0;\r\n endPixCheck = imageData.data.length;\r\n maxPixCount = imageData.data.length / 4;\r\n\r\n for (var pix = 0; pix < endPixCheck; pix += 4) {\r\n var currentColor = {\r\n r: imageData.data[pix],\r\n g: imageData.data[pix + 1],\r\n b: imageData.data[pix + 2]\r\n };\r\n var colorDifference = Math.sqrt(\r\n Math.pow(currentColor.r - sampleColor.r, 2) +\r\n Math.pow(currentColor.g - sampleColor.g, 2) +\r\n Math.pow(currentColor.b - sampleColor.b, 2)\r\n );\r\n // difference in color it is difference in color vectors (r1,g1,b1) <=> (r2,g2,b2)\r\n if (colorDifference <= maxColorDifference * pixTolerance) {\r\n matchPixCount++;\r\n }\r\n }\r\n finishImage();\r\n };\r\n image.src = _frames[f].image;\r\n } else {\r\n finishImage();\r\n }\r\n },\r\n callback: function() {\r\n resultFrames = resultFrames.concat(_frames.slice(endCheckFrame));\r\n\r\n if (resultFrames.length <= 0) {\r\n // at least one last frame should be available for next manipulation\r\n // if total duration of all frames will be < 1000 than ffmpeg doesn't work well...\r\n resultFrames.push(_frames[_frames.length - 1]);\r\n }\r\n callback(resultFrames);\r\n }\r\n });\r\n }\r\n\r\n var isStopDrawing = false;\r\n\r\n /**\r\n * This method stops recording video.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof WhammyRecorder\r\n * @example\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n */\r\n this.stop = function(callback) {\r\n callback = callback || function() {};\r\n\r\n isStopDrawing = true;\r\n\r\n var _this = this;\r\n // analyse of all frames takes some time!\r\n setTimeout(function() {\r\n // e.g. dropBlackFrames(frames, 10, 1, 1) - will cut all 10 frames\r\n // e.g. dropBlackFrames(frames, 10, 0.5, 0.5) - will analyse 10 frames\r\n // e.g. dropBlackFrames(frames, 10) === dropBlackFrames(frames, 10, 0, 0) - will analyse 10 frames with strict black color\r\n dropBlackFrames(whammy.frames, -1, null, null, function(frames) {\r\n whammy.frames = frames;\r\n\r\n // to display advertisement images!\r\n if (config.advertisement && config.advertisement.length) {\r\n whammy.frames = config.advertisement.concat(whammy.frames);\r\n }\r\n\r\n /**\r\n * @property {Blob} blob - Recorded frames in video/webm blob.\r\n * @memberof WhammyRecorder\r\n * @example\r\n * recorder.stop(function() {\r\n * var blob = recorder.blob;\r\n * });\r\n */\r\n whammy.compile(function(blob) {\r\n _this.blob = blob;\r\n\r\n if (_this.blob.forEach) {\r\n _this.blob = new Blob([], {\r\n type: 'video/webm'\r\n });\r\n }\r\n\r\n if (callback) {\r\n callback(_this.blob);\r\n }\r\n });\r\n });\r\n }, 10);\r\n };\r\n\r\n var isPausedRecording = false;\r\n\r\n /**\r\n * This method pauses the recording process.\r\n * @method\r\n * @memberof WhammyRecorder\r\n * @example\r\n * recorder.pause();\r\n */\r\n this.pause = function() {\r\n isPausedRecording = true;\r\n };\r\n\r\n /**\r\n * This method resumes the recording process.\r\n * @method\r\n * @memberof WhammyRecorder\r\n * @example\r\n * recorder.resume();\r\n */\r\n this.resume = function() {\r\n isPausedRecording = false;\r\n\r\n if (isStopDrawing) {\r\n this.record();\r\n }\r\n };\r\n\r\n /**\r\n * This method resets currently recorded data.\r\n * @method\r\n * @memberof WhammyRecorder\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n this.clearRecordedData = function() {\r\n if (!isStopDrawing) {\r\n this.stop(clearRecordedDataCB);\r\n }\r\n clearRecordedDataCB();\r\n };\r\n\r\n function clearRecordedDataCB() {\r\n whammy.frames = [];\r\n isStopDrawing = true;\r\n isPausedRecording = false;\r\n }\r\n\r\n // for debugging\r\n this.name = 'WhammyRecorder';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n\r\n var canvas = document.createElement('canvas');\r\n var context = canvas.getContext('2d');\r\n\r\n var video;\r\n var lastTime;\r\n var whammy;\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.WhammyRecorder = WhammyRecorder;\r\n}\n\r\n// https://github.com/antimatter15/whammy/blob/master/LICENSE\r\n// _________\r\n// Whammy.js\r\n\r\n// todo: Firefox now supports webp for webm containers!\r\n// their MediaRecorder implementation works well!\r\n// should we provide an option to record via Whammy.js or MediaRecorder API is a better solution?\r\n\r\n/**\r\n * Whammy is a standalone class used by {@link RecordRTC} to bring video recording in Chrome. It is written by {@link https://github.com/antimatter15|antimatter15}\r\n * @summary A real time javascript webm encoder based on a canvas hack.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef Whammy\r\n * @class\r\n * @example\r\n * var recorder = new Whammy().Video(15);\r\n * recorder.add(context || canvas || dataURL);\r\n * var output = recorder.compile();\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n */\r\n\r\nvar Whammy = (function() {\r\n // a more abstract-ish API\r\n\r\n function WhammyVideo(duration) {\r\n this.frames = [];\r\n this.duration = duration || 1;\r\n this.quality = 0.8;\r\n }\r\n\r\n /**\r\n * Pass Canvas or Context or image/webp(string) to {@link Whammy} encoder.\r\n * @method\r\n * @memberof Whammy\r\n * @example\r\n * recorder = new Whammy().Video(0.8, 100);\r\n * recorder.add(canvas || context || 'image/webp');\r\n * @param {string} frame - Canvas || Context || image/webp\r\n * @param {number} duration - Stick a duration (in milliseconds)\r\n */\r\n WhammyVideo.prototype.add = function(frame, duration) {\r\n if ('canvas' in frame) { //CanvasRenderingContext2D\r\n frame = frame.canvas;\r\n }\r\n\r\n if ('toDataURL' in frame) {\r\n frame = frame.toDataURL('image/webp', this.quality);\r\n }\r\n\r\n if (!(/^data:image\\/webp;base64,/ig).test(frame)) {\r\n throw 'Input must be formatted properly as a base64 encoded DataURI of type image/webp';\r\n }\r\n this.frames.push({\r\n image: frame,\r\n duration: duration || this.duration\r\n });\r\n };\r\n\r\n function processInWebWorker(_function) {\r\n var blob = URL.createObjectURL(new Blob([_function.toString(),\r\n 'this.onmessage = function (eee) {' + _function.name + '(eee.data);}'\r\n ], {\r\n type: 'application/javascript'\r\n }));\r\n\r\n var worker = new Worker(blob);\r\n URL.revokeObjectURL(blob);\r\n return worker;\r\n }\r\n\r\n function whammyInWebWorker(frames) {\r\n function ArrayToWebM(frames) {\r\n var info = checkFrames(frames);\r\n if (!info) {\r\n return [];\r\n }\r\n\r\n var clusterMaxDuration = 30000;\r\n\r\n var EBML = [{\r\n 'id': 0x1a45dfa3, // EBML\r\n 'data': [{\r\n 'data': 1,\r\n 'id': 0x4286 // EBMLVersion\r\n }, {\r\n 'data': 1,\r\n 'id': 0x42f7 // EBMLReadVersion\r\n }, {\r\n 'data': 4,\r\n 'id': 0x42f2 // EBMLMaxIDLength\r\n }, {\r\n 'data': 8,\r\n 'id': 0x42f3 // EBMLMaxSizeLength\r\n }, {\r\n 'data': 'webm',\r\n 'id': 0x4282 // DocType\r\n }, {\r\n 'data': 2,\r\n 'id': 0x4287 // DocTypeVersion\r\n }, {\r\n 'data': 2,\r\n 'id': 0x4285 // DocTypeReadVersion\r\n }]\r\n }, {\r\n 'id': 0x18538067, // Segment\r\n 'data': [{\r\n 'id': 0x1549a966, // Info\r\n 'data': [{\r\n 'data': 1e6, //do things in millisecs (num of nanosecs for duration scale)\r\n 'id': 0x2ad7b1 // TimecodeScale\r\n }, {\r\n 'data': 'whammy',\r\n 'id': 0x4d80 // MuxingApp\r\n }, {\r\n 'data': 'whammy',\r\n 'id': 0x5741 // WritingApp\r\n }, {\r\n 'data': doubleToString(info.duration),\r\n 'id': 0x4489 // Duration\r\n }]\r\n }, {\r\n 'id': 0x1654ae6b, // Tracks\r\n 'data': [{\r\n 'id': 0xae, // TrackEntry\r\n 'data': [{\r\n 'data': 1,\r\n 'id': 0xd7 // TrackNumber\r\n }, {\r\n 'data': 1,\r\n 'id': 0x73c5 // TrackUID\r\n }, {\r\n 'data': 0,\r\n 'id': 0x9c // FlagLacing\r\n }, {\r\n 'data': 'und',\r\n 'id': 0x22b59c // Language\r\n }, {\r\n 'data': 'V_VP8',\r\n 'id': 0x86 // CodecID\r\n }, {\r\n 'data': 'VP8',\r\n 'id': 0x258688 // CodecName\r\n }, {\r\n 'data': 1,\r\n 'id': 0x83 // TrackType\r\n }, {\r\n 'id': 0xe0, // Video\r\n 'data': [{\r\n 'data': info.width,\r\n 'id': 0xb0 // PixelWidth\r\n }, {\r\n 'data': info.height,\r\n 'id': 0xba // PixelHeight\r\n }]\r\n }]\r\n }]\r\n }]\r\n }];\r\n\r\n //Generate clusters (max duration)\r\n var frameNumber = 0;\r\n var clusterTimecode = 0;\r\n while (frameNumber < frames.length) {\r\n\r\n var clusterFrames = [];\r\n var clusterDuration = 0;\r\n do {\r\n clusterFrames.push(frames[frameNumber]);\r\n clusterDuration += frames[frameNumber].duration;\r\n frameNumber++;\r\n } while (frameNumber < frames.length && clusterDuration < clusterMaxDuration);\r\n\r\n var clusterCounter = 0;\r\n var cluster = {\r\n 'id': 0x1f43b675, // Cluster\r\n 'data': getClusterData(clusterTimecode, clusterCounter, clusterFrames)\r\n }; //Add cluster to segment\r\n EBML[1].data.push(cluster);\r\n clusterTimecode += clusterDuration;\r\n }\r\n\r\n return generateEBML(EBML);\r\n }\r\n\r\n function getClusterData(clusterTimecode, clusterCounter, clusterFrames) {\r\n return [{\r\n 'data': clusterTimecode,\r\n 'id': 0xe7 // Timecode\r\n }].concat(clusterFrames.map(function(webp) {\r\n var block = makeSimpleBlock({\r\n discardable: 0,\r\n frame: webp.data.slice(4),\r\n invisible: 0,\r\n keyframe: 1,\r\n lacing: 0,\r\n trackNum: 1,\r\n timecode: Math.round(clusterCounter)\r\n });\r\n clusterCounter += webp.duration;\r\n return {\r\n data: block,\r\n id: 0xa3\r\n };\r\n }));\r\n }\r\n\r\n // sums the lengths of all the frames and gets the duration\r\n\r\n function checkFrames(frames) {\r\n if (!frames[0]) {\r\n postMessage({\r\n error: 'Something went wrong. Maybe WebP format is not supported in the current browser.'\r\n });\r\n return;\r\n }\r\n\r\n var width = frames[0].width,\r\n height = frames[0].height,\r\n duration = frames[0].duration;\r\n\r\n for (var i = 1; i < frames.length; i++) {\r\n duration += frames[i].duration;\r\n }\r\n return {\r\n duration: duration,\r\n width: width,\r\n height: height\r\n };\r\n }\r\n\r\n function numToBuffer(num) {\r\n var parts = [];\r\n while (num > 0) {\r\n parts.push(num & 0xff);\r\n num = num >> 8;\r\n }\r\n return new Uint8Array(parts.reverse());\r\n }\r\n\r\n function strToBuffer(str) {\r\n return new Uint8Array(str.split('').map(function(e) {\r\n return e.charCodeAt(0);\r\n }));\r\n }\r\n\r\n function bitsToBuffer(bits) {\r\n var data = [];\r\n var pad = (bits.length % 8) ? (new Array(1 + 8 - (bits.length % 8))).join('0') : '';\r\n bits = pad + bits;\r\n for (var i = 0; i < bits.length; i += 8) {\r\n data.push(parseInt(bits.substr(i, 8), 2));\r\n }\r\n return new Uint8Array(data);\r\n }\r\n\r\n function generateEBML(json) {\r\n var ebml = [];\r\n for (var i = 0; i < json.length; i++) {\r\n var data = json[i].data;\r\n\r\n if (typeof data === 'object') {\r\n data = generateEBML(data);\r\n }\r\n\r\n if (typeof data === 'number') {\r\n data = bitsToBuffer(data.toString(2));\r\n }\r\n\r\n if (typeof data === 'string') {\r\n data = strToBuffer(data);\r\n }\r\n\r\n var len = data.size || data.byteLength || data.length;\r\n var zeroes = Math.ceil(Math.ceil(Math.log(len) / Math.log(2)) / 8);\r\n var sizeToString = len.toString(2);\r\n var padded = (new Array((zeroes * 7 + 7 + 1) - sizeToString.length)).join('0') + sizeToString;\r\n var size = (new Array(zeroes)).join('0') + '1' + padded;\r\n\r\n ebml.push(numToBuffer(json[i].id));\r\n ebml.push(bitsToBuffer(size));\r\n ebml.push(data);\r\n }\r\n\r\n return new Blob(ebml, {\r\n type: 'video/webm'\r\n });\r\n }\r\n\r\n function toBinStrOld(bits) {\r\n var data = '';\r\n var pad = (bits.length % 8) ? (new Array(1 + 8 - (bits.length % 8))).join('0') : '';\r\n bits = pad + bits;\r\n for (var i = 0; i < bits.length; i += 8) {\r\n data += String.fromCharCode(parseInt(bits.substr(i, 8), 2));\r\n }\r\n return data;\r\n }\r\n\r\n function makeSimpleBlock(data) {\r\n var flags = 0;\r\n\r\n if (data.keyframe) {\r\n flags |= 128;\r\n }\r\n\r\n if (data.invisible) {\r\n flags |= 8;\r\n }\r\n\r\n if (data.lacing) {\r\n flags |= (data.lacing << 1);\r\n }\r\n\r\n if (data.discardable) {\r\n flags |= 1;\r\n }\r\n\r\n if (data.trackNum > 127) {\r\n throw 'TrackNumber > 127 not supported';\r\n }\r\n\r\n var out = [data.trackNum | 0x80, data.timecode >> 8, data.timecode & 0xff, flags].map(function(e) {\r\n return String.fromCharCode(e);\r\n }).join('') + data.frame;\r\n\r\n return out;\r\n }\r\n\r\n function parseWebP(riff) {\r\n var VP8 = riff.RIFF[0].WEBP[0];\r\n\r\n var frameStart = VP8.indexOf('\\x9d\\x01\\x2a'); // A VP8 keyframe starts with the 0x9d012a header\r\n for (var i = 0, c = []; i < 4; i++) {\r\n c[i] = VP8.charCodeAt(frameStart + 3 + i);\r\n }\r\n\r\n var width, height, tmp;\r\n\r\n //the code below is literally copied verbatim from the bitstream spec\r\n tmp = (c[1] << 8) | c[0];\r\n width = tmp & 0x3FFF;\r\n tmp = (c[3] << 8) | c[2];\r\n height = tmp & 0x3FFF;\r\n return {\r\n width: width,\r\n height: height,\r\n data: VP8,\r\n riff: riff\r\n };\r\n }\r\n\r\n function getStrLength(string, offset) {\r\n return parseInt(string.substr(offset + 4, 4).split('').map(function(i) {\r\n var unpadded = i.charCodeAt(0).toString(2);\r\n return (new Array(8 - unpadded.length + 1)).join('0') + unpadded;\r\n }).join(''), 2);\r\n }\r\n\r\n function parseRIFF(string) {\r\n var offset = 0;\r\n var chunks = {};\r\n\r\n while (offset < string.length) {\r\n var id = string.substr(offset, 4);\r\n var len = getStrLength(string, offset);\r\n var data = string.substr(offset + 4 + 4, len);\r\n offset += 4 + 4 + len;\r\n chunks[id] = chunks[id] || [];\r\n\r\n if (id === 'RIFF' || id === 'LIST') {\r\n chunks[id].push(parseRIFF(data));\r\n } else {\r\n chunks[id].push(data);\r\n }\r\n }\r\n return chunks;\r\n }\r\n\r\n function doubleToString(num) {\r\n return [].slice.call(\r\n new Uint8Array((new Float64Array([num])).buffer), 0).map(function(e) {\r\n return String.fromCharCode(e);\r\n }).reverse().join('');\r\n }\r\n\r\n var webm = new ArrayToWebM(frames.map(function(frame) {\r\n var webp = parseWebP(parseRIFF(atob(frame.image.slice(23))));\r\n webp.duration = frame.duration;\r\n return webp;\r\n }));\r\n\r\n postMessage(webm);\r\n }\r\n\r\n /**\r\n * Encodes frames in WebM container. It uses WebWorkinvoke to invoke 'ArrayToWebM' method.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof Whammy\r\n * @example\r\n * recorder = new Whammy().Video(0.8, 100);\r\n * recorder.compile(function(blob) {\r\n * // blob.size - blob.type\r\n * });\r\n */\r\n WhammyVideo.prototype.compile = function(callback) {\r\n var webWorker = processInWebWorker(whammyInWebWorker);\r\n\r\n webWorker.onmessage = function(event) {\r\n if (event.data.error) {\r\n console.error(event.data.error);\r\n return;\r\n }\r\n callback(event.data);\r\n };\r\n\r\n webWorker.postMessage(this.frames);\r\n };\r\n\r\n return {\r\n /**\r\n * A more abstract-ish API.\r\n * @method\r\n * @memberof Whammy\r\n * @example\r\n * recorder = new Whammy().Video(0.8, 100);\r\n * @param {?number} speed - 0.8\r\n * @param {?number} quality - 100\r\n */\r\n Video: WhammyVideo\r\n };\r\n})();\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.Whammy = Whammy;\r\n}\n\r\n// ______________ (indexed-db)\r\n// DiskStorage.js\r\n\r\n/**\r\n * DiskStorage is a standalone object used by {@link RecordRTC} to store recorded blobs in IndexedDB storage.\r\n * @summary Writing blobs into IndexedDB.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @example\r\n * DiskStorage.Store({\r\n * audioBlob: yourAudioBlob,\r\n * videoBlob: yourVideoBlob,\r\n * gifBlob : yourGifBlob\r\n * });\r\n * DiskStorage.Fetch(function(dataURL, type) {\r\n * if(type === 'audioBlob') { }\r\n * if(type === 'videoBlob') { }\r\n * if(type === 'gifBlob') { }\r\n * });\r\n * // DiskStorage.dataStoreName = 'recordRTC';\r\n * // DiskStorage.onError = function(error) { };\r\n * @property {function} init - This method must be called once to initialize IndexedDB ObjectStore. Though, it is auto-used internally.\r\n * @property {function} Fetch - This method fetches stored blobs from IndexedDB.\r\n * @property {function} Store - This method stores blobs in IndexedDB.\r\n * @property {function} onError - This function is invoked for any known/unknown error.\r\n * @property {string} dataStoreName - Name of the ObjectStore created in IndexedDB storage.\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n */\r\n\r\n\r\nvar DiskStorage = {\r\n /**\r\n * This method must be called once to initialize IndexedDB ObjectStore. Though, it is auto-used internally.\r\n * @method\r\n * @memberof DiskStorage\r\n * @internal\r\n * @example\r\n * DiskStorage.init();\r\n */\r\n init: function() {\r\n var self = this;\r\n\r\n if (typeof indexedDB === 'undefined' || typeof indexedDB.open === 'undefined') {\r\n console.error('IndexedDB API are not available in this browser.');\r\n return;\r\n }\r\n\r\n var dbVersion = 1;\r\n var dbName = this.dbName || location.href.replace(/\\/|:|#|%|\\.|\\[|\\]/g, ''),\r\n db;\r\n var request = indexedDB.open(dbName, dbVersion);\r\n\r\n function createObjectStore(dataBase) {\r\n dataBase.createObjectStore(self.dataStoreName);\r\n }\r\n\r\n function putInDB() {\r\n var transaction = db.transaction([self.dataStoreName], 'readwrite');\r\n\r\n if (self.videoBlob) {\r\n transaction.objectStore(self.dataStoreName).put(self.videoBlob, 'videoBlob');\r\n }\r\n\r\n if (self.gifBlob) {\r\n transaction.objectStore(self.dataStoreName).put(self.gifBlob, 'gifBlob');\r\n }\r\n\r\n if (self.audioBlob) {\r\n transaction.objectStore(self.dataStoreName).put(self.audioBlob, 'audioBlob');\r\n }\r\n\r\n function getFromStore(portionName) {\r\n transaction.objectStore(self.dataStoreName).get(portionName).onsuccess = function(event) {\r\n if (self.callback) {\r\n self.callback(event.target.result, portionName);\r\n }\r\n };\r\n }\r\n\r\n getFromStore('audioBlob');\r\n getFromStore('videoBlob');\r\n getFromStore('gifBlob');\r\n }\r\n\r\n request.onerror = self.onError;\r\n\r\n request.onsuccess = function() {\r\n db = request.result;\r\n db.onerror = self.onError;\r\n\r\n if (db.setVersion) {\r\n if (db.version !== dbVersion) {\r\n var setVersion = db.setVersion(dbVersion);\r\n setVersion.onsuccess = function() {\r\n createObjectStore(db);\r\n putInDB();\r\n };\r\n } else {\r\n putInDB();\r\n }\r\n } else {\r\n putInDB();\r\n }\r\n };\r\n request.onupgradeneeded = function(event) {\r\n createObjectStore(event.target.result);\r\n };\r\n },\r\n /**\r\n * This method fetches stored blobs from IndexedDB.\r\n * @method\r\n * @memberof DiskStorage\r\n * @internal\r\n * @example\r\n * DiskStorage.Fetch(function(dataURL, type) {\r\n * if(type === 'audioBlob') { }\r\n * if(type === 'videoBlob') { }\r\n * if(type === 'gifBlob') { }\r\n * });\r\n */\r\n Fetch: function(callback) {\r\n this.callback = callback;\r\n this.init();\r\n\r\n return this;\r\n },\r\n /**\r\n * This method stores blobs in IndexedDB.\r\n * @method\r\n * @memberof DiskStorage\r\n * @internal\r\n * @example\r\n * DiskStorage.Store({\r\n * audioBlob: yourAudioBlob,\r\n * videoBlob: yourVideoBlob,\r\n * gifBlob : yourGifBlob\r\n * });\r\n */\r\n Store: function(config) {\r\n this.audioBlob = config.audioBlob;\r\n this.videoBlob = config.videoBlob;\r\n this.gifBlob = config.gifBlob;\r\n\r\n this.init();\r\n\r\n return this;\r\n },\r\n /**\r\n * This function is invoked for any known/unknown error.\r\n * @method\r\n * @memberof DiskStorage\r\n * @internal\r\n * @example\r\n * DiskStorage.onError = function(error){\r\n * alerot( JSON.stringify(error) );\r\n * };\r\n */\r\n onError: function(error) {\r\n console.error(JSON.stringify(error, null, '\\t'));\r\n },\r\n\r\n /**\r\n * @property {string} dataStoreName - Name of the ObjectStore created in IndexedDB storage.\r\n * @memberof DiskStorage\r\n * @internal\r\n * @example\r\n * DiskStorage.dataStoreName = 'recordRTC';\r\n */\r\n dataStoreName: 'recordRTC',\r\n dbName: null\r\n};\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.DiskStorage = DiskStorage;\r\n}\n\r\n// ______________\r\n// GifRecorder.js\r\n\r\n/**\r\n * GifRecorder is standalone calss used by {@link RecordRTC} to record video or canvas into animated gif.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef GifRecorder\r\n * @class\r\n * @example\r\n * var recorder = new GifRecorder(mediaStream || canvas || context, { onGifPreview: function, onGifRecordingStarted: function, width: 1280, height: 720, frameRate: 200, quality: 10 });\r\n * recorder.record();\r\n * recorder.stop(function(blob) {\r\n * img.src = URL.createObjectURL(blob);\r\n * });\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object or HTMLCanvasElement or CanvasRenderingContext2D.\r\n * @param {object} config - {disableLogs:true, initCallback: function, width: 320, height: 240, frameRate: 200, quality: 10}\r\n */\r\n\r\nfunction GifRecorder(mediaStream, config) {\r\n if (typeof GIFEncoder === 'undefined') {\r\n var script = document.createElement('script');\r\n script.src = 'https://www.webrtc-experiment.com/gif-recorder.js';\r\n (document.body || document.documentElement).appendChild(script);\r\n }\r\n\r\n config = config || {};\r\n\r\n var isHTMLObject = mediaStream instanceof CanvasRenderingContext2D || mediaStream instanceof HTMLCanvasElement;\r\n\r\n /**\r\n * This method records MediaStream.\r\n * @method\r\n * @memberof GifRecorder\r\n * @example\r\n * recorder.record();\r\n */\r\n this.record = function() {\r\n if (typeof GIFEncoder === 'undefined') {\r\n setTimeout(self.record, 1000);\r\n return;\r\n }\r\n\r\n if (!isLoadedMetaData) {\r\n setTimeout(self.record, 1000);\r\n return;\r\n }\r\n\r\n if (!isHTMLObject) {\r\n if (!config.width) {\r\n config.width = video.offsetWidth || 320;\r\n }\r\n\r\n if (!config.height) {\r\n config.height = video.offsetHeight || 240;\r\n }\r\n\r\n if (!config.video) {\r\n config.video = {\r\n width: config.width,\r\n height: config.height\r\n };\r\n }\r\n\r\n if (!config.canvas) {\r\n config.canvas = {\r\n width: config.width,\r\n height: config.height\r\n };\r\n }\r\n\r\n canvas.width = config.canvas.width || 320;\r\n canvas.height = config.canvas.height || 240;\r\n\r\n video.width = config.video.width || 320;\r\n video.height = config.video.height || 240;\r\n }\r\n\r\n // external library to record as GIF images\r\n gifEncoder = new GIFEncoder();\r\n\r\n // void setRepeat(int iter) \r\n // Sets the number of times the set of GIF frames should be played. \r\n // Default is 1; 0 means play indefinitely.\r\n gifEncoder.setRepeat(0);\r\n\r\n // void setFrameRate(Number fps) \r\n // Sets frame rate in frames per second. \r\n // Equivalent to setDelay(1000/fps).\r\n // Using \"setDelay\" instead of \"setFrameRate\"\r\n gifEncoder.setDelay(config.frameRate || 200);\r\n\r\n // void setQuality(int quality) \r\n // Sets quality of color quantization (conversion of images to the \r\n // maximum 256 colors allowed by the GIF specification). \r\n // Lower values (minimum = 1) produce better colors, \r\n // but slow processing significantly. 10 is the default, \r\n // and produces good color mapping at reasonable speeds. \r\n // Values greater than 20 do not yield significant improvements in speed.\r\n gifEncoder.setQuality(config.quality || 10);\r\n\r\n // Boolean start() \r\n // This writes the GIF Header and returns false if it fails.\r\n gifEncoder.start();\r\n\r\n if (typeof config.onGifRecordingStarted === 'function') {\r\n config.onGifRecordingStarted();\r\n }\r\n\r\n startTime = Date.now();\r\n\r\n function drawVideoFrame(time) {\r\n if (self.clearedRecordedData === true) {\r\n return;\r\n }\r\n\r\n if (isPausedRecording) {\r\n return setTimeout(function() {\r\n drawVideoFrame(time);\r\n }, 100);\r\n }\r\n\r\n lastAnimationFrame = requestAnimationFrame(drawVideoFrame);\r\n\r\n if (typeof lastFrameTime === undefined) {\r\n lastFrameTime = time;\r\n }\r\n\r\n // ~10 fps\r\n if (time - lastFrameTime < 90) {\r\n return;\r\n }\r\n\r\n if (!isHTMLObject && video.paused) {\r\n // via: https://github.com/muaz-khan/WebRTC-Experiment/pull/316\r\n // Tweak for Android Chrome\r\n video.play();\r\n }\r\n\r\n if (!isHTMLObject) {\r\n context.drawImage(video, 0, 0, canvas.width, canvas.height);\r\n }\r\n\r\n if (config.onGifPreview) {\r\n config.onGifPreview(canvas.toDataURL('image/png'));\r\n }\r\n\r\n gifEncoder.addFrame(context);\r\n lastFrameTime = time;\r\n }\r\n\r\n lastAnimationFrame = requestAnimationFrame(drawVideoFrame);\r\n\r\n if (config.initCallback) {\r\n config.initCallback();\r\n }\r\n };\r\n\r\n /**\r\n * This method stops recording MediaStream.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof GifRecorder\r\n * @example\r\n * recorder.stop(function(blob) {\r\n * img.src = URL.createObjectURL(blob);\r\n * });\r\n */\r\n this.stop = function(callback) {\r\n callback = callback || function() {};\r\n\r\n if (lastAnimationFrame) {\r\n cancelAnimationFrame(lastAnimationFrame);\r\n }\r\n\r\n endTime = Date.now();\r\n\r\n /**\r\n * @property {Blob} blob - The recorded blob object.\r\n * @memberof GifRecorder\r\n * @example\r\n * recorder.stop(function(){\r\n * var blob = recorder.blob;\r\n * });\r\n */\r\n this.blob = new Blob([new Uint8Array(gifEncoder.stream().bin)], {\r\n type: 'image/gif'\r\n });\r\n\r\n callback(this.blob);\r\n\r\n // bug: find a way to clear old recorded blobs\r\n gifEncoder.stream().bin = [];\r\n };\r\n\r\n var isPausedRecording = false;\r\n\r\n /**\r\n * This method pauses the recording process.\r\n * @method\r\n * @memberof GifRecorder\r\n * @example\r\n * recorder.pause();\r\n */\r\n this.pause = function() {\r\n isPausedRecording = true;\r\n };\r\n\r\n /**\r\n * This method resumes the recording process.\r\n * @method\r\n * @memberof GifRecorder\r\n * @example\r\n * recorder.resume();\r\n */\r\n this.resume = function() {\r\n isPausedRecording = false;\r\n };\r\n\r\n /**\r\n * This method resets currently recorded data.\r\n * @method\r\n * @memberof GifRecorder\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n this.clearRecordedData = function() {\r\n self.clearedRecordedData = true;\r\n clearRecordedDataCB();\r\n };\r\n\r\n function clearRecordedDataCB() {\r\n if (gifEncoder) {\r\n gifEncoder.stream().bin = [];\r\n }\r\n }\r\n\r\n // for debugging\r\n this.name = 'GifRecorder';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n\r\n var canvas = document.createElement('canvas');\r\n var context = canvas.getContext('2d');\r\n\r\n if (isHTMLObject) {\r\n if (mediaStream instanceof CanvasRenderingContext2D) {\r\n context = mediaStream;\r\n canvas = context.canvas;\r\n } else if (mediaStream instanceof HTMLCanvasElement) {\r\n context = mediaStream.getContext('2d');\r\n canvas = mediaStream;\r\n }\r\n }\r\n\r\n var isLoadedMetaData = true;\r\n\r\n if (!isHTMLObject) {\r\n var video = document.createElement('video');\r\n video.muted = true;\r\n video.autoplay = true;\r\n video.playsInline = true;\r\n\r\n isLoadedMetaData = false;\r\n video.onloadedmetadata = function() {\r\n isLoadedMetaData = true;\r\n };\r\n\r\n setSrcObject(mediaStream, video);\r\n\r\n video.play();\r\n }\r\n\r\n var lastAnimationFrame = null;\r\n var startTime, endTime, lastFrameTime;\r\n\r\n var gifEncoder;\r\n\r\n var self = this;\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.GifRecorder = GifRecorder;\r\n}\n\r\n// Last time updated: 2019-06-21 4:09:42 AM UTC\r\n\r\n// ________________________\r\n// MultiStreamsMixer v1.2.2\r\n\r\n// Open-Sourced: https://github.com/muaz-khan/MultiStreamsMixer\r\n\r\n// --------------------------------------------------\r\n// Muaz Khan - www.MuazKhan.com\r\n// MIT License - www.WebRTC-Experiment.com/licence\r\n// --------------------------------------------------\r\n\r\nfunction MultiStreamsMixer(arrayOfMediaStreams, elementClass) {\r\n\r\n var browserFakeUserAgent = 'Fake/5.0 (FakeOS) AppleWebKit/123 (KHTML, like Gecko) Fake/12.3.4567.89 Fake/123.45';\r\n\r\n (function(that) {\r\n if (typeof RecordRTC !== 'undefined') {\r\n return;\r\n }\r\n\r\n if (!that) {\r\n return;\r\n }\r\n\r\n if (typeof window !== 'undefined') {\r\n return;\r\n }\r\n\r\n if (typeof global === 'undefined') {\r\n return;\r\n }\r\n\r\n global.navigator = {\r\n userAgent: browserFakeUserAgent,\r\n getUserMedia: function() {}\r\n };\r\n\r\n if (!global.console) {\r\n global.console = {};\r\n }\r\n\r\n if (typeof global.console.log === 'undefined' || typeof global.console.error === 'undefined') {\r\n global.console.error = global.console.log = global.console.log || function() {\r\n console.log(arguments);\r\n };\r\n }\r\n\r\n if (typeof document === 'undefined') {\r\n /*global document:true */\r\n that.document = {\r\n documentElement: {\r\n appendChild: function() {\r\n return '';\r\n }\r\n }\r\n };\r\n\r\n document.createElement = document.captureStream = document.mozCaptureStream = function() {\r\n var obj = {\r\n getContext: function() {\r\n return obj;\r\n },\r\n play: function() {},\r\n pause: function() {},\r\n drawImage: function() {},\r\n toDataURL: function() {\r\n return '';\r\n },\r\n style: {}\r\n };\r\n return obj;\r\n };\r\n\r\n that.HTMLVideoElement = function() {};\r\n }\r\n\r\n if (typeof location === 'undefined') {\r\n /*global location:true */\r\n that.location = {\r\n protocol: 'file:',\r\n href: '',\r\n hash: ''\r\n };\r\n }\r\n\r\n if (typeof screen === 'undefined') {\r\n /*global screen:true */\r\n that.screen = {\r\n width: 0,\r\n height: 0\r\n };\r\n }\r\n\r\n if (typeof URL === 'undefined') {\r\n /*global screen:true */\r\n that.URL = {\r\n createObjectURL: function() {\r\n return '';\r\n },\r\n revokeObjectURL: function() {\r\n return '';\r\n }\r\n };\r\n }\r\n\r\n /*global window:true */\r\n that.window = global;\r\n })(typeof global !== 'undefined' ? global : null);\r\n\r\n // requires: chrome://flags/#enable-experimental-web-platform-features\r\n\r\n elementClass = elementClass || 'multi-streams-mixer';\r\n\r\n var videos = [];\r\n var isStopDrawingFrames = false;\r\n\r\n var canvas = document.createElement('canvas');\r\n var context = canvas.getContext('2d');\r\n canvas.style.opacity = 0;\r\n canvas.style.position = 'absolute';\r\n canvas.style.zIndex = -1;\r\n canvas.style.top = '-1000em';\r\n canvas.style.left = '-1000em';\r\n canvas.className = elementClass;\r\n (document.body || document.documentElement).appendChild(canvas);\r\n\r\n this.disableLogs = false;\r\n this.frameInterval = 10;\r\n\r\n this.width = 360;\r\n this.height = 240;\r\n\r\n // use gain node to prevent echo\r\n this.useGainNode = true;\r\n\r\n var self = this;\r\n\r\n // _____________________________\r\n // Cross-Browser-Declarations.js\r\n\r\n // WebAudio API representer\r\n var AudioContext = window.AudioContext;\r\n\r\n if (typeof AudioContext === 'undefined') {\r\n if (typeof webkitAudioContext !== 'undefined') {\r\n /*global AudioContext:true */\r\n AudioContext = webkitAudioContext;\r\n }\r\n\r\n if (typeof mozAudioContext !== 'undefined') {\r\n /*global AudioContext:true */\r\n AudioContext = mozAudioContext;\r\n }\r\n }\r\n\r\n /*jshint -W079 */\r\n var URL = window.URL;\r\n\r\n if (typeof URL === 'undefined' && typeof webkitURL !== 'undefined') {\r\n /*global URL:true */\r\n URL = webkitURL;\r\n }\r\n\r\n if (typeof navigator !== 'undefined' && typeof navigator.getUserMedia === 'undefined') { // maybe window.navigator?\r\n if (typeof navigator.webkitGetUserMedia !== 'undefined') {\r\n navigator.getUserMedia = navigator.webkitGetUserMedia;\r\n }\r\n\r\n if (typeof navigator.mozGetUserMedia !== 'undefined') {\r\n navigator.getUserMedia = navigator.mozGetUserMedia;\r\n }\r\n }\r\n\r\n var MediaStream = window.MediaStream;\r\n\r\n if (typeof MediaStream === 'undefined' && typeof webkitMediaStream !== 'undefined') {\r\n MediaStream = webkitMediaStream;\r\n }\r\n\r\n /*global MediaStream:true */\r\n if (typeof MediaStream !== 'undefined') {\r\n // override \"stop\" method for all browsers\r\n if (typeof MediaStream.prototype.stop === 'undefined') {\r\n MediaStream.prototype.stop = function() {\r\n this.getTracks().forEach(function(track) {\r\n track.stop();\r\n });\r\n };\r\n }\r\n }\r\n\r\n var Storage = {};\r\n\r\n if (typeof AudioContext !== 'undefined') {\r\n Storage.AudioContext = AudioContext;\r\n } else if (typeof webkitAudioContext !== 'undefined') {\r\n Storage.AudioContext = webkitAudioContext;\r\n }\r\n\r\n function setSrcObject(stream, element) {\r\n if ('srcObject' in element) {\r\n element.srcObject = stream;\r\n } else if ('mozSrcObject' in element) {\r\n element.mozSrcObject = stream;\r\n } else {\r\n element.srcObject = stream;\r\n }\r\n }\r\n\r\n this.startDrawingFrames = function() {\r\n drawVideosToCanvas();\r\n };\r\n\r\n function drawVideosToCanvas() {\r\n if (isStopDrawingFrames) {\r\n return;\r\n }\r\n\r\n var videosLength = videos.length;\r\n\r\n var fullcanvas = false;\r\n var remaining = [];\r\n videos.forEach(function(video) {\r\n if (!video.stream) {\r\n video.stream = {};\r\n }\r\n\r\n if (video.stream.fullcanvas) {\r\n fullcanvas = video;\r\n } else {\r\n // todo: video.stream.active or video.stream.live to fix blank frames issues?\r\n remaining.push(video);\r\n }\r\n });\r\n\r\n if (fullcanvas) {\r\n canvas.width = fullcanvas.stream.width;\r\n canvas.height = fullcanvas.stream.height;\r\n } else if (remaining.length) {\r\n canvas.width = videosLength > 1 ? remaining[0].width * 2 : remaining[0].width;\r\n\r\n var height = 1;\r\n if (videosLength === 3 || videosLength === 4) {\r\n height = 2;\r\n }\r\n if (videosLength === 5 || videosLength === 6) {\r\n height = 3;\r\n }\r\n if (videosLength === 7 || videosLength === 8) {\r\n height = 4;\r\n }\r\n if (videosLength === 9 || videosLength === 10) {\r\n height = 5;\r\n }\r\n canvas.height = remaining[0].height * height;\r\n } else {\r\n canvas.width = self.width || 360;\r\n canvas.height = self.height || 240;\r\n }\r\n\r\n if (fullcanvas && fullcanvas instanceof HTMLVideoElement) {\r\n drawImage(fullcanvas);\r\n }\r\n\r\n remaining.forEach(function(video, idx) {\r\n drawImage(video, idx);\r\n });\r\n\r\n setTimeout(drawVideosToCanvas, self.frameInterval);\r\n }\r\n\r\n function drawImage(video, idx) {\r\n if (isStopDrawingFrames) {\r\n return;\r\n }\r\n\r\n var x = 0;\r\n var y = 0;\r\n var width = video.width;\r\n var height = video.height;\r\n\r\n if (idx === 1) {\r\n x = video.width;\r\n }\r\n\r\n if (idx === 2) {\r\n y = video.height;\r\n }\r\n\r\n if (idx === 3) {\r\n x = video.width;\r\n y = video.height;\r\n }\r\n\r\n if (idx === 4) {\r\n y = video.height * 2;\r\n }\r\n\r\n if (idx === 5) {\r\n x = video.width;\r\n y = video.height * 2;\r\n }\r\n\r\n if (idx === 6) {\r\n y = video.height * 3;\r\n }\r\n\r\n if (idx === 7) {\r\n x = video.width;\r\n y = video.height * 3;\r\n }\r\n\r\n if (typeof video.stream.left !== 'undefined') {\r\n x = video.stream.left;\r\n }\r\n\r\n if (typeof video.stream.top !== 'undefined') {\r\n y = video.stream.top;\r\n }\r\n\r\n if (typeof video.stream.width !== 'undefined') {\r\n width = video.stream.width;\r\n }\r\n\r\n if (typeof video.stream.height !== 'undefined') {\r\n height = video.stream.height;\r\n }\r\n\r\n context.drawImage(video, x, y, width, height);\r\n\r\n if (typeof video.stream.onRender === 'function') {\r\n video.stream.onRender(context, x, y, width, height, idx);\r\n }\r\n }\r\n\r\n function getMixedStream() {\r\n isStopDrawingFrames = false;\r\n var mixedVideoStream = getMixedVideoStream();\r\n\r\n var mixedAudioStream = getMixedAudioStream();\r\n if (mixedAudioStream) {\r\n mixedAudioStream.getTracks().filter(function(t) {\r\n return t.kind === 'audio';\r\n }).forEach(function(track) {\r\n mixedVideoStream.addTrack(track);\r\n });\r\n }\r\n\r\n var fullcanvas;\r\n arrayOfMediaStreams.forEach(function(stream) {\r\n if (stream.fullcanvas) {\r\n fullcanvas = true;\r\n }\r\n });\r\n\r\n // mixedVideoStream.prototype.appendStreams = appendStreams;\r\n // mixedVideoStream.prototype.resetVideoStreams = resetVideoStreams;\r\n // mixedVideoStream.prototype.clearRecordedData = clearRecordedData;\r\n\r\n return mixedVideoStream;\r\n }\r\n\r\n function getMixedVideoStream() {\r\n resetVideoStreams();\r\n\r\n var capturedStream;\r\n\r\n if ('captureStream' in canvas) {\r\n capturedStream = canvas.captureStream();\r\n } else if ('mozCaptureStream' in canvas) {\r\n capturedStream = canvas.mozCaptureStream();\r\n } else if (!self.disableLogs) {\r\n console.error('Upgrade to latest Chrome or otherwise enable this flag: chrome://flags/#enable-experimental-web-platform-features');\r\n }\r\n\r\n var videoStream = new MediaStream();\r\n\r\n capturedStream.getTracks().filter(function(t) {\r\n return t.kind === 'video';\r\n }).forEach(function(track) {\r\n videoStream.addTrack(track);\r\n });\r\n\r\n canvas.stream = videoStream;\r\n\r\n return videoStream;\r\n }\r\n\r\n function getMixedAudioStream() {\r\n // via: @pehrsons\r\n if (!Storage.AudioContextConstructor) {\r\n Storage.AudioContextConstructor = new Storage.AudioContext();\r\n }\r\n\r\n self.audioContext = Storage.AudioContextConstructor;\r\n\r\n self.audioSources = [];\r\n\r\n if (self.useGainNode === true) {\r\n self.gainNode = self.audioContext.createGain();\r\n self.gainNode.connect(self.audioContext.destination);\r\n self.gainNode.gain.value = 0; // don't hear self\r\n }\r\n\r\n var audioTracksLength = 0;\r\n arrayOfMediaStreams.forEach(function(stream) {\r\n if (!stream.getTracks().filter(function(t) {\r\n return t.kind === 'audio';\r\n }).length) {\r\n return;\r\n }\r\n\r\n audioTracksLength++;\r\n\r\n var audioSource = self.audioContext.createMediaStreamSource(stream);\r\n\r\n if (self.useGainNode === true) {\r\n audioSource.connect(self.gainNode);\r\n }\r\n\r\n self.audioSources.push(audioSource);\r\n });\r\n\r\n if (!audioTracksLength) {\r\n // because \"self.audioContext\" is not initialized\r\n // that's why we've to ignore rest of the code\r\n return;\r\n }\r\n\r\n self.audioDestination = self.audioContext.createMediaStreamDestination();\r\n self.audioSources.forEach(function(audioSource) {\r\n audioSource.connect(self.audioDestination);\r\n });\r\n return self.audioDestination.stream;\r\n }\r\n\r\n function getVideo(stream) {\r\n var video = document.createElement('video');\r\n\r\n setSrcObject(stream, video);\r\n\r\n video.className = elementClass;\r\n\r\n video.muted = true;\r\n video.volume = 0;\r\n\r\n video.width = stream.width || self.width || 360;\r\n video.height = stream.height || self.height || 240;\r\n\r\n video.play();\r\n\r\n return video;\r\n }\r\n\r\n this.appendStreams = function(streams) {\r\n if (!streams) {\r\n throw 'First parameter is required.';\r\n }\r\n\r\n if (!(streams instanceof Array)) {\r\n streams = [streams];\r\n }\r\n\r\n streams.forEach(function(stream) {\r\n var newStream = new MediaStream();\r\n\r\n if (stream.getTracks().filter(function(t) {\r\n return t.kind === 'video';\r\n }).length) {\r\n var video = getVideo(stream);\r\n video.stream = stream;\r\n videos.push(video);\r\n\r\n newStream.addTrack(stream.getTracks().filter(function(t) {\r\n return t.kind === 'video';\r\n })[0]);\r\n }\r\n\r\n if (stream.getTracks().filter(function(t) {\r\n return t.kind === 'audio';\r\n }).length) {\r\n var audioSource = self.audioContext.createMediaStreamSource(stream);\r\n self.audioDestination = self.audioContext.createMediaStreamDestination();\r\n audioSource.connect(self.audioDestination);\r\n\r\n newStream.addTrack(self.audioDestination.stream.getTracks().filter(function(t) {\r\n return t.kind === 'audio';\r\n })[0]);\r\n }\r\n\r\n arrayOfMediaStreams.push(newStream);\r\n });\r\n };\r\n\r\n this.releaseStreams = function() {\r\n videos = [];\r\n isStopDrawingFrames = true;\r\n\r\n if (self.gainNode) {\r\n self.gainNode.disconnect();\r\n self.gainNode = null;\r\n }\r\n\r\n if (self.audioSources.length) {\r\n self.audioSources.forEach(function(source) {\r\n source.disconnect();\r\n });\r\n self.audioSources = [];\r\n }\r\n\r\n if (self.audioDestination) {\r\n self.audioDestination.disconnect();\r\n self.audioDestination = null;\r\n }\r\n\r\n if (self.audioContext) {\r\n self.audioContext.close();\r\n }\r\n\r\n self.audioContext = null;\r\n\r\n context.clearRect(0, 0, canvas.width, canvas.height);\r\n\r\n if (canvas.stream) {\r\n canvas.stream.stop();\r\n canvas.stream = null;\r\n }\r\n };\r\n\r\n this.resetVideoStreams = function(streams) {\r\n if (streams && !(streams instanceof Array)) {\r\n streams = [streams];\r\n }\r\n\r\n resetVideoStreams(streams);\r\n };\r\n\r\n function resetVideoStreams(streams) {\r\n videos = [];\r\n streams = streams || arrayOfMediaStreams;\r\n\r\n // via: @adrian-ber\r\n streams.forEach(function(stream) {\r\n if (!stream.getTracks().filter(function(t) {\r\n return t.kind === 'video';\r\n }).length) {\r\n return;\r\n }\r\n\r\n var video = getVideo(stream);\r\n video.stream = stream;\r\n videos.push(video);\r\n });\r\n }\r\n\r\n // for debugging\r\n this.name = 'MultiStreamsMixer';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n\r\n this.getMixedStream = getMixedStream;\r\n\r\n}\r\n\r\nif (typeof RecordRTC === 'undefined') {\r\n if (typeof module !== 'undefined' /* && !!module.exports*/ ) {\r\n module.exports = MultiStreamsMixer;\r\n }\r\n\r\n if (typeof define === 'function' && define.amd) {\r\n define('MultiStreamsMixer', [], function() {\r\n return MultiStreamsMixer;\r\n });\r\n }\r\n}\n\r\n// ______________________\r\n// MultiStreamRecorder.js\r\n\r\n/*\r\n * Video conference recording, using captureStream API along with WebAudio and Canvas2D API.\r\n */\r\n\r\n/**\r\n * MultiStreamRecorder can record multiple videos in single container.\r\n * @summary Multi-videos recorder.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef MultiStreamRecorder\r\n * @class\r\n * @example\r\n * var options = {\r\n * mimeType: 'video/webm'\r\n * }\r\n * var recorder = new MultiStreamRecorder(ArrayOfMediaStreams, options);\r\n * recorder.record();\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n *\r\n * // or\r\n * var blob = recorder.blob;\r\n * });\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStreams} mediaStreams - Array of MediaStreams.\r\n * @param {object} config - {disableLogs:true, frameInterval: 1, mimeType: \"video/webm\"}\r\n */\r\n\r\nfunction MultiStreamRecorder(arrayOfMediaStreams, options) {\r\n arrayOfMediaStreams = arrayOfMediaStreams || [];\r\n var self = this;\r\n\r\n var mixer;\r\n var mediaRecorder;\r\n\r\n options = options || {\r\n elementClass: 'multi-streams-mixer',\r\n mimeType: 'video/webm',\r\n video: {\r\n width: 360,\r\n height: 240\r\n }\r\n };\r\n\r\n if (!options.frameInterval) {\r\n options.frameInterval = 10;\r\n }\r\n\r\n if (!options.video) {\r\n options.video = {};\r\n }\r\n\r\n if (!options.video.width) {\r\n options.video.width = 360;\r\n }\r\n\r\n if (!options.video.height) {\r\n options.video.height = 240;\r\n }\r\n\r\n /**\r\n * This method records all MediaStreams.\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @example\r\n * recorder.record();\r\n */\r\n this.record = function() {\r\n // github/muaz-khan/MultiStreamsMixer\r\n mixer = new MultiStreamsMixer(arrayOfMediaStreams, options.elementClass || 'multi-streams-mixer');\r\n\r\n if (getAllVideoTracks().length) {\r\n mixer.frameInterval = options.frameInterval || 10;\r\n mixer.width = options.video.width || 360;\r\n mixer.height = options.video.height || 240;\r\n mixer.startDrawingFrames();\r\n }\r\n\r\n if (options.previewStream && typeof options.previewStream === 'function') {\r\n options.previewStream(mixer.getMixedStream());\r\n }\r\n\r\n // record using MediaRecorder API\r\n mediaRecorder = new MediaStreamRecorder(mixer.getMixedStream(), options);\r\n mediaRecorder.record();\r\n };\r\n\r\n function getAllVideoTracks() {\r\n var tracks = [];\r\n arrayOfMediaStreams.forEach(function(stream) {\r\n getTracks(stream, 'video').forEach(function(track) {\r\n tracks.push(track);\r\n });\r\n });\r\n return tracks;\r\n }\r\n\r\n /**\r\n * This method stops recording MediaStream.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @example\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n */\r\n this.stop = function(callback) {\r\n if (!mediaRecorder) {\r\n return;\r\n }\r\n\r\n mediaRecorder.stop(function(blob) {\r\n self.blob = blob;\r\n\r\n callback(blob);\r\n\r\n self.clearRecordedData();\r\n });\r\n };\r\n\r\n /**\r\n * This method pauses the recording process.\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @example\r\n * recorder.pause();\r\n */\r\n this.pause = function() {\r\n if (mediaRecorder) {\r\n mediaRecorder.pause();\r\n }\r\n };\r\n\r\n /**\r\n * This method resumes the recording process.\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @example\r\n * recorder.resume();\r\n */\r\n this.resume = function() {\r\n if (mediaRecorder) {\r\n mediaRecorder.resume();\r\n }\r\n };\r\n\r\n /**\r\n * This method resets currently recorded data.\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n this.clearRecordedData = function() {\r\n if (mediaRecorder) {\r\n mediaRecorder.clearRecordedData();\r\n mediaRecorder = null;\r\n }\r\n\r\n if (mixer) {\r\n mixer.releaseStreams();\r\n mixer = null;\r\n }\r\n };\r\n\r\n /**\r\n * Add extra media-streams to existing recordings.\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @param {MediaStreams} mediaStreams - Array of MediaStreams\r\n * @example\r\n * recorder.addStreams([newAudioStream, newVideoStream]);\r\n */\r\n this.addStreams = function(streams) {\r\n if (!streams) {\r\n throw 'First parameter is required.';\r\n }\r\n\r\n if (!(streams instanceof Array)) {\r\n streams = [streams];\r\n }\r\n\r\n arrayOfMediaStreams.concat(streams);\r\n\r\n if (!mediaRecorder || !mixer) {\r\n return;\r\n }\r\n\r\n mixer.appendStreams(streams);\r\n\r\n if (options.previewStream && typeof options.previewStream === 'function') {\r\n options.previewStream(mixer.getMixedStream());\r\n }\r\n };\r\n\r\n /**\r\n * Reset videos during live recording. Replace old videos e.g. replace cameras with full-screen.\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @param {MediaStreams} mediaStreams - Array of MediaStreams\r\n * @example\r\n * recorder.resetVideoStreams([newVideo1, newVideo2]);\r\n */\r\n this.resetVideoStreams = function(streams) {\r\n if (!mixer) {\r\n return;\r\n }\r\n\r\n if (streams && !(streams instanceof Array)) {\r\n streams = [streams];\r\n }\r\n\r\n mixer.resetVideoStreams(streams);\r\n };\r\n\r\n /**\r\n * Returns MultiStreamsMixer\r\n * @method\r\n * @memberof MultiStreamRecorder\r\n * @example\r\n * let mixer = recorder.getMixer();\r\n * mixer.appendStreams([newStream]);\r\n */\r\n this.getMixer = function() {\r\n return mixer;\r\n };\r\n\r\n // for debugging\r\n this.name = 'MultiStreamRecorder';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.MultiStreamRecorder = MultiStreamRecorder;\r\n}\n\r\n// _____________________\r\n// RecordRTC.promises.js\r\n\r\n/**\r\n * RecordRTCPromisesHandler adds promises support in {@link RecordRTC}. Try a {@link https://github.com/muaz-khan/RecordRTC/blob/master/simple-demos/RecordRTCPromisesHandler.html|demo here}\r\n * @summary Promises for {@link RecordRTC}\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef RecordRTCPromisesHandler\r\n * @class\r\n * @example\r\n * var recorder = new RecordRTCPromisesHandler(mediaStream, options);\r\n * recorder.startRecording()\r\n * .then(successCB)\r\n * .catch(errorCB);\r\n * // Note: You can access all RecordRTC API using \"recorder.recordRTC\" e.g. \r\n * recorder.recordRTC.onStateChanged = function(state) {};\r\n * recorder.recordRTC.setRecordingDuration(5000);\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - Single media-stream object, array of media-streams, html-canvas-element, etc.\r\n * @param {object} config - {type:\"video\", recorderType: MediaStreamRecorder, disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, etc.}\r\n * @throws Will throw an error if \"new\" keyword is not used to initiate \"RecordRTCPromisesHandler\". Also throws error if first argument \"MediaStream\" is missing.\r\n * @requires {@link RecordRTC}\r\n */\r\n\r\nfunction RecordRTCPromisesHandler(mediaStream, options) {\r\n if (!this) {\r\n throw 'Use \"new RecordRTCPromisesHandler()\"';\r\n }\r\n\r\n if (typeof mediaStream === 'undefined') {\r\n throw 'First argument \"MediaStream\" is required.';\r\n }\r\n\r\n var self = this;\r\n\r\n /**\r\n * @property {Blob} blob - Access/reach the native {@link RecordRTC} object.\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * let internal = recorder.recordRTC.getInternalRecorder();\r\n * alert(internal instanceof MediaStreamRecorder);\r\n * recorder.recordRTC.onStateChanged = function(state) {};\r\n */\r\n self.recordRTC = new RecordRTC(mediaStream, options);\r\n\r\n /**\r\n * This method records MediaStream.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * recorder.startRecording()\r\n * .then(successCB)\r\n * .catch(errorCB);\r\n */\r\n this.startRecording = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n self.recordRTC.startRecording();\r\n resolve();\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * This method stops the recording.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * recorder.stopRecording().then(function() {\r\n * var blob = recorder.getBlob();\r\n * }).catch(errorCB);\r\n */\r\n this.stopRecording = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n self.recordRTC.stopRecording(function(url) {\r\n self.blob = self.recordRTC.getBlob();\r\n\r\n if (!self.blob || !self.blob.size) {\r\n reject('Empty blob.', self.blob);\r\n return;\r\n }\r\n\r\n resolve(url);\r\n });\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * This method pauses the recording. You can resume recording using \"resumeRecording\" method.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * recorder.pauseRecording()\r\n * .then(successCB)\r\n * .catch(errorCB);\r\n */\r\n this.pauseRecording = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n self.recordRTC.pauseRecording();\r\n resolve();\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * This method resumes the recording.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * recorder.resumeRecording()\r\n * .then(successCB)\r\n * .catch(errorCB);\r\n */\r\n this.resumeRecording = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n self.recordRTC.resumeRecording();\r\n resolve();\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * This method returns data-url for the recorded blob.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * recorder.stopRecording().then(function() {\r\n * recorder.getDataURL().then(function(dataURL) {\r\n * window.open(dataURL);\r\n * }).catch(errorCB);;\r\n * }).catch(errorCB);\r\n */\r\n this.getDataURL = function(callback) {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n self.recordRTC.getDataURL(function(dataURL) {\r\n resolve(dataURL);\r\n });\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * This method returns the recorded blob.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * recorder.stopRecording().then(function() {\r\n * recorder.getBlob().then(function(blob) {})\r\n * }).catch(errorCB);\r\n */\r\n this.getBlob = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n resolve(self.recordRTC.getBlob());\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * This method returns the internal recording object.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * let internalRecorder = await recorder.getInternalRecorder();\r\n * if(internalRecorder instanceof MultiStreamRecorder) {\r\n * internalRecorder.addStreams([newAudioStream]);\r\n * internalRecorder.resetVideoStreams([screenStream]);\r\n * }\r\n * @returns {Object} \r\n */\r\n this.getInternalRecorder = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n resolve(self.recordRTC.getInternalRecorder());\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * This method resets the recorder. So that you can reuse single recorder instance many times.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * await recorder.reset();\r\n * recorder.startRecording(); // record again\r\n */\r\n this.reset = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n resolve(self.recordRTC.reset());\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * Destroy RecordRTC instance. Clear all recorders and objects.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * recorder.destroy().then(successCB).catch(errorCB);\r\n */\r\n this.destroy = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n resolve(self.recordRTC.destroy());\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * Get recorder's readonly state.\r\n * @method\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * let state = await recorder.getState();\r\n * // or\r\n * recorder.getState().then(state => { console.log(state); })\r\n * @returns {String} Returns recording state.\r\n */\r\n this.getState = function() {\r\n return new Promise(function(resolve, reject) {\r\n try {\r\n resolve(self.recordRTC.getState());\r\n } catch (e) {\r\n reject(e);\r\n }\r\n });\r\n };\r\n\r\n /**\r\n * @property {Blob} blob - Recorded data as \"Blob\" object.\r\n * @memberof RecordRTCPromisesHandler\r\n * @example\r\n * await recorder.stopRecording();\r\n * let blob = recorder.getBlob(); // or \"recorder.recordRTC.blob\"\r\n * invokeSaveAsDialog(blob);\r\n */\r\n this.blob = null;\r\n\r\n /**\r\n * RecordRTC version number\r\n * @property {String} version - Release version number.\r\n * @memberof RecordRTCPromisesHandler\r\n * @static\r\n * @readonly\r\n * @example\r\n * alert(recorder.version);\r\n */\r\n this.version = '5.6.1';\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.RecordRTCPromisesHandler = RecordRTCPromisesHandler;\r\n}\n\r\n// ______________________\r\n// WebAssemblyRecorder.js\r\n\r\n/**\r\n * WebAssemblyRecorder lets you create webm videos in JavaScript via WebAssembly. The library consumes raw RGBA32 buffers (4 bytes per pixel) and turns them into a webm video with the given framerate and quality. This makes it compatible out-of-the-box with ImageData from a CANVAS. With realtime mode you can also use webm-wasm for streaming webm videos.\r\n * @summary Video recording feature in Chrome, Firefox and maybe Edge.\r\n * @license {@link https://github.com/muaz-khan/RecordRTC/blob/master/LICENSE|MIT}\r\n * @author {@link https://MuazKhan.com|Muaz Khan}\r\n * @typedef WebAssemblyRecorder\r\n * @class\r\n * @example\r\n * var recorder = new WebAssemblyRecorder(mediaStream);\r\n * recorder.record();\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n * @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}\r\n * @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.\r\n * @param {object} config - {webAssemblyPath:'webm-wasm.wasm',workerPath: 'webm-worker.js', frameRate: 30, width: 1920, height: 1080, bitrate: 1024, realtime: true}\r\n */\r\nfunction WebAssemblyRecorder(stream, config) {\r\n // based on: github.com/GoogleChromeLabs/webm-wasm\r\n\r\n if (typeof ReadableStream === 'undefined' || typeof WritableStream === 'undefined') {\r\n // because it fixes readable/writable streams issues\r\n console.error('Following polyfill is strongly recommended: https://unpkg.com/@mattiasbuelens/web-streams-polyfill/dist/polyfill.min.js');\r\n }\r\n\r\n config = config || {};\r\n\r\n config.width = config.width || 640;\r\n config.height = config.height || 480;\r\n config.frameRate = config.frameRate || 30;\r\n config.bitrate = config.bitrate || 1200;\r\n config.realtime = config.realtime || true;\r\n\r\n function createBufferURL(buffer, type) {\r\n return URL.createObjectURL(new Blob([buffer], {\r\n type: type || ''\r\n }));\r\n }\r\n\r\n var finished;\r\n\r\n function cameraStream() {\r\n return new ReadableStream({\r\n start: function(controller) {\r\n var cvs = document.createElement('canvas');\r\n var video = document.createElement('video');\r\n var first = true;\r\n video.srcObject = stream;\r\n video.muted = true;\r\n video.height = config.height;\r\n video.width = config.width;\r\n video.volume = 0;\r\n video.onplaying = function() {\r\n cvs.width = config.width;\r\n cvs.height = config.height;\r\n var ctx = cvs.getContext('2d');\r\n var frameTimeout = 1000 / config.frameRate;\r\n var cameraTimer = setInterval(function f() {\r\n if (finished) {\r\n clearInterval(cameraTimer);\r\n controller.close();\r\n }\r\n\r\n if (first) {\r\n first = false;\r\n if (config.onVideoProcessStarted) {\r\n config.onVideoProcessStarted();\r\n }\r\n }\r\n\r\n ctx.drawImage(video, 0, 0);\r\n if (controller._controlledReadableStream.state !== 'closed') {\r\n try {\r\n controller.enqueue(\r\n ctx.getImageData(0, 0, config.width, config.height)\r\n );\r\n } catch (e) {}\r\n }\r\n }, frameTimeout);\r\n };\r\n video.play();\r\n }\r\n });\r\n }\r\n\r\n var worker;\r\n\r\n function startRecording(stream, buffer) {\r\n if (!config.workerPath && !buffer) {\r\n finished = false;\r\n\r\n // is it safe to use @latest ?\r\n\r\n fetch(\r\n 'https://unpkg.com/webm-wasm@latest/dist/webm-worker.js'\r\n ).then(function(r) {\r\n r.arrayBuffer().then(function(buffer) {\r\n startRecording(stream, buffer);\r\n });\r\n });\r\n return;\r\n }\r\n\r\n if (!config.workerPath && buffer instanceof ArrayBuffer) {\r\n var blob = new Blob([buffer], {\r\n type: 'text/javascript'\r\n });\r\n config.workerPath = URL.createObjectURL(blob);\r\n }\r\n\r\n if (!config.workerPath) {\r\n console.error('workerPath parameter is missing.');\r\n }\r\n\r\n worker = new Worker(config.workerPath);\r\n\r\n worker.postMessage(config.webAssemblyPath || 'https://unpkg.com/webm-wasm@latest/dist/webm-wasm.wasm');\r\n worker.addEventListener('message', function(event) {\r\n if (event.data === 'READY') {\r\n worker.postMessage({\r\n width: config.width,\r\n height: config.height,\r\n bitrate: config.bitrate || 1200,\r\n timebaseDen: config.frameRate || 30,\r\n realtime: config.realtime\r\n });\r\n\r\n cameraStream().pipeTo(new WritableStream({\r\n write: function(image) {\r\n if (finished) {\r\n console.error('Got image, but recorder is finished!');\r\n return;\r\n }\r\n\r\n worker.postMessage(image.data.buffer, [image.data.buffer]);\r\n }\r\n }));\r\n } else if (!!event.data) {\r\n if (!isPaused) {\r\n arrayOfBuffers.push(event.data);\r\n }\r\n }\r\n });\r\n }\r\n\r\n /**\r\n * This method records video.\r\n * @method\r\n * @memberof WebAssemblyRecorder\r\n * @example\r\n * recorder.record();\r\n */\r\n this.record = function() {\r\n arrayOfBuffers = [];\r\n isPaused = false;\r\n this.blob = null;\r\n startRecording(stream);\r\n\r\n if (typeof config.initCallback === 'function') {\r\n config.initCallback();\r\n }\r\n };\r\n\r\n var isPaused;\r\n\r\n /**\r\n * This method pauses the recording process.\r\n * @method\r\n * @memberof WebAssemblyRecorder\r\n * @example\r\n * recorder.pause();\r\n */\r\n this.pause = function() {\r\n isPaused = true;\r\n };\r\n\r\n /**\r\n * This method resumes the recording process.\r\n * @method\r\n * @memberof WebAssemblyRecorder\r\n * @example\r\n * recorder.resume();\r\n */\r\n this.resume = function() {\r\n isPaused = false;\r\n };\r\n\r\n function terminate(callback) {\r\n if (!worker) {\r\n if (callback) {\r\n callback();\r\n }\r\n\r\n return;\r\n }\r\n\r\n // Wait for null event data to indicate that the encoding is complete\r\n worker.addEventListener('message', function(event) {\r\n if (event.data === null) {\r\n worker.terminate();\r\n worker = null;\r\n\r\n if (callback) {\r\n callback();\r\n }\r\n }\r\n });\r\n\r\n worker.postMessage(null);\r\n }\r\n\r\n var arrayOfBuffers = [];\r\n\r\n /**\r\n * This method stops recording video.\r\n * @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.\r\n * @method\r\n * @memberof WebAssemblyRecorder\r\n * @example\r\n * recorder.stop(function(blob) {\r\n * video.src = URL.createObjectURL(blob);\r\n * });\r\n */\r\n this.stop = function(callback) {\r\n finished = true;\r\n\r\n var recorder = this;\r\n\r\n terminate(function() {\r\n recorder.blob = new Blob(arrayOfBuffers, {\r\n type: 'video/webm'\r\n });\r\n\r\n callback(recorder.blob);\r\n });\r\n };\r\n\r\n // for debugging\r\n this.name = 'WebAssemblyRecorder';\r\n this.toString = function() {\r\n return this.name;\r\n };\r\n\r\n /**\r\n * This method resets currently recorded data.\r\n * @method\r\n * @memberof WebAssemblyRecorder\r\n * @example\r\n * recorder.clearRecordedData();\r\n */\r\n this.clearRecordedData = function() {\r\n arrayOfBuffers = [];\r\n isPaused = false;\r\n this.blob = null;\r\n\r\n // todo: if recording-ON then STOP it first\r\n };\r\n\r\n /**\r\n * @property {Blob} blob - The recorded blob object.\r\n * @memberof WebAssemblyRecorder\r\n * @example\r\n * recorder.stop(function(){\r\n * var blob = recorder.blob;\r\n * });\r\n */\r\n this.blob = null;\r\n}\r\n\r\nif (typeof RecordRTC !== 'undefined') {\r\n RecordRTC.WebAssemblyRecorder = WebAssemblyRecorder;\r\n}\r\n"],"sourceRoot":""}