Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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>
6 #include "CSFLog.h"
8 #include "nspr.h"
9 #include "cc_constants.h"
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"
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
26 using namespace mozilla;
27 using namespace mozilla::dom;
29 namespace sipcc {
31 static const char* logTag = "PeerConnectionMedia";
32 static const mozilla::TrackID TRACK_AUDIO = 0;
33 static const mozilla::TrackID TRACK_VIDEO = 1;
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 }
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 }
53 unsigned
54 LocalSourceStreamInfo::AudioTrackCount()
55 {
56 return mAudioTracks.Length();
57 }
59 unsigned
60 LocalSourceStreamInfo::VideoTrackCount()
61 {
62 return mVideoTracks.Length();
63 }
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 }
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 }
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 }
104 void RemoteSourceStreamInfo::DetachMedia_m()
105 {
106 ASSERT_ON_THREAD(mParent->GetMainThread());
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 }
118 already_AddRefed<PeerConnectionImpl>
119 PeerConnectionImpl::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& rv)
120 {
121 nsRefPtr<PeerConnectionImpl> pc = new PeerConnectionImpl(&aGlobal);
123 CSFLogDebug(logTag, "Created PeerConnection: %p", pc.get());
125 return pc.forget();
126 }
128 PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection()
129 {
130 PeerConnectionImpl *pc = new PeerConnectionImpl();
132 CSFLogDebug(logTag, "Created PeerConnection: %p", pc);
134 return pc;
135 }
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()) {}
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);
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);
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 }
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 }
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 }
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 }
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);
232 return NS_OK;
233 }
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 }
243 DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream);
245 CSFLogDebug(logTag, "%s: MediaStream: %p",
246 __FUNCTION__, aMediaStream);
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
256 if (!(hints & (DOMMediaStream::HINT_CONTENTS_AUDIO |
257 DOMMediaStream::HINT_CONTENTS_VIDEO))) {
258 CSFLogDebug(logTag, "Empty Stream !!");
259 return NS_OK;
260 }
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];
270 if (localSourceStream->GetMediaStream()->GetHintContents() & hints) {
271 CSFLogError(logTag, "Only one stream of any given type allowed");
272 return NS_ERROR_FAILURE;
273 }
274 }
276 // OK, we're good to add
277 nsRefPtr<LocalSourceStreamInfo> localSourceStream =
278 new LocalSourceStreamInfo(stream, this);
279 *stream_id = mLocalSourceStreams.Length();
281 if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) {
282 localSourceStream->ExpectAudio(TRACK_AUDIO);
283 }
285 if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) {
286 localSourceStream->ExpectVideo(TRACK_VIDEO);
287 }
289 mLocalSourceStreams.AppendElement(localSourceStream);
291 return NS_OK;
292 }
294 nsresult
295 PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id)
296 {
297 MOZ_ASSERT(aMediaStream);
299 DOMMediaStream* stream = static_cast<DOMMediaStream*>(aMediaStream);
301 CSFLogDebug(logTag, "%s: MediaStream: %p",
302 __FUNCTION__, aMediaStream);
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 }
313 return NS_ERROR_ILLEGAL_VALUE;
314 }
316 void
317 PeerConnectionMedia::SelfDestruct()
318 {
319 ASSERT_ON_THREAD(mMainThread);
321 CSFLogDebug(logTag, "%s: ", __FUNCTION__);
323 // Shut down the media
324 for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
325 mLocalSourceStreams[i]->DetachMedia_m();
326 }
328 for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
329 mRemoteSourceStreams[i]->DetachMedia_m();
330 }
332 // Shutdown the transport (async)
333 RUN_ON_THREAD(mSTSThread, WrapRunnable(
334 this, &PeerConnectionMedia::ShutdownMediaTransport_s),
335 NS_DISPATCH_NORMAL);
337 CSFLogDebug(logTag, "%s: Media shut down", __FUNCTION__);
338 }
340 void
341 PeerConnectionMedia::SelfDestruct_m()
342 {
343 CSFLogDebug(logTag, "%s: ", __FUNCTION__);
345 ASSERT_ON_THREAD(mMainThread);
346 mLocalSourceStreams.Clear();
347 mRemoteSourceStreams.Clear();
349 // Final self-destruct.
350 this->Release();
351 }
353 void
354 PeerConnectionMedia::ShutdownMediaTransport_s()
355 {
356 ASSERT_ON_THREAD(mSTSThread);
358 CSFLogDebug(logTag, "%s: ", __FUNCTION__);
360 for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) {
361 mLocalSourceStreams[i]->DetachTransport_s();
362 }
364 for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) {
365 mRemoteSourceStreams[i]->DetachTransport_s();
366 }
368 disconnect_all();
369 mTransportFlows.clear();
370 mIceStreams.clear();
371 mIceCtx = nullptr;
373 mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m),
374 NS_DISPATCH_NORMAL);
375 }
377 LocalSourceStreamInfo*
378 PeerConnectionMedia::GetLocalStream(int aIndex)
379 {
380 if(aIndex < 0 || aIndex >= (int) mLocalSourceStreams.Length()) {
381 return nullptr;
382 }
384 MOZ_ASSERT(mLocalSourceStreams[aIndex]);
385 return mLocalSourceStreams[aIndex];
386 }
388 RemoteSourceStreamInfo*
389 PeerConnectionMedia::GetRemoteStream(int aIndex)
390 {
391 if(aIndex < 0 || aIndex >= (int) mRemoteSourceStreams.Length()) {
392 return nullptr;
393 }
395 MOZ_ASSERT(mRemoteSourceStreams[aIndex]);
396 return mRemoteSourceStreams[aIndex];
397 }
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 }
415 static void
416 UpdateFilterFromRemoteDescription_s(
417 RefPtr<mozilla::MediaPipeline> receive,
418 RefPtr<mozilla::MediaPipeline> transmit,
419 nsAutoPtr<mozilla::MediaPipelineFilter> filter) {
421 // Update filter, and make a copy of the final version.
422 mozilla::MediaPipelineFilter *finalFilter(
423 receive->UpdateFilterFromRemoteDescription_s(filter));
425 if (finalFilter) {
426 filter = new mozilla::MediaPipelineFilter(*finalFilter);
427 }
429 // Set same filter on transmit pipeline too.
430 transmit->UpdateFilterFromRemoteDescription_s(filter);
431 }
433 bool
434 PeerConnectionMedia::UpdateFilterFromRemoteDescription_m(
435 int level,
436 nsAutoPtr<mozilla::MediaPipelineFilter> filter)
437 {
438 ASSERT_ON_THREAD(mMainThread);
440 RefPtr<mozilla::MediaPipeline> receive;
441 for (size_t i = 0; !receive && i < mRemoteSourceStreams.Length(); ++i) {
442 receive = mRemoteSourceStreams[i]->GetPipelineByLevel_m(level);
443 }
445 RefPtr<mozilla::MediaPipeline> transmit;
446 for (size_t i = 0; !transmit && i < mLocalSourceStreams.Length(); ++i) {
447 transmit = mLocalSourceStreams[i]->GetPipelineByLevel_m(level);
448 }
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 }
471 nsresult
472 PeerConnectionMedia::AddRemoteStream(nsRefPtr<RemoteSourceStreamInfo> aInfo,
473 int *aIndex)
474 {
475 MOZ_ASSERT(aIndex);
477 *aIndex = mRemoteSourceStreams.Length();
479 mRemoteSourceStreams.AppendElement(aInfo);
481 return NS_OK;
482 }
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 }
492 RemoteSourceStreamInfo *pInfo = mRemoteSourceStreams.ElementAt(aIndex);
493 MOZ_ASSERT(pInfo);
495 if (aIsVideo) {
496 pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO;
497 } else {
498 pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO;
499 }
501 return NS_OK;
502 }
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 }
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 }
539 void
540 PeerConnectionMedia::IceGatheringStateChange_m(NrIceCtx* ctx,
541 NrIceCtx::GatheringState state)
542 {
543 ASSERT_ON_THREAD(mMainThread);
544 SignalIceGatheringStateChange(ctx, state);
545 }
547 void
548 PeerConnectionMedia::IceConnectionStateChange_m(NrIceCtx* ctx,
549 NrIceCtx::ConnectionState state)
550 {
551 ASSERT_ON_THREAD(mMainThread);
552 SignalIceConnectionStateChange(ctx, state);
553 }
555 void
556 PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream)
557 {
558 MOZ_ASSERT(aStream);
560 CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str());
561 }
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 }
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 }
614 RefPtr<MediaPipeline> SourceStreamInfo::GetPipelineByLevel_m(int level) {
615 ASSERT_ON_THREAD(mParent->GetMainThread());
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 }
630 return nullptr;
631 }
633 bool RemoteSourceStreamInfo::SetUsingBundle_m(int aLevel, bool decision) {
634 ASSERT_ON_THREAD(mParent->GetMainThread());
636 RefPtr<MediaPipeline> pipeline(GetPipelineByLevel_m(aLevel));
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 }
651 } // namespace sipcc