mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-23 19:09:49 +00:00
Bug 802694: Pass along constraints from PC JS module to PCImpl; r=ekr,jesup
This commit is contained in:
parent
fc01bff121
commit
f90c9929ee
@ -230,22 +230,51 @@ PeerConnection.prototype = {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Constraints look like this:
|
||||
*
|
||||
* {
|
||||
* mandatory: {"foo": true, "bar": 10, "baz": "boo"},
|
||||
* optional: [{"foo": true}, {"bar": 10}]
|
||||
* }
|
||||
*
|
||||
* We check for basic structure but not the validity of the constraints
|
||||
* themselves before passing them along to C++.
|
||||
*/
|
||||
_validateConstraints: function(constraints) {
|
||||
function isObject(obj) {
|
||||
return obj && (typeof obj === "object");
|
||||
}
|
||||
function isArray(obj) {
|
||||
return isObject(obj) &&
|
||||
(Object.prototype.toString.call(obj) === "[object Array]");
|
||||
}
|
||||
|
||||
if (!isObject(constraints)) {
|
||||
return false;
|
||||
}
|
||||
if (constraints.mandatory && !isObject(constraints.mandatory)) {
|
||||
return false;
|
||||
}
|
||||
if (constraints.optional && !isArray(constraints.optional)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
createOffer: function(onSuccess, onError, constraints) {
|
||||
if (this._onCreateOfferSuccess) {
|
||||
if (onError) {
|
||||
onError.onCallback("createOffer already called");
|
||||
}
|
||||
return;
|
||||
throw new Error("createOffer already called");
|
||||
}
|
||||
|
||||
if (!this._validateConstraints(constraints)) {
|
||||
throw new Error("createOffer passed invalid constraints");
|
||||
}
|
||||
|
||||
this._onCreateOfferSuccess = onSuccess;
|
||||
this._onCreateOfferFailure = onError;
|
||||
|
||||
// TODO: Implement constraints/hints.
|
||||
if (!constraints) {
|
||||
constraints = "";
|
||||
}
|
||||
|
||||
this._queueOrRun({
|
||||
func: this._pc.createOffer,
|
||||
args: [constraints],
|
||||
@ -255,56 +284,39 @@ PeerConnection.prototype = {
|
||||
|
||||
createAnswer: function(onSuccess, onError, constraints, provisional) {
|
||||
if (this._onCreateAnswerSuccess) {
|
||||
if (onError) {
|
||||
try {
|
||||
onError.onCallback("createAnswer already called");
|
||||
} catch(e) {}
|
||||
}
|
||||
return;
|
||||
throw new Error("createAnswer already called");
|
||||
}
|
||||
|
||||
if (!this.remoteDescription) {
|
||||
if (onError) {
|
||||
try {
|
||||
onError.onCallback("setRemoteDescription not called");
|
||||
} catch(e) {}
|
||||
}
|
||||
throw new Error("setRemoteDescription not called");
|
||||
}
|
||||
|
||||
if (this.remoteDescription.type != "offer") {
|
||||
if (onError) {
|
||||
try {
|
||||
onError.onCallback("No outstanding offer");
|
||||
} catch(e) {}
|
||||
}
|
||||
throw new Error("No outstanding offer");
|
||||
}
|
||||
|
||||
if (!this._validateConstraints(constraints)) {
|
||||
throw new Error("createAnswer passed invalid constraints");
|
||||
}
|
||||
|
||||
this._onCreateAnswerSuccess = onSuccess;
|
||||
this._onCreateAnswerFailure = onError;
|
||||
|
||||
if (!constraints) {
|
||||
constraints = "";
|
||||
}
|
||||
if (!provisional) {
|
||||
provisional = false;
|
||||
}
|
||||
|
||||
// TODO: Implement provisional answer & constraints.
|
||||
// TODO: Implement provisional answer.
|
||||
this._queueOrRun({
|
||||
func: this._pc.createAnswer,
|
||||
args: ["", this.remoteDescription.sdp],
|
||||
args: [constraints],
|
||||
wait: true
|
||||
});
|
||||
},
|
||||
|
||||
setLocalDescription: function(desc, onSuccess, onError) {
|
||||
if (this._onSetLocalDescriptionSuccess) {
|
||||
if (onError) {
|
||||
try {
|
||||
onError.onCallback("setLocalDescription already called");
|
||||
} catch(e) {}
|
||||
}
|
||||
return;
|
||||
throw new Error("setLocalDescription already called");
|
||||
}
|
||||
|
||||
this._onSetLocalDescriptionSuccess = onSuccess;
|
||||
@ -319,14 +331,9 @@ PeerConnection.prototype = {
|
||||
type = Ci.IPeerConnection.kActionAnswer;
|
||||
break;
|
||||
default:
|
||||
if (onError) {
|
||||
try {
|
||||
onError.onCallback(
|
||||
"Invalid type " + desc.type + " provided to setLocalDescription"
|
||||
);
|
||||
} catch(e) {}
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
"Invalid type " + desc.type + " provided to setLocalDescription"
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -339,12 +346,7 @@ PeerConnection.prototype = {
|
||||
|
||||
setRemoteDescription: function(desc, onSuccess, onError) {
|
||||
if (this._onSetRemoteDescriptionSuccess) {
|
||||
if (onError) {
|
||||
try {
|
||||
onError.onCallback("setRemoteDescription already called");
|
||||
} catch(e) {}
|
||||
}
|
||||
return;
|
||||
throw new Error("setRemoteDescription already called");
|
||||
}
|
||||
|
||||
this._onSetRemoteDescriptionSuccess = onSuccess;
|
||||
@ -359,14 +361,9 @@ PeerConnection.prototype = {
|
||||
type = Ci.IPeerConnection.kActionAnswer;
|
||||
break;
|
||||
default:
|
||||
if (onError) {
|
||||
try {
|
||||
onError.onCallback(
|
||||
"Invalid type " + desc.type + " provided to setRemoteDescription"
|
||||
);
|
||||
} catch(e) {}
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
"Invalid type " + desc.type + " provided to setRemoteDescription"
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "nsIThread.idl"
|
||||
#include "nsIDOMWindow.idl"
|
||||
#include "nsIPropertyBag2.idl"
|
||||
|
||||
interface nsIDOMMediaStream;
|
||||
interface nsIDOMDataChannel;
|
||||
@ -51,7 +52,7 @@ interface IPeerConnectionObserver : nsISupports
|
||||
void foundIceCandidate(in string candidate);
|
||||
};
|
||||
|
||||
[scriptable, uuid(cb3f0048-1009-11e2-b822-87ee49eface7)]
|
||||
[scriptable, uuid(f6819246-f5af-40f2-ab82-e166d5da7ba0)]
|
||||
interface IPeerConnection : nsISupports
|
||||
{
|
||||
const unsigned long kHintAudio = 0x00000001;
|
||||
@ -73,8 +74,8 @@ interface IPeerConnection : nsISupports
|
||||
[optional] in nsIThread thread);
|
||||
|
||||
/* JSEP calls */
|
||||
void createOffer(in string hints);
|
||||
void createAnswer(in string hints, in string offer);
|
||||
[implicit_jscontext] void createOffer(in jsval constraints);
|
||||
[implicit_jscontext] void createAnswer(in jsval constraints);
|
||||
void setLocalDescription(in long action, in string sdp);
|
||||
void setRemoteDescription(in long action, in string sdp);
|
||||
|
||||
|
@ -15,14 +15,19 @@
|
||||
#include "cpr_string.h"
|
||||
#include "cpr_stdlib.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "nspr.h"
|
||||
#include "nss.h"
|
||||
#include "pk11pub.h"
|
||||
|
||||
#include "nsNetCID.h"
|
||||
#include "nsIProperty.h"
|
||||
#include "nsIPropertyBag2.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsISocketTransportService.h"
|
||||
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsProxyRelease.h"
|
||||
|
||||
@ -730,24 +735,91 @@ PeerConnectionImpl::NotifyDataChannel(mozilla::DataChannel *aChannel)
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* the Constraints UI IDL work is being done. The CreateOffer below is the one
|
||||
* currently called by the signaling unit tests.
|
||||
/**
|
||||
* Constraints look like this:
|
||||
*
|
||||
* {
|
||||
* "mandatory": {"foo":"hello", "bar": false, "baz": 10},
|
||||
* "optional": [{"hello":"foo"}, {"baz": false}]
|
||||
* }
|
||||
*
|
||||
* Optional constraints are ordered, and hence in an array. This function
|
||||
* converts a jsval that looks like the above into a MediaConstraints object.
|
||||
*/
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::CreateOffer(const char* constraints) {
|
||||
MOZ_ASSERT(constraints);
|
||||
nsresult
|
||||
PeerConnectionImpl::ConvertConstraints(
|
||||
const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx)
|
||||
{
|
||||
size_t i;
|
||||
jsval mandatory, optional;
|
||||
JSObject& constraints = aConstraints.toObject();
|
||||
|
||||
// Mandatory constraints.
|
||||
if (JS_GetProperty(aCx, &constraints, "mandatory", &mandatory)) {
|
||||
if (JSVAL_IS_PRIMITIVE(mandatory) && mandatory.isObject() && !JSVAL_IS_NULL(mandatory)) {
|
||||
JSObject* opts = JSVAL_TO_OBJECT(mandatory);
|
||||
JS::AutoIdArray mandatoryOpts(aCx, JS_Enumerate(aCx, opts));
|
||||
|
||||
// Iterate over each property.
|
||||
for (i = 0; i < mandatoryOpts.length(); i++) {
|
||||
jsval option, optionName;
|
||||
if (JS_GetPropertyById(aCx, opts, mandatoryOpts[i], &option)) {
|
||||
if (JS_IdToValue(aCx, mandatoryOpts[i], &optionName)) {
|
||||
// We only support boolean constraints for now.
|
||||
if (JSVAL_IS_BOOLEAN(option)) {
|
||||
JSString* optionNameString = JS_ValueToString(aCx, optionName);
|
||||
NS_ConvertUTF16toUTF8 stringVal(JS_GetStringCharsZ(aCx, optionNameString));
|
||||
aObj->setBooleanConstraint(stringVal.get(), JSVAL_TO_BOOLEAN(option), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Optional constraints.
|
||||
if (JS_GetProperty(aCx, &constraints, "optional", &optional)) {
|
||||
if (JSVAL_IS_PRIMITIVE(optional) && optional.isObject() && !JSVAL_IS_NULL(optional)) {
|
||||
JSObject* opts = JSVAL_TO_OBJECT(optional);
|
||||
if (JS_IsArrayObject(aCx, opts)) {
|
||||
uint32_t length;
|
||||
if (!JS_GetArrayLength(aCx, opts, &length)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
for (i = 0; i < length; i++) {
|
||||
jsval val;
|
||||
JS_GetElement(aCx, opts, i, &val);
|
||||
if (JSVAL_IS_PRIMITIVE(val)) {
|
||||
// Extract name & value and store.
|
||||
// FIXME: MediaConstraints does not support optional constraints?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CheckIceState();
|
||||
mRole = kRoleOfferer; // TODO(ekr@rtfm.com): Interrogate SIPCC here?
|
||||
MediaConstraints aconstraints;
|
||||
CreateOffer(aconstraints);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::CreateOffer(MediaConstraints& constraints) {
|
||||
PeerConnectionImpl::CreateOffer(const JS::Value& aConstraints, JSContext* aCx)
|
||||
{
|
||||
CheckIceState();
|
||||
mRole = kRoleOfferer; // TODO(ekr@rtfm.com): Interrogate SIPCC here?
|
||||
|
||||
MediaConstraints* cs = new MediaConstraints();
|
||||
nsresult rv = ConvertConstraints(aConstraints, cs, aCx);
|
||||
if (rv != NS_OK) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return CreateOffer(*cs);
|
||||
}
|
||||
|
||||
// Used by unit tests and the IDL CreateOffer.
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::CreateOffer(MediaConstraints& constraints)
|
||||
{
|
||||
cc_media_constraints_t* cc_constraints = nullptr;
|
||||
constraints.buildArray(&cc_constraints);
|
||||
|
||||
@ -755,27 +827,25 @@ PeerConnectionImpl::CreateOffer(MediaConstraints& constraints) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* the Constraints UI IDL work is being done. The CreateAnswer below is the one
|
||||
* currently called by the signaling unit tests.
|
||||
*
|
||||
* The aOffer parameter needs to be removed here and in the PeerConnection IDL
|
||||
*/
|
||||
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::CreateAnswer(const char* constraints, const char* aOffer) {
|
||||
MOZ_ASSERT(constraints);
|
||||
|
||||
PeerConnectionImpl::CreateAnswer(const JS::Value& aConstraints, JSContext* aCx)
|
||||
{
|
||||
CheckIceState();
|
||||
mRole = kRoleAnswerer; // TODO(ekr@rtfm.com): Interrogate SIPCC here?
|
||||
MediaConstraints aconstraints;
|
||||
CreateAnswer(aconstraints);
|
||||
|
||||
MediaConstraints* cs = new MediaConstraints();
|
||||
nsresult rv = ConvertConstraints(aConstraints, cs, aCx);
|
||||
if (rv != NS_OK) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
CreateAnswer(*cs);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints) {
|
||||
|
||||
PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints)
|
||||
{
|
||||
cc_media_constraints_t* cc_constraints = nullptr;
|
||||
constraints.buildArray(&cc_constraints);
|
||||
|
||||
@ -784,7 +854,8 @@ PeerConnectionImpl::CreateAnswer(MediaConstraints& constraints) {
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) {
|
||||
PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP)
|
||||
{
|
||||
if (!aSDP) {
|
||||
CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
|
||||
return NS_ERROR_FAILURE;
|
||||
@ -797,7 +868,8 @@ PeerConnectionImpl::SetLocalDescription(int32_t aAction, const char* aSDP) {
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP) {
|
||||
PeerConnectionImpl::SetRemoteDescription(int32_t action, const char* aSDP)
|
||||
{
|
||||
if (!aSDP) {
|
||||
CSFLogError(logTag, "%s - aSDP is NULL", __FUNCTION__);
|
||||
return NS_ERROR_FAILURE;
|
||||
|
@ -396,9 +396,12 @@ public:
|
||||
|
||||
nsPIDOMWindow* GetWindow() const { return mWindow; }
|
||||
|
||||
NS_IMETHODIMP CreateOffer(MediaConstraints& constraints);
|
||||
|
||||
NS_IMETHODIMP CreateAnswer(MediaConstraints& constraints);
|
||||
// Validate constraints and construct a MediaConstraints object
|
||||
// from a JS::Value.
|
||||
nsresult ConvertConstraints(
|
||||
const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx);
|
||||
NS_IMETHODIMP CreateOffer(MediaConstraints& aConstraints);
|
||||
NS_IMETHODIMP CreateAnswer(MediaConstraints& aConstraints);
|
||||
|
||||
private:
|
||||
PeerConnectionImpl(const PeerConnectionImpl&rhs);
|
||||
|
Loading…
x
Reference in New Issue
Block a user