1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/DOMMediaStream.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,397 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "DOMMediaStream.h" 1.10 +#include "nsContentUtils.h" 1.11 +#include "mozilla/dom/MediaStreamBinding.h" 1.12 +#include "mozilla/dom/LocalMediaStreamBinding.h" 1.13 +#include "mozilla/dom/AudioNode.h" 1.14 +#include "MediaStreamGraph.h" 1.15 +#include "AudioStreamTrack.h" 1.16 +#include "VideoStreamTrack.h" 1.17 + 1.18 +using namespace mozilla; 1.19 +using namespace mozilla::dom; 1.20 + 1.21 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream) 1.22 + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1.23 + NS_INTERFACE_MAP_ENTRY(nsISupports) 1.24 + NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream) 1.25 +NS_INTERFACE_MAP_END 1.26 + 1.27 +NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMediaStream) 1.28 +NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream) 1.29 + 1.30 +NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream) 1.31 + 1.32 +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMMediaStream) 1.33 + tmp->Destroy(); 1.34 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) 1.35 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks) 1.36 + NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive) 1.37 + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1.38 +NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1.39 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMMediaStream) 1.40 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) 1.41 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks) 1.42 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive) 1.43 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS 1.44 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.45 +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DOMMediaStream) 1.46 + 1.47 +NS_IMPL_ISUPPORTS_INHERITED(DOMLocalMediaStream, DOMMediaStream, 1.48 + nsIDOMLocalMediaStream) 1.49 + 1.50 +NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream, 1.51 + mStreamNode) 1.52 + 1.53 +NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream) 1.54 +NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream) 1.55 + 1.56 +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream) 1.57 +NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) 1.58 + 1.59 +class DOMMediaStream::StreamListener : public MediaStreamListener { 1.60 +public: 1.61 + StreamListener(DOMMediaStream* aStream) 1.62 + : mStream(aStream) 1.63 + {} 1.64 + 1.65 + // Main thread only 1.66 + void Forget() { mStream = nullptr; } 1.67 + DOMMediaStream* GetStream() { return mStream; } 1.68 + 1.69 + class TrackChange : public nsRunnable { 1.70 + public: 1.71 + TrackChange(StreamListener* aListener, 1.72 + TrackID aID, TrackTicks aTrackOffset, 1.73 + uint32_t aEvents, MediaSegment::Type aType) 1.74 + : mListener(aListener), mID(aID), mEvents(aEvents), mType(aType) 1.75 + { 1.76 + } 1.77 + 1.78 + NS_IMETHOD Run() 1.79 + { 1.80 + NS_ASSERTION(NS_IsMainThread(), "main thread only"); 1.81 + 1.82 + DOMMediaStream* stream = mListener->GetStream(); 1.83 + if (!stream) { 1.84 + return NS_OK; 1.85 + } 1.86 + 1.87 + nsRefPtr<MediaStreamTrack> track; 1.88 + if (mEvents & MediaStreamListener::TRACK_EVENT_CREATED) { 1.89 + track = stream->CreateDOMTrack(mID, mType); 1.90 + } else { 1.91 + track = stream->GetDOMTrackFor(mID); 1.92 + } 1.93 + if (mEvents & MediaStreamListener::TRACK_EVENT_ENDED) { 1.94 + track->NotifyEnded(); 1.95 + } 1.96 + return NS_OK; 1.97 + } 1.98 + 1.99 + StreamTime mEndTime; 1.100 + nsRefPtr<StreamListener> mListener; 1.101 + TrackID mID; 1.102 + uint32_t mEvents; 1.103 + MediaSegment::Type mType; 1.104 + }; 1.105 + 1.106 + /** 1.107 + * Notify that changes to one of the stream tracks have been queued. 1.108 + * aTrackEvents can be any combination of TRACK_EVENT_CREATED and 1.109 + * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track 1.110 + * at aTrackOffset (relative to the start of the stream). 1.111 + * aQueuedMedia can be null if there is no output. 1.112 + */ 1.113 + virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, 1.114 + TrackRate aTrackRate, 1.115 + TrackTicks aTrackOffset, 1.116 + uint32_t aTrackEvents, 1.117 + const MediaSegment& aQueuedMedia) MOZ_OVERRIDE 1.118 + { 1.119 + if (aTrackEvents & (TRACK_EVENT_CREATED | TRACK_EVENT_ENDED)) { 1.120 + nsRefPtr<TrackChange> runnable = 1.121 + new TrackChange(this, aID, aTrackOffset, aTrackEvents, 1.122 + aQueuedMedia.GetType()); 1.123 + NS_DispatchToMainThread(runnable); 1.124 + } 1.125 + } 1.126 + 1.127 +private: 1.128 + // These fields may only be accessed on the main thread 1.129 + DOMMediaStream* mStream; 1.130 +}; 1.131 + 1.132 +DOMMediaStream::DOMMediaStream() 1.133 + : mLogicalStreamStartTime(0), 1.134 + mStream(nullptr), mHintContents(0), mTrackTypesAvailable(0), 1.135 + mNotifiedOfMediaStreamGraphShutdown(false) 1.136 +{ 1.137 + SetIsDOMBinding(); 1.138 +} 1.139 + 1.140 +DOMMediaStream::~DOMMediaStream() 1.141 +{ 1.142 + Destroy(); 1.143 +} 1.144 + 1.145 +void 1.146 +DOMMediaStream::Destroy() 1.147 +{ 1.148 + if (mListener) { 1.149 + mListener->Forget(); 1.150 + mListener = nullptr; 1.151 + } 1.152 + if (mStream) { 1.153 + mStream->Destroy(); 1.154 + mStream = nullptr; 1.155 + } 1.156 +} 1.157 + 1.158 +JSObject* 1.159 +DOMMediaStream::WrapObject(JSContext* aCx) 1.160 +{ 1.161 + return dom::MediaStreamBinding::Wrap(aCx, this); 1.162 +} 1.163 + 1.164 +double 1.165 +DOMMediaStream::CurrentTime() 1.166 +{ 1.167 + if (!mStream) { 1.168 + return 0.0; 1.169 + } 1.170 + return MediaTimeToSeconds(mStream->GetCurrentTime() - mLogicalStreamStartTime); 1.171 +} 1.172 + 1.173 +void 1.174 +DOMMediaStream::GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks) 1.175 +{ 1.176 + for (uint32_t i = 0; i < mTracks.Length(); ++i) { 1.177 + AudioStreamTrack* t = mTracks[i]->AsAudioStreamTrack(); 1.178 + if (t) { 1.179 + aTracks.AppendElement(t); 1.180 + } 1.181 + } 1.182 +} 1.183 + 1.184 +void 1.185 +DOMMediaStream::GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks) 1.186 +{ 1.187 + for (uint32_t i = 0; i < mTracks.Length(); ++i) { 1.188 + VideoStreamTrack* t = mTracks[i]->AsVideoStreamTrack(); 1.189 + if (t) { 1.190 + aTracks.AppendElement(t); 1.191 + } 1.192 + } 1.193 +} 1.194 + 1.195 +bool 1.196 +DOMMediaStream::IsFinished() 1.197 +{ 1.198 + return !mStream || mStream->IsFinished(); 1.199 +} 1.200 + 1.201 +void 1.202 +DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents) 1.203 +{ 1.204 + mWindow = aWindow; 1.205 + SetHintContents(aHintContents); 1.206 + MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); 1.207 + InitStreamCommon(gm->CreateSourceStream(this)); 1.208 +} 1.209 + 1.210 +void 1.211 +DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents) 1.212 +{ 1.213 + mWindow = aWindow; 1.214 + SetHintContents(aHintContents); 1.215 + MediaStreamGraph* gm = MediaStreamGraph::GetInstance(); 1.216 + InitStreamCommon(gm->CreateTrackUnionStream(this)); 1.217 +} 1.218 + 1.219 +void 1.220 +DOMMediaStream::InitStreamCommon(MediaStream* aStream) 1.221 +{ 1.222 + mStream = aStream; 1.223 + 1.224 + // Setup track listener 1.225 + mListener = new StreamListener(this); 1.226 + aStream->AddListener(mListener); 1.227 +} 1.228 + 1.229 +already_AddRefed<DOMMediaStream> 1.230 +DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents) 1.231 +{ 1.232 + nsRefPtr<DOMMediaStream> stream = new DOMMediaStream(); 1.233 + stream->InitSourceStream(aWindow, aHintContents); 1.234 + return stream.forget(); 1.235 +} 1.236 + 1.237 +already_AddRefed<DOMMediaStream> 1.238 +DOMMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents) 1.239 +{ 1.240 + nsRefPtr<DOMMediaStream> stream = new DOMMediaStream(); 1.241 + stream->InitTrackUnionStream(aWindow, aHintContents); 1.242 + return stream.forget(); 1.243 +} 1.244 + 1.245 +void 1.246 +DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled) 1.247 +{ 1.248 + if (mStream) { 1.249 + mStream->SetTrackEnabled(aTrackID, aEnabled); 1.250 + } 1.251 +} 1.252 + 1.253 +bool 1.254 +DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal) 1.255 +{ 1.256 + return nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal); 1.257 +} 1.258 + 1.259 +MediaStreamTrack* 1.260 +DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType) 1.261 +{ 1.262 + MediaStreamTrack* track; 1.263 + switch (aType) { 1.264 + case MediaSegment::AUDIO: 1.265 + track = new AudioStreamTrack(this, aTrackID); 1.266 + mTrackTypesAvailable |= HINT_CONTENTS_AUDIO; 1.267 + break; 1.268 + case MediaSegment::VIDEO: 1.269 + track = new VideoStreamTrack(this, aTrackID); 1.270 + mTrackTypesAvailable |= HINT_CONTENTS_VIDEO; 1.271 + break; 1.272 + default: 1.273 + MOZ_CRASH("Unhandled track type"); 1.274 + } 1.275 + mTracks.AppendElement(track); 1.276 + 1.277 + CheckTracksAvailable(); 1.278 + 1.279 + return track; 1.280 +} 1.281 + 1.282 +MediaStreamTrack* 1.283 +DOMMediaStream::GetDOMTrackFor(TrackID aTrackID) 1.284 +{ 1.285 + for (uint32_t i = 0; i < mTracks.Length(); ++i) { 1.286 + MediaStreamTrack* t = mTracks[i]; 1.287 + // We may add streams to our track list that are actually owned by 1.288 + // a different DOMMediaStream. Ignore those. 1.289 + if (t->GetTrackID() == aTrackID && t->GetStream() == this) { 1.290 + return t; 1.291 + } 1.292 + } 1.293 + return nullptr; 1.294 +} 1.295 + 1.296 +void 1.297 +DOMMediaStream::NotifyMediaStreamGraphShutdown() 1.298 +{ 1.299 + // No more tracks will ever be added, so just clear these callbacks now 1.300 + // to prevent leaks. 1.301 + mNotifiedOfMediaStreamGraphShutdown = true; 1.302 + mRunOnTracksAvailable.Clear(); 1.303 + 1.304 + mConsumersToKeepAlive.Clear(); 1.305 +} 1.306 + 1.307 +void 1.308 +DOMMediaStream::NotifyStreamStateChanged() 1.309 +{ 1.310 + if (IsFinished()) { 1.311 + mConsumersToKeepAlive.Clear(); 1.312 + } 1.313 +} 1.314 + 1.315 +void 1.316 +DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable) 1.317 +{ 1.318 + if (mNotifiedOfMediaStreamGraphShutdown) { 1.319 + // No more tracks will ever be added, so just delete the callback now. 1.320 + delete aRunnable; 1.321 + return; 1.322 + } 1.323 + mRunOnTracksAvailable.AppendElement(aRunnable); 1.324 + CheckTracksAvailable(); 1.325 +} 1.326 + 1.327 +void 1.328 +DOMMediaStream::CheckTracksAvailable() 1.329 +{ 1.330 + if (mTrackTypesAvailable == 0) { 1.331 + return; 1.332 + } 1.333 + nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks; 1.334 + callbacks.SwapElements(mRunOnTracksAvailable); 1.335 + 1.336 + for (uint32_t i = 0; i < callbacks.Length(); ++i) { 1.337 + OnTracksAvailableCallback* cb = callbacks[i]; 1.338 + if (~mTrackTypesAvailable & cb->GetExpectedTracks()) { 1.339 + // Some expected tracks not available yet. Try this callback again later. 1.340 + *mRunOnTracksAvailable.AppendElement() = callbacks[i].forget(); 1.341 + continue; 1.342 + } 1.343 + cb->NotifyTracksAvailable(this); 1.344 + } 1.345 +} 1.346 + 1.347 +DOMLocalMediaStream::~DOMLocalMediaStream() 1.348 +{ 1.349 + if (mStream) { 1.350 + // Make sure Listeners of this stream know it's going away 1.351 + Stop(); 1.352 + } 1.353 +} 1.354 + 1.355 +JSObject* 1.356 +DOMLocalMediaStream::WrapObject(JSContext* aCx) 1.357 +{ 1.358 + return dom::LocalMediaStreamBinding::Wrap(aCx, this); 1.359 +} 1.360 + 1.361 +void 1.362 +DOMLocalMediaStream::Stop() 1.363 +{ 1.364 + if (mStream && mStream->AsSourceStream()) { 1.365 + mStream->AsSourceStream()->EndAllTrackAndFinish(); 1.366 + } 1.367 +} 1.368 + 1.369 +already_AddRefed<DOMLocalMediaStream> 1.370 +DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, 1.371 + TrackTypeHints aHintContents) 1.372 +{ 1.373 + nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream(); 1.374 + stream->InitSourceStream(aWindow, aHintContents); 1.375 + return stream.forget(); 1.376 +} 1.377 + 1.378 +already_AddRefed<DOMLocalMediaStream> 1.379 +DOMLocalMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, 1.380 + TrackTypeHints aHintContents) 1.381 +{ 1.382 + nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream(); 1.383 + stream->InitTrackUnionStream(aWindow, aHintContents); 1.384 + return stream.forget(); 1.385 +} 1.386 + 1.387 +DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(AudioNode* aNode) 1.388 +: mStreamNode(aNode) 1.389 +{ 1.390 +} 1.391 + 1.392 +already_AddRefed<DOMAudioNodeMediaStream> 1.393 +DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, 1.394 + AudioNode* aNode, 1.395 + TrackTypeHints aHintContents) 1.396 +{ 1.397 + nsRefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aNode); 1.398 + stream->InitTrackUnionStream(aWindow, aHintContents); 1.399 + return stream.forget(); 1.400 +}