michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include michael@0: michael@0: #include "CSFLog.h" michael@0: michael@0: #include "nspr.h" michael@0: #include "cc_constants.h" michael@0: michael@0: #include "nricectx.h" michael@0: #include "nricemediastream.h" michael@0: #include "PeerConnectionImpl.h" michael@0: #include "PeerConnectionMedia.h" michael@0: #include "AudioConduit.h" michael@0: #include "VideoConduit.h" michael@0: #include "runnable_utils.h" michael@0: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: #include "MediaStreamList.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/RTCStatsReportBinding.h" michael@0: #endif michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: namespace sipcc { michael@0: michael@0: static const char* logTag = "PeerConnectionMedia"; michael@0: static const mozilla::TrackID TRACK_AUDIO = 0; michael@0: static const mozilla::TrackID TRACK_VIDEO = 1; michael@0: michael@0: /* If the ExpectAudio hint is on we will add a track at the default first michael@0: * audio track ID (0) michael@0: * FIX - Do we need to iterate over the tracks instead of taking these hints? michael@0: */ michael@0: void michael@0: LocalSourceStreamInfo::ExpectAudio(const mozilla::TrackID aID) michael@0: { michael@0: mAudioTracks.AppendElement(aID); michael@0: } michael@0: michael@0: // If the ExpectVideo hint is on we will add a track at the default first michael@0: // video track ID (1). michael@0: void michael@0: LocalSourceStreamInfo::ExpectVideo(const mozilla::TrackID aID) michael@0: { michael@0: mVideoTracks.AppendElement(aID); michael@0: } michael@0: michael@0: unsigned michael@0: LocalSourceStreamInfo::AudioTrackCount() michael@0: { michael@0: return mAudioTracks.Length(); michael@0: } michael@0: michael@0: unsigned michael@0: LocalSourceStreamInfo::VideoTrackCount() michael@0: { michael@0: return mVideoTracks.Length(); michael@0: } michael@0: michael@0: void LocalSourceStreamInfo::DetachTransport_s() michael@0: { michael@0: ASSERT_ON_THREAD(mParent->GetSTSThread()); michael@0: // walk through all the MediaPipelines and call the shutdown michael@0: // functions for transport. Must be on the STS thread. michael@0: for (std::map >::iterator it = michael@0: mPipelines.begin(); it != mPipelines.end(); michael@0: ++it) { michael@0: it->second->ShutdownTransport_s(); michael@0: } michael@0: } michael@0: michael@0: void LocalSourceStreamInfo::DetachMedia_m() michael@0: { michael@0: ASSERT_ON_THREAD(mParent->GetMainThread()); michael@0: // walk through all the MediaPipelines and call the shutdown michael@0: // functions. Must be on the main thread. michael@0: for (std::map >::iterator it = michael@0: mPipelines.begin(); it != mPipelines.end(); michael@0: ++it) { michael@0: it->second->ShutdownMedia_m(); michael@0: } michael@0: mAudioTracks.Clear(); michael@0: mVideoTracks.Clear(); michael@0: mMediaStream = nullptr; michael@0: } michael@0: michael@0: void RemoteSourceStreamInfo::DetachTransport_s() michael@0: { michael@0: ASSERT_ON_THREAD(mParent->GetSTSThread()); michael@0: // walk through all the MediaPipelines and call the shutdown michael@0: // transport functions. Must be on the STS thread. michael@0: for (std::map >::iterator it = michael@0: mPipelines.begin(); it != mPipelines.end(); michael@0: ++it) { michael@0: it->second->ShutdownTransport_s(); michael@0: } michael@0: } michael@0: michael@0: void RemoteSourceStreamInfo::DetachMedia_m() michael@0: { michael@0: ASSERT_ON_THREAD(mParent->GetMainThread()); michael@0: michael@0: // walk through all the MediaPipelines and call the shutdown michael@0: // media functions. Must be on the main thread. michael@0: for (std::map >::iterator it = michael@0: mPipelines.begin(); it != mPipelines.end(); michael@0: ++it) { michael@0: it->second->ShutdownMedia_m(); michael@0: } michael@0: mMediaStream = nullptr; michael@0: } michael@0: michael@0: already_AddRefed michael@0: PeerConnectionImpl::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& rv) michael@0: { michael@0: nsRefPtr pc = new PeerConnectionImpl(&aGlobal); michael@0: michael@0: CSFLogDebug(logTag, "Created PeerConnection: %p", pc.get()); michael@0: michael@0: return pc.forget(); michael@0: } michael@0: michael@0: PeerConnectionImpl* PeerConnectionImpl::CreatePeerConnection() michael@0: { michael@0: PeerConnectionImpl *pc = new PeerConnectionImpl(); michael@0: michael@0: CSFLogDebug(logTag, "Created PeerConnection: %p", pc); michael@0: michael@0: return pc; michael@0: } michael@0: michael@0: michael@0: PeerConnectionMedia::PeerConnectionMedia(PeerConnectionImpl *parent) michael@0: : mParent(parent), michael@0: mLocalSourceStreamsLock("PeerConnectionMedia.mLocalSourceStreamsLock"), michael@0: mIceCtx(nullptr), michael@0: mDNSResolver(new mozilla::NrIceResolver()), michael@0: mMainThread(mParent->GetMainThread()), michael@0: mSTSThread(mParent->GetSTSThread()) {} michael@0: michael@0: nsresult PeerConnectionMedia::Init(const std::vector& stun_servers, michael@0: const std::vector& turn_servers) michael@0: { michael@0: // TODO(ekr@rtfm.com): need some way to set not offerer later michael@0: // Looks like a bug in the NrIceCtx API. michael@0: mIceCtx = NrIceCtx::Create("PC:" + mParent->GetName(), true); michael@0: if(!mIceCtx) { michael@0: CSFLogError(logTag, "%s: Failed to create Ice Context", __FUNCTION__); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: nsresult rv; michael@0: if (NS_FAILED(rv = mIceCtx->SetStunServers(stun_servers))) { michael@0: CSFLogError(logTag, "%s: Failed to set stun servers", __FUNCTION__); michael@0: return rv; michael@0: } michael@0: // Give us a way to globally turn off TURN support michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: bool disabled = Preferences::GetBool("media.peerconnection.turn.disable", false); michael@0: #else michael@0: bool disabled = false; michael@0: #endif michael@0: if (!disabled) { michael@0: if (NS_FAILED(rv = mIceCtx->SetTurnServers(turn_servers))) { michael@0: CSFLogError(logTag, "%s: Failed to set turn servers", __FUNCTION__); michael@0: return rv; michael@0: } michael@0: } else if (turn_servers.size() != 0) { michael@0: CSFLogError(logTag, "%s: Setting turn servers disabled", __FUNCTION__); michael@0: } michael@0: if (NS_FAILED(rv = mDNSResolver->Init())) { michael@0: CSFLogError(logTag, "%s: Failed to initialize dns resolver", __FUNCTION__); michael@0: return rv; michael@0: } michael@0: if (NS_FAILED(rv = mIceCtx->SetResolver(mDNSResolver->AllocateResolver()))) { michael@0: CSFLogError(logTag, "%s: Failed to get dns resolver", __FUNCTION__); michael@0: return rv; michael@0: } michael@0: mIceCtx->SignalGatheringStateChange.connect( michael@0: this, michael@0: &PeerConnectionMedia::IceGatheringStateChange_s); michael@0: mIceCtx->SignalConnectionStateChange.connect( michael@0: this, michael@0: &PeerConnectionMedia::IceConnectionStateChange_s); michael@0: michael@0: // Create three streams to start with. michael@0: // One each for audio, video and DataChannel michael@0: // TODO: this will be re-visited michael@0: RefPtr audioStream = michael@0: mIceCtx->CreateStream((mParent->GetName()+": stream1/audio").c_str(), 2); michael@0: RefPtr videoStream = michael@0: mIceCtx->CreateStream((mParent->GetName()+": stream2/video").c_str(), 2); michael@0: RefPtr dcStream = michael@0: mIceCtx->CreateStream((mParent->GetName()+": stream3/data").c_str(), 2); michael@0: michael@0: if (!audioStream) { michael@0: CSFLogError(logTag, "%s: audio stream is NULL", __FUNCTION__); michael@0: return NS_ERROR_FAILURE; michael@0: } else { michael@0: mIceStreams.push_back(audioStream); michael@0: } michael@0: michael@0: if (!videoStream) { michael@0: CSFLogError(logTag, "%s: video stream is NULL", __FUNCTION__); michael@0: return NS_ERROR_FAILURE; michael@0: } else { michael@0: mIceStreams.push_back(videoStream); michael@0: } michael@0: michael@0: if (!dcStream) { michael@0: CSFLogError(logTag, "%s: datachannel stream is NULL", __FUNCTION__); michael@0: return NS_ERROR_FAILURE; michael@0: } else { michael@0: mIceStreams.push_back(dcStream); michael@0: } michael@0: michael@0: // TODO(ekr@rtfm.com): This is not connected to the PCCimpl. michael@0: // Will need to do that later. michael@0: for (std::size_t i=0; iSignalReady.connect(this, &PeerConnectionMedia::IceStreamReady); michael@0: } michael@0: michael@0: // TODO(ekr@rtfm.com): When we have a generic error reporting mechanism, michael@0: // figure out how to report that StartGathering failed. Bug 827982. michael@0: RUN_ON_THREAD(mIceCtx->thread(), michael@0: WrapRunnable(mIceCtx, &NrIceCtx::StartGathering), NS_DISPATCH_NORMAL); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: PeerConnectionMedia::AddStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id) michael@0: { michael@0: if (!aMediaStream) { michael@0: CSFLogError(logTag, "%s - aMediaStream is NULL", __FUNCTION__); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DOMMediaStream* stream = static_cast(aMediaStream); michael@0: michael@0: CSFLogDebug(logTag, "%s: MediaStream: %p", michael@0: __FUNCTION__, aMediaStream); michael@0: michael@0: // Adding tracks here based on nsDOMMediaStream expectation settings michael@0: uint32_t hints = stream->GetHintContents(); michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: if (!Preferences::GetBool("media.peerconnection.video.enabled", true)) { michael@0: hints &= ~(DOMMediaStream::HINT_CONTENTS_VIDEO); michael@0: } michael@0: #endif michael@0: michael@0: if (!(hints & (DOMMediaStream::HINT_CONTENTS_AUDIO | michael@0: DOMMediaStream::HINT_CONTENTS_VIDEO))) { michael@0: CSFLogDebug(logTag, "Empty Stream !!"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Now see if we already have a stream of this type, since we only michael@0: // allow one of each. michael@0: // TODO(ekr@rtfm.com): remove this when multiple of each stream michael@0: // is allowed michael@0: mozilla::MutexAutoLock lock(mLocalSourceStreamsLock); michael@0: for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { michael@0: nsRefPtr localSourceStream = mLocalSourceStreams[u]; michael@0: michael@0: if (localSourceStream->GetMediaStream()->GetHintContents() & hints) { michael@0: CSFLogError(logTag, "Only one stream of any given type allowed"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: // OK, we're good to add michael@0: nsRefPtr localSourceStream = michael@0: new LocalSourceStreamInfo(stream, this); michael@0: *stream_id = mLocalSourceStreams.Length(); michael@0: michael@0: if (hints & DOMMediaStream::HINT_CONTENTS_AUDIO) { michael@0: localSourceStream->ExpectAudio(TRACK_AUDIO); michael@0: } michael@0: michael@0: if (hints & DOMMediaStream::HINT_CONTENTS_VIDEO) { michael@0: localSourceStream->ExpectVideo(TRACK_VIDEO); michael@0: } michael@0: michael@0: mLocalSourceStreams.AppendElement(localSourceStream); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: PeerConnectionMedia::RemoveStream(nsIDOMMediaStream* aMediaStream, uint32_t *stream_id) michael@0: { michael@0: MOZ_ASSERT(aMediaStream); michael@0: michael@0: DOMMediaStream* stream = static_cast(aMediaStream); michael@0: michael@0: CSFLogDebug(logTag, "%s: MediaStream: %p", michael@0: __FUNCTION__, aMediaStream); michael@0: michael@0: mozilla::MutexAutoLock lock(mLocalSourceStreamsLock); michael@0: for (uint32_t u = 0; u < mLocalSourceStreams.Length(); u++) { michael@0: nsRefPtr localSourceStream = mLocalSourceStreams[u]; michael@0: if (localSourceStream->GetMediaStream() == stream) { michael@0: *stream_id = u; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: void michael@0: PeerConnectionMedia::SelfDestruct() michael@0: { michael@0: ASSERT_ON_THREAD(mMainThread); michael@0: michael@0: CSFLogDebug(logTag, "%s: ", __FUNCTION__); michael@0: michael@0: // Shut down the media michael@0: for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) { michael@0: mLocalSourceStreams[i]->DetachMedia_m(); michael@0: } michael@0: michael@0: for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) { michael@0: mRemoteSourceStreams[i]->DetachMedia_m(); michael@0: } michael@0: michael@0: // Shutdown the transport (async) michael@0: RUN_ON_THREAD(mSTSThread, WrapRunnable( michael@0: this, &PeerConnectionMedia::ShutdownMediaTransport_s), michael@0: NS_DISPATCH_NORMAL); michael@0: michael@0: CSFLogDebug(logTag, "%s: Media shut down", __FUNCTION__); michael@0: } michael@0: michael@0: void michael@0: PeerConnectionMedia::SelfDestruct_m() michael@0: { michael@0: CSFLogDebug(logTag, "%s: ", __FUNCTION__); michael@0: michael@0: ASSERT_ON_THREAD(mMainThread); michael@0: mLocalSourceStreams.Clear(); michael@0: mRemoteSourceStreams.Clear(); michael@0: michael@0: // Final self-destruct. michael@0: this->Release(); michael@0: } michael@0: michael@0: void michael@0: PeerConnectionMedia::ShutdownMediaTransport_s() michael@0: { michael@0: ASSERT_ON_THREAD(mSTSThread); michael@0: michael@0: CSFLogDebug(logTag, "%s: ", __FUNCTION__); michael@0: michael@0: for (uint32_t i=0; i < mLocalSourceStreams.Length(); ++i) { michael@0: mLocalSourceStreams[i]->DetachTransport_s(); michael@0: } michael@0: michael@0: for (uint32_t i=0; i < mRemoteSourceStreams.Length(); ++i) { michael@0: mRemoteSourceStreams[i]->DetachTransport_s(); michael@0: } michael@0: michael@0: disconnect_all(); michael@0: mTransportFlows.clear(); michael@0: mIceStreams.clear(); michael@0: mIceCtx = nullptr; michael@0: michael@0: mMainThread->Dispatch(WrapRunnable(this, &PeerConnectionMedia::SelfDestruct_m), michael@0: NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: LocalSourceStreamInfo* michael@0: PeerConnectionMedia::GetLocalStream(int aIndex) michael@0: { michael@0: if(aIndex < 0 || aIndex >= (int) mLocalSourceStreams.Length()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(mLocalSourceStreams[aIndex]); michael@0: return mLocalSourceStreams[aIndex]; michael@0: } michael@0: michael@0: RemoteSourceStreamInfo* michael@0: PeerConnectionMedia::GetRemoteStream(int aIndex) michael@0: { michael@0: if(aIndex < 0 || aIndex >= (int) mRemoteSourceStreams.Length()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: MOZ_ASSERT(mRemoteSourceStreams[aIndex]); michael@0: return mRemoteSourceStreams[aIndex]; michael@0: } michael@0: michael@0: bool michael@0: PeerConnectionMedia::SetUsingBundle_m(int level, bool decision) michael@0: { michael@0: ASSERT_ON_THREAD(mMainThread); michael@0: for (size_t i = 0; i < mRemoteSourceStreams.Length(); ++i) { michael@0: if (mRemoteSourceStreams[i]->SetUsingBundle_m(level, decision)) { michael@0: // Found the MediaPipeline for |level| michael@0: return true; michael@0: } michael@0: } michael@0: CSFLogWarn(logTag, "Could not locate level %d to set bundle flag to %s", michael@0: static_cast(level), michael@0: decision ? "true" : "false"); michael@0: return false; michael@0: } michael@0: michael@0: static void michael@0: UpdateFilterFromRemoteDescription_s( michael@0: RefPtr receive, michael@0: RefPtr transmit, michael@0: nsAutoPtr filter) { michael@0: michael@0: // Update filter, and make a copy of the final version. michael@0: mozilla::MediaPipelineFilter *finalFilter( michael@0: receive->UpdateFilterFromRemoteDescription_s(filter)); michael@0: michael@0: if (finalFilter) { michael@0: filter = new mozilla::MediaPipelineFilter(*finalFilter); michael@0: } michael@0: michael@0: // Set same filter on transmit pipeline too. michael@0: transmit->UpdateFilterFromRemoteDescription_s(filter); michael@0: } michael@0: michael@0: bool michael@0: PeerConnectionMedia::UpdateFilterFromRemoteDescription_m( michael@0: int level, michael@0: nsAutoPtr filter) michael@0: { michael@0: ASSERT_ON_THREAD(mMainThread); michael@0: michael@0: RefPtr receive; michael@0: for (size_t i = 0; !receive && i < mRemoteSourceStreams.Length(); ++i) { michael@0: receive = mRemoteSourceStreams[i]->GetPipelineByLevel_m(level); michael@0: } michael@0: michael@0: RefPtr transmit; michael@0: for (size_t i = 0; !transmit && i < mLocalSourceStreams.Length(); ++i) { michael@0: transmit = mLocalSourceStreams[i]->GetPipelineByLevel_m(level); michael@0: } michael@0: michael@0: if (receive && transmit) { michael@0: // GetPipelineByLevel_m will return nullptr if shutdown is in progress; michael@0: // since shutdown is initiated in main, and involves a dispatch to STS michael@0: // before the pipelines are released, our dispatch to STS will complete michael@0: // before any release can happen due to a shutdown that hasn't started yet. michael@0: RUN_ON_THREAD(GetSTSThread(), michael@0: WrapRunnableNM( michael@0: &UpdateFilterFromRemoteDescription_s, michael@0: receive, michael@0: transmit, michael@0: filter michael@0: ), michael@0: NS_DISPATCH_NORMAL); michael@0: return true; michael@0: } else { michael@0: CSFLogWarn(logTag, "Could not locate level %d to update filter", michael@0: static_cast(level)); michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: nsresult michael@0: PeerConnectionMedia::AddRemoteStream(nsRefPtr aInfo, michael@0: int *aIndex) michael@0: { michael@0: MOZ_ASSERT(aIndex); michael@0: michael@0: *aIndex = mRemoteSourceStreams.Length(); michael@0: michael@0: mRemoteSourceStreams.AppendElement(aInfo); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: PeerConnectionMedia::AddRemoteStreamHint(int aIndex, bool aIsVideo) michael@0: { michael@0: if (aIndex < 0 || michael@0: static_cast(aIndex) >= mRemoteSourceStreams.Length()) { michael@0: return NS_ERROR_ILLEGAL_VALUE; michael@0: } michael@0: michael@0: RemoteSourceStreamInfo *pInfo = mRemoteSourceStreams.ElementAt(aIndex); michael@0: MOZ_ASSERT(pInfo); michael@0: michael@0: if (aIsVideo) { michael@0: pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_VIDEO; michael@0: } else { michael@0: pInfo->mTrackTypeHints |= DOMMediaStream::HINT_CONTENTS_AUDIO; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: void michael@0: PeerConnectionMedia::IceGatheringStateChange_s(NrIceCtx* ctx, michael@0: NrIceCtx::GatheringState state) michael@0: { michael@0: ASSERT_ON_THREAD(mSTSThread); michael@0: // ShutdownMediaTransport_s has not run yet because it unhooks this function michael@0: // from its signal, which means that SelfDestruct_m has not been dispatched michael@0: // yet either, so this PCMedia will still be around when this dispatch reaches michael@0: // main. michael@0: GetMainThread()->Dispatch( michael@0: WrapRunnable(this, michael@0: &PeerConnectionMedia::IceGatheringStateChange_m, michael@0: ctx, michael@0: state), michael@0: NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: void michael@0: PeerConnectionMedia::IceConnectionStateChange_s(NrIceCtx* ctx, michael@0: NrIceCtx::ConnectionState state) michael@0: { michael@0: ASSERT_ON_THREAD(mSTSThread); michael@0: // ShutdownMediaTransport_s has not run yet because it unhooks this function michael@0: // from its signal, which means that SelfDestruct_m has not been dispatched michael@0: // yet either, so this PCMedia will still be around when this dispatch reaches michael@0: // main. michael@0: GetMainThread()->Dispatch( michael@0: WrapRunnable(this, michael@0: &PeerConnectionMedia::IceConnectionStateChange_m, michael@0: ctx, michael@0: state), michael@0: NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: void michael@0: PeerConnectionMedia::IceGatheringStateChange_m(NrIceCtx* ctx, michael@0: NrIceCtx::GatheringState state) michael@0: { michael@0: ASSERT_ON_THREAD(mMainThread); michael@0: SignalIceGatheringStateChange(ctx, state); michael@0: } michael@0: michael@0: void michael@0: PeerConnectionMedia::IceConnectionStateChange_m(NrIceCtx* ctx, michael@0: NrIceCtx::ConnectionState state) michael@0: { michael@0: ASSERT_ON_THREAD(mMainThread); michael@0: SignalIceConnectionStateChange(ctx, state); michael@0: } michael@0: michael@0: void michael@0: PeerConnectionMedia::IceStreamReady(NrIceMediaStream *aStream) michael@0: { michael@0: MOZ_ASSERT(aStream); michael@0: michael@0: CSFLogDebug(logTag, "%s: %s", __FUNCTION__, aStream->name().c_str()); michael@0: } michael@0: michael@0: void michael@0: LocalSourceStreamInfo::StorePipeline(int aTrack, michael@0: mozilla::RefPtr aPipeline) michael@0: { michael@0: MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end()); michael@0: if (mPipelines.find(aTrack) != mPipelines.end()) { michael@0: CSFLogError(logTag, "%s: Storing duplicate track", __FUNCTION__); michael@0: return; michael@0: } michael@0: //TODO: Revisit once we start supporting multiple streams or multiple tracks michael@0: // of same type michael@0: mPipelines[aTrack] = aPipeline; michael@0: } michael@0: michael@0: void michael@0: RemoteSourceStreamInfo::StorePipeline(int aTrack, michael@0: bool aIsVideo, michael@0: mozilla::RefPtr aPipeline) michael@0: { michael@0: MOZ_ASSERT(mPipelines.find(aTrack) == mPipelines.end()); michael@0: if (mPipelines.find(aTrack) != mPipelines.end()) { michael@0: CSFLogError(logTag, "%s: Request to store duplicate track %d", __FUNCTION__, aTrack); michael@0: return; michael@0: } michael@0: CSFLogDebug(logTag, "%s track %d %s = %p", __FUNCTION__, aTrack, aIsVideo ? "video" : "audio", michael@0: aPipeline.get()); michael@0: // See if we have both audio and video here, and if so cross the streams and sync them michael@0: // XXX Needs to be adjusted when we support multiple streams of the same type michael@0: for (std::map::iterator it = mTypes.begin(); it != mTypes.end(); ++it) { michael@0: if (it->second != aIsVideo) { michael@0: // Ok, we have one video, one non-video - cross the streams! michael@0: mozilla::WebrtcAudioConduit *audio_conduit = static_cast michael@0: (aIsVideo ? michael@0: mPipelines[it->first]->Conduit() : michael@0: aPipeline->Conduit()); michael@0: mozilla::WebrtcVideoConduit *video_conduit = static_cast michael@0: (aIsVideo ? michael@0: aPipeline->Conduit() : michael@0: mPipelines[it->first]->Conduit()); michael@0: video_conduit->SyncTo(audio_conduit); michael@0: CSFLogDebug(logTag, "Syncing %p to %p, %d to %d", video_conduit, audio_conduit, michael@0: aTrack, it->first); michael@0: } michael@0: } michael@0: //TODO: Revisit once we start supporting multiple streams or multiple tracks michael@0: // of same type michael@0: mPipelines[aTrack] = aPipeline; michael@0: //TODO: move to attribute on Pipeline michael@0: mTypes[aTrack] = aIsVideo; michael@0: } michael@0: michael@0: RefPtr SourceStreamInfo::GetPipelineByLevel_m(int level) { michael@0: ASSERT_ON_THREAD(mParent->GetMainThread()); michael@0: michael@0: // Refuse to hand out references if we're tearing down. michael@0: // (Since teardown involves a dispatch to and from STS before MediaPipelines michael@0: // are released, it is safe to start other dispatches to and from STS with a michael@0: // RefPtr, since that reference won't be the last one michael@0: // standing) michael@0: if (mMediaStream) { michael@0: for (auto p = mPipelines.begin(); p != mPipelines.end(); ++p) { michael@0: if (p->second->level() == level) { michael@0: return p->second; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool RemoteSourceStreamInfo::SetUsingBundle_m(int aLevel, bool decision) { michael@0: ASSERT_ON_THREAD(mParent->GetMainThread()); michael@0: michael@0: RefPtr pipeline(GetPipelineByLevel_m(aLevel)); michael@0: michael@0: if (pipeline) { michael@0: RUN_ON_THREAD(mParent->GetSTSThread(), michael@0: WrapRunnable( michael@0: pipeline, michael@0: &MediaPipeline::SetUsingBundle_s, michael@0: decision michael@0: ), michael@0: NS_DISPATCH_NORMAL); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: } // namespace sipcc