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 #ifndef MOZILLA_MEDIASTREAMGRAPH_H_
7 #define MOZILLA_MEDIASTREAMGRAPH_H_
9 #include "mozilla/Mutex.h"
10 #include "mozilla/LinkedList.h"
11 #include "AudioStream.h"
12 #include "nsTArray.h"
13 #include "nsIRunnable.h"
14 #include "StreamBuffer.h"
15 #include "TimeVarying.h"
16 #include "VideoFrameContainer.h"
17 #include "VideoSegment.h"
18 #include "MainThreadUtils.h"
19 #include "nsAutoRef.h"
20 #include "speex/speex_resampler.h"
21 #include "AudioMixer.h"
22 #include "mozilla/dom/AudioChannelBinding.h"
24 class nsIRunnable;
26 template <>
27 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
28 {
29 public:
30 static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); }
31 };
33 namespace mozilla {
35 class DOMMediaStream;
37 #ifdef PR_LOGGING
38 extern PRLogModuleInfo* gMediaStreamGraphLog;
39 #endif
41 /**
42 * Microseconds relative to the start of the graph timeline.
43 */
44 typedef int64_t GraphTime;
45 const GraphTime GRAPH_TIME_MAX = MEDIA_TIME_MAX;
47 /*
48 * MediaStreamGraph is a framework for synchronized audio/video processing
49 * and playback. It is designed to be used by other browser components such as
50 * HTML media elements, media capture APIs, real-time media streaming APIs,
51 * multitrack media APIs, and advanced audio APIs.
52 *
53 * The MediaStreamGraph uses a dedicated thread to process media --- the media
54 * graph thread. This ensures that we can process media through the graph
55 * without blocking on main-thread activity. The media graph is only modified
56 * on the media graph thread, to ensure graph changes can be processed without
57 * interfering with media processing. All interaction with the media graph
58 * thread is done with message passing.
59 *
60 * APIs that modify the graph or its properties are described as "control APIs".
61 * These APIs are asynchronous; they queue graph changes internally and
62 * those changes are processed all-at-once by the MediaStreamGraph. The
63 * MediaStreamGraph monitors the main thread event loop via nsIAppShell::RunInStableState
64 * to ensure that graph changes from a single event loop task are always
65 * processed all together. Control APIs should only be used on the main thread,
66 * currently; we may be able to relax that later.
67 *
68 * To allow precise synchronization of times in the control API, the
69 * MediaStreamGraph maintains a "media timeline". Control APIs that take or
70 * return times use that timeline. Those times never advance during
71 * an event loop task. This time is returned by MediaStreamGraph::GetCurrentTime().
72 *
73 * Media decoding, audio processing and media playback use thread-safe APIs to
74 * the media graph to ensure they can continue while the main thread is blocked.
75 *
76 * When the graph is changed, we may need to throw out buffered data and
77 * reprocess it. This is triggered automatically by the MediaStreamGraph.
78 */
80 class MediaStreamGraph;
82 /**
83 * This is a base class for media graph thread listener callbacks.
84 * Override methods to be notified of audio or video data or changes in stream
85 * state.
86 *
87 * This can be used by stream recorders or network connections that receive
88 * stream input. It could also be used for debugging.
89 *
90 * All notification methods are called from the media graph thread. Overriders
91 * of these methods are responsible for all synchronization. Beware!
92 * These methods are called without the media graph monitor held, so
93 * reentry into media graph methods is possible, although very much discouraged!
94 * You should do something non-blocking and non-reentrant (e.g. dispatch an
95 * event to some thread) and return.
96 * The listener is not allowed to add/remove any listeners from the stream.
97 *
98 * When a listener is first attached, we guarantee to send a NotifyBlockingChanged
99 * callback to notify of the initial blocking state. Also, if a listener is
100 * attached to a stream that has already finished, we'll call NotifyFinished.
101 */
102 class MediaStreamListener {
103 protected:
104 // Protected destructor, to discourage deletion outside of Release():
105 virtual ~MediaStreamListener() {}
107 public:
108 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener)
110 enum Consumption {
111 CONSUMED,
112 NOT_CONSUMED
113 };
114 /**
115 * Notify that the stream is hooked up and we'd like to start or stop receiving
116 * data on it. Only fires on SourceMediaStreams.
117 * The initial state is assumed to be NOT_CONSUMED.
118 */
119 virtual void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming) {}
121 /**
122 * When a SourceMediaStream has pulling enabled, and the MediaStreamGraph
123 * control loop is ready to pull, this gets called. A NotifyPull implementation
124 * is allowed to call the SourceMediaStream methods that alter track
125 * data. It is not allowed to make other MediaStream API calls, including
126 * calls to add or remove MediaStreamListeners. It is not allowed to block
127 * for any length of time.
128 * aDesiredTime is the stream time we would like to get data up to. Data
129 * beyond this point will not be played until NotifyPull runs again, so there's
130 * not much point in providing it. Note that if the stream is blocked for
131 * some reason, then data before aDesiredTime may not be played immediately.
132 */
133 virtual void NotifyPull(MediaStreamGraph* aGraph, StreamTime aDesiredTime) {}
135 enum Blocking {
136 BLOCKED,
137 UNBLOCKED
138 };
139 /**
140 * Notify that the blocking status of the stream changed. The initial state
141 * is assumed to be BLOCKED.
142 */
143 virtual void NotifyBlockingChanged(MediaStreamGraph* aGraph, Blocking aBlocked) {}
145 /**
146 * Notify that the stream has data in each track
147 * for the stream's current time. Once this state becomes true, it will
148 * always be true since we block stream time from progressing to times where
149 * there isn't data in each track.
150 */
151 virtual void NotifyHasCurrentData(MediaStreamGraph* aGraph) {}
153 /**
154 * Notify that the stream output is advancing. aCurrentTime is the graph's
155 * current time. MediaStream::GraphTimeToStreamTime can be used to get the
156 * stream time.
157 */
158 virtual void NotifyOutput(MediaStreamGraph* aGraph, GraphTime aCurrentTime) {}
160 /**
161 * Notify that the stream finished.
162 */
163 virtual void NotifyFinished(MediaStreamGraph* aGraph) {}
165 /**
166 * Notify that your listener has been removed, either due to RemoveListener(),
167 * or due to the stream being destroyed. You will get no further notifications.
168 */
169 virtual void NotifyRemoved(MediaStreamGraph* aGraph) {}
171 enum {
172 TRACK_EVENT_CREATED = 0x01,
173 TRACK_EVENT_ENDED = 0x02
174 };
175 /**
176 * Notify that changes to one of the stream tracks have been queued.
177 * aTrackEvents can be any combination of TRACK_EVENT_CREATED and
178 * TRACK_EVENT_ENDED. aQueuedMedia is the data being added to the track
179 * at aTrackOffset (relative to the start of the stream).
180 */
181 virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
182 TrackRate aTrackRate,
183 TrackTicks aTrackOffset,
184 uint32_t aTrackEvents,
185 const MediaSegment& aQueuedMedia) {}
186 };
188 /**
189 * This is a base class for media graph thread listener direct callbacks
190 * from within AppendToTrack(). Note that your regular listener will
191 * still get NotifyQueuedTrackChanges() callbacks from the MSG thread, so
192 * you must be careful to ignore them if AddDirectListener was successful.
193 */
194 class MediaStreamDirectListener : public MediaStreamListener
195 {
196 public:
197 virtual ~MediaStreamDirectListener() {}
199 /*
200 * This will be called on any MediaStreamDirectListener added to a
201 * a SourceMediaStream when AppendToTrack() is called. The MediaSegment
202 * will be the RawSegment (unresampled) if available in AppendToTrack().
203 * Note that NotifyQueuedTrackChanges() calls will also still occur.
204 */
205 virtual void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
206 TrackRate aTrackRate,
207 TrackTicks aTrackOffset,
208 uint32_t aTrackEvents,
209 const MediaSegment& aMedia) {}
210 };
212 /**
213 * This is a base class for main-thread listener callbacks.
214 * This callback is invoked on the main thread when the main-thread-visible
215 * state of a stream has changed.
216 *
217 * These methods are called with the media graph monitor held, so
218 * reentry into general media graph methods is not possible.
219 * You should do something non-blocking and non-reentrant (e.g. dispatch an
220 * event) and return. DispatchFromMainThreadAfterNextStreamStateUpdate
221 * would be a good choice.
222 * The listener is allowed to synchronously remove itself from the stream, but
223 * not add or remove any other listeners.
224 */
225 class MainThreadMediaStreamListener {
226 public:
227 virtual void NotifyMainThreadStateChanged() = 0;
228 };
230 /**
231 * Helper struct used to keep track of memory usage by AudioNodes.
232 */
233 struct AudioNodeSizes
234 {
235 size_t mDomNode;
236 size_t mStream;
237 size_t mEngine;
238 nsCString mNodeType;
239 };
241 class MediaStreamGraphImpl;
242 class SourceMediaStream;
243 class ProcessedMediaStream;
244 class MediaInputPort;
245 class AudioNodeEngine;
246 class AudioNodeExternalInputStream;
247 class AudioNodeStream;
248 struct AudioChunk;
250 /**
251 * A stream of synchronized audio and video data. All (not blocked) streams
252 * progress at the same rate --- "real time". Streams cannot seek. The only
253 * operation readers can perform on a stream is to read the next data.
254 *
255 * Consumers of a stream can be reading from it at different offsets, but that
256 * should only happen due to the order in which consumers are being run.
257 * Those offsets must not diverge in the long term, otherwise we would require
258 * unbounded buffering.
259 *
260 * Streams can be in a "blocked" state. While blocked, a stream does not
261 * produce data. A stream can be explicitly blocked via the control API,
262 * or implicitly blocked by whatever's generating it (e.g. an underrun in the
263 * source resource), or implicitly blocked because something consuming it
264 * blocks, or implicitly because it has finished.
265 *
266 * A stream can be in a "finished" state. "Finished" streams are permanently
267 * blocked.
268 *
269 * Transitions into and out of the "blocked" and "finished" states are managed
270 * by the MediaStreamGraph on the media graph thread.
271 *
272 * We buffer media data ahead of the consumers' reading offsets. It is possible
273 * to have buffered data but still be blocked.
274 *
275 * Any stream can have its audio and video playing when requested. The media
276 * stream graph plays audio by constructing audio output streams as necessary.
277 * Video is played by setting video frames into an VideoFrameContainer at the right
278 * time. To ensure video plays in sync with audio, make sure that the same
279 * stream is playing both the audio and video.
280 *
281 * The data in a stream is managed by StreamBuffer. It consists of a set of
282 * tracks of various types that can start and end over time.
283 *
284 * Streams are explicitly managed. The client creates them via
285 * MediaStreamGraph::CreateInput/ProcessedMediaStream, and releases them by calling
286 * Destroy() when no longer needed (actual destruction will be deferred).
287 * The actual object is owned by the MediaStreamGraph. The basic idea is that
288 * main thread objects will keep Streams alive as long as necessary (using the
289 * cycle collector to clean up whenever needed).
290 *
291 * We make them refcounted only so that stream-related messages with MediaStream*
292 * pointers can be sent to the main thread safely.
293 *
294 * The lifetimes of MediaStreams are controlled from the main thread.
295 * For MediaStreams exposed to the DOM, the lifetime is controlled by the DOM
296 * wrapper; the DOM wrappers own their associated MediaStreams. When a DOM
297 * wrapper is destroyed, it sends a Destroy message for the associated
298 * MediaStream and clears its reference (the last main-thread reference to
299 * the object). When the Destroy message is processed on the graph
300 * manager thread we immediately release the affected objects (disentangling them
301 * from other objects as necessary).
302 *
303 * This could cause problems for media processing if a MediaStream is
304 * destroyed while a downstream MediaStream is still using it. Therefore
305 * the DOM wrappers must keep upstream MediaStreams alive as long as they
306 * could be being used in the media graph.
307 *
308 * At any time, however, a set of MediaStream wrappers could be
309 * collected via cycle collection. Destroy messages will be sent
310 * for those objects in arbitrary order and the MediaStreamGraph has to be able
311 * to handle this.
312 */
313 class MediaStream : public mozilla::LinkedListElement<MediaStream> {
314 public:
315 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
317 MediaStream(DOMMediaStream* aWrapper);
319 protected:
320 // Protected destructor, to discourage deletion outside of Release():
321 virtual ~MediaStream()
322 {
323 MOZ_COUNT_DTOR(MediaStream);
324 NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
325 NS_ASSERTION(mMainThreadListeners.IsEmpty(),
326 "All main thread listeners should have been removed");
327 }
329 public:
330 /**
331 * Returns the graph that owns this stream.
332 */
333 MediaStreamGraphImpl* GraphImpl();
334 MediaStreamGraph* Graph();
335 /**
336 * Sets the graph that owns this stream. Should only be called once.
337 */
338 void SetGraphImpl(MediaStreamGraphImpl* aGraph);
339 void SetGraphImpl(MediaStreamGraph* aGraph);
341 // Control API.
342 // Since a stream can be played multiple ways, we need to combine independent
343 // volume settings. The aKey parameter is used to keep volume settings
344 // separate. Since the stream is always playing the same contents, only
345 // a single audio output stream is used; the volumes are combined.
346 // Currently only the first enabled audio track is played.
347 // XXX change this so all enabled audio tracks are mixed and played.
348 virtual void AddAudioOutput(void* aKey);
349 virtual void SetAudioOutputVolume(void* aKey, float aVolume);
350 virtual void RemoveAudioOutput(void* aKey);
351 // Since a stream can be played multiple ways, we need to be able to
352 // play to multiple VideoFrameContainers.
353 // Only the first enabled video track is played.
354 virtual void AddVideoOutput(VideoFrameContainer* aContainer);
355 virtual void RemoveVideoOutput(VideoFrameContainer* aContainer);
356 // Explicitly block. Useful for example if a media element is pausing
357 // and we need to stop its stream emitting its buffered data.
358 virtual void ChangeExplicitBlockerCount(int32_t aDelta);
359 // Events will be dispatched by calling methods of aListener.
360 virtual void AddListener(MediaStreamListener* aListener);
361 virtual void RemoveListener(MediaStreamListener* aListener);
362 // A disabled track has video replaced by black, and audio replaced by
363 // silence.
364 void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
365 // Events will be dispatched by calling methods of aListener. It is the
366 // responsibility of the caller to remove aListener before it is destroyed.
367 void AddMainThreadListener(MainThreadMediaStreamListener* aListener)
368 {
369 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
370 mMainThreadListeners.AppendElement(aListener);
371 }
372 // It's safe to call this even if aListener is not currently a listener;
373 // the call will be ignored.
374 void RemoveMainThreadListener(MainThreadMediaStreamListener* aListener)
375 {
376 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
377 mMainThreadListeners.RemoveElement(aListener);
378 }
379 /**
380 * Ensure a runnable will run on the main thread after running all pending
381 * updates that were sent from the graph thread or will be sent before the
382 * graph thread receives the next graph update.
383 *
384 * If the graph has been shut down or destroyed, then the runnable will be
385 * dispatched to the event queue immediately. If the graph is non-realtime
386 * and has not started, then the runnable will be run
387 * synchronously/immediately. (There are no pending updates in these
388 * situations.)
389 *
390 * Main thread only.
391 */
392 void RunAfterPendingUpdates(nsRefPtr<nsIRunnable> aRunnable);
394 // Signal that the client is done with this MediaStream. It will be deleted later.
395 virtual void Destroy();
396 // Returns the main-thread's view of how much data has been processed by
397 // this stream.
398 StreamTime GetCurrentTime()
399 {
400 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
401 return mMainThreadCurrentTime;
402 }
403 // Return the main thread's view of whether this stream has finished.
404 bool IsFinished()
405 {
406 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
407 return mMainThreadFinished;
408 }
409 bool IsDestroyed()
410 {
411 NS_ASSERTION(NS_IsMainThread(), "Call only on main thread");
412 return mMainThreadDestroyed;
413 }
415 friend class MediaStreamGraphImpl;
416 friend class MediaInputPort;
417 friend class AudioNodeExternalInputStream;
419 virtual SourceMediaStream* AsSourceStream() { return nullptr; }
420 virtual ProcessedMediaStream* AsProcessedStream() { return nullptr; }
421 virtual AudioNodeStream* AsAudioNodeStream() { return nullptr; }
423 // media graph thread only
424 void Init();
425 // These Impl methods perform the core functionality of the control methods
426 // above, on the media graph thread.
427 /**
428 * Stop all stream activity and disconnect it from all inputs and outputs.
429 * This must be idempotent.
430 */
431 virtual void DestroyImpl();
432 StreamTime GetBufferEnd() { return mBuffer.GetEnd(); }
433 #ifdef DEBUG
434 void DumpTrackInfo() { return mBuffer.DumpTrackInfo(); }
435 #endif
436 void SetAudioOutputVolumeImpl(void* aKey, float aVolume);
437 void AddAudioOutputImpl(void* aKey)
438 {
439 mAudioOutputs.AppendElement(AudioOutput(aKey));
440 }
441 void RemoveAudioOutputImpl(void* aKey);
442 void AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer)
443 {
444 *mVideoOutputs.AppendElement() = aContainer;
445 }
446 void RemoveVideoOutputImpl(VideoFrameContainer* aContainer)
447 {
448 mVideoOutputs.RemoveElement(aContainer);
449 }
450 void ChangeExplicitBlockerCountImpl(GraphTime aTime, int32_t aDelta)
451 {
452 mExplicitBlockerCount.SetAtAndAfter(aTime, mExplicitBlockerCount.GetAt(aTime) + aDelta);
453 }
454 void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
455 void RemoveListenerImpl(MediaStreamListener* aListener);
456 void RemoveAllListenersImpl();
457 void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
458 /**
459 * Returns true when this stream requires the contents of its inputs even if
460 * its own outputs are not being consumed. This is used to signal inputs to
461 * this stream that they are being consumed; when they're not being consumed,
462 * we make some optimizations.
463 */
464 virtual bool IsIntrinsicallyConsumed() const
465 {
466 return !mAudioOutputs.IsEmpty() || !mVideoOutputs.IsEmpty();
467 }
469 void AddConsumer(MediaInputPort* aPort)
470 {
471 mConsumers.AppendElement(aPort);
472 }
473 void RemoveConsumer(MediaInputPort* aPort)
474 {
475 mConsumers.RemoveElement(aPort);
476 }
477 uint32_t ConsumerCount()
478 {
479 return mConsumers.Length();
480 }
481 const StreamBuffer& GetStreamBuffer() { return mBuffer; }
482 GraphTime GetStreamBufferStartTime() { return mBufferStartTime; }
483 /**
484 * Convert graph time to stream time. aTime must be <= mStateComputedTime
485 * to ensure we know exactly how much time this stream will be blocked during
486 * the interval.
487 */
488 StreamTime GraphTimeToStreamTime(GraphTime aTime);
489 /**
490 * Convert graph time to stream time. aTime can be > mStateComputedTime,
491 * in which case we optimistically assume the stream will not be blocked
492 * after mStateComputedTime.
493 */
494 StreamTime GraphTimeToStreamTimeOptimistic(GraphTime aTime);
495 /**
496 * Convert stream time to graph time. The result can be > mStateComputedTime,
497 * in which case we did the conversion optimistically assuming the stream
498 * will not be blocked after mStateComputedTime.
499 */
500 GraphTime StreamTimeToGraphTime(StreamTime aTime);
501 bool IsFinishedOnGraphThread() { return mFinished; }
502 void FinishOnGraphThread();
503 /**
504 * Identify which graph update index we are currently processing.
505 */
506 int64_t GetProcessingGraphUpdateIndex();
508 bool HasCurrentData() { return mHasCurrentData; }
510 StreamBuffer::Track* EnsureTrack(TrackID aTrack, TrackRate aSampleRate);
512 void ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment = nullptr);
514 DOMMediaStream* GetWrapper()
515 {
516 NS_ASSERTION(NS_IsMainThread(), "Only use DOMMediaStream on main thread");
517 return mWrapper;
518 }
520 // Return true if the main thread needs to observe updates from this stream.
521 virtual bool MainThreadNeedsUpdates() const
522 {
523 return true;
524 }
526 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const;
527 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
529 void SetAudioChannelType(dom::AudioChannel aType) { mAudioChannelType = aType; }
531 protected:
532 virtual void AdvanceTimeVaryingValuesToCurrentTime(GraphTime aCurrentTime, GraphTime aBlockedTime)
533 {
534 mBufferStartTime += aBlockedTime;
535 mGraphUpdateIndices.InsertTimeAtStart(aBlockedTime);
536 mGraphUpdateIndices.AdvanceCurrentTime(aCurrentTime);
537 mExplicitBlockerCount.AdvanceCurrentTime(aCurrentTime);
539 mBuffer.ForgetUpTo(aCurrentTime - mBufferStartTime);
540 }
542 // This state is all initialized on the main thread but
543 // otherwise modified only on the media graph thread.
545 // Buffered data. The start of the buffer corresponds to mBufferStartTime.
546 // Conceptually the buffer contains everything this stream has ever played,
547 // but we forget some prefix of the buffered data to bound the space usage.
548 StreamBuffer mBuffer;
549 // The time when the buffered data could be considered to have started playing.
550 // This increases over time to account for time the stream was blocked before
551 // mCurrentTime.
552 GraphTime mBufferStartTime;
554 // Client-set volume of this stream
555 struct AudioOutput {
556 AudioOutput(void* aKey) : mKey(aKey), mVolume(1.0f) {}
557 void* mKey;
558 float mVolume;
559 };
560 nsTArray<AudioOutput> mAudioOutputs;
561 nsTArray<nsRefPtr<VideoFrameContainer> > mVideoOutputs;
562 // We record the last played video frame to avoid redundant setting
563 // of the current video frame.
564 VideoFrame mLastPlayedVideoFrame;
565 // The number of times this stream has been explicitly blocked by the control
566 // API, minus the number of times it has been explicitly unblocked.
567 TimeVarying<GraphTime,uint32_t,0> mExplicitBlockerCount;
568 nsTArray<nsRefPtr<MediaStreamListener> > mListeners;
569 nsTArray<MainThreadMediaStreamListener*> mMainThreadListeners;
570 nsTArray<TrackID> mDisabledTrackIDs;
572 // Precomputed blocking status (over GraphTime).
573 // This is only valid between the graph's mCurrentTime and
574 // mStateComputedTime. The stream is considered to have
575 // not been blocked before mCurrentTime (its mBufferStartTime is increased
576 // as necessary to account for that time instead) --- this avoids us having to
577 // record the entire history of the stream's blocking-ness in mBlocked.
578 TimeVarying<GraphTime,bool,5> mBlocked;
579 // Maps graph time to the graph update that affected this stream at that time
580 TimeVarying<GraphTime,int64_t,0> mGraphUpdateIndices;
582 // MediaInputPorts to which this is connected
583 nsTArray<MediaInputPort*> mConsumers;
585 // Where audio output is going. There is one AudioOutputStream per
586 // audio track.
587 struct AudioOutputStream {
588 // When we started audio playback for this track.
589 // Add mStream->GetPosition() to find the current audio playback position.
590 GraphTime mAudioPlaybackStartTime;
591 // Amount of time that we've wanted to play silence because of the stream
592 // blocking.
593 MediaTime mBlockedAudioTime;
594 // Last tick written to the audio output.
595 TrackTicks mLastTickWritten;
596 RefPtr<AudioStream> mStream;
597 TrackID mTrackID;
599 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
600 {
601 size_t amount = 0;
602 amount += mStream->SizeOfIncludingThis(aMallocSizeOf);
603 return amount;
604 }
605 };
606 nsTArray<AudioOutputStream> mAudioOutputStreams;
608 /**
609 * When true, this means the stream will be finished once all
610 * buffered data has been consumed.
611 */
612 bool mFinished;
613 /**
614 * When true, mFinished is true and we've played all the data in this stream
615 * and fired NotifyFinished notifications.
616 */
617 bool mNotifiedFinished;
618 /**
619 * When true, the last NotifyBlockingChanged delivered to the listeners
620 * indicated that the stream is blocked.
621 */
622 bool mNotifiedBlocked;
623 /**
624 * True if some data can be present by this stream if/when it's unblocked.
625 * Set by the stream itself on the MediaStreamGraph thread. Only changes
626 * from false to true once a stream has data, since we won't
627 * unblock it until there's more data.
628 */
629 bool mHasCurrentData;
630 /**
631 * True if mHasCurrentData is true and we've notified listeners.
632 */
633 bool mNotifiedHasCurrentData;
635 // Temporary data for ordering streams by dependency graph
636 bool mHasBeenOrdered;
637 bool mIsOnOrderingStack;
638 // True if the stream is being consumed (i.e. has track data being played,
639 // or is feeding into some stream that is being consumed).
640 bool mIsConsumed;
641 // Temporary data for computing blocking status of streams
642 // True if we've added this stream to the set of streams we're computing
643 // blocking for.
644 bool mInBlockingSet;
645 // True if this stream should be blocked in this phase.
646 bool mBlockInThisPhase;
648 // This state is only used on the main thread.
649 DOMMediaStream* mWrapper;
650 // Main-thread views of state
651 StreamTime mMainThreadCurrentTime;
652 bool mMainThreadFinished;
653 bool mMainThreadDestroyed;
655 // Our media stream graph. null if destroyed on the graph thread.
656 MediaStreamGraphImpl* mGraph;
658 dom::AudioChannel mAudioChannelType;
659 };
661 /**
662 * This is a stream into which a decoder can write audio and video.
663 *
664 * Audio and video can be written on any thread, but you probably want to
665 * always write from the same thread to avoid unexpected interleavings.
666 */
667 class SourceMediaStream : public MediaStream {
668 public:
669 SourceMediaStream(DOMMediaStream* aWrapper) :
670 MediaStream(aWrapper),
671 mLastConsumptionState(MediaStreamListener::NOT_CONSUMED),
672 mMutex("mozilla::media::SourceMediaStream"),
673 mUpdateKnownTracksTime(0),
674 mPullEnabled(false),
675 mUpdateFinished(false)
676 {}
678 virtual SourceMediaStream* AsSourceStream() { return this; }
680 // Media graph thread only
681 virtual void DestroyImpl();
683 // Call these on any thread.
684 /**
685 * Enable or disable pulling. When pulling is enabled, NotifyPull
686 * gets called on MediaStreamListeners for this stream during the
687 * MediaStreamGraph control loop. Pulling is initially disabled.
688 * Due to unavoidable race conditions, after a call to SetPullEnabled(false)
689 * it is still possible for a NotifyPull to occur.
690 */
691 void SetPullEnabled(bool aEnabled);
693 void AddDirectListener(MediaStreamDirectListener* aListener);
694 void RemoveDirectListener(MediaStreamDirectListener* aListener);
696 /**
697 * Add a new track to the stream starting at the given base time (which
698 * must be greater than or equal to the last time passed to
699 * AdvanceKnownTracksTime). Takes ownership of aSegment. aSegment should
700 * contain data starting after aStart.
701 */
702 void AddTrack(TrackID aID, TrackRate aRate, TrackTicks aStart,
703 MediaSegment* aSegment);
705 /**
706 * Append media data to a track. Ownership of aSegment remains with the caller,
707 * but aSegment is emptied.
708 * Returns false if the data was not appended because no such track exists
709 * or the stream was already finished.
710 */
711 bool AppendToTrack(TrackID aID, MediaSegment* aSegment, MediaSegment *aRawSegment = nullptr);
712 /**
713 * Returns true if the buffer currently has enough data.
714 * Returns false if there isn't enough data or if no such track exists.
715 */
716 bool HaveEnoughBuffered(TrackID aID);
717 /**
718 * Ensures that aSignalRunnable will be dispatched to aSignalThread
719 * when we don't have enough buffered data in the track (which could be
720 * immediately). Will dispatch the runnable immediately if the track
721 * does not exist. No op if a runnable is already present for this track.
722 */
723 void DispatchWhenNotEnoughBuffered(TrackID aID,
724 nsIEventTarget* aSignalThread, nsIRunnable* aSignalRunnable);
725 /**
726 * Indicate that a track has ended. Do not do any more API calls
727 * affecting this track.
728 * Ignored if the track does not exist.
729 */
730 void EndTrack(TrackID aID);
731 /**
732 * Indicate that no tracks will be added starting before time aKnownTime.
733 * aKnownTime must be >= its value at the last call to AdvanceKnownTracksTime.
734 */
735 void AdvanceKnownTracksTime(StreamTime aKnownTime);
736 /**
737 * Indicate that this stream should enter the "finished" state. All tracks
738 * must have been ended via EndTrack. The finish time of the stream is
739 * when all tracks have ended.
740 */
741 void FinishWithLockHeld();
742 void Finish()
743 {
744 MutexAutoLock lock(mMutex);
745 FinishWithLockHeld();
746 }
748 // Overriding allows us to hold the mMutex lock while changing the track enable status
749 void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled) {
750 MutexAutoLock lock(mMutex);
751 MediaStream::SetTrackEnabledImpl(aTrackID, aEnabled);
752 }
754 /**
755 * End all tracks and Finish() this stream. Used to voluntarily revoke access
756 * to a LocalMediaStream.
757 */
758 void EndAllTrackAndFinish();
760 /**
761 * Note: Only call from Media Graph thread (eg NotifyPull)
762 *
763 * Returns amount of time (data) that is currently buffered in the track,
764 * assuming playout via PlayAudio or via a TrackUnion - note that
765 * NotifyQueuedTrackChanges() on a SourceMediaStream will occur without
766 * any "extra" buffering, but NotifyQueued TrackChanges() on a TrackUnion
767 * will be buffered.
768 */
769 TrackTicks GetBufferedTicks(TrackID aID);
771 void RegisterForAudioMixing();
773 // XXX need a Reset API
775 friend class MediaStreamGraphImpl;
777 protected:
778 struct ThreadAndRunnable {
779 void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable)
780 {
781 mTarget = aTarget;
782 mRunnable = aRunnable;
783 }
785 nsCOMPtr<nsIEventTarget> mTarget;
786 nsCOMPtr<nsIRunnable> mRunnable;
787 };
788 enum TrackCommands {
789 TRACK_CREATE = MediaStreamListener::TRACK_EVENT_CREATED,
790 TRACK_END = MediaStreamListener::TRACK_EVENT_ENDED
791 };
792 /**
793 * Data for each track that hasn't ended.
794 */
795 struct TrackData {
796 TrackID mID;
797 // Sample rate of the input data.
798 TrackRate mInputRate;
799 // Sample rate of the output data, always equal to the sample rate of the
800 // graph.
801 TrackRate mOutputRate;
802 // Resampler if the rate of the input track does not match the
803 // MediaStreamGraph's.
804 nsAutoRef<SpeexResamplerState> mResampler;
805 TrackTicks mStart;
806 // Each time the track updates are flushed to the media graph thread,
807 // this is cleared.
808 uint32_t mCommands;
809 // Each time the track updates are flushed to the media graph thread,
810 // the segment buffer is emptied.
811 nsAutoPtr<MediaSegment> mData;
812 nsTArray<ThreadAndRunnable> mDispatchWhenNotEnough;
813 bool mHaveEnough;
814 };
816 bool NeedsMixing();
818 void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment);
820 TrackData* FindDataForTrack(TrackID aID)
821 {
822 for (uint32_t i = 0; i < mUpdateTracks.Length(); ++i) {
823 if (mUpdateTracks[i].mID == aID) {
824 return &mUpdateTracks[i];
825 }
826 }
827 return nullptr;
828 }
830 /**
831 * Notify direct consumers of new data to one of the stream tracks.
832 * The data doesn't have to be resampled (though it may be). This is called
833 * from AppendToTrack on the thread providing the data, and will call
834 * the Listeners on this thread.
835 */
836 void NotifyDirectConsumers(TrackData *aTrack,
837 MediaSegment *aSegment);
839 // Media stream graph thread only
840 MediaStreamListener::Consumption mLastConsumptionState;
842 // This must be acquired *before* MediaStreamGraphImpl's lock, if they are
843 // held together.
844 Mutex mMutex;
845 // protected by mMutex
846 StreamTime mUpdateKnownTracksTime;
847 nsTArray<TrackData> mUpdateTracks;
848 nsTArray<nsRefPtr<MediaStreamDirectListener> > mDirectListeners;
849 bool mPullEnabled;
850 bool mUpdateFinished;
851 bool mNeedsMixing;
852 };
854 /**
855 * Represents a connection between a ProcessedMediaStream and one of its
856 * input streams.
857 * We make these refcounted so that stream-related messages with MediaInputPort*
858 * pointers can be sent to the main thread safely.
859 *
860 * When a port's source or destination stream dies, the stream's DestroyImpl
861 * calls MediaInputPort::Disconnect to disconnect the port from
862 * the source and destination streams.
863 *
864 * The lifetimes of MediaInputPort are controlled from the main thread.
865 * The media graph adds a reference to the port. When a MediaInputPort is no
866 * longer needed, main-thread code sends a Destroy message for the port and
867 * clears its reference (the last main-thread reference to the object). When
868 * the Destroy message is processed on the graph manager thread we disconnect
869 * the port and drop the graph's reference, destroying the object.
870 */
871 class MediaInputPort MOZ_FINAL {
872 private:
873 // Do not call this constructor directly. Instead call aDest->AllocateInputPort.
874 MediaInputPort(MediaStream* aSource, ProcessedMediaStream* aDest,
875 uint32_t aFlags, uint16_t aInputNumber,
876 uint16_t aOutputNumber)
877 : mSource(aSource)
878 , mDest(aDest)
879 , mFlags(aFlags)
880 , mInputNumber(aInputNumber)
881 , mOutputNumber(aOutputNumber)
882 , mGraph(nullptr)
883 {
884 MOZ_COUNT_CTOR(MediaInputPort);
885 }
887 // Private destructor, to discourage deletion outside of Release():
888 ~MediaInputPort()
889 {
890 MOZ_COUNT_DTOR(MediaInputPort);
891 }
893 public:
894 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaInputPort)
896 /**
897 * The FLAG_BLOCK_INPUT and FLAG_BLOCK_OUTPUT flags can be used to control
898 * exactly how the blocking statuses of the input and output streams affect
899 * each other.
900 */
901 enum {
902 // When set, blocking on the output stream forces blocking on the input
903 // stream.
904 FLAG_BLOCK_INPUT = 0x01,
905 // When set, blocking on the input stream forces blocking on the output
906 // stream.
907 FLAG_BLOCK_OUTPUT = 0x02
908 };
910 // Called on graph manager thread
911 // Do not call these from outside MediaStreamGraph.cpp!
912 void Init();
913 // Called during message processing to trigger removal of this stream.
914 void Disconnect();
916 // Control API
917 /**
918 * Disconnects and destroys the port. The caller must not reference this
919 * object again.
920 */
921 void Destroy();
923 // Any thread
924 MediaStream* GetSource() { return mSource; }
925 ProcessedMediaStream* GetDestination() { return mDest; }
927 uint16_t InputNumber() const { return mInputNumber; }
928 uint16_t OutputNumber() const { return mOutputNumber; }
930 // Call on graph manager thread
931 struct InputInterval {
932 GraphTime mStart;
933 GraphTime mEnd;
934 bool mInputIsBlocked;
935 };
936 // Find the next time interval starting at or after aTime during which
937 // mDest is not blocked and mSource's blocking status does not change.
938 InputInterval GetNextInputInterval(GraphTime aTime);
940 /**
941 * Returns the graph that owns this port.
942 */
943 MediaStreamGraphImpl* GraphImpl();
944 MediaStreamGraph* Graph();
945 /**
946 * Sets the graph that owns this stream. Should only be called once.
947 */
948 void SetGraphImpl(MediaStreamGraphImpl* aGraph);
950 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
951 {
952 size_t amount = 0;
954 // Not owned:
955 // - mSource
956 // - mDest
957 // - mGraph
958 return amount;
959 }
961 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
962 {
963 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
964 }
966 private:
967 friend class MediaStreamGraphImpl;
968 friend class MediaStream;
969 friend class ProcessedMediaStream;
970 // Never modified after Init()
971 MediaStream* mSource;
972 ProcessedMediaStream* mDest;
973 uint32_t mFlags;
974 // The input and output numbers are optional, and are currently only used by
975 // Web Audio.
976 const uint16_t mInputNumber;
977 const uint16_t mOutputNumber;
979 // Our media stream graph
980 MediaStreamGraphImpl* mGraph;
981 };
983 /**
984 * This stream processes zero or more input streams in parallel to produce
985 * its output. The details of how the output is produced are handled by
986 * subclasses overriding the ProcessInput method.
987 */
988 class ProcessedMediaStream : public MediaStream {
989 public:
990 ProcessedMediaStream(DOMMediaStream* aWrapper)
991 : MediaStream(aWrapper), mAutofinish(false), mInCycle(false)
992 {}
994 // Control API.
995 /**
996 * Allocates a new input port attached to source aStream.
997 * This stream can be removed by calling MediaInputPort::Remove().
998 */
999 already_AddRefed<MediaInputPort> AllocateInputPort(MediaStream* aStream,
1000 uint32_t aFlags = 0,
1001 uint16_t aInputNumber = 0,
1002 uint16_t aOutputNumber = 0);
1003 /**
1004 * Force this stream into the finished state.
1005 */
1006 void Finish();
1007 /**
1008 * Set the autofinish flag on this stream (defaults to false). When this flag
1009 * is set, and all input streams are in the finished state (including if there
1010 * are no input streams), this stream automatically enters the finished state.
1011 */
1012 void SetAutofinish(bool aAutofinish);
1014 virtual ProcessedMediaStream* AsProcessedStream() { return this; }
1016 friend class MediaStreamGraphImpl;
1018 // Do not call these from outside MediaStreamGraph.cpp!
1019 virtual void AddInput(MediaInputPort* aPort);
1020 virtual void RemoveInput(MediaInputPort* aPort)
1021 {
1022 mInputs.RemoveElement(aPort);
1023 }
1024 bool HasInputPort(MediaInputPort* aPort)
1025 {
1026 return mInputs.Contains(aPort);
1027 }
1028 uint32_t InputPortCount()
1029 {
1030 return mInputs.Length();
1031 }
1032 virtual void DestroyImpl();
1033 /**
1034 * This gets called after we've computed the blocking states for all
1035 * streams (mBlocked is up to date up to mStateComputedTime).
1036 * Also, we've produced output for all streams up to this one. If this stream
1037 * is not in a cycle, then all its source streams have produced data.
1038 * Generate output from aFrom to aTo.
1039 * This will be called on streams that have finished. Most stream types should
1040 * just return immediately if IsFinishedOnGraphThread(), but some may wish to
1041 * update internal state (see AudioNodeStream).
1042 * ProcessInput is allowed to call FinishOnGraphThread only if ALLOW_FINISH
1043 * is in aFlags. (This flag will be set when aTo >= mStateComputedTime, i.e.
1044 * when we've producing the last block of data we need to produce.) Otherwise
1045 * we can get into a situation where we've determined the stream should not
1046 * block before mStateComputedTime, but the stream finishes before
1047 * mStateComputedTime, violating the invariant that finished streams are blocked.
1048 */
1049 enum {
1050 ALLOW_FINISH = 0x01
1051 };
1052 virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) = 0;
1053 void SetAutofinishImpl(bool aAutofinish) { mAutofinish = aAutofinish; }
1055 /**
1056 * Forward SetTrackEnabled() to the input MediaStream(s) and translate the ID
1057 */
1058 virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) {};
1060 bool InCycle() const { return mInCycle; }
1062 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
1063 {
1064 size_t amount = MediaStream::SizeOfExcludingThis(aMallocSizeOf);
1065 // Not owned:
1066 // - mInputs elements
1067 amount += mInputs.SizeOfExcludingThis(aMallocSizeOf);
1068 return amount;
1069 }
1071 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE
1072 {
1073 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
1074 }
1076 protected:
1077 // This state is all accessed only on the media graph thread.
1079 // The list of all inputs that are currently enabled or waiting to be enabled.
1080 nsTArray<MediaInputPort*> mInputs;
1081 bool mAutofinish;
1082 // True if and only if this stream is in a cycle.
1083 // Updated by MediaStreamGraphImpl::UpdateStreamOrder.
1084 bool mInCycle;
1085 };
1087 /**
1088 * Initially, at least, we will have a singleton MediaStreamGraph per
1089 * process. Each OfflineAudioContext object creates its own MediaStreamGraph
1090 * object too.
1091 */
1092 class MediaStreamGraph {
1093 public:
1094 // We ensure that the graph current time advances in multiples of
1095 // IdealAudioBlockSize()/AudioStream::PreferredSampleRate(). A stream that
1096 // never blocks and has a track with the ideal audio rate will produce audio
1097 // in multiples of the block size.
1099 // Main thread only
1100 static MediaStreamGraph* GetInstance();
1101 static MediaStreamGraph* CreateNonRealtimeInstance(TrackRate aSampleRate);
1102 // Idempotent
1103 static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
1105 // Control API.
1106 /**
1107 * Create a stream that a media decoder (or some other source of
1108 * media data, such as a camera) can write to.
1109 */
1110 SourceMediaStream* CreateSourceStream(DOMMediaStream* aWrapper);
1111 /**
1112 * Create a stream that will form the union of the tracks of its input
1113 * streams.
1114 * A TrackUnionStream contains all the tracks of all its input streams.
1115 * Adding a new input stream makes that stream's tracks immediately appear as new
1116 * tracks starting at the time the input stream was added.
1117 * Removing an input stream makes the output tracks corresponding to the
1118 * removed tracks immediately end.
1119 * For each added track, the track ID of the output track is the track ID
1120 * of the input track or one plus the maximum ID of all previously added
1121 * tracks, whichever is greater.
1122 * TODO at some point we will probably need to add API to select
1123 * particular tracks of each input stream.
1124 */
1125 ProcessedMediaStream* CreateTrackUnionStream(DOMMediaStream* aWrapper);
1126 // Internal AudioNodeStreams can only pass their output to another
1127 // AudioNode, whereas external AudioNodeStreams can pass their output
1128 // to an nsAudioStream for playback.
1129 enum AudioNodeStreamKind { SOURCE_STREAM, INTERNAL_STREAM, EXTERNAL_STREAM };
1130 /**
1131 * Create a stream that will process audio for an AudioNode.
1132 * Takes ownership of aEngine. aSampleRate is the sampling rate used
1133 * for the stream. If 0 is passed, the sampling rate of the engine's
1134 * node will get used.
1135 */
1136 AudioNodeStream* CreateAudioNodeStream(AudioNodeEngine* aEngine,
1137 AudioNodeStreamKind aKind,
1138 TrackRate aSampleRate = 0);
1140 AudioNodeExternalInputStream*
1141 CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine,
1142 TrackRate aSampleRate = 0);
1144 bool IsNonRealtime() const;
1145 /**
1146 * Start processing non-realtime for a specific number of ticks.
1147 */
1148 void StartNonRealtimeProcessing(TrackRate aRate, uint32_t aTicksToProcess);
1150 /**
1151 * Media graph thread only.
1152 * Dispatches a runnable that will run on the main thread after all
1153 * main-thread stream state has been next updated.
1154 * Should only be called during MediaStreamListener callbacks or during
1155 * ProcessedMediaStream::ProcessInput().
1156 */
1157 void DispatchToMainThreadAfterStreamStateUpdate(already_AddRefed<nsIRunnable> aRunnable)
1158 {
1159 *mPendingUpdateRunnables.AppendElement() = aRunnable;
1160 }
1162 protected:
1163 MediaStreamGraph()
1164 : mNextGraphUpdateIndex(1)
1165 {
1166 MOZ_COUNT_CTOR(MediaStreamGraph);
1167 }
1168 virtual ~MediaStreamGraph()
1169 {
1170 MOZ_COUNT_DTOR(MediaStreamGraph);
1171 }
1173 // Media graph thread only
1174 nsTArray<nsCOMPtr<nsIRunnable> > mPendingUpdateRunnables;
1176 // Main thread only
1177 // The number of updates we have sent to the media graph thread + 1.
1178 // We start this at 1 just to ensure that 0 is usable as a special value.
1179 int64_t mNextGraphUpdateIndex;
1180 };
1182 }
1184 #endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */