1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,398 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +#include "CSFLog.h" 1.9 + 1.10 +#include "base/histogram.h" 1.11 +#include "CallControlManager.h" 1.12 +#include "CC_Device.h" 1.13 +#include "CC_Call.h" 1.14 +#include "CC_Observer.h" 1.15 +#include "ccapi_call_info.h" 1.16 +#include "CC_SIPCCCallInfo.h" 1.17 +#include "ccapi_device_info.h" 1.18 +#include "CC_SIPCCDeviceInfo.h" 1.19 +#include "vcm.h" 1.20 +#include "VcmSIPCCBinding.h" 1.21 +#include "PeerConnectionImpl.h" 1.22 +#include "PeerConnectionCtx.h" 1.23 +#include "runnable_utils.h" 1.24 +#include "cpr_socket.h" 1.25 +#include "debug-psipcc-types.h" 1.26 +#include "prcvar.h" 1.27 + 1.28 +#include "mozilla/Telemetry.h" 1.29 + 1.30 +#ifdef MOZILLA_INTERNAL_API 1.31 +#include "mozilla/dom/RTCPeerConnectionBinding.h" 1.32 +#include "mozilla/Preferences.h" 1.33 +#endif 1.34 + 1.35 +#include "nsIObserverService.h" 1.36 +#include "nsIObserver.h" 1.37 +#include "mozilla/Services.h" 1.38 +#include "StaticPtr.h" 1.39 +extern "C" { 1.40 +#include "../sipcc/core/common/thread_monitor.h" 1.41 +} 1.42 + 1.43 +static const char* logTag = "PeerConnectionCtx"; 1.44 + 1.45 +extern "C" { 1.46 +extern PRCondVar *ccAppReadyToStartCond; 1.47 +extern PRLock *ccAppReadyToStartLock; 1.48 +extern char ccAppReadyToStart; 1.49 +} 1.50 + 1.51 +namespace mozilla { 1.52 + 1.53 +using namespace dom; 1.54 + 1.55 +// Convert constraints to C structures 1.56 + 1.57 +#ifdef MOZILLA_INTERNAL_API 1.58 +static void 1.59 +Apply(const Optional<bool> &aSrc, cc_boolean_constraint_t *aDst, 1.60 + bool mandatory = false) { 1.61 + if (aSrc.WasPassed() && (mandatory || !aDst->was_passed)) { 1.62 + aDst->was_passed = true; 1.63 + aDst->value = aSrc.Value(); 1.64 + aDst->mandatory = mandatory; 1.65 + } 1.66 +} 1.67 +#endif 1.68 + 1.69 +MediaConstraintsExternal::MediaConstraintsExternal() { 1.70 + memset(&mConstraints, 0, sizeof(mConstraints)); 1.71 +} 1.72 + 1.73 +MediaConstraintsExternal::MediaConstraintsExternal( 1.74 + const MediaConstraintsInternal &aSrc) { 1.75 + cc_media_constraints_t* c = &mConstraints; 1.76 + memset(c, 0, sizeof(*c)); 1.77 +#ifdef MOZILLA_INTERNAL_API 1.78 + Apply(aSrc.mMandatory.mOfferToReceiveAudio, &c->offer_to_receive_audio, true); 1.79 + Apply(aSrc.mMandatory.mOfferToReceiveVideo, &c->offer_to_receive_video, true); 1.80 + if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) { 1.81 + c->offer_to_receive_video.was_passed = true; 1.82 + c->offer_to_receive_video.value = false; 1.83 + } 1.84 + Apply(aSrc.mMandatory.mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel, 1.85 + true); 1.86 + Apply(aSrc.mMandatory.mMozBundleOnly, &c->moz_bundle_only, true); 1.87 + if (aSrc.mOptional.WasPassed()) { 1.88 + const Sequence<MediaConstraintSet> &array = aSrc.mOptional.Value(); 1.89 + for (uint32_t i = 0; i < array.Length(); i++) { 1.90 + Apply(array[i].mOfferToReceiveAudio, &c->offer_to_receive_audio); 1.91 + Apply(array[i].mOfferToReceiveVideo, &c->offer_to_receive_video); 1.92 + Apply(array[i].mMozDontOfferDataChannel, &c->moz_dont_offer_datachannel); 1.93 + Apply(array[i].mMozBundleOnly, &c->moz_bundle_only); 1.94 + } 1.95 + } 1.96 +#endif 1.97 +} 1.98 + 1.99 +cc_media_constraints_t* 1.100 +MediaConstraintsExternal::build() const { 1.101 + cc_media_constraints_t* cc = (cc_media_constraints_t*) 1.102 + cpr_malloc(sizeof(cc_media_constraints_t)); 1.103 + if (cc) { 1.104 + *cc = mConstraints; 1.105 + } 1.106 + return cc; 1.107 +} 1.108 + 1.109 +class PeerConnectionCtxShutdown : public nsIObserver 1.110 +{ 1.111 +public: 1.112 + NS_DECL_ISUPPORTS 1.113 + 1.114 + PeerConnectionCtxShutdown() {} 1.115 + 1.116 + void Init() 1.117 + { 1.118 + nsCOMPtr<nsIObserverService> observerService = 1.119 + services::GetObserverService(); 1.120 + if (!observerService) 1.121 + return; 1.122 + 1.123 + nsresult rv = NS_OK; 1.124 + 1.125 +#ifdef MOZILLA_INTERNAL_API 1.126 + rv = observerService->AddObserver(this, 1.127 + NS_XPCOM_SHUTDOWN_OBSERVER_ID, 1.128 + false); 1.129 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); 1.130 +#endif 1.131 + (void) rv; 1.132 + } 1.133 + 1.134 + virtual ~PeerConnectionCtxShutdown() 1.135 + { 1.136 + nsCOMPtr<nsIObserverService> observerService = 1.137 + services::GetObserverService(); 1.138 + if (observerService) 1.139 + observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID); 1.140 + } 1.141 + 1.142 + NS_IMETHODIMP Observe(nsISupports* aSubject, const char* aTopic, 1.143 + const char16_t* aData) { 1.144 + if (strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0) { 1.145 + CSFLogDebug(logTag, "Shutting down PeerConnectionCtx"); 1.146 + sipcc::PeerConnectionCtx::Destroy(); 1.147 + 1.148 + nsCOMPtr<nsIObserverService> observerService = 1.149 + services::GetObserverService(); 1.150 + if (!observerService) 1.151 + return NS_ERROR_FAILURE; 1.152 + 1.153 + nsresult rv = observerService->RemoveObserver(this, 1.154 + NS_XPCOM_SHUTDOWN_OBSERVER_ID); 1.155 + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(rv)); 1.156 + 1.157 + // Make sure we're not deleted while still inside ::Observe() 1.158 + nsRefPtr<PeerConnectionCtxShutdown> kungFuDeathGrip(this); 1.159 + sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown = nullptr; 1.160 + } 1.161 + return NS_OK; 1.162 + } 1.163 +}; 1.164 + 1.165 +NS_IMPL_ISUPPORTS(PeerConnectionCtxShutdown, nsIObserver); 1.166 +} 1.167 + 1.168 +using namespace mozilla; 1.169 +namespace sipcc { 1.170 + 1.171 +PeerConnectionCtx* PeerConnectionCtx::gInstance; 1.172 +nsIThread* PeerConnectionCtx::gMainThread; 1.173 +StaticRefPtr<PeerConnectionCtxShutdown> PeerConnectionCtx::gPeerConnectionCtxShutdown; 1.174 + 1.175 +// Since we have a pointer to main-thread, help make it safe for lower-level 1.176 +// SIPCC threads to use SyncRunnable without deadlocking, by exposing main's 1.177 +// dispatcher and waiter functions. See sipcc/core/common/thread_monitor.c. 1.178 + 1.179 +static void thread_ended_dispatcher(thread_ended_funct func, thread_monitor_id_t id) 1.180 +{ 1.181 + nsresult rv = PeerConnectionCtx::gMainThread->Dispatch(WrapRunnableNM(func, id), 1.182 + NS_DISPATCH_NORMAL); 1.183 + if (NS_FAILED(rv)) { 1.184 + CSFLogError( logTag, "%s(): Could not dispatch to main thread", __FUNCTION__); 1.185 + } 1.186 +} 1.187 + 1.188 +static void join_waiter() { 1.189 + NS_ProcessPendingEvents(PeerConnectionCtx::gMainThread); 1.190 +} 1.191 + 1.192 +nsresult PeerConnectionCtx::InitializeGlobal(nsIThread *mainThread, 1.193 + nsIEventTarget* stsThread) { 1.194 + if (!gMainThread) { 1.195 + gMainThread = mainThread; 1.196 + CSF::VcmSIPCCBinding::setMainThread(gMainThread); 1.197 + init_thread_monitor(&thread_ended_dispatcher, &join_waiter); 1.198 + } else { 1.199 +#ifdef MOZILLA_INTERNAL_API 1.200 + MOZ_ASSERT(gMainThread == mainThread); 1.201 +#endif 1.202 + } 1.203 + 1.204 + CSF::VcmSIPCCBinding::setSTSThread(stsThread); 1.205 + 1.206 + nsresult res; 1.207 + 1.208 +#ifdef MOZILLA_INTERNAL_API 1.209 + // This check fails on the unit tests because they do not 1.210 + // have the right thread behavior. 1.211 + bool on; 1.212 + res = gMainThread->IsOnCurrentThread(&on); 1.213 + NS_ENSURE_SUCCESS(res, res); 1.214 + MOZ_ASSERT(on); 1.215 +#endif 1.216 + 1.217 + if (!gInstance) { 1.218 + CSFLogDebug(logTag, "Creating PeerConnectionCtx"); 1.219 + PeerConnectionCtx *ctx = new PeerConnectionCtx(); 1.220 + 1.221 + res = ctx->Initialize(); 1.222 + PR_ASSERT(NS_SUCCEEDED(res)); 1.223 + if (!NS_SUCCEEDED(res)) 1.224 + return res; 1.225 + 1.226 + gInstance = ctx; 1.227 + 1.228 + if (!sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown) { 1.229 + sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown = new PeerConnectionCtxShutdown(); 1.230 + sipcc::PeerConnectionCtx::gPeerConnectionCtxShutdown->Init(); 1.231 + } 1.232 + } 1.233 + 1.234 + return NS_OK; 1.235 +} 1.236 + 1.237 +PeerConnectionCtx* PeerConnectionCtx::GetInstance() { 1.238 + MOZ_ASSERT(gInstance); 1.239 + return gInstance; 1.240 +} 1.241 + 1.242 +bool PeerConnectionCtx::isActive() { 1.243 + return gInstance; 1.244 +} 1.245 + 1.246 +void PeerConnectionCtx::Destroy() { 1.247 + CSFLogDebug(logTag, "%s", __FUNCTION__); 1.248 + 1.249 + if (gInstance) { 1.250 + gInstance->Cleanup(); 1.251 + delete gInstance; 1.252 + gInstance = nullptr; 1.253 + } 1.254 +} 1.255 + 1.256 +nsresult PeerConnectionCtx::Initialize() { 1.257 + mCCM = CSF::CallControlManager::create(); 1.258 + 1.259 + NS_ENSURE_TRUE(mCCM.get(), NS_ERROR_FAILURE); 1.260 + 1.261 + // Add the local audio codecs 1.262 + // FIX - Get this list from MediaEngine instead 1.263 + int codecMask = 0; 1.264 + codecMask |= VCM_CODEC_RESOURCE_G711; 1.265 + codecMask |= VCM_CODEC_RESOURCE_OPUS; 1.266 + //codecMask |= VCM_CODEC_RESOURCE_LINEAR; 1.267 + //codecMask |= VCM_CODEC_RESOURCE_G722; 1.268 + //codecMask |= VCM_CODEC_RESOURCE_iLBC; 1.269 + //codecMask |= VCM_CODEC_RESOURCE_iSAC; 1.270 + mCCM->setAudioCodecs(codecMask); 1.271 + 1.272 + //Add the local video codecs 1.273 + // FIX - Get this list from MediaEngine instead 1.274 + // Turning them all on for now 1.275 + codecMask = 0; 1.276 + // Only adding codecs supported 1.277 + //codecMask |= VCM_CODEC_RESOURCE_H263; 1.278 + 1.279 +#ifdef MOZILLA_INTERNAL_API 1.280 + if (Preferences::GetBool("media.peerconnection.video.h264_enabled")) { 1.281 + codecMask |= VCM_CODEC_RESOURCE_H264; 1.282 + } 1.283 +#endif 1.284 + 1.285 + codecMask |= VCM_CODEC_RESOURCE_VP8; 1.286 + //codecMask |= VCM_CODEC_RESOURCE_I420; 1.287 + mCCM->setVideoCodecs(codecMask); 1.288 + 1.289 + ccAppReadyToStartLock = PR_NewLock(); 1.290 + if (!ccAppReadyToStartLock) { 1.291 + return NS_ERROR_FAILURE; 1.292 + } 1.293 + 1.294 + ccAppReadyToStartCond = PR_NewCondVar(ccAppReadyToStartLock); 1.295 + if (!ccAppReadyToStartCond) { 1.296 + return NS_ERROR_FAILURE; 1.297 + } 1.298 + 1.299 + if (!mCCM->startSDPMode()) 1.300 + return NS_ERROR_FAILURE; 1.301 + 1.302 + mDevice = mCCM->getActiveDevice(); 1.303 + mCCM->addCCObserver(this); 1.304 + NS_ENSURE_TRUE(mDevice.get(), NS_ERROR_FAILURE); 1.305 + ChangeSipccState(dom::PCImplSipccState::Starting); 1.306 + 1.307 + // Now that everything is set up, we let the CCApp thread 1.308 + // know that it's okay to start processing messages. 1.309 + PR_Lock(ccAppReadyToStartLock); 1.310 + ccAppReadyToStart = 1; 1.311 + PR_NotifyAllCondVar(ccAppReadyToStartCond); 1.312 + PR_Unlock(ccAppReadyToStartLock); 1.313 + 1.314 + mConnectionCounter = 0; 1.315 +#ifdef MOZILLA_INTERNAL_API 1.316 + Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(0); 1.317 +#endif 1.318 + 1.319 + return NS_OK; 1.320 +} 1.321 + 1.322 +nsresult PeerConnectionCtx::Cleanup() { 1.323 + CSFLogDebug(logTag, "%s", __FUNCTION__); 1.324 + 1.325 + mCCM->destroy(); 1.326 + mCCM->removeCCObserver(this); 1.327 + return NS_OK; 1.328 +} 1.329 + 1.330 +CSF::CC_CallPtr PeerConnectionCtx::createCall() { 1.331 + return mDevice->createCall(); 1.332 +} 1.333 + 1.334 +void PeerConnectionCtx::onDeviceEvent(ccapi_device_event_e aDeviceEvent, 1.335 + CSF::CC_DevicePtr aDevice, 1.336 + CSF::CC_DeviceInfoPtr aInfo ) { 1.337 + cc_service_state_t state = aInfo->getServiceState(); 1.338 + // We are keeping this in a local var to avoid a data race 1.339 + // with ChangeSipccState in the debug message and compound if below 1.340 + dom::PCImplSipccState currentSipccState = mSipccState; 1.341 + 1.342 + switch (aDeviceEvent) { 1.343 + case CCAPI_DEVICE_EV_STATE: 1.344 + CSFLogDebug(logTag, "%s - %d : %d", __FUNCTION__, state, 1.345 + static_cast<uint32_t>(currentSipccState)); 1.346 + 1.347 + if (CC_STATE_INS == state) { 1.348 + // SIPCC is up 1.349 + if (dom::PCImplSipccState::Starting == currentSipccState || 1.350 + dom::PCImplSipccState::Idle == currentSipccState) { 1.351 + ChangeSipccState(dom::PCImplSipccState::Started); 1.352 + } else { 1.353 + CSFLogError(logTag, "%s PeerConnection already started", __FUNCTION__); 1.354 + } 1.355 + } else { 1.356 + NS_NOTREACHED("Unsupported Signaling State Transition"); 1.357 + } 1.358 + break; 1.359 + default: 1.360 + CSFLogDebug(logTag, "%s: Ignoring event: %s\n",__FUNCTION__, 1.361 + device_event_getname(aDeviceEvent)); 1.362 + } 1.363 +} 1.364 + 1.365 +static void onCallEvent_m(nsAutoPtr<std::string> peerconnection, 1.366 + ccapi_call_event_e aCallEvent, 1.367 + CSF::CC_CallInfoPtr aInfo); 1.368 + 1.369 +void PeerConnectionCtx::onCallEvent(ccapi_call_event_e aCallEvent, 1.370 + CSF::CC_CallPtr aCall, 1.371 + CSF::CC_CallInfoPtr aInfo) { 1.372 + // This is called on a SIPCC thread. 1.373 + // 1.374 + // We cannot use SyncRunnable to main thread, as that would deadlock on 1.375 + // shutdown. Instead, we dispatch asynchronously. We copy getPeerConnection(), 1.376 + // a "weak ref" to the PC, which is safe in shutdown, and CC_CallInfoPtr (an 1.377 + // nsRefPtr) is thread-safe and keeps aInfo alive. 1.378 + nsAutoPtr<std::string> pcDuped(new std::string(aCall->getPeerConnection())); 1.379 + 1.380 + // DISPATCH_NORMAL with duped string 1.381 + nsresult rv = gMainThread->Dispatch(WrapRunnableNM(&onCallEvent_m, pcDuped, 1.382 + aCallEvent, aInfo), 1.383 + NS_DISPATCH_NORMAL); 1.384 + if (NS_FAILED(rv)) { 1.385 + CSFLogError( logTag, "%s(): Could not dispatch to main thread", __FUNCTION__); 1.386 + } 1.387 +} 1.388 + 1.389 +// Demux the call event to the right PeerConnection 1.390 +static void onCallEvent_m(nsAutoPtr<std::string> peerconnection, 1.391 + ccapi_call_event_e aCallEvent, 1.392 + CSF::CC_CallInfoPtr aInfo) { 1.393 + CSFLogDebug(logTag, "onCallEvent()"); 1.394 + PeerConnectionWrapper pc(peerconnection->c_str()); 1.395 + if (!pc.impl()) // This must be an event on a dead PC. Ignore 1.396 + return; 1.397 + CSFLogDebug(logTag, "Calling PC"); 1.398 + pc.impl()->onCallEvent(OnCallEventArgs(aCallEvent, aInfo)); 1.399 +} 1.400 + 1.401 +} // namespace sipcc