media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 #include "CSFLog.h"
michael@0 6
michael@0 7 #include "base/histogram.h"
michael@0 8 #include "CallControlManager.h"
michael@0 9 #include "CC_Device.h"
michael@0 10 #include "CC_Call.h"
michael@0 11 #include "CC_Observer.h"
michael@0 12 #include "ccapi_call_info.h"
michael@0 13 #include "CC_SIPCCCallInfo.h"
michael@0 14 #include "ccapi_device_info.h"
michael@0 15 #include "CC_SIPCCDeviceInfo.h"
michael@0 16 #include "vcm.h"
michael@0 17 #include "VcmSIPCCBinding.h"
michael@0 18 #include "PeerConnectionImpl.h"
michael@0 19 #include "PeerConnectionCtx.h"
michael@0 20 #include "runnable_utils.h"
michael@0 21 #include "cpr_socket.h"
michael@0 22 #include "debug-psipcc-types.h"
michael@0 23 #include "prcvar.h"
michael@0 24
michael@0 25 #include "mozilla/Telemetry.h"
michael@0 26
michael@0 27 #ifdef MOZILLA_INTERNAL_API
michael@0 28 #include "mozilla/dom/RTCPeerConnectionBinding.h"
michael@0 29 #include "mozilla/Preferences.h"
michael@0 30 #endif
michael@0 31
michael@0 32 #include "nsIObserverService.h"
michael@0 33 #include "nsIObserver.h"
michael@0 34 #include "mozilla/Services.h"
michael@0 35 #include "StaticPtr.h"
michael@0 36 extern "C" {
michael@0 37 #include "../sipcc/core/common/thread_monitor.h"
michael@0 38 }
michael@0 39
michael@0 40 static const char* logTag = "PeerConnectionCtx";
michael@0 41
michael@0 42 extern "C" {
michael@0 43 extern PRCondVar *ccAppReadyToStartCond;
michael@0 44 extern PRLock *ccAppReadyToStartLock;
michael@0 45 extern char ccAppReadyToStart;
michael@0 46 }
michael@0 47
michael@0 48 namespace mozilla {
michael@0 49
michael@0 50 using namespace dom;
michael@0 51
michael@0 52 // Convert constraints to C structures
michael@0 53
michael@0 54 #ifdef MOZILLA_INTERNAL_API
michael@0 55 static void
michael@0 56 Apply(const Optional<bool> &aSrc, cc_boolean_constraint_t *aDst,
michael@0 57 bool mandatory = false) {
michael@0 58 if (aSrc.WasPassed() && (mandatory || !aDst->was_passed)) {
michael@0 59 aDst->was_passed = true;
michael@0 60 aDst->value = aSrc.Value();
michael@0 61 aDst->mandatory = mandatory;
michael@0 62 }
michael@0 63 }
michael@0 64 #endif
michael@0 65
michael@0 66 MediaConstraintsExternal::MediaConstraintsExternal() {
michael@0 67 memset(&mConstraints, 0, sizeof(mConstraints));
michael@0 68 }
michael@0 69
michael@0 70 MediaConstraintsExternal::MediaConstraintsExternal(
michael@0 71 const MediaConstraintsInternal &aSrc) {
michael@0 72 cc_media_constraints_t* c = &mConstraints;
michael@0 73 memset(c, 0, sizeof(*c));
michael@0 74 #ifdef MOZILLA_INTERNAL_API
michael@0 75 Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true);
michael@0 76 Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true);
michael@0 77 if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) {
michael@0 78 c->offer_to_receive_video.was_passed = true;
michael@0 79 c->offer_to_receive_video.value = false;
michael@0 80 }
michael@0 81 Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel,
michael@0 82 true);
michael@0 83 Apply(aSrc.mMandatory.mMozBundleOnly, &c->moz_bundle_only, true);
michael@0 84 if (aSrc.mOptional.WasPassed()) {
michael@0 85 const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value();
michael@0 86 for (uint32_t i = 0; i < array.Length(); i++) {
michael@0 87 Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio);
michael@0 88 Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video);
michael@0 89 Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel);
michael@0 90 Apply(array[i].mMozBundleOnly, &c->moz_bundle_only);
michael@0 91 }
michael@0 92 }
michael@0 93 #endif
michael@0 94 }
michael@0 95
michael@0 96 cc_media_constraints_t*
michael@0 97 MediaConstraintsExternal::build() const {
michael@0 98 cc_media_constraints_t* cc = (cc_media_constraints_t*)
michael@0 99 cpr_malloc(sizeof(cc_media_constraints_t));
michael@0 100 if (cc) {
michael@0 101 *cc = mConstraints;
michael@0 102 }
michael@0 103 return cc;
michael@0 104 }
michael@0 105
michael@0 106 class PeerConnectionCtxShutdown : public nsIObserver
michael@0 107 {
michael@0 108 public:
michael@0 109 NS_DECL_ISUPPORTS
michael@0 110
michael@0 111 PeerConnectionCtxShutdown() {}
michael@0 112
michael@0 113 void Init()
michael@0 114 {
michael@0 115 nsCOMPtr<nsIObserverService> observerService =
michael@0 116 services::GetObserverService();
michael@0 117 if (!observerService)
michael@0 118 return;
michael@0 119
michael@0 120 nsresult rv = NS_OK;
michael@0 121
michael@0 122 #ifdef MOZILLA_INTERNAL_API
michael@0 123 rv = observerService->AddObserver(this,
michael@0 124 NS_XPCOM_SHUTDOWN_OBSERVER_ID,
michael@0 125 false);
michael@0 126 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
michael@0 127 #endif
michael@0 128 (void) rv;
michael@0 129 }
michael@0 130
michael@0 131 virtual ~PeerConnectionCtxShutdown()
michael@0 132 {
michael@0 133 nsCOMPtr<nsIObserverService> observerService =
michael@0 134 services::GetObserverService();
michael@0 135 if (observerService)
michael@0 136 observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
michael@0 137 }
michael@0 138
michael@0 139 NS_IMETHODIMP Observe(nsISupports* aSubject, const char* aTopic,
michael@0 140 const char16_t* aData) {
michael@0 141 if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) {
michael@0 142 CSFLogDebug(logTag, "Shutting down PeerConnectionCtx");
michael@0 143 sipcc::PeerConnectionCtx::Destroy();
michael@0 144
michael@0 145 nsCOMPtr<nsIObserverService> observerService =
michael@0 146 services::GetObserverService();
michael@0 147 if (!observerService)
michael@0 148 return NS_ERROR_FAILURE;
michael@0 149
michael@0 150 nsresult rv = observerService->RemoveObserver(this,
michael@0 151 NS_XPCOM_SHUTDOWN_OBSERVER_ID);
michael@0 152 MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv));
michael@0 153
michael@0 154 // Make sure we're not deleted while still inside ::Observe()
michael@0 155 nsRefPtr<PeerConnectionCtxShutdown> kungFuDeathGrip(this);
michael@0 156 sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown = nullptr;
michael@0 157 }
michael@0 158 return NS_OK;
michael@0 159 }
michael@0 160 };
michael@0 161
michael@0 162 NS_IMPL_ISUPPORTS(PeerConnectionCtxShutdown, nsIObserver);
michael@0 163 }
michael@0 164
michael@0 165 using namespace mozilla;
michael@0 166 namespace sipcc {
michael@0 167
michael@0 168 PeerConnectionCtx* PeerConnectionCtx::gInstance;
michael@0 169 nsIThread* PeerConnectionCtx::gMainThread;
michael@0 170 StaticRefPtr<PeerConnectionCtxShutdown> PeerConnectionCtx::gPeerConnectionCtxShutdown;
michael@0 171
michael@0 172 // Since we have a pointer to main-thread, help make it safe for lower-level
michael@0 173 // SIPCC threads to use SyncRunnable without deadlocking, by exposing main's
michael@0 174 // dispatcher and waiter functions. See sipcc/core/common/thread_monitor.c.
michael@0 175
michael@0 176 static void thread_ended_dispatcher(thread_ended_funct func, thread_monitor_id_t id)
michael@0 177 {
michael@0 178 nsresult rv = PeerConnectionCtx::gMainThread->Dispatch(WrapRunnableNM(func, id),
michael@0 179 NS_DISPATCH_NORMAL);
michael@0 180 if (NS_FAILED(rv)) {
michael@0 181 CSFLogError( logTag, "%s(): Could not dispatch to main thread", __FUNCTION__);
michael@0 182 }
michael@0 183 }
michael@0 184
michael@0 185 static void join_waiter() {
michael@0 186 NS_ProcessPendingEvents(PeerConnectionCtx::gMainThread);
michael@0 187 }
michael@0 188
michael@0 189 nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread,
michael@0 190 nsIEventTarget* stsThread) {
michael@0 191 if (!gMainThread) {
michael@0 192 gMainThread = mainThread;
michael@0 193 CSF::VcmSIPCCBinding::setMainThread(gMainThread);
michael@0 194 init_thread_monitor(&thread_ended_dispatcher, &join_waiter);
michael@0 195 } else {
michael@0 196 #ifdef MOZILLA_INTERNAL_API
michael@0 197 MOZ_ASSERT(gMainThread == mainThread);
michael@0 198 #endif
michael@0 199 }
michael@0 200
michael@0 201 CSF::VcmSIPCCBinding::setSTSThread(stsThread);
michael@0 202
michael@0 203 nsresult res;
michael@0 204
michael@0 205 #ifdef MOZILLA_INTERNAL_API
michael@0 206 // This check fails on the unit tests because they do not
michael@0 207 // have the right thread behavior.
michael@0 208 bool on;
michael@0 209 res = gMainThread->IsOnCurrentThread(&on);
michael@0 210 NS_ENSURE_SUCCESS(res, res);
michael@0 211 MOZ_ASSERT(on);
michael@0 212 #endif
michael@0 213
michael@0 214 if (!gInstance) {
michael@0 215 CSFLogDebug(logTag, "Creating PeerConnectionCtx");
michael@0 216 PeerConnectionCtx *ctx = new PeerConnectionCtx();
michael@0 217
michael@0 218 res = ctx->Initialize();
michael@0 219 PR_ASSERT(NS_SUCCEEDED(res));
michael@0 220 if (!NS_SUCCEEDED(res))
michael@0 221 return res;
michael@0 222
michael@0 223 gInstance = ctx;
michael@0 224
michael@0 225 if (!sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown) {
michael@0 226 sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown = new PeerConnectionCtxShutdown();
michael@0 227 sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown->Init();
michael@0 228 }
michael@0 229 }
michael@0 230
michael@0 231 return NS_OK;
michael@0 232 }
michael@0 233
michael@0 234 PeerConnectionCtx* PeerConnectionCtx::GetInstance() {
michael@0 235 MOZ_ASSERT(gInstance);
michael@0 236 return gInstance;
michael@0 237 }
michael@0 238
michael@0 239 bool PeerConnectionCtx::isActive() {
michael@0 240 return gInstance;
michael@0 241 }
michael@0 242
michael@0 243 void PeerConnectionCtx::Destroy() {
michael@0 244 CSFLogDebug(logTag, "%s", __FUNCTION__);
michael@0 245
michael@0 246 if (gInstance) {
michael@0 247 gInstance->Cleanup();
michael@0 248 delete gInstance;
michael@0 249 gInstance = nullptr;
michael@0 250 }
michael@0 251 }
michael@0 252
michael@0 253 nsresult PeerConnectionCtx::Initialize() {
michael@0 254 mCCM = CSF::CallControlManager::create();
michael@0 255
michael@0 256 NS_ENSURE_TRUE(mCCM.get(), NS_ERROR_FAILURE);
michael@0 257
michael@0 258 // Add the local audio codecs
michael@0 259 // FIX - Get this list from MediaEngine instead
michael@0 260 int codecMask = 0;
michael@0 261 codecMask |= VCM_CODEC_RESOURCE_G711;
michael@0 262 codecMask |= VCM_CODEC_RESOURCE_OPUS;
michael@0 263 //codecMask |= VCM_CODEC_RESOURCE_LINEAR;
michael@0 264 //codecMask |= VCM_CODEC_RESOURCE_G722;
michael@0 265 //codecMask |= VCM_CODEC_RESOURCE_iLBC;
michael@0 266 //codecMask |= VCM_CODEC_RESOURCE_iSAC;
michael@0 267 mCCM->setAudioCodecs(codecMask);
michael@0 268
michael@0 269 //Add the local video codecs
michael@0 270 // FIX - Get this list from MediaEngine instead
michael@0 271 // Turning them all on for now
michael@0 272 codecMask = 0;
michael@0 273 // Only adding codecs supported
michael@0 274 //codecMask |= VCM_CODEC_RESOURCE_H263;
michael@0 275
michael@0 276 #ifdef MOZILLA_INTERNAL_API
michael@0 277 if (Preferences::GetBool("media.peerconnection.video.h264_enabled")) {
michael@0 278 codecMask |= VCM_CODEC_RESOURCE_H264;
michael@0 279 }
michael@0 280 #endif
michael@0 281
michael@0 282 codecMask |= VCM_CODEC_RESOURCE_VP8;
michael@0 283 //codecMask |= VCM_CODEC_RESOURCE_I420;
michael@0 284 mCCM->setVideoCodecs(codecMask);
michael@0 285
michael@0 286 ccAppReadyToStartLock = PR_NewLock();
michael@0 287 if (!ccAppReadyToStartLock) {
michael@0 288 return NS_ERROR_FAILURE;
michael@0 289 }
michael@0 290
michael@0 291 ccAppReadyToStartCond = PR_NewCondVar(ccAppReadyToStartLock);
michael@0 292 if (!ccAppReadyToStartCond) {
michael@0 293 return NS_ERROR_FAILURE;
michael@0 294 }
michael@0 295
michael@0 296 if (!mCCM->startSDPMode())
michael@0 297 return NS_ERROR_FAILURE;
michael@0 298
michael@0 299 mDevice = mCCM->getActiveDevice();
michael@0 300 mCCM->addCCObserver(this);
michael@0 301 NS_ENSURE_TRUE(mDevice.get(), NS_ERROR_FAILURE);
michael@0 302 ChangeSipccState(dom::PCImplSipccState::Starting);
michael@0 303
michael@0 304 // Now that everything is set up, we let the CCApp thread
michael@0 305 // know that it's okay to start processing messages.
michael@0 306 PR_Lock(ccAppReadyToStartLock);
michael@0 307 ccAppReadyToStart = 1;
michael@0 308 PR_NotifyAllCondVar(ccAppReadyToStartCond);
michael@0 309 PR_Unlock(ccAppReadyToStartLock);
michael@0 310
michael@0 311 mConnectionCounter = 0;
michael@0 312 #ifdef MOZILLA_INTERNAL_API
michael@0 313 Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(0);
michael@0 314 #endif
michael@0 315
michael@0 316 return NS_OK;
michael@0 317 }
michael@0 318
michael@0 319 nsresult PeerConnectionCtx::Cleanup() {
michael@0 320 CSFLogDebug(logTag, "%s", __FUNCTION__);
michael@0 321
michael@0 322 mCCM->destroy();
michael@0 323 mCCM->removeCCObserver(this);
michael@0 324 return NS_OK;
michael@0 325 }
michael@0 326
michael@0 327 CSF::CC_CallPtr PeerConnectionCtx::createCall() {
michael@0 328 return mDevice->createCall();
michael@0 329 }
michael@0 330
michael@0 331 void PeerConnectionCtx::onDeviceEvent(ccapi_device_event_e aDeviceEvent,
michael@0 332 CSF::CC_DevicePtr aDevice,
michael@0 333 CSF::CC_DeviceInfoPtr aInfo ) {
michael@0 334 cc_service_state_t state = aInfo->getServiceState();
michael@0 335 // We are keeping this in a local var to avoid a data race
michael@0 336 // with ChangeSipccState in the debug message and compound if below
michael@0 337 dom::PCImplSipccState currentSipccState = mSipccState;
michael@0 338
michael@0 339 switch (aDeviceEvent) {
michael@0 340 case CCAPI_DEVICE_EV_STATE:
michael@0 341 CSFLogDebug(logTag, "%s - %d : %d", __FUNCTION__, state,
michael@0 342 static_cast<uint32_t>(currentSipccState));
michael@0 343
michael@0 344 if (CC_STATE_INS == state) {
michael@0 345 // SIPCC is up
michael@0 346 if (dom::PCImplSipccState::Starting == currentSipccState ||
michael@0 347 dom::PCImplSipccState::Idle == currentSipccState) {
michael@0 348 ChangeSipccState(dom::PCImplSipccState::Started);
michael@0 349 } else {
michael@0 350 CSFLogError(logTag, "%s PeerConnection already started", __FUNCTION__);
michael@0 351 }
michael@0 352 } else {
michael@0 353 NS_NOTREACHED("Unsupported Signaling State Transition");
michael@0 354 }
michael@0 355 break;
michael@0 356 default:
michael@0 357 CSFLogDebug(logTag, "%s: Ignoring event: %s\n",__FUNCTION__,
michael@0 358 device_event_getname(aDeviceEvent));
michael@0 359 }
michael@0 360 }
michael@0 361
michael@0 362 static void onCallEvent_m(nsAutoPtr<std::string> peerconnection,
michael@0 363 ccapi_call_event_e aCallEvent,
michael@0 364 CSF::CC_CallInfoPtr aInfo);
michael@0 365
michael@0 366 void PeerConnectionCtx::onCallEvent(ccapi_call_event_e aCallEvent,
michael@0 367 CSF::CC_CallPtr aCall,
michael@0 368 CSF::CC_CallInfoPtr aInfo) {
michael@0 369 // This is called on a SIPCC thread.
michael@0 370 //
michael@0 371 // We cannot use SyncRunnable to main thread, as that would deadlock on
michael@0 372 // shutdown. Instead, we dispatch asynchronously. We copy getPeerConnection(),
michael@0 373 // a "weak ref" to the PC, which is safe in shutdown, and CC_CallInfoPtr (an
michael@0 374 // nsRefPtr) is thread-safe and keeps aInfo alive.
michael@0 375 nsAutoPtr<std::string> pcDuped(new std::string(aCall->getPeerConnection()));
michael@0 376
michael@0 377 // DISPATCH_NORMAL with duped string
michael@0 378 nsresult rv = gMainThread->Dispatch(WrapRunnableNM(&onCallEvent_m, pcDuped,
michael@0 379 aCallEvent, aInfo),
michael@0 380 NS_DISPATCH_NORMAL);
michael@0 381 if (NS_FAILED(rv)) {
michael@0 382 CSFLogError( logTag, "%s(): Could not dispatch to main thread", __FUNCTION__);
michael@0 383 }
michael@0 384 }
michael@0 385
michael@0 386 // Demux the call event to the right PeerConnection
michael@0 387 static void onCallEvent_m(nsAutoPtr<std::string> peerconnection,
michael@0 388 ccapi_call_event_e aCallEvent,
michael@0 389 CSF::CC_CallInfoPtr aInfo) {
michael@0 390 CSFLogDebug(logTag, "onCallEvent()");
michael@0 391 PeerConnectionWrapper pc(peerconnection->c_str());
michael@0 392 if (!pc.impl()) // This must be an event on a dead PC. Ignore
michael@0 393 return;
michael@0 394 CSFLogDebug(logTag, "Calling PC");
michael@0 395 pc.impl()->onCallEvent(OnCallEventArgs(aCallEvent, aInfo));
michael@0 396 }
michael@0 397
michael@0 398 } // namespace sipcc

mercurial