Skip to content

Commit

Permalink
Added support for MediaRecorder API in Chrome.
Browse files Browse the repository at this point in the history
Currently requires:

var recorder = RecordRTC(stream, {recorderType: MediaStreamRecorder});
  • Loading branch information
muaz-khan committed Oct 4, 2015
1 parent 88e5de5 commit 565347c
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 20 deletions.
65 changes: 56 additions & 9 deletions RecordRTC.js
@@ -1,4 +1,4 @@
// Last time updated at September 27, 2015
// Last time updated at October 04, 2015

// links:
// Open-Sourced: https://github.com/muaz-khan/RecordRTC
Expand All @@ -9,6 +9,7 @@

// updates?
/*
-. Added support for MediaRecorder API in Chrome. Currently requires: RecordRTC(stream, {recorderType: MediaStreamRecorder})
-. mimeType, bitsPerSecond, audioBitsPerSecond, videoBitsPerSecond added.
-. CanvasRecorder.js updated to support Firefox. (experimental)
-. Now you can reuse single RecordRTC object i.e. stop/start/stop/start/ and so on.
Expand Down Expand Up @@ -135,7 +136,7 @@ function RecordRTC(mediaStream, config) {
};
}

var Recorder = new GetRecorderType(config);
var Recorder = new GetRecorderType(mediaStream, config);

mediaRecorder = new Recorder(mediaStream, config);
mediaRecorder.record();
Expand Down Expand Up @@ -770,10 +771,11 @@ function RecordRTCConfiguration(mediaStream, config) {
* var RecorderType = GetRecorderType(options);
* var recorder = new RecorderType(options);
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {type:"video", disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, etc.}
*/

