|
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 file, |
|
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
4 #include <string> |
|
5 |
|
6 #include "CSFLog.h" |
|
7 |
|
8 #include "nspr.h" |
|
9 #include "cc_constants.h" |
|
10 |
|
11 #include "nricectx.h" |
|
12 #include "nricemediastream.h" |
|
13 #include "PeerConnectionImpl.h" |
|
14 #include "PeerConnectionMedia.h" |
|
15 #include "AudioConduit.h" |
|
16 #include "VideoConduit.h" |
|
17 #include "runnable_utils.h" |
|
18 |
|
19 #ifdef MOZILLA_INTERNAL_API |
|
20 #include "MediaStreamList.h" |
|
21 #include "nsIScriptGlobalObject.h" |
|
22 #include "mozilla/Preferences.h" |
|
23 #include "mozilla/dom/RTCStatsReportBinding.h" |
|
24 #endif |
|
25 |
|
26 using namespace mozilla; |
|
27 using namespace mozilla::dom; |
|
28 |
|
29 namespace sipcc { |
|
30 |
|
31 static const char* logTag = "PeerConnectionMedia"; |
|
32 static const mozilla::TrackID TRACK_AUDIO = 0; |
|
33 static const mozilla::TrackID TRACK_VIDEO = 1; |
|
34 |
|
35 /* If the ExpectAudio hint is on we will add a track at the default first |
|
36 * audio track ID (0) |
|
37 * FIX - Do we need to iterate over the tracks instead of taking these hints? |
|
38 */ |
|
39 void |
|
40 LocalSourceStreamInfo::ExpectAudio(const mozilla::TrackID aID) |
|
41 { |
|
42 mAudioTracks.AppendElement(aID); |
|
43 } |
|
44 |
|
45 // If the ExpectVideo hint is on we will add a track at the default first |
|
46 // video track ID (1). |
|
47 void |
|
48 LocalSourceStreamInfo::ExpectVideo(const mozilla::TrackID aID) |
|
49 { |
|
50 mVideoTracks.AppendElement(aID); |
|
51 } |
|
52 |
|
53 unsigned |
|
54 LocalSourceStreamInfo::AudioTrackCount() |
|
55 { |
|
56 return mAudioTracks.Length(); |
|
57 } |
|
58 |
|
59 unsigned |
|
60 LocalSourceStreamInfo::VideoTrackCount() |
|
61 { |
|
62 return mVideoTracks.Length(); |
|
63 } |
|
64 |
|
65 void LocalSourceStreamInfo::DetachTransport_s() |
|
66 { |
|
67 ASSERT_ON_THREAD(mParent->GetSTSThread()); |
|
68 // walk through all the MediaPipelines and call the shutdown |
|
69 // functions for transport. Must be on the STS thread. |
|
70 for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it = |
|
71 mPipelines.begin(); it != mPipelines.end(); |
|
72 ++it) { |
|
73 it->second->ShutdownTransport_s(); |
|
74 } |
|
75 } |
|
76 |
|
77 void LocalSourceStreamInfo::DetachMedia_m() |
|
78 { |
|
79 ASSERT_ON_THREAD(mParent->GetMainThread()); |
|
80 // walk through all the MediaPipelines and call the shutdown |
|
81 // functions. Must be on the main thread. |
|
82 for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it = |
|
83 mPipelines.begin(); it != mPipelines.end(); |
|
84 ++it) { |
|
85 it->second->ShutdownMedia_m(); |
|
86 } |
|
87 mAudioTracks.Clear(); |
|
88 mVideoTracks.Clear(); |
|
89 mMediaStream = nullptr; |
|
90 } |
|
91 |
|
92 void RemoteSourceStreamInfo::DetachTransport_s() |
|
93 { |
|
94 ASSERT_ON_THREAD(mParent->GetSTSThread()); |
|
95 // walk through all the MediaPipelines and call the shutdown |
|
96 // transport functions. Must be on the STS thread. |
|
97 for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it = |
|
98 mPipelines.begin(); it != mPipelines.end(); |
|
99 ++it) { |
|
100 it->second->ShutdownTransport_s(); |
|
101 } |
|
102 } |
|
103 |
|
104 void RemoteSourceStreamInfo::DetachMedia_m() |
|
105 { |
|
106 ASSERT_ON_THREAD(mParent->GetMainThread()); |
|
107 |
|
108 // walk through all the MediaPipelines and call the shutdown |
|
109 // media functions. Must be on the main thread. |
|
110 for (std::map<int, mozilla::RefPtr<mozilla::MediaPipeline> >::iterator it = |
|
111 mPipelines.begin(); it != mPipelines.end(); |
|
112 ++it) { |
|
113 it->second->ShutdownMedia_m(); |
|
114 } |
|
115 mMediaStream = nullptr; |
|
116 } |
|
117 |
|
118 already_AddRefed<PeerConnectionImpl> |
|
119 PeerConnectionImpl::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& rv) |
|
120 { |
|
121 nsRefPtr<PeerConnectionImpl> pc = new PeerConnectionImpl(&aGlobal); |
|
122 |
|
123 CSFLogDebug(logTag, "Created PeerConnection: %p", pc.get()); |
|
124 |
|
125 return pc.forget(); |
|
126 } |
|
127 |
|
128 PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection() |
|
129 { |
|
130 PeerConnectionImpl *pc = new PeerConnectionImpl(); |
|
131 |
|
132 CSFLogDebug(logTag, "Created PeerConnection: %p", pc); |
|
133 |
|
134 return pc; |
|
135 } |
|
136 |
|
137 |
|
138 PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent) |
|
139 : mParent(parent), |
|
140 mLocalSourceStreamsLock("PeerConnectionMedia.mLocalSourceStreamsLock"), |
|
141 mIceCtx(nullptr), |
|
142 mDNSResolver(new mozilla::NrIceResolver()), |
|
143 mMainThread(mParent->GetMainThread()), |
|
144 mSTSThread(mParent->GetSTSThread()) {} |
|
145 |
|
146 nsresult PeerConnectionMedia::Init(const std::vector<NrIceStunServer>& stun_servers, |
|
147 const std::vector<NrIceTurnServer>& turn_servers) |
|
148 { |
|
149 // TODO(ekr@rtfm.com): need some way to set not offerer later |
|
150 // Looks like a bug in the NrIceCtx API. |
|
151 mIceCtx = NrIceCtx::Create("PC:" + mParent->GetName(), true); |
|
152 if(!mIceCtx) { |
|
153 CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__); |
|
154 return NS_ERROR_FAILURE; |
|
155 } |
|
156 nsresult rv; |
|
157 if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) { |
|
158 CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__); |
|
159 return rv; |
|
160 } |
|
161 // Give us a way to globally turn off TURN support |
|
162 #ifdef MOZILLA_INTERNAL_API |
|
163 bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false); |
|
164 #else |
|
165 bool disabled = false; |
|
166 #endif |
|
167 if (!disabled) { |
|
168 if (NS_FAILED(rv = mIceCtx->SetTurnServers(turn_servers))) { |
|
169 CSFLogError(logTag, "%s: Failed to set turn servers", __FUNCTION__); |
|
170 return rv; |
|
171 } |
|
172 } else if (turn_servers.size() != 0) { |
|
173 CSFLogError(logTag, "%s: Setting turn servers disabled", __FUNCTION__); |
|
174 } |
|
175 if (NS_FAILED(rv = mDNSResolver->Init())) { |
|
176 CSFLogError(logTag, "%s: Failed to initialize dns resolver", __FUNCTION__); |
|
177 return rv; |
|
178 } |
|
179 if (NS_FAILED(rv = mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) { |
|
180 CSFLogError(logTag, "%s: Failed to get dns resolver", __FUNCTION__); |
|
181 return rv; |
|
182 } |
|
183 mIceCtx->SignalGatheringStateChange.connect( |
|
184 this, |
|
185 &PeerConnectionMedia::IceGatheringStateChange_s); |
|
186 mIceCtx->SignalConnectionStateChange.connect( |
|
187 this, |
|
188 &PeerConnectionMedia::IceConnectionStateChange_s); |
|
189 |
|
190 // Create three streams to start with. |
|
191 // One each for audio, video and DataChannel |
|
192 // TODO: this will be re-visited |
|
193 RefPtr<NrIceMediaStream> audioStream = |
|
194 mIceCtx->CreateStream((mParent->GetName()+": stream1/audio").c_str(), 2); |
|
195 RefPtr<NrIceMediaStream> videoStream = |
|
196 mIceCtx->CreateStream((mParent->GetName()+": stream2/video").c_str(), 2); |
|
197 RefPtr<NrIceMediaStream> dcStream = |
|
198 mIceCtx->CreateStream((mParent->GetName()+": stream3/data").c_str(), 2); |
|
199 |
|
200 if (!audioStream) { |
|
201 CSFLogError(logTag, "%s: audio stream is NULL", __FUNCTION__); |
|
202 return NS_ERROR_FAILURE; |
|
203 } else { |
|
204 mIceStreams.push_back(audioStream); |
|
205 } |
|
206 |
|
207 if (!videoStream) { |
|
208 CSFLogError(logTag, "%s: video stream is NULL", __FUNCTION__); |
|
209 return NS_ERROR_FAILURE; |
|
210 } else { |
|
211 mIceStreams.push_back(videoStream); |
|
212 } |
|
213 |
|
214 if (!dcStream) { |
|
215 CSFLogError(logTag, "%s: datachannel stream is NULL", __FUNCTION__); |
|
216 return NS_ERROR_FAILURE; |
|
217 } else { |
|
218 mIceStreams.push_back(dcStream); |
|
219 } |
|
220 |
|
221 // TODO(ekr@rtfm.com): This is not connected to the PCCimpl. |
|
222 // Will need to do that later. |
|
223 for (std::size_t i=0; i<mIceStreams.size(); i++) { |
|
224 mIceStreams[i]->SignalReady.connect(this, &PeerConnectionMedia::IceStreamReady); |
|
225 } |
|
226 |
|
227 // TODO(ekr@rtfm.com): When we have a generic error reporting mechanism, |
|
228 // figure out how to report that StartGathering failed. Bug 827982. |
|
229 RUN_ON_THREAD(mIceCtx->thread(), |
|
230 WrapRunnable(mIceCtx, &NrIceCtx::StartGathering), NS_DISPATCH_NORMAL); |
|
231 |
|
232 return NS_OK; |
|
233 } |
|
234 |
|
235 nsresult |
|
236 PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id) |
|
237 { |
|
238 if (!aMediaStream) { |
|
239 CSFLogError(logTag, "%s - aMediaStream is NULL", __FUNCTION__); |
|
240 return NS_ERROR_FAILURE; |
|
241 } |
|
242 |
|
243 DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream); |
|
244 |
|
245 CSFLogDebug(logTag, "%s: MediaStream: %p", |
|
246 __FUNCTION__, aMediaStream); |
|
247 |
|
248 // Adding tracks here based on nsDOMMediaStream expectation settings |
|
249 uint32_t hints = stream->GetHintContents(); |
|
250 #ifdef MOZILLA_INTERNAL_API |
|
251 if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) { |
|
252 hints &= ~(DOMMediaStream::HINT_CONTENTS_VIDEO); |
|
253 } |
|
254 #endif |
|
255 |
|
256 if (!(hints & (DOMMediaStream::HINT_CONTENTS_AUDIO | |
|
257 DOMMediaStream::HINT_CONTENTS_VIDEO))) { |
|
258 CSFLogDebug(logTag, "Empty Stream !!"); |
|
259 return NS_OK; |
|
260 } |
|
261 |
|
262 // Now see if we already have a stream of this type, since we only |
|
263 // allow one of each. |
|
264 // TODO(ekr@rtfm.com): remove this when multiple of each stream |
|
265 // is allowed |
|
266 mozilla::MutexAutoLock lock(mLocalSourceStreamsLock); |
|
267 for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { |
|
268 nsRefPtr<LocalSourceStreamInfo> localSourceStream = mLocalSourceStreams[u]; |
|
269 |
|
270 if (localSourceStream->GetMediaStream()->GetHintContents() & hints) { |
|
271 CSFLogError(logTag, "Only one stream of any given type allowed"); |
|
272 return NS_ERROR_FAILURE; |
|
273 } |
|
274 } |
|
275 |
|
276 // OK, we're good to add |
|
277 nsRefPtr<LocalSourceStreamInfo> localSourceStream = |
|
278 new LocalSourceStreamInfo(stream, this); |
|
279 *stream_id = mLocalSourceStreams.Length(); |
|
280 |
|
281 if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { |
|
282 localSourceStream->ExpectAudio(TRACK_AUDIO); |
|
283 } |
|
284 |
|
285 if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { |
|
286 localSourceStream->ExpectVideo(TRACK_VIDEO); |
|
287 } |
|
288 |
|
289 mLocalSourceStreams.AppendElement(localSourceStream); |
|
290 |
|
291 return NS_OK; |
|
292 } |
|
293 |
|
294 nsresult |
|
295 PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id) |
|
296 { |
|
297 MOZ_ASSERT(aMediaStream); |
|
298 |
|
299 DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream); |
|
300 |
|
301 CSFLogDebug(logTag, "%s: MediaStream: %p", |
|
302 __FUNCTION__, aMediaStream); |
|
303 |
|
304 mozilla::MutexAutoLock lock(mLocalSourceStreamsLock); |
|
305 for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { |
|
306 nsRefPtr<LocalSourceStreamInfo> localSourceStream = mLocalSourceStreams[u]; |
|
307 if (localSourceStream->GetMediaStream() == stream) { |
|
308 *stream_id = u; |
|
309 return NS_OK; |
|
310 } |
|
311 } |
|
312 |
|
313 return NS_ERROR_ILLEGAL_VALUE; |
|
314 } |
|
315 |
|
316 void |
|
317 PeerConnectionMedia::SelfDestruct() |
|
318 { |
|
319 ASSERT_ON_THREAD(mMainThread); |
|
320 |
|
321 CSFLogDebug(logTag, "%s: ", __FUNCTION__); |
|
322 |
|
323 // Shut down the media |
|
324 for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) { |
|
325 mLocalSourceStreams[i]->DetachMedia_m(); |
|
326 } |
|
327 |
|
328 for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) { |
|
329 mRemoteSourceStreams[i]->DetachMedia_m(); |
|
330 } |
|
331 |
|
332 // Shutdown the transport (async) |
|
333 RUN_ON_THREAD(mSTSThread, WrapRunnable( |
|
334 this, &PeerConnectionMedia::ShutdownMediaTransport_s), |
|
335 NS_DISPATCH_NORMAL); |
|
336 |
|
337 CSFLogDebug(logTag, "%s: Media shut down", __FUNCTION__); |
|
338 } |
|
339 |
|
340 void |
|
341 PeerConnectionMedia::SelfDestruct_m() |
|
342 { |
|
343 CSFLogDebug(logTag, "%s: ", __FUNCTION__); |
|
344 |
|
345 ASSERT_ON_THREAD(mMainThread); |
|
346 mLocalSourceStreams.Clear(); |
|
347 mRemoteSourceStreams.Clear(); |
|
348 |
|
349 // Final self-destruct. |
|
350 this->Release(); |
|
351 } |
|
352 |
|
353 void |
|
354 PeerConnectionMedia::ShutdownMediaTransport_s() |
|
355 { |
|
356 ASSERT_ON_THREAD(mSTSThread); |
|
357 |
|
358 CSFLogDebug(logTag, "%s: ", __FUNCTION__); |
|
359 |
|
360 for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) { |
|
361 mLocalSourceStreams[i]->DetachTransport_s(); |
|
362 } |
|
363 |
|
364 for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) { |
|
365 mRemoteSourceStreams[i]->DetachTransport_s(); |
|
366 } |
|
367 |
|
368 disconnect_all(); |
|
369 mTransportFlows.clear(); |
|
370 mIceStreams.clear(); |
|
371 mIceCtx = nullptr; |
|
372 |
|
373 mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m), |
|
374 NS_DISPATCH_NORMAL); |
|
375 } |
|
376 |
|
377 LocalSourceStreamInfo* |
|
378 PeerConnectionMedia::GetLocalStream(int aIndex) |
|
379 { |
|
380 if(aIndex < 0 || aIndex >= (int) mLocalSourceStreams.Length()) { |
|
381 return nullptr; |
|
382 } |
|
383 |
|
384 MOZ_ASSERT(mLocalSourceStreams[aIndex]); |
|
385 return mLocalSourceStreams[aIndex]; |
|
386 } |
|
387 |
|
388 RemoteSourceStreamInfo* |
|
389 PeerConnectionMedia::GetRemoteStream(int aIndex) |
|
390 { |
|
391 if(aIndex < 0 || aIndex >= (int) mRemoteSourceStreams.Length()) { |
|
392 return nullptr; |
|
393 } |
|
394 |
|
395 MOZ_ASSERT(mRemoteSourceStreams[aIndex]); |
|
396 return mRemoteSourceStreams[aIndex]; |
|
397 } |
|
398 |
|
399 bool |
|
400 PeerConnectionMedia::SetUsingBundle_m(int level, bool decision) |
|
401 { |
|
402 ASSERT_ON_THREAD(mMainThread); |
|
403 for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) { |
|
404 if (mRemoteSourceStreams[i]->SetUsingBundle_m(level, decision)) { |
|
405 // Found the MediaPipeline for |level| |
|
406 return true; |
|
407 } |
|
408 } |
|
409 CSFLogWarn(logTag, "Could not locate level %d to set bundle flag to %s", |
|
410 static_cast<int>(level), |
|
411 decision ? "true" : "false"); |
|
412 return false; |
|
413 } |
|
414 |
|
415 static void |
|
416 UpdateFilterFromRemoteDescription_s( |
|
417 RefPtr<mozilla::MediaPipeline> receive, |
|
418 RefPtr<mozilla::MediaPipeline> transmit, |
|
419 nsAutoPtr<mozilla::MediaPipelineFilter> filter) { |
|
420 |
|
421 // Update filter, and make a copy of the final version. |
|
422 mozilla::MediaPipelineFilter *finalFilter( |
|
423 receive->UpdateFilterFromRemoteDescription_s(filter)); |
|
424 |
|
425 if (finalFilter) { |
|
426 filter = new mozilla::MediaPipelineFilter(*finalFilter); |
|
427 } |
|
428 |
|
429 // Set same filter on transmit pipeline too. |
|
430 transmit->UpdateFilterFromRemoteDescription_s(filter); |
|
431 } |
|
432 |
|
433 bool |
|
434 PeerConnectionMedia::UpdateFilterFromRemoteDescription_m( |
|
435 int level, |
|
436 nsAutoPtr<mozilla::MediaPipelineFilter> filter) |
|
437 { |
|
438 ASSERT_ON_THREAD(mMainThread); |
|
439 |
|
440 RefPtr<mozilla::MediaPipeline> receive; |
|
441 for (size_t i = 0; !receive && i < mRemoteSourceStreams.Length(); ++i) { |
|
442 receive = mRemoteSourceStreams[i]->GetPipelineByLevel_m(level); |
|
443 } |
|
444 |
|
445 RefPtr<mozilla::MediaPipeline> transmit; |
|
446 for (size_t i = 0; !transmit && i < mLocalSourceStreams.Length(); ++i) { |
|
447 transmit = mLocalSourceStreams[i]->GetPipelineByLevel_m(level); |
|
448 } |
|
449 |
|
450 if (receive && transmit) { |
|
451 // GetPipelineByLevel_m will return nullptr if shutdown is in progress; |
|
452 // since shutdown is initiated in main, and involves a dispatch to STS |
|
453 // before the pipelines are released, our dispatch to STS will complete |
|
454 // before any release can happen due to a shutdown that hasn't started yet. |
|
455 RUN_ON_THREAD(GetSTSThread(), |
|
456 WrapRunnableNM( |
|
457 &UpdateFilterFromRemoteDescription_s, |
|
458 receive, |
|
459 transmit, |
|
460 filter |
|
461 ), |
|
462 NS_DISPATCH_NORMAL); |
|
463 return true; |
|
464 } else { |
|
465 CSFLogWarn(logTag, "Could not locate level %d to update filter", |
|
466 static_cast<int>(level)); |
|
467 } |
|
468 return false; |
|
469 } |
|
470 |
|
471 nsresult |
|
472 PeerConnectionMedia::AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo, |
|
473 int *aIndex) |
|
474 { |
|
475 MOZ_ASSERT(aIndex); |
|
476 |
|
477 *aIndex = mRemoteSourceStreams.Length(); |
|
478 |
|
479 mRemoteSourceStreams.AppendElement(aInfo); |
|
480 |
|
481 return NS_OK; |
|
482 } |
|
483 |
|
484 nsresult |
|
485 PeerConnectionMedia::AddRemoteStreamHint(int aIndex, bool aIsVideo) |
|
486 { |
|
487 if (aIndex < 0 || |
|
488 static_cast<unsigned int>(aIndex) >= mRemoteSourceStreams.Length()) { |
|
489 return NS_ERROR_ILLEGAL_VALUE; |
|
490 } |
|
491 |
|
492 RemoteSourceStreamInfo *pInfo = mRemoteSourceStreams.ElementAt(aIndex); |
|
493 MOZ_ASSERT(pInfo); |
|
494 |
|
495 if (aIsVideo) { |
|
496 pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO; |
|
497 } else { |
|
498 pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO; |
|
499 } |
|
500 |
|
501 return NS_OK; |
|
502 } |
|
503 |
|
504 |
|
505 void |
|
506 PeerConnectionMedia::IceGatheringStateChange_s(NrIceCtx* ctx, |
|
507 NrIceCtx::GatheringState state) |
|
508 { |
|
509 ASSERT_ON_THREAD(mSTSThread); |
|
510 // ShutdownMediaTransport_s has not run yet because it unhooks this function |
|
511 // from its signal, which means that SelfDestruct_m has not been dispatched |
|
512 // yet either, so this PCMedia will still be around when this dispatch reaches |
|
513 // main. |
|
514 GetMainThread()->Dispatch( |
|
515 WrapRunnable(this, |
|
516 &PeerConnectionMedia::IceGatheringStateChange_m, |
|
517 ctx, |
|
518 state), |
|
519 NS_DISPATCH_NORMAL); |
|
520 } |
|
521 |
|
522 void |
|
523 PeerConnectionMedia::IceConnectionStateChange_s(NrIceCtx* ctx, |
|
524 NrIceCtx::ConnectionState state) |
|
525 { |
|
526 ASSERT_ON_THREAD(mSTSThread); |
|
527 // ShutdownMediaTransport_s has not run yet because it unhooks this function |
|
528 // from its signal, which means that SelfDestruct_m has not been dispatched |
|
529 // yet either, so this PCMedia will still be around when this dispatch reaches |
|
530 // main. |
|
531 GetMainThread()->Dispatch( |
|
532 WrapRunnable(this, |
|
533 &PeerConnectionMedia::IceConnectionStateChange_m, |
|
534 ctx, |
|
535 state), |
|
536 NS_DISPATCH_NORMAL); |
|
537 } |
|
538 |
|
539 void |
|
540 PeerConnectionMedia::IceGatheringStateChange_m(NrIceCtx* ctx, |
|
541 NrIceCtx::GatheringState state) |
|
542 { |
|
543 ASSERT_ON_THREAD(mMainThread); |
|
544 SignalIceGatheringStateChange(ctx, state); |
|
545 } |
|
546 |
|
547 void |
|
548 PeerConnectionMedia::IceConnectionStateChange_m(NrIceCtx* ctx, |
|
549 NrIceCtx::ConnectionState state) |
|
550 { |
|
551 ASSERT_ON_THREAD(mMainThread); |
|
552 SignalIceConnectionStateChange(ctx, state); |
|
553 } |
|
554 |
|
555 void |
|
556 PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream) |
|
557 { |
|
558 MOZ_ASSERT(aStream); |
|
559 |
|
560 CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str()); |
|
561 } |
|
562 |
|
563 void |
|
564 LocalSourceStreamInfo::StorePipeline(int aTrack, |
|
565 mozilla::RefPtr<mozilla::MediaPipeline> aPipeline) |
|
566 { |
|
567 MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end()); |
|
568 if (mPipelines.find(aTrack) != mPipelines.end()) { |
|
569 CSFLogError(logTag, "%s: Storing duplicate track", __FUNCTION__); |
|
570 return; |
|
571 } |
|
572 //TODO: Revisit once we start supporting multiple streams or multiple tracks |
|
573 // of same type |
|
574 mPipelines[aTrack] = aPipeline; |
|
575 } |
|
576 |
|
577 void |
|
578 RemoteSourceStreamInfo::StorePipeline(int aTrack, |
|
579 bool aIsVideo, |
|
580 mozilla::RefPtr<mozilla::MediaPipeline> aPipeline) |
|
581 { |
|
582 MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end()); |
|
583 if (mPipelines.find(aTrack) != mPipelines.end()) { |
|
584 CSFLogError(logTag, "%s: Request to store duplicate track %d", __FUNCTION__, aTrack); |
|
585 return; |
|
586 } |
|
587 CSFLogDebug(logTag, "%s track %d %s = %p", __FUNCTION__, aTrack, aIsVideo ? "video" : "audio", |
|
588 aPipeline.get()); |
|
589 // See if we have both audio and video here, and if so cross the streams and sync them |
|
590 // XXX Needs to be adjusted when we support multiple streams of the same type |
|
591 for (std::map<int, bool>::iterator it = mTypes.begin(); it != mTypes.end(); ++it) { |
|
592 if (it->second != aIsVideo) { |
|
593 // Ok, we have one video, one non-video - cross the streams! |
|
594 mozilla::WebrtcAudioConduit *audio_conduit = static_cast<mozilla::WebrtcAudioConduit*> |
|
595 (aIsVideo ? |
|
596 mPipelines[it->first]->Conduit() : |
|
597 aPipeline->Conduit()); |
|
598 mozilla::WebrtcVideoConduit *video_conduit = static_cast<mozilla::WebrtcVideoConduit*> |
|
599 (aIsVideo ? |
|
600 aPipeline->Conduit() : |
|
601 mPipelines[it->first]->Conduit()); |
|
602 video_conduit->SyncTo(audio_conduit); |
|
603 CSFLogDebug(logTag, "Syncing %p to %p, %d to %d", video_conduit, audio_conduit, |
|
604 aTrack, it->first); |
|
605 } |
|
606 } |
|
607 //TODO: Revisit once we start supporting multiple streams or multiple tracks |
|
608 // of same type |
|
609 mPipelines[aTrack] = aPipeline; |
|
610 //TODO: move to attribute on Pipeline |
|
611 mTypes[aTrack] = aIsVideo; |
|
612 } |
|
613 |
|
614 RefPtr<MediaPipeline> SourceStreamInfo::GetPipelineByLevel_m(int level) { |
|
615 ASSERT_ON_THREAD(mParent->GetMainThread()); |
|
616 |
|
617 // Refuse to hand out references if we're tearing down. |
|
618 // (Since teardown involves a dispatch to and from STS before MediaPipelines |
|
619 // are released, it is safe to start other dispatches to and from STS with a |
|
620 // RefPtr<MediaPipeline>, since that reference won't be the last one |
|
621 // standing) |
|
622 if (mMediaStream) { |
|
623 for (auto p = mPipelines.begin(); p != mPipelines.end(); ++p) { |
|
624 if (p->second->level() == level) { |
|
625 return p->second; |
|
626 } |
|
627 } |
|
628 } |
|
629 |
|
630 return nullptr; |
|
631 } |
|
632 |
|
633 bool RemoteSourceStreamInfo::SetUsingBundle_m(int aLevel, bool decision) { |
|
634 ASSERT_ON_THREAD(mParent->GetMainThread()); |
|
635 |
|
636 RefPtr<MediaPipeline> pipeline(GetPipelineByLevel_m(aLevel)); |
|
637 |
|
638 if (pipeline) { |
|
639 RUN_ON_THREAD(mParent->GetSTSThread(), |
|
640 WrapRunnable( |
|
641 pipeline, |
|
642 &MediaPipeline::SetUsingBundle_s, |
|
643 decision |
|
644 ), |
|
645 NS_DISPATCH_NORMAL); |
|
646 return true; |
|
647 } |
|
648 return false; |
|
649 } |
|
650 |
|
651 } // namespace sipcc |