Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "DOMMediaStream.h"
7 #include "nsContentUtils.h"
8 #include "mozilla/dom/MediaStreamBinding.h"
9 #include "mozilla/dom/LocalMediaStreamBinding.h"
10 #include "mozilla/dom/AudioNode.h"
11 #include "MediaStreamGraph.h"
12 #include "AudioStreamTrack.h"
13 #include "VideoStreamTrack.h"
15 using namespace mozilla;
16 using namespace mozilla::dom;
18 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream)
19 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
20 NS_INTERFACE_MAP_ENTRY(nsISupports)
21 NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream)
22 NS_INTERFACE_MAP_END
24 NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMediaStream)
25 NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream)
27 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream)
29 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DOMMediaStream)
30 tmp->Destroy();
31 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
32 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
33 NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
34 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
35 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DOMMediaStream)
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
39 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
40 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
41 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
42 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DOMMediaStream)
44 NS_IMPL_ISUPPORTS_INHERITED(DOMLocalMediaStream, DOMMediaStream,
45 nsIDOMLocalMediaStream)
47 NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream,
48 mStreamNode)
50 NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
51 NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
53 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream)
54 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
56 class DOMMediaStream::StreamListener : public MediaStreamListener {
57 public:
58 StreamListener(DOMMediaStream* aStream)
59 : mStream(aStream)
60 {}
62 // Main thread only
63 void Forget() { mStream = nullptr; }
64 DOMMediaStream* GetStream() { return mStream; }
66 class TrackChange : public nsRunnable {
67 public:
68 TrackChange(StreamListener* aListener,
69 TrackID aID, TrackTicks aTrackOffset,
70 uint32_t aEvents, MediaSegment::Type aType)
71 : mListener(aListener), mID(aID), mEvents(aEvents), mType(aType)
72 {
73 }
75 NS_IMETHOD Run()
76 {
77 NS_ASSERTION(NS_IsMainThread(), "main thread only");
79 DOMMediaStream* stream = mListener->GetStream();
80 if (!stream) {
81 return NS_OK;
82 }
84 nsRefPtr<MediaStreamTrack> track;
85 if (mEvents & MediaStreamListener::TRACK_EVENT_CREATED) {
86 track = stream->CreateDOMTrack(mID, mType);
87 } else {
88 track = stream->GetDOMTrackFor(mID);
89 }
90 if (mEvents & MediaStreamListener::TRACK_EVENT_ENDED) {
91 track->NotifyEnded();
92 }
93 return NS_OK;
94 }
96 StreamTime mEndTime;
97 nsRefPtr<StreamListener> mListener;
98 TrackID mID;
99 uint32_t mEvents;
100 MediaSegment::Type mType;
101 };
103 /**
104 * Notify that changes to one of the stream tracks have been queued.
105 * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
106 * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
107 * at aTrackOffset (relative to the start of the stream).
108 * aQueuedMedia can be null if there is no output.
109 */
110 virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
111 TrackRate aTrackRate,
112 TrackTicks aTrackOffset,
113 uint32_t aTrackEvents,
114 const MediaSegment& aQueuedMedia) MOZ_OVERRIDE
115 {
116 if (aTrackEvents & (TRACK_EVENT_CREATED | TRACK_EVENT_ENDED)) {
117 nsRefPtr<TrackChange> runnable =
118 new TrackChange(this, aID, aTrackOffset, aTrackEvents,
119 aQueuedMedia.GetType());
120 NS_DispatchToMainThread(runnable);
121 }
122 }
124 private:
125 // These fields may only be accessed on the main thread
126 DOMMediaStream* mStream;
127 };
129 DOMMediaStream::DOMMediaStream()
130 : mLogicalStreamStartTime(0),
131 mStream(nullptr), mHintContents(0), mTrackTypesAvailable(0),
132 mNotifiedOfMediaStreamGraphShutdown(false)
133 {
134 SetIsDOMBinding();
135 }
137 DOMMediaStream::~DOMMediaStream()
138 {
139 Destroy();
140 }
142 void
143 DOMMediaStream::Destroy()
144 {
145 if (mListener) {
146 mListener->Forget();
147 mListener = nullptr;
148 }
149 if (mStream) {
150 mStream->Destroy();
151 mStream = nullptr;
152 }
153 }
155 JSObject*
156 DOMMediaStream::WrapObject(JSContext* aCx)
157 {
158 return dom::MediaStreamBinding::Wrap(aCx, this);
159 }
161 double
162 DOMMediaStream::CurrentTime()
163 {
164 if (!mStream) {
165 return 0.0;
166 }
167 return MediaTimeToSeconds(mStream->GetCurrentTime() - mLogicalStreamStartTime);
168 }
170 void
171 DOMMediaStream::GetAudioTracks(nsTArray<nsRefPtr<AudioStreamTrack> >& aTracks)
172 {
173 for (uint32_t i = 0; i < mTracks.Length(); ++i) {
174 AudioStreamTrack* t = mTracks[i]->AsAudioStreamTrack();
175 if (t) {
176 aTracks.AppendElement(t);
177 }
178 }
179 }
181 void
182 DOMMediaStream::GetVideoTracks(nsTArray<nsRefPtr<VideoStreamTrack> >& aTracks)
183 {
184 for (uint32_t i = 0; i < mTracks.Length(); ++i) {
185 VideoStreamTrack* t = mTracks[i]->AsVideoStreamTrack();
186 if (t) {
187 aTracks.AppendElement(t);
188 }
189 }
190 }
192 bool
193 DOMMediaStream::IsFinished()
194 {
195 return !mStream || mStream->IsFinished();
196 }
198 void
199 DOMMediaStream::InitSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
200 {
201 mWindow = aWindow;
202 SetHintContents(aHintContents);
203 MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
204 InitStreamCommon(gm->CreateSourceStream(this));
205 }
207 void
208 DOMMediaStream::InitTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
209 {
210 mWindow = aWindow;
211 SetHintContents(aHintContents);
212 MediaStreamGraph* gm = MediaStreamGraph::GetInstance();
213 InitStreamCommon(gm->CreateTrackUnionStream(this));
214 }
216 void
217 DOMMediaStream::InitStreamCommon(MediaStream* aStream)
218 {
219 mStream = aStream;
221 // Setup track listener
222 mListener = new StreamListener(this);
223 aStream->AddListener(mListener);
224 }
226 already_AddRefed<DOMMediaStream>
227 DOMMediaStream::CreateSourceStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
228 {
229 nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
230 stream->InitSourceStream(aWindow, aHintContents);
231 return stream.forget();
232 }
234 already_AddRefed<DOMMediaStream>
235 DOMMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow, TrackTypeHints aHintContents)
236 {
237 nsRefPtr<DOMMediaStream> stream = new DOMMediaStream();
238 stream->InitTrackUnionStream(aWindow, aHintContents);
239 return stream.forget();
240 }
242 void
243 DOMMediaStream::SetTrackEnabled(TrackID aTrackID, bool aEnabled)
244 {
245 if (mStream) {
246 mStream->SetTrackEnabled(aTrackID, aEnabled);
247 }
248 }
250 bool
251 DOMMediaStream::CombineWithPrincipal(nsIPrincipal* aPrincipal)
252 {
253 return nsContentUtils::CombineResourcePrincipals(&mPrincipal, aPrincipal);
254 }
256 MediaStreamTrack*
257 DOMMediaStream::CreateDOMTrack(TrackID aTrackID, MediaSegment::Type aType)
258 {
259 MediaStreamTrack* track;
260 switch (aType) {
261 case MediaSegment::AUDIO:
262 track = new AudioStreamTrack(this, aTrackID);
263 mTrackTypesAvailable |= HINT_CONTENTS_AUDIO;
264 break;
265 case MediaSegment::VIDEO:
266 track = new VideoStreamTrack(this, aTrackID);
267 mTrackTypesAvailable |= HINT_CONTENTS_VIDEO;
268 break;
269 default:
270 MOZ_CRASH("Unhandled track type");
271 }
272 mTracks.AppendElement(track);
274 CheckTracksAvailable();
276 return track;
277 }
279 MediaStreamTrack*
280 DOMMediaStream::GetDOMTrackFor(TrackID aTrackID)
281 {
282 for (uint32_t i = 0; i < mTracks.Length(); ++i) {
283 MediaStreamTrack* t = mTracks[i];
284 // We may add streams to our track list that are actually owned by
285 // a different DOMMediaStream. Ignore those.
286 if (t->GetTrackID() == aTrackID && t->GetStream() == this) {
287 return t;
288 }
289 }
290 return nullptr;
291 }
293 void
294 DOMMediaStream::NotifyMediaStreamGraphShutdown()
295 {
296 // No more tracks will ever be added, so just clear these callbacks now
297 // to prevent leaks.
298 mNotifiedOfMediaStreamGraphShutdown = true;
299 mRunOnTracksAvailable.Clear();
301 mConsumersToKeepAlive.Clear();
302 }
304 void
305 DOMMediaStream::NotifyStreamStateChanged()
306 {
307 if (IsFinished()) {
308 mConsumersToKeepAlive.Clear();
309 }
310 }
312 void
313 DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable)
314 {
315 if (mNotifiedOfMediaStreamGraphShutdown) {
316 // No more tracks will ever be added, so just delete the callback now.
317 delete aRunnable;
318 return;
319 }
320 mRunOnTracksAvailable.AppendElement(aRunnable);
321 CheckTracksAvailable();
322 }
324 void
325 DOMMediaStream::CheckTracksAvailable()
326 {
327 if (mTrackTypesAvailable == 0) {
328 return;
329 }
330 nsTArray<nsAutoPtr<OnTracksAvailableCallback> > callbacks;
331 callbacks.SwapElements(mRunOnTracksAvailable);
333 for (uint32_t i = 0; i < callbacks.Length(); ++i) {
334 OnTracksAvailableCallback* cb = callbacks[i];
335 if (~mTrackTypesAvailable & cb->GetExpectedTracks()) {
336 // Some expected tracks not available yet. Try this callback again later.
337 *mRunOnTracksAvailable.AppendElement() = callbacks[i].forget();
338 continue;
339 }
340 cb->NotifyTracksAvailable(this);
341 }
342 }
344 DOMLocalMediaStream::~DOMLocalMediaStream()
345 {
346 if (mStream) {
347 // Make sure Listeners of this stream know it's going away
348 Stop();
349 }
350 }
352 JSObject*
353 DOMLocalMediaStream::WrapObject(JSContext* aCx)
354 {
355 return dom::LocalMediaStreamBinding::Wrap(aCx, this);
356 }
358 void
359 DOMLocalMediaStream::Stop()
360 {
361 if (mStream && mStream->AsSourceStream()) {
362 mStream->AsSourceStream()->EndAllTrackAndFinish();
363 }
364 }
366 already_AddRefed<DOMLocalMediaStream>
367 DOMLocalMediaStream::CreateSourceStream(nsIDOMWindow* aWindow,
368 TrackTypeHints aHintContents)
369 {
370 nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
371 stream->InitSourceStream(aWindow, aHintContents);
372 return stream.forget();
373 }
375 already_AddRefed<DOMLocalMediaStream>
376 DOMLocalMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
377 TrackTypeHints aHintContents)
378 {
379 nsRefPtr<DOMLocalMediaStream> stream = new DOMLocalMediaStream();
380 stream->InitTrackUnionStream(aWindow, aHintContents);
381 return stream.forget();
382 }
384 DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(AudioNode* aNode)
385 : mStreamNode(aNode)
386 {
387 }
389 already_AddRefed<DOMAudioNodeMediaStream>
390 DOMAudioNodeMediaStream::CreateTrackUnionStream(nsIDOMWindow* aWindow,
391 AudioNode* aNode,
392 TrackTypeHints aHintContents)
393 {
394 nsRefPtr<DOMAudioNodeMediaStream> stream = new DOMAudioNodeMediaStream(aNode);
395 stream->InitTrackUnionStream(aWindow, aHintContents);
396 return stream.forget();
397 }