function RecordRTC(mediaStream, config) {
if (!mediaStream) {
throw 'MediaStream is mandatory.';
}
config = config || {
type: 'video'
};
config = new RecordRTCConfiguration(mediaStream, config);
// a reference to user's recordRTC object
var self = this;
function startRecording() {
if (!config.disableLogs) {
console.debug('started recording ' + config.type + ' stream.');
}
if (mediaRecorder) {
mediaRecorder.clearRecordedData();
mediaRecorder.record();
if (self.recordingDuration) {
handleRecordingDuration();
}
return self;
}
initRecorder(function() {
if (self.recordingDuration) {
handleRecordingDuration();
}
});
return self;
}
function initRecorder(initCallback) {
if (initCallback) {
config.initCallback = function() {
initCallback();
initCallback = config.initCallback = null; // recordRTC.initRecorder should be call-backed once.
};
}
var Recorder = new GetRecorderType(mediaStream, config);
mediaRecorder = new Recorder(mediaStream, config);
mediaRecorder.record();
if (!config.disableLogs) {
console.debug('Initialized recorderType:', mediaRecorder.constructor.name, 'for output-type:', config.type);
}
}
function stopRecording(callback) {
if (!mediaRecorder) {
return console.warn(WARNING);
}
/*jshint validthis:true */
var recordRTC = this;
if (!config.disableLogs) {
console.warn('Stopped recording ' + config.type + ' stream.');
}
if (config.type !== 'gif') {
mediaRecorder.stop(_callback);
} else {
mediaRecorder.stop();
_callback();
}
function _callback(__blob) {
for (var item in mediaRecorder) {
if (self) {
self[item] = mediaRecorder[item];
}
if (recordRTC) {
recordRTC[item] = mediaRecorder[item];
}
}
var blob = mediaRecorder.blob;
if (!blob) {
if (__blob) {
mediaRecorder.blob = blob = __blob;
} else {
throw 'Recording failed.';
}
}
if (callback) {
var url = URL.createObjectURL(blob);
callback(url);
}
if (blob && !config.disableLogs) {
console.debug(blob.type, '->', bytesToSize(blob.size));
}
if (!config.autoWriteToDisk) {
return;
}
getDataURL(function(dataURL) {
var parameter = {};
parameter[config.type + 'Blob'] = dataURL;
DiskStorage.Store(parameter);
});
}
}
function pauseRecording() {
if (!mediaRecorder) {
return console.warn(WARNING);
}
mediaRecorder.pause();
if (!config.disableLogs) {
console.debug('Paused recording.');
}
}
function resumeRecording() {
if (!mediaRecorder) {
return console.warn(WARNING);
}
// not all libs have this method yet
mediaRecorder.resume();
if (!config.disableLogs) {
console.debug('Resumed recording.');
}
}
function readFile(_blob) {
postMessage(new FileReaderSync().readAsDataURL(_blob));
}
function getDataURL(callback, _mediaRecorder) {
if (!callback) {
throw 'Pass a callback function over getDataURL.';
}
var blob = _mediaRecorder ? _mediaRecorder.blob : (mediaRecorder || {}).blob;
if (!blob) {
if (!config.disableLogs) {
console.warn('Blob encoder did not f ...
n/a
function CanvasRecorder(htmlElement, config) {
if (typeof html2canvas === 'undefined' && htmlElement.nodeName.toLowerCase() !== 'canvas') {
throw 'Please link: https://cdn.webrtc-experiment.com/screenshot.js';
}
config = config || {};
if (!config.frameInterval) {
config.frameInterval = 10;
}
// via DetectRTC.js
var isCanvasSupportsStreamCapturing = false;
['captureStream', 'mozCaptureStream', 'webkitCaptureStream'].forEach(function(item) {
if (item in document.createElement('canvas')) {
isCanvasSupportsStreamCapturing = true;
}
});
var _isChrome = (!!window.webkitRTCPeerConnection || !!window.webkitGetUserMedia) && !!window.chrome;
var chromeVersion = 50;
var matchArray = navigator.userAgent.match(/Chrom(e|ium)\/([0-9]+)\./);
if (_isChrome && matchArray && matchArray[2]) {
chromeVersion = parseInt(matchArray[2], 10);
}
if (_isChrome && chromeVersion < 52) {
isCanvasSupportsStreamCapturing = false;
}
var globalCanvas, mediaStreamRecorder;
if (isCanvasSupportsStreamCapturing) {
if (!config.disableLogs) {
console.debug('Your browser supports both MediRecorder API and canvas.captureStream!');
}
if (htmlElement instanceof HTMLCanvasElement) {
globalCanvas = htmlElement;
} else if (htmlElement instanceof CanvasRenderingContext2D) {
globalCanvas = htmlElement.canvas;
} else {
throw 'Please pass either HTMLCanvasElement or CanvasRenderingContext2D.';
}
} else if (!!navigator.mozGetUserMedia) {
if (!config.disableLogs) {
console.error('Canvas recording is NOT supported in Firefox.');
}
}
var isRecording;
/**
* This method records Canvas.
* @method
* @memberof CanvasRecorder
* @example
* recorder.record();
*/
this.record = function() {
isRecording = true;
if (isCanvasSupportsStreamCapturing) {
// CanvasCaptureMediaStream
var canvasMediaStream;
if ('captureStream' in globalCanvas) {
canvasMediaStream = globalCanvas.captureStream(25); // 25 FPS
} else if ('mozCaptureStream' in globalCanvas) {
canvasMediaStream = globalCanvas.mozCaptureStream(25);
} else if ('webkitCaptureStream' in globalCanvas) {
canvasMediaStream = globalCanvas.webkitCaptureStream(25);
}
try {
var mdStream = new MediaStream();
mdStream.addTrack(canvasMediaStream.getVideoTracks()[0]);
canvasMediaStream = mdStream;
} catch (e) {}
if (!canvasMediaStream) {
throw 'captureStream API are NOT available.';
}
// Note: Jan 18, 2016 status is that,
// Firefox MediaRecorder API can't record CanvasCaptureMediaStream object.
mediaStreamRecorder = new MediaStreamRecorder(canvasMediaStream, {
mimeType: 'video/webm'
});
mediaStreamRecorder.record();
} else {
whammy.frames = [];
lastTime = new Date().getTime();
drawCanvasFrame();
}
if (config.initCallback) {
config.initCallback();
}
};
this.getWebPImages = function(callback) {
if (htmlElement.nodeName.toLowerCase() !== 'canvas') {
callback();
return;
}
var framesLength = whammy.frames.length;
whammy.frames.forEach(function(frame, idx) {
var framesRemaining = framesLength - idx;
if (!config.disableLogs) {
console.debug(framesRemaining + '/' + framesLength + ' frames remaining');
}
if (config.onEncodingCallback) {
config.onEncodingCallback(framesRemaining, framesLength);
}
var webp = frame.image.toDataURL('image/webp', 1);
whammy.frames[idx] ...
n/a
function GifRecorder(mediaStream, config) {
if (typeof GIFEncoder === 'undefined') {
throw 'Please link: https://cdn.webrtc-experiment.com/gif-recorder.js';
}
config = config || {};
var isHTMLObject = mediaStream instanceof CanvasRenderingContext2D || mediaStream instanceof HTMLCanvasElement;
/**
* This method records MediaStream.
* @method
* @memberof GifRecorder
* @example
* recorder.record();
*/
this.record = function() {
if (!isHTMLObject) {
if (!config.width) {
config.width = video.offsetWidth || 320;
}
if (!this.height) {
config.height = video.offsetHeight || 240;
}
if (!config.video) {
config.video = {
width: config.width,
height: config.height
};
}
if (!config.canvas) {
config.canvas = {
width: config.width,
height: config.height
};
}
canvas.width = config.canvas.width || 320;
canvas.height = config.canvas.height || 240;
video.width = config.video.width || 320;
video.height = config.video.height || 240;
}
// external library to record as GIF images
gifEncoder = new GIFEncoder();
// void setRepeat(int iter)
// Sets the number of times the set of GIF frames should be played.
// Default is 1; 0 means play indefinitely.
gifEncoder.setRepeat(0);
// void setFrameRate(Number fps)
// Sets frame rate in frames per second.
// Equivalent to setDelay(1000/fps).
// Using "setDelay" instead of "setFrameRate"
gifEncoder.setDelay(config.frameRate || 200);
// void setQuality(int quality)
// Sets quality of color quantization (conversion of images to the
// maximum 256 colors allowed by the GIF specification).
// Lower values (minimum = 1) produce better colors,
// but slow processing significantly. 10 is the default,
// and produces good color mapping at reasonable speeds.
// Values greater than 20 do not yield significant improvements in speed.
gifEncoder.setQuality(config.quality || 10);
// Boolean start()
// This writes the GIF Header and returns false if it fails.
gifEncoder.start();
startTime = Date.now();
var self = this;
function drawVideoFrame(time) {
if (isPausedRecording) {
return setTimeout(function() {
drawVideoFrame(time);
}, 100);
}
lastAnimationFrame = requestAnimationFrame(drawVideoFrame);
if (typeof lastFrameTime === undefined) {
lastFrameTime = time;
}
// ~10 fps
if (time - lastFrameTime < 90) {
return;
}
if (!isHTMLObject && video.paused) {
// via: https://github.com/muaz-khan/WebRTC-Experiment/pull/316
// Tweak for Android Chrome
video.play();
}
if (!isHTMLObject) {
context.drawImage(video, 0, 0, canvas.width, canvas.height);
}
if (config.onGifPreview) {
config.onGifPreview(canvas.toDataURL('image/png'));
}
gifEncoder.addFrame(context);
lastFrameTime = time;
}
lastAnimationFrame = requestAnimationFrame(drawVideoFrame);
if (config.initCallback) {
config.initCallback();
}
};
/**
* This method stops recording MediaStream.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof GifRecorder
* @example
* recorder.stop(function(blob) {
* img.src = URL.createObjectURL(blob);
* }); ...
n/a
function MRecordRTC(mediaStream) {
/**
* This method attaches MediaStream object to {@link MRecordRTC}.
* @param {MediaStream} mediaStream - A MediaStream object, either fetched using getUserMedia API, or generated using captureStreamUntilEnded
or WebAudio API.
* @method
* @memberof MRecordRTC
* @example
* recorder.addStream(MediaStream);
*/
this.addStream = function(_mediaStream) {
if (_mediaStream) {
mediaStream = _mediaStream;
}
};
/**
* This property can be used to set the recording type e.g. audio, or video, or gif, or canvas.
* @property {object} mediaType - {audio: true, video: true, gif: true}
* @memberof MRecordRTC
* @example
* var recorder = new MRecordRTC();
* recorder.mediaType = {
* audio: true, // TRUE or StereoAudioRecorder or MediaStreamRecorder
* video: true, // TRUE or WhammyRecorder or MediaStreamRecorder
* gif : true // TRUE or GifRecorder
* };
*/
this.mediaType = {
audio: true,
video: true
};
/**
* This method starts recording.
* @method
* @memberof MRecordRTC
* @example
* recorder.startRecording();
*/
this.startRecording = function() {
var mediaType = this.mediaType;
var recorderType;
var mimeType = this.mimeType || {
audio: null,
video: null,
gif: null
};
if (typeof mediaType.audio !== 'function' && isMediaRecorderCompatible() && mediaStream.getAudioTracks && !mediaStream.getAudioTracks
().length) {
// Firefox supports both audio/video in single blob
mediaType.audio = false;
}
if (typeof mediaType.video !== 'function' && isMediaRecorderCompatible() && mediaStream.getVideoTracks && !mediaStream.getVideoTracks
().length) {
// Firefox supports both audio/video in single blob
mediaType.video = false;
}
if (!mediaType.audio && !mediaType.video) {
throw 'MediaStream must have either audio or video tracks.';
}
if (!!mediaType.audio) {
recorderType = null;
if (typeof mediaType.audio === 'function') {
recorderType = mediaType.audio;
}
this.audioRecorder = new RecordRTC(mediaStream, {
type: 'audio',
bufferSize: this.bufferSize,
sampleRate: this.sampleRate,
numberOfAudioChannels: this.numberOfAudioChannels || 2,
disableLogs: this.disableLogs,
recorderType: recorderType,
mimeType: mimeType.audio
});
if (!mediaType.video) {
this.audioRecorder.startRecording();
}
}
if (!!mediaType.video) {
recorderType = null;
if (typeof mediaType.video === 'function') {
recorderType = mediaType.video;
}
var newStream = mediaStream;
if (isMediaRecorderCompatible() && !!mediaType.audio && typeof mediaType.audio === 'function') {
var videoTrack = mediaStream.getVideoTracks()[0];
if (!!navigator.mozGetUserMedia) {
newStream = new MediaStream();
newStream.addTrack(videoTrack);
if (recorderType && recorderType === WhammyRecorder) {
// Firefox does NOT support webp-encoding yet
recorderType = MediaStreamRecorder;
}
} else {
newStream = new MediaStream([videoTrack]);
}
}
this.videoRecorder = new RecordRTC(newStream, {
type: 'video',
video: this.video,
canvas: this.canvas,
frameInterval: this.frameInterval || 10,
disableLogs: this.disableLogs,
recorderType: recorderType,
mimeType ...
n/a
function MediaStreamRecorder(mediaStream, config) {
var self = this;
config = config || {
// bitsPerSecond: 256 * 8 * 1024,
mimeType: 'video/webm'
};
if (config.type === 'audio') {
if (mediaStream.getVideoTracks().length && mediaStream.getAudioTracks().length) {
var stream;
if (!!navigator.mozGetUserMedia) {
stream = new MediaStream();
stream.addTrack(mediaStream.getAudioTracks()[0]);
} else {
// webkitMediaStream
stream = new MediaStream(mediaStream.getAudioTracks());
}
mediaStream = stream;
}
if (!config.mimeType || config.mimeType.toString().toLowerCase().indexOf('audio') === -1) {
config.mimeType = isChrome ? 'audio/webm' : 'audio/ogg';
}
if (config.mimeType && config.mimeType.toString().toLowerCase() !== 'audio/ogg' && !!navigator.mozGetUserMedia) {
// forcing better codecs on Firefox (via #166)
config.mimeType = 'audio/ogg';
}
}
/**
* This method records MediaStream.
* @method
* @memberof MediaStreamRecorder
* @example
* recorder.record();
*/
this.record = function() {
self.blob = null;
var recorderHints = config;
if (!config.disableLogs) {
console.log('Passing following config over MediaRecorder API.', recorderHints);
}
if (mediaRecorder) {
// mandatory to make sure Firefox doesn't fails to record streams 3-4 times without reloading the page.
mediaRecorder = null;
}
if (isChrome && !isMediaRecorderCompatible()) {
// to support video-only recording on stable
recorderHints = 'video/vp8';
}
if (typeof MediaRecorder.isTypeSupported === 'function' && recorderHints.mimeType) {
if (!MediaRecorder.isTypeSupported(recorderHints.mimeType)) {
if (!config.disableLogs) {
console.warn('MediaRecorder API seems unable to record mimeType:', recorderHints.mimeType);
}
recorderHints.mimeType = config.type === 'audio' ? 'audio/webm' : 'video/webm';
}
}
// http://dxr.mozilla.org/mozilla-central/source/content/media/MediaRecorder.cpp
// https://wiki.mozilla.org/Gecko:MediaRecorder
// https://dvcs.w3.org/hg/dap/raw-file/default/media-stream-capture/MediaRecorder.html
// starting a recording session; which will initiate "Reading Thread"
// "Reading Thread" are used to prevent main-thread blocking scenarios
try {
mediaRecorder = new MediaRecorder(mediaStream, recorderHints);
} catch (e) {
mediaRecorder = new MediaRecorder(mediaStream);
}
if (!MediaRecorder.isTypeSupported && 'canRecordMimeType' in mediaRecorder && mediaRecorder.canRecordMimeType(config.mimeType
) === false) {
if (!config.disableLogs) {
console.warn('MediaRecorder API seems unable to record mimeType:', config.mimeType);
}
}
// i.e. stop recording when <video> is paused by the user; and auto restart recording
// when video is resumed. E.g. yourStream.getVideoTracks()[0].muted = true; // it will auto-stop recording.
mediaRecorder.ignoreMutedMedia = config.ignoreMutedMedia || false;
// Dispatching OnDataAvailable Handler
mediaRecorder.ondataavailable = function(e) {
if (self.dontFireOnDataAvailableEvent) {
return;
}
if (!e.data || !e.data.size || e.data.size < 100 || self.blob) {
// make sure that stopRecording always getting fired
// even if there is invalid data
if (self.recordingCallback) {
self.recordingCallback(new Blob([], {
type: recorderHints.mimeType || 'video/webm'
}));
self.recordin ...
n/a
function MultiStreamRecorder(arrayOfMediaStreams, options) {
var self = this;
options = options || {
mimeType: 'video/webm',
video: {
width: 320,
height: 240
}
};
if (!options.frameInterval) {
options.frameInterval = 10;
}
if (!options.video) {
options.video = {};
}
if (!options.video.width) {
options.video.width = 320;
}
if (!options.video.height) {
options.video.height = 240;
}
/**
* This method records all MediaStreams.
* @method
* @memberof MultiStreamRecorder
* @example
* recorder.record();
*/
this.record = function() {
isStoppedRecording = false;
var mixedVideoStream = getMixedVideoStream();
var mixedAudioStream = getMixedAudioStream();
if (mixedAudioStream) {
mixedAudioStream.getAudioTracks().forEach(function(track) {
mixedVideoStream.addTrack(track);
});
}
if (options.previewStream && typeof options.previewStream === 'function') {
options.previewStream(mixedVideoStream);
}
mediaRecorder = new MediaStreamRecorder(mixedVideoStream, {
mimeType: 'video/webm'
});
canvas.width = videos.length > 1 ? videos[0].width * 2 : videos[0].width;
canvas.height = videos.length > 2 ? videos[0].height * 2 : videos[0].height;
drawVideosToCanvas();
mediaRecorder.record();
};
/**
* This method stops recording MediaStream.
* @param {function} callback - Callback function, that is used to pass recorded blob back to the callee.
* @method
* @memberof MultiStreamRecorder
* @example
* recorder.stop(function(blob) {
* video.src = URL.createObjectURL(blob);
* });
*/
this.stop = function(callback) {
isStoppedRecording = true;
if (!mediaRecorder) {
return;
}
mediaRecorder.stop(function(blob) {
callback(blob);
});
};
function getMixedAudioStream() {
// via: @pehrsons
var audioContext = new AudioContext();
var audioSources = [];
var audioTracksLength = 0;
arrayOfMediaStreams.forEach(function(stream) {
if (!stream.getAudioTracks().length) {
return;
}
audioTracksLength++;
audioSources.push(audioContext.createMediaStreamSource(stream));
});
if (!audioTracksLength) {
return;
}
var audioiDestination = audioContext.createMediaStreamDestination();
audioSources.forEach(function(audioSource) {
audioSource.connect(audioiDestination);
});
return audioiDestination.stream;
}
var videos = [];
var mediaRecorder;
function getMixedVideoStream() {
// via: @adrian-ber
arrayOfMediaStreams.forEach(function(stream) {
if (!stream.getVideoTracks().length) {
return;
}
var video = getVideo(stream);
video.width = options.video.width;
video.height = options.video.height;
videos.push(video);
});
var capturedStream;
if ('captureStream' in canvas) {
capturedStream = canvas.captureStream();
} else if ('mozCaptureStream' in canvas) {
capturedStream = canvas.mozCaptureStream();
} else if (!options.disableLogs) {
console.error('captureStream API requires this flag: chrome://flags/#enable-experimental-web-platform-features');
}
return capturedStream;
}
function getVideo(stream) {
var video = document.createElement('video');
video.src = URL.createObjectURL(stream);
video.play();
return video;
}
var isStoppedRecording = false;
function drawVideosToCanvas() {
if (isStoppedRecording) {
return;
}
var videosLength = videos.lengt ...
n/a
function StereoAudioRecorder(mediaStream, config) {
if (!mediaStream.getAudioTracks().length) {
throw 'Your stream has no audio tracks.';
}
config = config || {};
var self = this;
// variables
var leftchannel = [];
var rightchannel = [];
var recording = false;
var recordingLength = 0;
var jsAudioNode;
var numberOfAudioChannels = 2;
// backward compatibility
if (config.leftChannel === true) {
numberOfAudioChannels = 1;
}
if (config.numberOfAudioChannels === 1) {
numberOfAudioChannels = 1;
}
if (!config.disableLogs) {
console.debug('StereoAudioRecorder is set to record number of channels: ', numberOfAudioChannels);
}
function isMediaStreamActive() {
if ('active' in mediaStream) {
if (!mediaStream.active) {
return false;
}
} else if ('ended' in mediaStream) { // old hack
if (mediaStream.ended) {
return false;
}
}
return true;
}
/**
* This method records MediaStream.
* @method
* @memberof StereoAudioRecorder
* @example
* recorder.record();
*/
this.record = function() {
if (isMediaStreamActive() === false) {
throw 'Please make sure MediaStream is active.';
}
// reset the buffers for the new recording
leftchannel.length = rightchannel.length = 0;
recordingLength = 0;
if (audioInput) {
audioInput.connect(jsAudioNode);
}
// to prevent self audio to be connected with speakers
// jsAudioNode.connect(context.destination);
isAudioProcessStarted = isPaused = false;
recording = true;
};
function mergeLeftRightBuffers(config, callback) {
function mergeAudioBuffers(config, cb) {
var numberOfAudioChannels = config.numberOfAudioChannels;
// todo: "slice(0)" --- is it causes loop? Should be removed?
var leftBuffers = config.leftBuffers.slice(0);
var rightBuffers = config.rightBuffers.slice(0);
var sampleRate = config.sampleRate;
var internalInterleavedLength = config.internalInterleavedLength;
if (numberOfAudioChannels === 2) {
leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength);
rightBuffers = mergeBuffers(rightBuffers, internalInterleavedLength);
}
if (numberOfAudioChannels === 1) {
leftBuffers = mergeBuffers(leftBuffers, internalInterleavedLength);
}
function mergeBuffers(channelBuffer, rLength) {
var result = new Float64Array(rLength);
var offset = 0;
var lng = channelBuffer.length;
for (var i = 0; i < lng; i++) {
var buffer = channelBuffer[i];
result.set(buffer, offset);
offset += buffer.length;
}
return result;
}
function interleave(leftChannel, rightChannel) {
var length = leftChannel.length + rightChannel.length;
var result = new Float64Array(length);
var inputIndex = 0;
for (var index = 0; index < length;) {
result[index++] = leftChannel[inputIndex];
result[index++] = rightChannel[inputIndex];
inputIndex++;
}
return result;
}
function writeUTFBytes(view, offset, string) {
var lng = string.length;
for (var i = 0; i < lng; i++) {
view.setUint8(offset + i, string.charCodeAt(i));
}
}
// interleave both channels together
var interleaved;
if (numberOfAudioChannels === 2) {
interleaved = interleave(leftBuffers, rightBuffers);
}
if ...
n/a
function WhammyVideo(duration) { this.frames = []; this.duration = duration || 1; this.quality = 0.8; }
n/a
function WhammyRecorder(mediaStream, config) {
config = config || {};
if (!config.frameInterval) {
config.frameInterval = 10;
}
if (!config.disableLogs) {
console.log('Using frames-interval:', config.frameInterval);
}
/**
* This method records video.
* @method
* @memberof WhammyRecorder
* @example
* recorder.record();
*/
this.record = function() {
if (!config.width) {
config.width = 320;
}
if (!config.height) {
config.height = 240;
}
if (!config.video) {
config.video = {
width: config.width,
height: config.height
};
}
if (!config.canvas) {
config.canvas = {
width: config.width,
height: config.height
};
}
canvas.width = config.canvas.width || 320;
canvas.height = config.canvas.height || 240;
context = canvas.getContext('2d');
// setting defaults
if (config.video && config.video instanceof HTMLVideoElement) {
video = config.video.cloneNode();
if (config.initCallback) {
config.initCallback();
}
} else {
video = document.createElement('video');
if (typeof video.srcObject !== 'undefined') {
video.srcObject = mediaStream;
} else {
video.src = URL.createObjectURL(mediaStream);
}
video.onloadedmetadata = function() { // "onloadedmetadata" may NOT work in FF?
if (config.initCallback) {
config.initCallback();
}
};
video.width = config.video.width;
video.height = config.video.height;
}
video.muted = true;
video.play();
lastTime = new Date().getTime();
whammy = new Whammy.Video();
if (!config.disableLogs) {
console.log('canvas resolutions', canvas.width, '*', canvas.height);
console.log('video width/height', video.width || canvas.width, '*', video.height || canvas.height);
}
drawFrames(config.frameInterval);
};
/**
* Draw and push frames to Whammy
* @param {integer} frameInterval - set minimum interval (in milliseconds) between each time we push a frame to Whammy
*/
function drawFrames(frameInterval) {
frameInterval = typeof frameInterval !== 'undefined' ? frameInterval : 10;
var duration = new Date().getTime() - lastTime;
if (!duration) {
return setTimeout(drawFrames, frameInterval, frameInterval);
}
if (isPausedRecording) {
lastTime = new Date().getTime();
return setTimeout(drawFrames, 100);
}
// via #206, by Jack i.e. @Seymourr
lastTime = new Date().getTime();
if (video.paused) {
// via: https://github.com/muaz-khan/WebRTC-Experiment/pull/316
// Tweak for Android Chrome
video.play();
}
context.drawImage(video, 0, 0, canvas.width, canvas.height);
whammy.frames.push({
duration: duration,
image: canvas.toDataURL('image/webp')
});
if (!isStopDrawing) {
setTimeout(drawFrames, frameInterval, frameInterval);
}
}
function asyncLoop(o) {
var i = -1,
length = o.length;
var loop = function() {
i++;
if (i === length) {
o.callback();
return;
}
o.functionToLoop(loop, i);
};
loop(); //init
}
/**
* remove black frames from the beginning to the specified frame
* @param {Array} _frames - array of frames to be checked
* @param {number} _framesToCheck - number of frame until check will be executed (-1 - will drop all frames until frame not
matched will be found)
* @param {number} _pi ...
n/a
getFromDisk = function (type, callback) { if (!callback) { throw 'callback is mandatory.'; } console.log('Getting recorded ' + (type === 'all' ? 'blobs' : type + ' blob ') + ' from disk!'); DiskStorage.Fetch(function(dataURL, _type) { if (type !== 'all' && _type === type + 'Blob' && callback) { callback(dataURL); } if (type === 'all' && callback) { callback(dataURL, _type.replace('Blob', '')); } }); }
...
## `getFromDisk`
You can get recorded blob from disk using `getFromDisk` method:
```javascript
// get all blobs from disk
RecordRTC.getFromDisk('all', function(dataURL, type) {
type == 'audio'
type == 'video'
type == 'gif'
});
// or get just single blob
RecordRTC.getFromDisk('audio', function(dataURL) {
...
writeToDisk = function (options) { console.log('Writing recorded blob(s) to disk!'); options = options || {}; if (options.audio && options.video && options.gif) { options.audio.getDataURL(function(audioDataURL) { options.video.getDataURL(function(videoDataURL) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ audioBlob: audioDataURL, videoBlob: videoDataURL, gifBlob: gifDataURL }); }); }); }); } else if (options.audio && options.video) { options.audio.getDataURL(function(audioDataURL) { options.video.getDataURL(function(videoDataURL) { DiskStorage.Store({ audioBlob: audioDataURL, videoBlob: videoDataURL }); }); }); } else if (options.audio && options.gif) { options.audio.getDataURL(function(audioDataURL) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ audioBlob: audioDataURL, gifBlob: gifDataURL }); }); }); } else if (options.video && options.gif) { options.video.getDataURL(function(videoDataURL) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ videoBlob: videoDataURL, gifBlob: gifDataURL }); }); }); } else if (options.audio) { options.audio.getDataURL(function(audioDataURL) { DiskStorage.Store({ audioBlob: audioDataURL }); }); } else if (options.video) { options.video.getDataURL(function(videoDataURL) { DiskStorage.Store({ videoBlob: videoDataURL }); }); } else if (options.gif) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ gifBlob: gifDataURL }); }); } }
...
## `writeToDisk`
You can write recorded blob to disk using `writeToDisk` method:
```javascript
recordRTC.stopRecording();
recordRTC.writeToDisk();
```
## `getFromDisk`
You can get recorded blob from disk using `getFromDisk` method:
```javascript
...
Fetch = function (callback) { this.callback = callback; this.init(); return this; }
...
*/
RecordRTC.getFromDisk = function(type, callback) {
if (!callback) {
throw 'callback is mandatory.';
}
console.log('Getting recorded ' + (type === 'all' ? 'blobs' : type + ' blob ') + '
; from disk!');
DiskStorage.Fetch(function(dataURL, _type) {
if (type !== 'all' && _type === type + 'Blob' && callback) {
callback(dataURL);
}
if (type === 'all' && callback) {
callback(dataURL, _type.replace('Blob', ''));
}
...
Store = function (config) { this.audioBlob = config.audioBlob; this.videoBlob = config.videoBlob; this.gifBlob = config.gifBlob; this.init(); return this; }
...
if (!config.autoWriteToDisk) {
return;
}
getDataURL(function(dataURL) {
var parameter = {};
parameter[config.type + 'Blob'] = dataURL;
DiskStorage.Store(parameter);
});
}
}
function pauseRecording() {
if (!mediaRecorder) {
return console.warn(WARNING);
...
init = function () { var self = this; if (typeof indexedDB === 'undefined' || typeof indexedDB.open === 'undefined') { console.error('IndexedDB API are not available in this browser.'); return; } var dbVersion = 1; var dbName = this.dbName || location.href.replace(/\/|:|#|%|\.|\[|\]/g, ''), db; var request = indexedDB.open(dbName, dbVersion); function createObjectStore(dataBase) { dataBase.createObjectStore(self.dataStoreName); } function putInDB() { var transaction = db.transaction([self.dataStoreName], 'readwrite'); if (self.videoBlob) { transaction.objectStore(self.dataStoreName).put(self.videoBlob, 'videoBlob'); } if (self.gifBlob) { transaction.objectStore(self.dataStoreName).put(self.gifBlob, 'gifBlob'); } if (self.audioBlob) { transaction.objectStore(self.dataStoreName).put(self.audioBlob, 'audioBlob'); } function getFromStore(portionName) { transaction.objectStore(self.dataStoreName).get(portionName).onsuccess = function(event) { if (self.callback) { self.callback(event.target.result, portionName); } }; } getFromStore('audioBlob'); getFromStore('videoBlob'); getFromStore('gifBlob'); } request.onerror = self.onError; request.onsuccess = function() { db = request.result; db.onerror = self.onError; if (db.setVersion) { if (db.version !== dbVersion) { var setVersion = db.setVersion(dbVersion); setVersion.onsuccess = function() { createObjectStore(db); putInDB(); }; } else { putInDB(); } } else { putInDB(); } }; request.onupgradeneeded = function(event) { createObjectStore(event.target.result); }; }
...
var DiskStorage = {
/**
* This method must be called once to initialize IndexedDB ObjectStore. Though, it is auto-used internally.
* @method
* @memberof DiskStorage
* @internal
* @example
* DiskStorage.init();
*/
init: function() {
var self = this;
if (typeof indexedDB === 'undefined' || typeof indexedDB.open === 'undefined') {
console.error('IndexedDB API are not available in this browser.');
return;
...
onError = function (error) { console.error(JSON.stringify(error, null, '\t')); }
...
this.retryHandler.reset();
this.sendFile_();
}
};
MediaUploader.prototype.onContentUploadError_ = function(e) {
if (e.target.status && e.target.status < 500) {
this.onError(e.target.response);
} else {
this.retryHandler.retry(this.resume_.bind(this));
}
};
MediaUploader.prototype.onUploadError_ = function(e) {
this.onError(e.target.response); // TODO - Retries for initial upload
...
function MRecordRTC(mediaStream) {
/**
* This method attaches MediaStream object to {@link MRecordRTC}.
* @param {MediaStream} mediaStream - A MediaStream object, either fetched using getUserMedia API, or generated using captureStreamUntilEnded
or WebAudio API.
* @method
* @memberof MRecordRTC
* @example
* recorder.addStream(MediaStream);
*/
this.addStream = function(_mediaStream) {
if (_mediaStream) {
mediaStream = _mediaStream;
}
};
/**
* This property can be used to set the recording type e.g. audio, or video, or gif, or canvas.
* @property {object} mediaType - {audio: true, video: true, gif: true}
* @memberof MRecordRTC
* @example
* var recorder = new MRecordRTC();
* recorder.mediaType = {
* audio: true, // TRUE or StereoAudioRecorder or MediaStreamRecorder
* video: true, // TRUE or WhammyRecorder or MediaStreamRecorder
* gif : true // TRUE or GifRecorder
* };
*/
this.mediaType = {
audio: true,
video: true
};
/**
* This method starts recording.
* @method
* @memberof MRecordRTC
* @example
* recorder.startRecording();
*/
this.startRecording = function() {
var mediaType = this.mediaType;
var recorderType;
var mimeType = this.mimeType || {
audio: null,
video: null,
gif: null
};
if (typeof mediaType.audio !== 'function' && isMediaRecorderCompatible() && mediaStream.getAudioTracks && !mediaStream.getAudioTracks
().length) {
// Firefox supports both audio/video in single blob
mediaType.audio = false;
}
if (typeof mediaType.video !== 'function' && isMediaRecorderCompatible() && mediaStream.getVideoTracks && !mediaStream.getVideoTracks
().length) {
// Firefox supports both audio/video in single blob
mediaType.video = false;
}
if (!mediaType.audio && !mediaType.video) {
throw 'MediaStream must have either audio or video tracks.';
}
if (!!mediaType.audio) {
recorderType = null;
if (typeof mediaType.audio === 'function') {
recorderType = mediaType.audio;
}
this.audioRecorder = new RecordRTC(mediaStream, {
type: 'audio',
bufferSize: this.bufferSize,
sampleRate: this.sampleRate,
numberOfAudioChannels: this.numberOfAudioChannels || 2,
disableLogs: this.disableLogs,
recorderType: recorderType,
mimeType: mimeType.audio
});
if (!mediaType.video) {
this.audioRecorder.startRecording();
}
}
if (!!mediaType.video) {
recorderType = null;
if (typeof mediaType.video === 'function') {
recorderType = mediaType.video;
}
var newStream = mediaStream;
if (isMediaRecorderCompatible() && !!mediaType.audio && typeof mediaType.audio === 'function') {
var videoTrack = mediaStream.getVideoTracks()[0];
if (!!navigator.mozGetUserMedia) {
newStream = new MediaStream();
newStream.addTrack(videoTrack);
if (recorderType && recorderType === WhammyRecorder) {
// Firefox does NOT support webp-encoding yet
recorderType = MediaStreamRecorder;
}
} else {
newStream = new MediaStream([videoTrack]);
}
}
this.videoRecorder = new RecordRTC(newStream, {
type: 'video',
video: this.video,
canvas: this.canvas,
frameInterval: this.frameInterval || 10,
disableLogs: this.disableLogs,
recorderType: recorderType,
mimeType ...
n/a
getFromDisk = function (type, callback) { if (!callback) { throw 'callback is mandatory.'; } console.log('Getting recorded ' + (type === 'all' ? 'blobs' : type + ' blob ') + ' from disk!'); DiskStorage.Fetch(function(dataURL, _type) { if (type !== 'all' && _type === type + 'Blob' && callback) { callback(dataURL); } if (type === 'all' && callback) { callback(dataURL, _type.replace('Blob', '')); } }); }
...
## `getFromDisk`
You can get recorded blob from disk using `getFromDisk` method:
```javascript
// get all blobs from disk
RecordRTC.getFromDisk('all', function(dataURL, type) {
type == 'audio'
type == 'video'
type == 'gif'
});
// or get just single blob
RecordRTC.getFromDisk('audio', function(dataURL) {
...
writeToDisk = function (options) { console.log('Writing recorded blob(s) to disk!'); options = options || {}; if (options.audio && options.video && options.gif) { options.audio.getDataURL(function(audioDataURL) { options.video.getDataURL(function(videoDataURL) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ audioBlob: audioDataURL, videoBlob: videoDataURL, gifBlob: gifDataURL }); }); }); }); } else if (options.audio && options.video) { options.audio.getDataURL(function(audioDataURL) { options.video.getDataURL(function(videoDataURL) { DiskStorage.Store({ audioBlob: audioDataURL, videoBlob: videoDataURL }); }); }); } else if (options.audio && options.gif) { options.audio.getDataURL(function(audioDataURL) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ audioBlob: audioDataURL, gifBlob: gifDataURL }); }); }); } else if (options.video && options.gif) { options.video.getDataURL(function(videoDataURL) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ videoBlob: videoDataURL, gifBlob: gifDataURL }); }); }); } else if (options.audio) { options.audio.getDataURL(function(audioDataURL) { DiskStorage.Store({ audioBlob: audioDataURL }); }); } else if (options.video) { options.video.getDataURL(function(videoDataURL) { DiskStorage.Store({ videoBlob: videoDataURL }); }); } else if (options.gif) { options.gif.getDataURL(function(gifDataURL) { DiskStorage.Store({ gifBlob: gifDataURL }); }); } }
...
## `writeToDisk`
You can write recorded blob to disk using `writeToDisk` method:
```javascript
recordRTC.stopRecording();
recordRTC.writeToDisk();
```
## `getFromDisk`
You can get recorded blob from disk using `getFromDisk` method:
```javascript
...
function WhammyVideo(duration) { this.frames = []; this.duration = duration || 1; this.quality = 0.8; }
...
```javascript
var RecordRTC = require('recordrtc');
var Whammy = RecordRTC.Whammy;
var WhammyRecorder = RecordRTC.WhammyRecorder;
var StereoAudioRecorder = RecordRTC.StereoAudioRecorder;
// and so on
var video = new Whammy.Video(100);
var recorder = new StereoAudioRecorder(stream, options);
```
```html
<!-- link npm package scripts -->
<script src="./node_modules/recordrtc/RecordRTC.js"></script>
```
...
function WhammyVideo(duration) { this.frames = []; this.duration = duration || 1; this.quality = 0.8; }
...
```javascript
var RecordRTC = require('recordrtc');
var Whammy = RecordRTC.Whammy;
var WhammyRecorder = RecordRTC.WhammyRecorder;
var StereoAudioRecorder = RecordRTC.StereoAudioRecorder;
// and so on
var video = new Whammy.Video(100);
var recorder = new StereoAudioRecorder(stream, options);
```
```html
<!-- link npm package scripts -->
<script src="./node_modules/recordrtc/RecordRTC.js"></script>
```
...
add = function (frame, duration) { if ('canvas' in frame) { //CanvasRenderingContext2D frame = frame.canvas; } if ('toDataURL' in frame) { frame = frame.toDataURL('image/webp', this.quality); } if (!(/^data:image\/webp;base64,/ig).test(frame)) { throw 'Input must be formatted properly as a base64 encoded DataURI of type image/webp'; } this.frames.push({ image: frame, duration: duration || this.duration }); }
...
* @summary A real time javascript webm encoder based on a canvas hack.
* @license {@link https://github.com/muaz-khan/RecordRTC#license|MIT}
* @author {@link http://www.MuazKhan.com|Muaz Khan}
* @typedef Whammy
* @class
* @example
* var recorder = new Whammy().Video(15);
* recorder.add(context || canvas || dataURL);
* var output = recorder.compile();
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
*/
var Whammy = (function() {
// a more abstract-ish API
...
compile = function (callback) { var webWorker = processInWebWorker(whammyInWebWorker); webWorker.onmessage = function(event) { if (event.data.error) { console.error(event.data.error); return; } callback(event.data); }; webWorker.postMessage(this.frames); }
...
* @property {Blob} blob - Recorded frames in video/webm blob.
* @memberof CanvasRecorder
* @example
* recorder.stop(function() {
* var blob = recorder.blob;
* });
*/
whammy.compile(function(blob) {
if (!config.disableLogs) {
console.debug('Recording finished!');
}
that.blob = blob;
if (that.blob.forEach) {
...