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