author | Jan-Ivar Bruaroey <jib@mozilla.com> |
Wed, 23 Jan 2013 14:21:25 -0500 | |
changeset 119727 | 6b5016ab9ebbb2ec884c4d865266446adebbc162 |
parent 119726 | 0eda7bbdfedf848d425d4aebaf256c4a443fda25 |
child 119728 | f8f4c3163cd9b8807548d765c2e869613869c45b |
push id | 24219 |
push user | ryanvm@gmail.com |
push date | Thu, 24 Jan 2013 17:36:06 +0000 |
treeherder | mozilla-central@fa969919b1bb [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bz, jesup |
bugs | 825703 |
milestone | 21.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/media/PeerConnection.js +++ b/dom/media/PeerConnection.js @@ -1,15 +1,15 @@ /* 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/. */ "use strict"; -const {classes: Cc, interfaces: Ci, utils: Cu} = Components; +const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); const PC_CONTRACT = "@mozilla.org/dom/peerconnection;1"; const PC_ICE_CONTRACT = "@mozilla.org/dom/rtcicecandidate;1"; const PC_SESSION_CONTRACT = "@mozilla.org/dom/rtcsessiondescription;1"; const PC_MANAGER_CONTRACT = "@mozilla.org/dom/peerconnectionmanager;1"; @@ -42,17 +42,17 @@ GlobalPCList.prototype = { Ci.nsIObserver, Ci.nsISupportsWeakReference, Ci.IPeerConnectionManager ]}), _xpcom_factory: { createInstance: function(outer, iid) { if (outer) { - throw Components.results.NS_ERROR_NO_AGGREGATION; + throw Cr.NS_ERROR_NO_AGGREGATION; } return _globalPCList.QueryInterface(iid); } }, addPC: function(pc) { let winID = pc._winID; if (this._list[winID]) { @@ -249,35 +249,41 @@ PeerConnection.prototype = { QueryInterface: XPCOMUtils.generateQI([ Ci.nsIDOMRTCPeerConnection, Ci.nsIDOMGlobalObjectConstructor, Ci.nsISupportsWeakReference ]), // Constructor is an explicit function, because of nsIDOMGlobalObjectConstructor. - constructor: function(win) { + constructor: function(win, rtcConfig) { if (!Services.prefs.getBoolPref("media.peerconnection.enabled")) { throw new Error("PeerConnection not enabled (did you set the pref?)"); } if (this._win) { - throw new Error("Constructor already called"); + throw new Error("RTCPeerConnection constructor already called"); } + if (!rtcConfig) { + // TODO(jib@mozilla.com): Hardcoded server pending Mozilla one. Bug 807494 + rtcConfig = [{ url: "stun:23.21.150.121" }]; + } + this._mustValidateRTCConfiguration(rtcConfig, + "RTCPeerConnection constructor passed invalid RTCConfiguration"); if (_globalPCList._networkdown) { - throw new Error("Can't create RTPPeerConnections when the network is down"); + throw new Error("Can't create RTCPeerConnections when the network is down"); } this._pc = Cc["@mozilla.org/peerconnection;1"]. createInstance(Ci.IPeerConnection); this._observer = new PeerConnectionObserver(this); // Nothing starts until ICE gathering completes. this._queueOrRun({ func: this._pc.initialize, - args: [this._observer, win, Services.tm.currentThread], + args: [this._observer, win, rtcConfig, Services.tm.currentThread], wait: true }); this._win = win; this._winID = this._win.QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils).currentInnerWindowID; // Add a reference to the PeerConnection to global list. @@ -317,16 +323,59 @@ PeerConnection.prototype = { this._executeNext(); } } else { this._pending = false; } }, /** + * An RTCConfiguration looks like this: + * + * [ { url:"stun:23.21.150.121" }, + * { url:"turn:user@turn.example.org", credential:"myPassword"} ] + * + * We check for basic structure and well-formed stun/turn urls, but not + * validity of servers themselves, before passing along to C++. + * ErrorMsg is passed in to detail which array-entry failed, if any. + */ + _mustValidateRTCConfiguration: function(rtcConfig, errorMsg) { + function isObject(obj) { + return obj && (typeof obj === "object"); + } + function isArray(obj) { + return isObject(obj) && + (Object.prototype.toString.call(obj) === "[object Array]"); + } + function nicerNewURI(uriStr, errorMsg) { + let ios = Cc['@mozilla.org/network/io-service;1'].getService(Ci.nsIIOService); + try { + return ios.newURI(uriStr, null, null); + } catch (e if (e.result == Cr.NS_ERROR_MALFORMED_URI)) { + throw new Error(errorMsg + " - malformed URI: " + uriStr); + } + } + function mustValidateServer(server) { + let url = nicerNewURI(server.url, errorMsg); + if (!(url.scheme in { stun:1, stuns:1, turn:1 })) { + throw new Error (errorMsg + " - improper scheme: " + url.scheme); + } + if (server.credential && isObject(server.credential)) { + throw new Error (errorMsg + " - invalid credential"); + } + } + if (!isArray(rtcConfig)) { + throw new Error (errorMsg); + } + for (let i=0; i < rtcConfig.length; i++) { + mustValidateServer (rtcConfig[i], errorMsg); + } + }, + + /** * 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 @@ -476,21 +525,21 @@ PeerConnection.prototype = { }, updateIce: function(config, constraints, restart) { return Cr.NS_ERROR_NOT_IMPLEMENTED; }, addIceCandidate: function(cand) { if (!cand) { - throw "NULL candidate passed to addIceCandidate!"; + throw new Error ("NULL candidate passed to addIceCandidate!"); } if (!cand.candidate || !cand.sdpMLineIndex) { - throw "Invalid candidate passed to addIceCandidate!"; + throw new Error ("Invalid candidate passed to addIceCandidate!"); } this._queueOrRun({ func: this._pc.addIceCandidate, args: [cand.candidate, cand.sdpMid || "", cand.sdpMLineIndex], wait: false }); },
--- a/dom/media/bridge/IPeerConnection.idl +++ b/dom/media/bridge/IPeerConnection.idl @@ -60,17 +60,17 @@ interface IPeerConnectionObserver : nsIS /* When SDP is parsed and a candidate line is found this method is called. * It should hook back into the media transport to notify it of ICE candidates * listed in the SDP PeerConnectionImpl does not parse ICE candidates, just * pulls them out of the SDP. */ void foundIceCandidate(in string candidate); }; -[scriptable, uuid(c86903c7-0a2e-42b4-903c-1518f03b9ba5)] +[scriptable, uuid(cc8327f5-66f4-42f4-820d-9a9db0474b6e)] interface IPeerConnection : nsISupports { const unsigned long kHintAudio = 0x00000001; const unsigned long kHintVideo = 0x00000002; const long kActionNone = -1; const long kActionOffer = 0; const long kActionAnswer = 1; @@ -83,17 +83,18 @@ interface IPeerConnection : nsISupports const long kIceFailed = 4; /* for 'type' in DataChannelInit dictionary */ const unsigned short kDataChannelReliable = 0; const unsigned short kDataChannelPartialReliableRexmit = 1; const unsigned short kDataChannelPartialReliableTimed = 2; /* Must be called first. Observer events will be dispatched on the thread provided */ - void initialize(in IPeerConnectionObserver observer, in nsIDOMWindow window, + [implicit_jscontext] void initialize(in IPeerConnectionObserver observer, in nsIDOMWindow window, + [optional] in jsval iceServers, [optional] in nsIThread thread); /* JSEP calls */ [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);
--- a/dom/media/tests/mochitest/Makefile.in +++ b/dom/media/tests/mochitest/Makefile.in @@ -15,16 +15,17 @@ MOCHITEST_FILES = \ test_getUserMedia_basicAudio.html \ test_getUserMedia_basicVideo.html \ test_getUserMedia_basicVideoAudio.html \ test_peerConnection_basicAudio.html \ test_peerConnection_basicAudioVideo.html \ test_peerConnection_basicAudioVideoCombined.html \ test_peerConnection_basicVideo.html \ test_peerConnection_bug827843.html \ + test_peerConnection_bug825703.html \ head.js \ mediaStreamPlayback.js \ pc.js \ $(NULL) # The following tests are leaking and cannot be run by default yet ifdef MOZ_WEBRTC_LEAKING_TESTS MOCHITEST_FILES += \
new file mode 100644 --- /dev/null +++ b/dom/media/tests/mochitest/test_peerConnection_bug825703.html @@ -0,0 +1,57 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=825703 +--> +<head> + <meta charset="utf-8"> + <title>Bug 825703: RTCConfiguration valid/invalid permutations</title> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> + <script type="application/javascript" src="/MochiKit/MochiKit.js"></script> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="application/javascript" src="head.js"></script> + </meta> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=825703">RTCConfiguration valid/invalid permutations</a> +<p id="display"></p> +<pre id="test"> +<script class="testbody" type="application/javascript"> +runTest(function () { + var pc; + var pcs; + var exception = null; + try { pcs = new mozRTCPeerConnection(); } catch (e) { exception = e; } + ok(!exception, "mozRTCPeerConnection() succeeds"); + exception = null; + try { pc = new mozRTCPeerConnection(1); } catch (e) { exception = e; } + ok(exception, "mozRTCPeerConnection(1) throws"); + exception = null; + try { pc = new mozRTCPeerConnection({}); } catch (e) { exception = e; } + ok(exception, "mozRTCPeerConnection({}) throws"); + exception = null; + try { pcs = new mozRTCPeerConnection([]); } catch (e) { exception = e; } + ok(!exception, "mozRTCPeerConnection([]) succeeds"); + exception = null; + try { pc = new mozRTCPeerConnection([{}]); } catch (e) { exception = e; } + ok(exception, "mozRTCPeerConnection([{}]) throws"); + exception = null; + try { pc = new mozRTCPeerConnection([{ url:"" }]); } catch (e) { exception = e; } + ok(exception, "mozRTCPeerConnection([{ url:\"\" }]) throws"); + exception = null; + try { pc = new mozRTCPeerConnection([{ url:"http:0.0.0.0" }]); } catch (e) { exception = e; } + ok(exception, "mozRTCPeerConnection([{ url:\"http:0.0.0.0\" }]) throws"); + exception = null; + try { pcs = new mozRTCPeerConnection([{ url:"stun:0.0.0.0" }, { url:"stuns:x.net", foo:"" }, { url:"turn:user@x.org:42", credential:"p" }]); } catch (e) { exception = e; } + ok(!exception, "mozRTCPeerConnection([{ url:\"stun:0.0.0.0\" }, { url:\"stuns:x.net\", foo:\"\" }, { url:\"turn:user@x.org:42\", credential:\"p\" }]) succeeds"); + exception = null; + try { pc = new mozRTCPeerConnection([{ url:"stun:0.0.0.0", credential:{}}]); } catch (e) { exception = e; } + ok(exception, "mozRTCPeerConnection([{ url:\"stun:0.0.0.0\", credential:{}}]) throws"); + pc = null; + pcs = null; + SimpleTest.finish(); +}, true); +</script> +</pre> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/dom/webidl/RTCIceServer.webidl @@ -0,0 +1,10 @@ +/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. + */ + +dictionary RTCIceServer { + DOMString url; + DOMString? credential = null; +};
--- a/dom/webidl/WebIDL.mk +++ b/dom/webidl/WebIDL.mk @@ -186,16 +186,17 @@ ifdef MOZ_WEBGL webidl_files += \ WebGLRenderingContext.webidl \ $(NULL) endif ifdef MOZ_WEBRTC webidl_files += \ MediaStreamList.webidl \ + RTCIceServer.webidl \ $(NULL) endif ifdef MOZ_B2G_RIL webidl_files += \ USSDReceivedEvent.webidl \ $(NULL) endif
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp @@ -21,36 +21,45 @@ #include "nsNetCID.h" #include "nsIProperty.h" #include "nsIPropertyBag2.h" #include "nsIServiceManager.h" #include "nsISimpleEnumerator.h" #include "nsServiceManagerUtils.h" #include "nsISocketTransportService.h" - +#include "nsIConsoleService.h" #include "nsThreadUtils.h" #include "nsProxyRelease.h" #include "runnable_utils.h" #include "PeerConnectionCtx.h" #include "PeerConnectionImpl.h" - #include "nsPIDOMWindow.h" #include "nsDOMDataChannel.h" #ifdef MOZILLA_INTERNAL_API +#include "nsContentUtils.h" +#include "nsDOMJSUtils.h" +#include "nsIScriptError.h" +#include "nsPrintfCString.h" +#include "nsURLHelper.h" +#include "nsNetUtil.h" +#include "mozilla/dom/BindingUtils.h" +#include "mozilla/dom/RTCIceServerBinding.h" #include "MediaStreamList.h" #include "nsIScriptGlobalObject.h" #include "jsapi.h" #endif #ifndef USE_FAKE_MEDIA_STREAMS #include "MediaSegment.h" #endif +#define ICE_PARSING "In RTCConfiguration passed to RTCPeerConnection constructor" + using namespace mozilla; using namespace mozilla::dom; namespace mozilla { class DataChannel; } class nsIDOMDataChannel; @@ -286,39 +295,155 @@ PeerConnectionImpl::CreateRemoteSourceSt nsRefPtr<RemoteSourceStreamInfo> remote; remote = new RemoteSourceStreamInfo(comstream); *aInfo = remote; return NS_OK; } +#ifdef MOZILLA_INTERNAL_API +static void +Warn(JSContext* aCx, const nsCString& aMsg) { + CSFLogErrorS(logTag, "Warning: " << aMsg.get()); + nsIScriptContext* sc = GetScriptContextFromJSContext(aCx); + if (sc) { + nsCOMPtr<nsIDocument> doc; + doc = nsContentUtils::GetDocumentFromScriptContext(sc); + if (doc) { + // Passing in line-# 1 hides peerconnection.js (shows document instead) + nsContentUtils::ReportToConsoleNonLocalized(NS_ConvertUTF8toUTF16 (aMsg), + nsIScriptError::warningFlag, logTag, doc, nullptr, EmptyString(), 1); + } + } +} +#endif + +/** + * In JS, an RTCConfiguration looks like this: + * + * [ { url:"stun:23.21.150.121" }, + * { url:"turn:user@turn.example.org", credential:"myPassword"} ] + * + * This function converts an already-validated jsval that looks like the above + * into an RTCConfiguration object. + */ +nsresult +PeerConnectionImpl::ConvertRTCConfiguration(const JS::Value& aSrc, + RTCConfiguration *aDst, + JSContext* aCx) +{ +#ifdef MOZILLA_INTERNAL_API + if (!aSrc.isObject()) { + return NS_ERROR_FAILURE; + } + JSObject& servers = aSrc.toObject(); + JSAutoCompartment ac(aCx, &servers); + uint32_t len; + if (!(IsArrayLike(aCx, &servers) && JS_GetArrayLength(aCx, &servers, &len))) { + return NS_ERROR_FAILURE; + } + for (uint32_t i = 0; i < len; i++) { + nsresult rv; + RTCIceServer server; + { + JS::Value v; + if (!(JS_GetElement(aCx, &servers, i, &v) && server.Init(aCx, nullptr, v))) { + return NS_ERROR_FAILURE; + } + } + if (!server.mUrl.WasPassed()) { + return NS_ERROR_FAILURE; + } + nsRefPtr<nsIURI> url; + rv = NS_NewURI(getter_AddRefs(url), server.mUrl.Value()); + NS_ENSURE_SUCCESS(rv, rv); + bool isStun = false, isStuns = false, isTurn = false, isTurns = false; + url->SchemeIs("stun", &isStun); + url->SchemeIs("stuns", &isStuns); + url->SchemeIs("turn", &isTurn); + url->SchemeIs("turns", &isTurns); + if (!(isStun || isStuns || isTurn || isTurns)) { + return NS_ERROR_FAILURE; + } + nsAutoCString spec; + rv = url->GetSpec(spec); + NS_ENSURE_SUCCESS(rv, rv); + if (!server.mCredential.IsEmpty()) { + // TODO(jib@mozilla.com): Support username, credentials & TURN servers + Warn(aCx, nsPrintfCString(ICE_PARSING + ": Credentials not yet implemented. Omitting \"%s\"", spec.get())); + continue; + } + if (isTurn || isTurns) { + Warn(aCx, nsPrintfCString(ICE_PARSING + ": TURN servers not yet supported. Treating as STUN: \"%s\"", spec.get())); + } + // TODO(jib@mozilla.com): Revisit once nsURI supports host and port on STUN + int32_t port; + nsAutoCString host; + { + uint32_t hostPos; + int32_t hostLen; + nsAutoCString path; + rv = url->GetPath(path); + NS_ENSURE_SUCCESS(rv, rv); + rv = net_GetAuthURLParser()->ParseAuthority(path.get(), path.Length(), + nullptr, nullptr, + nullptr, nullptr, + &hostPos, &hostLen, &port); + NS_ENSURE_SUCCESS(rv, rv); + if (!hostLen) { + return NS_ERROR_FAILURE; + } + path.Mid(host, hostPos, hostLen); + } + if (port == -1) + port = (isStuns || isTurns)? 5349 : 3478; + if (!aDst->addServer(host.get(), port)) { + Warn(aCx, nsPrintfCString(ICE_PARSING + ": FQDN not yet implemented (only IP-#s). Omitting \"%s\"", spec.get())); + } + } +#endif + return NS_OK; +} + NS_IMETHODIMP PeerConnectionImpl::Initialize(IPeerConnectionObserver* aObserver, nsIDOMWindow* aWindow, - nsIThread* aThread) { + const JS::Value &aRTCConfiguration, + nsIThread* aThread, + JSContext* aCx) +{ + return Initialize(aObserver, aWindow, nullptr, &aRTCConfiguration, aThread, aCx); +} + +nsresult +PeerConnectionImpl::Initialize(IPeerConnectionObserver* aObserver, + nsIDOMWindow* aWindow, + const RTCConfiguration* aConfiguration, + const JS::Value* aRTCConfiguration, + nsIThread* aThread, + JSContext* aCx) +{ #ifdef MOZILLA_INTERNAL_API MOZ_ASSERT(NS_IsMainThread()); #endif MOZ_ASSERT(aObserver); MOZ_ASSERT(aThread); + mThread = aThread; mPCObserver = do_GetWeakReference(aObserver); nsresult res; - #ifdef MOZILLA_INTERNAL_API // This code interferes with the C++ unit test startup code. nsCOMPtr<nsISupports> nssDummy = do_GetService("@mozilla.org/psm;1", &res); NS_ENSURE_SUCCESS(res, res); -#endif - - mThread = aThread; - -#ifdef MOZILLA_INTERNAL_API // Currently no standalone unit tests for DataChannel, // which is the user of mWindow MOZ_ASSERT(aWindow); mWindow = do_QueryInterface(aWindow); NS_ENSURE_STATE(mWindow); #endif res = PeerConnectionCtx::InitializeGlobal(mThread); @@ -333,17 +458,24 @@ PeerConnectionImpl::Initialize(IPeerConn return NS_ERROR_FAILURE; } // Connect ICE slots. mMedia->SignalIceGatheringCompleted.connect(this, &PeerConnectionImpl::IceGatheringCompleted); mMedia->SignalIceCompleted.connect(this, &PeerConnectionImpl::IceCompleted); // Initialize the media object. - res = mMedia->Init(); + if (aRTCConfiguration) { + RTCConfiguration ic; + res = ConvertRTCConfiguration(*aRTCConfiguration, &ic, aCx); + NS_ENSURE_SUCCESS(res, res); + res = mMedia->Init(ic.getServers()); + } else { + res = mMedia->Init(aConfiguration->getServers()); + } if (NS_FAILED(res)) { CSFLogErrorS(logTag, __FUNCTION__ << ": Couldn't initialize media object"); return res; } // Generate a random handle unsigned char handle_bin[8]; PK11_GenerateRandom(handle_bin, sizeof(handle_bin)); @@ -610,34 +742,34 @@ PeerConnectionImpl::NotifyDataChannel(al * * Optional constraints are ordered, and hence in an array. This function * converts a jsval that looks like the above into a MediaConstraints object. */ nsresult PeerConnectionImpl::ConvertConstraints( const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx) { - jsval mandatory, optional; + JS::Value mandatory, optional; JSObject& constraints = aConstraints.toObject(); // Mandatory constraints. Note that we only care if the constraint array exists if (!JS_GetProperty(aCx, &constraints, "mandatory", &mandatory)) { return NS_ERROR_FAILURE; } if (!mandatory.isNullOrUndefined()) { if (!mandatory.isObject()) { return NS_ERROR_FAILURE; } JSObject* opts = JSVAL_TO_OBJECT(mandatory); JS::AutoIdArray mandatoryOpts(aCx, JS_Enumerate(aCx, opts)); // Iterate over each property. for (size_t i = 0; i < mandatoryOpts.length(); i++) { - jsval option, optionName; + JS::Value option, optionName; if (!JS_GetPropertyById(aCx, opts, mandatoryOpts[i], &option) || !JS_IdToValue(aCx, mandatoryOpts[i], &optionName) || // We only support boolean constraints for now. !JSVAL_IS_BOOLEAN(option)) { return NS_ERROR_FAILURE; } JSString* optionNameString = JS_ValueToString(aCx, optionName); if (!optionNameString) { @@ -658,18 +790,18 @@ PeerConnectionImpl::ConvertConstraints( } JSObject* opts = JSVAL_TO_OBJECT(optional); uint32_t length; if (!JS_IsArrayObject(aCx, opts) || !JS_GetArrayLength(aCx, opts, &length)) { return NS_ERROR_FAILURE; } - for (size_t i = 0; i < length; i++) { - jsval val; + for (uint32_t i = 0; i < length; i++) { + JS::Value val; if (!JS_GetElement(aCx, opts, i, &val) || !val.isObject()) { return NS_ERROR_FAILURE; } // Extract name & value and store. // FIXME: MediaConstraints does not support optional constraints? } }
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h @@ -41,32 +41,52 @@ namespace mozilla { #endif using namespace mozilla; namespace sipcc { class PeerConnectionWrapper; -struct ConstraintInfo { +struct ConstraintInfo +{ std::string value; bool mandatory; }; typedef std::map<std::string, ConstraintInfo> constraints_map; -class MediaConstraints { +class MediaConstraints +{ public: void setBooleanConstraint(const std::string& constraint, bool enabled, bool mandatory); void buildArray(cc_media_constraints_t** constraintarray); private: constraints_map mConstraints; }; +class RTCConfiguration +{ +public: + bool addServer(const std::string& addr, uint16_t port) + { + NrIceStunServer* server(NrIceStunServer::Create(addr, port)); + if (!server) { + return false; + } + addServer(*server); + return true; + } + void addServer(const NrIceStunServer& server) { mServers.push_back (server); } + const std::vector<NrIceStunServer>& getServers() const { return mServers; } +private: + std::vector<NrIceStunServer> mServers; +}; + class PeerConnectionWrapper; // Enter an API call and check that the state is OK, // the PC isn't closed, etc. #define PC_AUTO_ENTER_API_CALL(assert_ice_ready) \ do { \ /* do/while prevents res from conflicting with locals */ \ nsresult res = CheckApiState(assert_ice_ready); \ @@ -74,17 +94,18 @@ class PeerConnectionWrapper; } while(0) #define PC_AUTO_ENTER_API_CALL_NO_CHECK() CheckThread() class PeerConnectionImpl MOZ_FINAL : public IPeerConnection, #ifdef MOZILLA_INTERNAL_API public mozilla::DataChannelConnection::DataConnectionListener, #endif - public sigslot::has_slots<> { + public sigslot::has_slots<> +{ public: PeerConnectionImpl(); ~PeerConnectionImpl(); enum ReadyState { kNew, kNegotiating, kActive, @@ -112,16 +133,18 @@ public: kRoleOfferer, kRoleAnswerer }; NS_DECL_ISUPPORTS NS_DECL_IPEERCONNECTION static PeerConnectionImpl* CreatePeerConnection(); + static nsresult ConvertRTCConfiguration(const JS::Value& aSrc, + RTCConfiguration *aDst, JSContext* aCx); static nsresult ConvertConstraints( const JS::Value& aConstraints, MediaConstraints* aObj, JSContext* aCx); static nsresult MakeMediaStream(uint32_t aHint, nsIDOMMediaStream** aStream); Role GetRole() const { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mRole; } @@ -179,25 +202,38 @@ public: // Create a fake media stream nsresult CreateFakeMediaStream(uint32_t hint, nsIDOMMediaStream** retval); nsPIDOMWindow* GetWindow() const { PC_AUTO_ENTER_API_CALL_NO_CHECK(); return mWindow; } + // Initialize PeerConnection from an RTCConfiguration object. + nsresult Initialize(IPeerConnectionObserver* aObserver, + nsIDOMWindow* aWindow, + const RTCConfiguration& aConfiguration, + nsIThread* aThread) { + return Initialize(aObserver, aWindow, &aConfiguration, nullptr, aThread, nullptr); + } + // Validate constraints and construct a MediaConstraints object // from a JS::Value. NS_IMETHODIMP CreateOffer(MediaConstraints& aConstraints); NS_IMETHODIMP CreateAnswer(MediaConstraints& aConstraints); private: PeerConnectionImpl(const PeerConnectionImpl&rhs); PeerConnectionImpl& operator=(PeerConnectionImpl); - + nsresult Initialize(IPeerConnectionObserver* aObserver, + nsIDOMWindow* aWindow, + const RTCConfiguration* aConfiguration, + const JS::Value* aRTCConfiguration, + nsIThread* aThread, + JSContext* aCx); NS_IMETHODIMP CreateOfferInt(MediaConstraints& constraints); NS_IMETHODIMP CreateAnswerInt(MediaConstraints& constraints); nsresult CloseInt(bool aIsSynchronous); void ChangeReadyState(ReadyState aReadyState); nsresult CheckApiState(bool assert_ice_ready) const; void CheckThread() const { NS_ABORT_IF_FALSE(CheckThreadInt(), "Wrong thread"); @@ -267,17 +303,18 @@ private: public: //these are temporary until the DataChannel Listen/Connect API is removed unsigned short listenPort; unsigned short connectPort; char *connectStr; // XXX ownership/free }; // This is what is returned when you acquire on a handle -class PeerConnectionWrapper { +class PeerConnectionWrapper +{ public: PeerConnectionWrapper(const std::string& handle); PeerConnectionImpl *impl() { return impl_; } private: nsRefPtr<PeerConnectionImpl> impl_; };
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.cpp @@ -63,43 +63,29 @@ PeerConnectionImpl* PeerConnectionImpl:: PeerConnectionImpl *pc = new PeerConnectionImpl(); CSFLogDebugS(logTag, "Created PeerConnection: " << static_cast<void*>(pc)); return pc; } -nsresult PeerConnectionMedia::Init() +nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers) { // TODO(ekr@rtfm.com): need some way to set not offerer later // Looks like a bug in the NrIceCtx API. mIceCtx = NrIceCtx::Create("PC:" + mParent->GetHandle(), true); if(!mIceCtx) { CSFLogErrorS(logTag, __FUNCTION__ << ": Failed to create Ice Context"); return NS_ERROR_FAILURE; } - - // Temporarily hardwire the ICE server. - // TODO(ekr@rtfm.com): Remove this when we have ICE configuration - // settings. - std::vector<NrIceStunServer> stun_servers; - ScopedDeletePtr<NrIceStunServer> server(NrIceStunServer::Create( - std::string((char *)"216.93.246.14"), 3478)); - MOZ_ASSERT(server); - if (!server) { - CSFLogErrorS(logTag, __FUNCTION__ << ": Could not parse STUN server string"); - return NS_ERROR_FAILURE; + nsresult rv = mIceCtx->SetStunServers(stun_servers); + if (NS_FAILED(rv)) { + return rv; } - - stun_servers.push_back(*server); - nsresult rv = mIceCtx->SetStunServers(stun_servers); - if (NS_FAILED(rv)) - return rv; - mIceCtx->SignalGatheringCompleted.connect(this, &PeerConnectionMedia::IceGatheringCompleted); mIceCtx->SignalCompleted.connect(this, &PeerConnectionMedia::IceCompleted); // Create three streams to start with. // One each for audio, video and DataChannel // TODO: this will be re-visited
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionMedia.h @@ -231,17 +231,17 @@ class PeerConnectionMedia : public sigsl : mParent(parent), mLocalSourceStreamsLock(PR_NewLock()), mIceCtx(NULL) {} ~PeerConnectionMedia() { PR_DestroyLock(mLocalSourceStreamsLock); } - nsresult Init(); + nsresult Init(const std::vector<mozilla::NrIceStunServer>& stun_servers); // WARNING: This destroys the object! void SelfDestruct(); mozilla::RefPtr<mozilla::NrIceCtx> ice_ctx() const { return mIceCtx; } mozilla::RefPtr<mozilla::NrIceMediaStream> ice_media_stream(size_t i) const { // TODO(ekr@rtfm.com): If someone asks for a value that doesn't exist,
--- a/media/webrtc/signaling/test/signaling_unittests.cpp +++ b/media/webrtc/signaling/test/signaling_unittests.cpp @@ -23,16 +23,17 @@ using namespace std; #include "FakeMediaStreams.h" #include "FakeMediaStreamsImpl.h" #include "PeerConnectionImpl.h" #include "PeerConnectionCtx.h" #include "runnable_utils.h" #include "nsStaticComponents.h" #include "nsIDOMRTCPeerConnection.h" #include "nsWeakReference.h" +#include "nricectx.h" #include "mtransport_test_utils.h" MtransportTestUtils *test_utils; static int kDefaultTimeout = 5000; @@ -500,17 +501,19 @@ class SignalingAgent { ASSERT_TRUE(found > 0); pc = sipcc::PeerConnectionImpl::CreatePeerConnection(); ASSERT_TRUE(pc); pObserver = new TestObserver(pc); ASSERT_TRUE(pObserver); - ASSERT_EQ(pc->Initialize(pObserver, nullptr, thread), NS_OK); + sipcc::RTCConfiguration cfg; + cfg.addServer("23.21.150.121", 3478); + ASSERT_EQ(pc->Initialize(pObserver, nullptr, cfg, thread), NS_OK); } void Init(nsCOMPtr<nsIThread> thread) { thread->Dispatch( WrapRunnable(this, &SignalingAgent::Init_m, thread), NS_DISPATCH_SYNC);