mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-09 12:51:09 +02:00
150 lines
5.1 KiB
JavaScript
150 lines
5.1 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
|
|
var loop = loop || {};
|
|
|
|
/**
|
|
* Monkeypatch getUserMedia in a way that prevents additional camera and
|
|
* microphone prompts, at the cost of ignoring all constraints other than
|
|
* the first set passed in.
|
|
*
|
|
* The first call to navigator.getUserMedia (also now aliased to
|
|
* multiplexGum.getPermsAndCacheMedia to allow for explicit calling code)
|
|
* will cause the underlying gUM implementation to be called.
|
|
*
|
|
* While permission is pending, subsequent calls will result in the callbacks
|
|
* being queued. Once the call succeeds or fails, all queued success or
|
|
* failure callbacks will be invoked. Subsequent calls to either function will
|
|
* cause the success or failure callback to be invoked immediately.
|
|
*/
|
|
loop.standaloneMedia = (function() {
|
|
"use strict";
|
|
|
|
function patchSymbolIfExtant(objectName, propertyName, replacement) {
|
|
var object;
|
|
if (window[objectName]) {
|
|
object = window[objectName];
|
|
}
|
|
if (object && object[propertyName]) {
|
|
object[propertyName] = replacement;
|
|
}
|
|
}
|
|
|
|
// originalGum _must_ be on navigator; otherwise things blow up.
|
|
// For TBPlugin users, navigator.originalGum is set after the TB SDK is loaded.
|
|
navigator.originalGum = navigator.getUserMedia ||
|
|
navigator.mozGetUserMedia ||
|
|
navigator.webkitGetUserMedia;
|
|
|
|
function _MultiplexGum() {
|
|
this.reset();
|
|
}
|
|
|
|
_MultiplexGum.prototype = {
|
|
/**
|
|
* @see The docs at the top of this file for overall semantics,
|
|
* & http://developer.mozilla.org/en-US/docs/NavigatorUserMedia.getUserMedia
|
|
* for params, since this is intended to be purely a passthrough to gUM.
|
|
*/
|
|
getPermsAndCacheMedia: function(constraints, onSuccess, onError) {
|
|
function handleResult(callbacks, param) {
|
|
// Operate on a copy of the array in case any of the callbacks
|
|
// calls reset, which would cause an infinite-recursion.
|
|
this.userMedia.successCallbacks = [];
|
|
this.userMedia.errorCallbacks = [];
|
|
callbacks.forEach(function(cb) {
|
|
if (typeof cb == "function") {
|
|
cb(param);
|
|
}
|
|
})
|
|
}
|
|
function handleSuccess(localStream) {
|
|
this.userMedia.pending = false;
|
|
this.userMedia.localStream = localStream;
|
|
this.userMedia.error = null;
|
|
handleResult.call(this, this.userMedia.successCallbacks.slice(0), localStream);
|
|
}
|
|
|
|
function handleError(error) {
|
|
this.userMedia.pending = false;
|
|
this.userMedia.error = error;
|
|
handleResult.call(this, this.userMedia.errorCallbacks.slice(0), error);
|
|
this.error = null;
|
|
}
|
|
|
|
if (this.userMedia.localStream &&
|
|
this.userMedia.localStream.ended) {
|
|
this.userMedia.localStream = null;
|
|
}
|
|
|
|
this.userMedia.errorCallbacks.push(onError);
|
|
this.userMedia.successCallbacks.push(onSuccess);
|
|
|
|
if (this.userMedia.localStream) {
|
|
handleSuccess.call(this, this.userMedia.localStream);
|
|
return;
|
|
} else if (this.userMedia.error) {
|
|
handleError.call(this, this.userMedia.error);
|
|
return;
|
|
}
|
|
|
|
if (this.userMedia.pending) {
|
|
return;
|
|
}
|
|
this.userMedia.pending = true;
|
|
|
|
navigator.originalGum(constraints, handleSuccess.bind(this),
|
|
handleError.bind(this));
|
|
},
|
|
|
|
/**
|
|
* Reset the cached permissions, callbacks, and media to their default
|
|
* state and call any error callbacks to let any waiting callers know
|
|
* not to ever expect any more callbacks. We use "PERMISSION_DENIED",
|
|
* for lack of a better, more specific gUM code that callers are likely
|
|
* to be prepared to handle.
|
|
*/
|
|
reset: function() {
|
|
// When called from the ctor, userMedia is not created yet.
|
|
if (this.userMedia) {
|
|
this.userMedia.errorCallbacks.forEach(function(cb) {
|
|
if (typeof cb == "function") {
|
|
cb("PERMISSION_DENIED");
|
|
}
|
|
});
|
|
if (this.userMedia.localStream &&
|
|
typeof this.userMedia.localStream.stop == "function") {
|
|
this.userMedia.localStream.stop();
|
|
}
|
|
}
|
|
this.userMedia = {
|
|
error: null,
|
|
localStream: null,
|
|
pending: false,
|
|
errorCallbacks: [],
|
|
successCallbacks: [],
|
|
};
|
|
}
|
|
};
|
|
|
|
var singletonMultiplexGum = new _MultiplexGum();
|
|
function myGetUserMedia() {
|
|
// This function is needed to pull in the instance
|
|
// of the singleton for tests to overwrite the used instance.
|
|
singletonMultiplexGum.getPermsAndCacheMedia.apply(singletonMultiplexGum, arguments);
|
|
};
|
|
patchSymbolIfExtant("navigator", "mozGetUserMedia", myGetUserMedia);
|
|
patchSymbolIfExtant("navigator", "webkitGetUserMedia", myGetUserMedia);
|
|
patchSymbolIfExtant("navigator", "getUserMedia", myGetUserMedia);
|
|
patchSymbolIfExtant("TBPlugin", "getUserMedia", myGetUserMedia);
|
|
|
|
return {
|
|
multiplexGum: singletonMultiplexGum,
|
|
_MultiplexGum: _MultiplexGum,
|
|
setSingleton: function(singleton) {
|
|
singletonMultiplexGum = singleton;
|
|
},
|
|
};
|
|
})();
|