function GetRecorderType(config) {
function GetRecorderType(mediaStream, config) {
var recorder;

// StereoAudioRecorder can work with all three: Edge, Firefox and Chrome
Expand All @@ -784,7 +786,7 @@ function GetRecorderType(config) {
recorder = StereoAudioRecorder;
}

if (typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {
if (typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype && !isChrome) {
recorder = MediaStreamRecorder;
}

Expand All @@ -803,6 +805,16 @@ function GetRecorderType(config) {
recorder = CanvasRecorder;
}

// todo: enable below block when MediaRecorder in Chrome gets out of flags; and it also supports audio recording.
if (false && isChrome && recorder === WhammyRecorder && typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {
if (mediaStream.getVideoTracks().length) {
recorder = MediaStreamRecorder;
if (!config.disableLogs) {
console.debug('Using MediaRecorder API in chrome!');
}
}
}

if (config.recorderType) {
recorder = config.recorderType;
}
Expand Down Expand Up @@ -1348,7 +1360,7 @@ function MediaStreamRecorder(mediaStream, config) {
// if user chosen only audio option; and he tried to pass MediaStream with
// both audio and video tracks;
// using a dirty workaround to generate audio-only stream so that we can get audio/ogg output.
if (config.mimeType && config.mimeType !== 'video/webm' && mediaStream.getVideoTracks && mediaStream.getVideoTracks().length) {
if (!isChrome && config.mimeType && config.mimeType !== 'video/webm' && mediaStream.getVideoTracks && mediaStream.getVideoTracks().length) {
var context = new AudioContext();
var mediaStreamSource = context.createMediaStreamSource(mediaStream);

Expand All @@ -1363,6 +1375,7 @@ function MediaStreamRecorder(mediaStream, config) {
}

var dataAvailable = false;
var recordedBuffers = [];

/**
* This method records MediaStream.
Expand All @@ -1372,8 +1385,19 @@ function MediaStreamRecorder(mediaStream, config) {
* recorder.record();
*/
this.record = function() {
var recorderHints = config;

if (isChrome) {
if (!recorderHints || typeof recorderHints !== 'string') {
recorderHints = 'video/vp8';

// chrome currently supports only video recording
mediaStream = new MediaStream(mediaStream.getVideoTracks());
}
}

if (!config.disableLogs) {
console.log('Passing following config over MediaRecorder API.', config);
console.log('Passing following config over MediaRecorder API.', recorderHints);
}

// http://dxr.mozilla.org/mozilla-central/source/content/media/MediaRecorder.cpp
Expand All @@ -1382,7 +1406,7 @@ function MediaStreamRecorder(mediaStream, config) {

// starting a recording session; which will initiate "Reading Thread"
// "Reading Thread" are used to prevent main-thread blocking scenarios
mediaRecorder = new MediaRecorder(mediaStream, config);
mediaRecorder = new MediaRecorder(mediaStream, recorderHints);

if ('canRecordMimeType' in mediaRecorder && mediaRecorder.canRecordMimeType(config.mimeType) === false) {
if (!config.disableLogs) {
Expand All @@ -1396,13 +1420,36 @@ function MediaStreamRecorder(mediaStream, config) {

// Dispatching OnDataAvailable Handler
mediaRecorder.ondataavailable = function(e) {
if (e.data && !e.data.size) {
e.data.size = e.data.length || e.data.byteLength || 0;
}

if (isChrome && e.data && e.data.size) {
recordedBuffers.push(e.data);
return;
}

if (self.dontFireOnDataAvailableEvent || dataAvailable) {
return;
}

if (!e.data.size) {
if (!e.data || !e.data.size) {
if (recordedBuffers.length) {
self.blob = new Blob(recordedBuffers, {
type: config.mimeType || 'video/webm'
});

if (self.callback) {
self.callback();
}
return;
}

if (!config.disableLogs) {
console.warn('Recording of', (e.data.type || mediaRecorder.mimeType || config.mimeType), 'failed.');
if (!e.data) {
console.error('MediaRecorder.onDataAvailable returned nothing.', e);
}
console.warn('Recording of', ((e.data ? e.data.type : null) || mediaRecorder.mimeType || config.mimeType), 'failed.');
}
return;
}
Expand Down
4 changes: 2 additions & 2 deletions RecordRTC.min.js

Large diffs are not rendered by default.

15 changes: 13 additions & 2 deletions dev/GetRecorderType.js
Expand Up @@ -12,10 +12,11 @@
* var RecorderType = GetRecorderType(options);
* var recorder = new RecorderType(options);
* @see {@link https://github.com/muaz-khan/RecordRTC|RecordRTC Source Code}
* @param {MediaStream} mediaStream - MediaStream object fetched using getUserMedia API or generated using captureStreamUntilEnded or WebAudio API.
* @param {object} config - {type:"video", disableLogs: true, numberOfAudioChannels: 1, bufferSize: 0, sampleRate: 0, video: HTMLVideoElement, etc.}
*/

function GetRecorderType(config) {
function GetRecorderType(mediaStream, config) {
var recorder;

// StereoAudioRecorder can work with all three: Edge, Firefox and Chrome
Expand All @@ -26,7 +27,7 @@ function GetRecorderType(config) {
recorder = StereoAudioRecorder;
}

if (typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {
if (typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype && !isChrome) {
recorder = MediaStreamRecorder;
}

Expand All @@ -45,6 +46,16 @@ function GetRecorderType(config) {
recorder = CanvasRecorder;
}

// todo: enable below block when MediaRecorder in Chrome gets out of flags; and it also supports audio recording.
if (false && isChrome && recorder === WhammyRecorder && typeof MediaRecorder !== 'undefined' && 'requestData' in MediaRecorder.prototype) {
if (mediaStream.getVideoTracks().length) {
recorder = MediaStreamRecorder;
if (!config.disableLogs) {
console.debug('Using MediaRecorder API in chrome!');
}
}
}

if (config.recorderType) {
recorder = config.recorderType;
}
Expand Down
45 changes: 40 additions & 5 deletions dev/MediaStreamRecorder.js
Expand Up @@ -55,7 +55,7 @@ function MediaStreamRecorder(mediaStream, config) {
// if user chosen only audio option; and he tried to pass MediaStream with
// both audio and video tracks;
// using a dirty workaround to generate audio-only stream so that we can get audio/ogg output.
if (config.mimeType && config.mimeType !== 'video/webm' && mediaStream.getVideoTracks && mediaStream.getVideoTracks().length) {
if (!isChrome && config.mimeType && config.mimeType !== 'video/webm' && mediaStream.getVideoTracks && mediaStream.getVideoTracks().length) {
var context = new AudioContext();
var mediaStreamSource = context.createMediaStreamSource(mediaStream);

Expand All @@ -70,6 +70,7 @@ function MediaStreamRecorder(mediaStream, config) {
}

var dataAvailable = false;
var recordedBuffers = [];

/**
* This method records MediaStream.
Expand All @@ -79,8 +80,19 @@ function MediaStreamRecorder(mediaStream, config) {
* recorder.record();
*/
this.record = function() {
var recorderHints = config;

if (isChrome) {
if (!recorderHints || typeof recorderHints !== 'string') {
recorderHints = 'video/vp8';

// chrome currently supports only video recording
mediaStream = new MediaStream(mediaStream.getVideoTracks());
}
}

if (!config.disableLogs) {
console.log('Passing following config over MediaRecorder API.', config);
console.log('Passing following config over MediaRecorder API.', recorderHints);
}

// http://dxr.mozilla.org/mozilla-central/source/content/media/MediaRecorder.cpp
Expand All @@ -89,7 +101,7 @@ function MediaStreamRecorder(mediaStream, config) {

// starting a recording session; which will initiate "Reading Thread"
// "Reading Thread" are used to prevent main-thread blocking scenarios
mediaRecorder = new MediaRecorder(mediaStream, config);
mediaRecorder = new MediaRecorder(mediaStream, recorderHints);

if ('canRecordMimeType' in mediaRecorder && mediaRecorder.canRecordMimeType(config.mimeType) === false) {
if (!config.disableLogs) {
Expand All @@ -103,13 +115,36 @@ function MediaStreamRecorder(mediaStream, config) {

// Dispatching OnDataAvailable Handler
mediaRecorder.ondataavailable = function(e) {
if (e.data && !e.data.size) {
e.data.size = e.data.length || e.data.byteLength || 0;
}

if (isChrome && e.data && e.data.size) {
recordedBuffers.push(e.data);
return;
}

if (self.dontFireOnDataAvailableEvent || dataAvailable) {
return;
}

if (!e.data.size) {
if (!e.data || !e.data.size) {
if (recordedBuffers.length) {
self.blob = new Blob(recordedBuffers, {
type: config.mimeType || 'video/webm'
});

if (self.callback) {
self.callback();
}
return;
}

if (!config.disableLogs) {
console.warn('Recording of', (e.data.type || mediaRecorder.mimeType || config.mimeType), 'failed.');
if (!e.data) {
console.error('MediaRecorder.onDataAvailable returned nothing.', e);
}
console.warn('Recording of', ((e.data ? e.data.type : null) || mediaRecorder.mimeType || config.mimeType), 'failed.');
}
return;
}
Expand Down
2 changes: 1 addition & 1 deletion dev/RecordRTC.js
Expand Up @@ -67,7 +67,7 @@ function RecordRTC(mediaStream, config) {
};
}

var Recorder = new GetRecorderType(config);
var Recorder = new GetRecorderType(mediaStream, config);

mediaRecorder = new Recorder(mediaStream, config);
mediaRecorder.record();
Expand Down
3 changes: 2 additions & 1 deletion dev/head.js
@@ -1,4 +1,4 @@
// Last time updated at September 27, 2015
// Last time updated at October 04, 2015

// links:
// Open-Sourced: https://github.com/muaz-khan/RecordRTC
Expand All @@ -9,6 +9,7 @@

// updates?
/*
-. Added support for MediaRecorder API in Chrome. Currently requires: RecordRTC(stream, {recorderType: MediaStreamRecorder})
-. mimeType, bitsPerSecond, audioBitsPerSecond, videoBitsPerSecond added.
-. CanvasRecorder.js updated to support Firefox. (experimental)
-. Now you can reuse single RecordRTC object i.e. stop/start/stop/start/ and so on.
Expand Down
3 changes: 3 additions & 0 deletions index.html
Expand Up @@ -268,6 +268,7 @@ <h2 class="header">
button.mediaCapturedCallback = function() {
button.recordRTC = RecordRTC(button.stream, {
type: mediaContainerFormat.value === 'Gif' ? 'gif' : 'video',
recorderType: isChrome && mediaContainerFormat.value !== 'Gif' && typeof MediaRecorder !== 'undefined' ? MediaStreamRecorder : null,
mimeType: mimeType,
disableLogs: params.disableLogs || false,
canvas: {
Expand Down Expand Up @@ -362,6 +363,7 @@ <h2 class="header">

var videoRecorder = RecordRTC(button.stream, {
type: 'video',
// recorderType: isChrome && typeof MediaRecorder !== 'undefined' ? MediaStreamRecorder : null,
disableLogs: params.disableLogs || false,
canvas: {
width: params.canvas_width || recordingPlayer.clientWidth,
Expand Down Expand Up @@ -430,6 +432,7 @@ <h2 class="header">
button.recordRTC = RecordRTC(button.stream, {
type: mediaContainerFormat.value === 'Gif' ? 'gif' : 'video',
mimeType: mimeType,
recorderType: isChrome && mediaContainerFormat.value !== 'Gif' && typeof MediaRecorder !== 'undefined' ? MediaStreamRecorder : null,
disableLogs: params.disableLogs || false,
canvas: {
width: params.canvas_width || recordingPlayer.clientWidth,
Expand Down

0 comments on commit 565347c

Please sign in to comment.