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

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

mercurial