{"version":3,"file":"static/js/vendors-33f59662.76b01774.js","mappings":"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","sources":["../node_modules/recordrtc/RecordRTC.js"],"sourcesContent":["'use strict';\r\n\r\n// Last time updated: 2021-03-09 3:20:22 AM UTC\r\n\r\n// ________________\r\n// RecordRTC v5.6.2\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.2'\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.2';\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 if (fileExtension.indexOf(';') !== -1) {\r\n // extended mimetype, e.g. 'video/webm;codecs=vp8,opus'\r\n fileExtension = fileExtension.split(';')[0];\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}\r\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) {\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}\r\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 * numberOfAudioChannels * 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}\r\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.2';\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}\n"],"names":["RecordRTC","mediaStream","config","RecordRTCConfiguration","type","self","this","initRecorder","initCallback","Recorder","GetRecorderType","mediaRecorder","record","setState","disableLogs","stopRecording","callback","state","resumeRecording","setTimeout","stop","_callback","warningLog","__blob","Object","keys","forEach","key","blob","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","name","worker","revokeObjectURL","processInWebWorker","onmessage","data","handleRecordingDuration","counter","recordingDuration","onRecordingStopped","onStateChanged","returnObject","startRecording","config2","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","version","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","console","log","error","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","bytesToSize","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","size","recordingCallback","getNativeBlob","onstart","onpause","onresume","onstop","onerror","manuallyStopped","timeslice","start","getAllStates","checkForInactiveTracks","looper","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","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","newCanvas","grabMouse","showMousePointer","onrendered","Whammy","Video","drawFrames","paused","isStopDrawing","dropBlackFrames","_frames","_framesToCheck","_pixTolerance","_frameTolerance","localCanvas","o","context2d","resultFrames","checkUntilNotBlack","endCheckFrame","sampleColor","maxColorDifference","sqrt","pixTolerance","frameTolerance","doNotCheckNext","functionToLoop","loop","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","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"],"sourceRoot":""}