|
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/. */ |
|
5 |
|
6 #ifndef MOZILLA_MEDIASTREAMGRAPH_H_ |
|
7 #define MOZILLA_MEDIASTREAMGRAPH_H_ |
|
8 |
|
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" |
|
23 |
|
24 class nsIRunnable; |
|
25 |
|
26 template <> |
|
27 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState> |
|
28 { |
|
29 public: |
|
30 static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); } |
|
31 }; |
|
32 |
|
33 namespace mozilla { |
|
34 |
|
35 class DOMMediaStream; |
|
36 |
|
37 #ifdef PR_LOGGING |
|
38 extern PRLogModuleInfo* gMediaStreamGraphLog; |
|
39 #endif |
|
40 |
|
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; |
|
46 |
|
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 */ |
|
79 |
|
80 class MediaStreamGraph; |
|
81 |
|
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() {} |
|
106 |
|
107 public: |
|
108 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStreamListener) |
|
109 |
|
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) {} |
|
120 |
|
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) {} |
|
134 |
|
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) {} |
|
144 |
|
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) {} |
|
152 |
|
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) {} |
|
159 |
|
160 /** |
|
161 * Notify that the stream finished. |
|
162 */ |
|
163 virtual void NotifyFinished(MediaStreamGraph* aGraph) {} |
|
164 |
|
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) {} |
|
170 |
|
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 }; |
|
187 |
|
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() {} |
|
198 |
|
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 }; |
|
211 |
|
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 }; |
|
229 |
|
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 }; |
|
240 |
|
241 class MediaStreamGraphImpl; |
|
242 class SourceMediaStream; |
|
243 class ProcessedMediaStream; |
|
244 class MediaInputPort; |
|
245 class AudioNodeEngine; |
|
246 class AudioNodeExternalInputStream; |
|
247 class AudioNodeStream; |
|
248 struct AudioChunk; |
|
249 |
|
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) |
|
316 |
|
317 MediaStream(DOMMediaStream* aWrapper); |
|
318 |
|
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 } |
|
328 |
|
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); |
|
340 |
|
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); |
|
393 |
|
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 } |
|
414 |
|
415 friend class MediaStreamGraphImpl; |
|
416 friend class MediaInputPort; |
|
417 friend class AudioNodeExternalInputStream; |
|
418 |
|
419 virtual SourceMediaStream* AsSourceStream() { return nullptr; } |
|
420 virtual ProcessedMediaStream* AsProcessedStream() { return nullptr; } |
|
421 virtual AudioNodeStream* AsAudioNodeStream() { return nullptr; } |
|
422 |
|
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 } |
|
468 |
|
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(); |
|
507 |
|
508 bool HasCurrentData() { return mHasCurrentData; } |
|
509 |
|
510 StreamBuffer::Track* EnsureTrack(TrackID aTrack, TrackRate aSampleRate); |
|
511 |
|
512 void ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment = nullptr); |
|
513 |
|
514 DOMMediaStream* GetWrapper() |
|
515 { |
|
516 NS_ASSERTION(NS_IsMainThread(), "Only use DOMMediaStream on main thread"); |
|
517 return mWrapper; |
|
518 } |
|
519 |
|
520 // Return true if the main thread needs to observe updates from this stream. |
|
521 virtual bool MainThreadNeedsUpdates() const |
|
522 { |
|
523 return true; |
|
524 } |
|
525 |
|
526 virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; |
|
527 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const; |
|
528 |
|
529 void SetAudioChannelType(dom::AudioChannel aType) { mAudioChannelType = aType; } |
|
530 |
|
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); |
|
538 |
|
539 mBuffer.ForgetUpTo(aCurrentTime - mBufferStartTime); |
|
540 } |
|
541 |
|
542 // This state is all initialized on the main thread but |
|
543 // otherwise modified only on the media graph thread. |
|
544 |
|
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; |
|
553 |
|
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; |
|
571 |
|
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; |
|
581 |
|
582 // MediaInputPorts to which this is connected |
|
583 nsTArray<MediaInputPort*> mConsumers; |
|
584 |
|
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; |
|
598 |
|
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; |
|
607 |
|
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; |
|
634 |
|
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; |
|
647 |
|
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; |
|
654 |
|
655 // Our media stream graph. null if destroyed on the graph thread. |
|
656 MediaStreamGraphImpl* mGraph; |
|
657 |
|
658 dom::AudioChannel mAudioChannelType; |
|
659 }; |
|
660 |
|
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 {} |
|
677 |
|
678 virtual SourceMediaStream* AsSourceStream() { return this; } |
|
679 |
|
680 // Media graph thread only |
|
681 virtual void DestroyImpl(); |
|
682 |
|
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); |
|
692 |
|
693 void AddDirectListener(MediaStreamDirectListener* aListener); |
|
694 void RemoveDirectListener(MediaStreamDirectListener* aListener); |
|
695 |
|
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); |
|
704 |
|
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 } |
|
747 |
|
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 } |
|
753 |
|
754 /** |
|
755 * End all tracks and Finish() this stream. Used to voluntarily revoke access |
|
756 * to a LocalMediaStream. |
|
757 */ |
|
758 void EndAllTrackAndFinish(); |
|
759 |
|
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); |
|
770 |
|
771 void RegisterForAudioMixing(); |
|
772 |
|
773 // XXX need a Reset API |
|
774 |
|
775 friend class MediaStreamGraphImpl; |
|
776 |
|
777 protected: |
|
778 struct ThreadAndRunnable { |
|
779 void Init(nsIEventTarget* aTarget, nsIRunnable* aRunnable) |
|
780 { |
|
781 mTarget = aTarget; |
|
782 mRunnable = aRunnable; |
|
783 } |
|
784 |
|
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 }; |
|
815 |
|
816 bool NeedsMixing(); |
|
817 |
|
818 void ResampleAudioToGraphSampleRate(TrackData* aTrackData, MediaSegment* aSegment); |
|
819 |
|
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 } |
|
829 |
|
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); |
|
838 |
|
839 // Media stream graph thread only |
|
840 MediaStreamListener::Consumption mLastConsumptionState; |
|
841 |
|
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 }; |
|
853 |
|
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 } |
|
886 |
|
887 // Private destructor, to discourage deletion outside of Release(): |
|
888 ~MediaInputPort() |
|
889 { |
|
890 MOZ_COUNT_DTOR(MediaInputPort); |
|
891 } |
|
892 |
|
893 public: |
|
894 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaInputPort) |
|
895 |
|
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 }; |
|
909 |
|
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(); |
|
915 |
|
916 // Control API |
|
917 /** |
|
918 * Disconnects and destroys the port. The caller must not reference this |
|
919 * object again. |
|
920 */ |
|
921 void Destroy(); |
|
922 |
|
923 // Any thread |
|
924 MediaStream* GetSource() { return mSource; } |
|
925 ProcessedMediaStream* GetDestination() { return mDest; } |
|
926 |
|
927 uint16_t InputNumber() const { return mInputNumber; } |
|
928 uint16_t OutputNumber() const { return mOutputNumber; } |
|
929 |
|
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); |
|
939 |
|
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); |
|
949 |
|
950 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const |
|
951 { |
|
952 size_t amount = 0; |
|
953 |
|
954 // Not owned: |
|
955 // - mSource |
|
956 // - mDest |
|
957 // - mGraph |
|
958 return amount; |
|
959 } |
|
960 |
|
961 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const |
|
962 { |
|
963 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
964 } |
|
965 |
|
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; |
|
978 |
|
979 // Our media stream graph |
|
980 MediaStreamGraphImpl* mGraph; |
|
981 }; |
|
982 |
|
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 {} |
|
993 |
|
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); |
|
1013 |
|
1014 virtual ProcessedMediaStream* AsProcessedStream() { return this; } |
|
1015 |
|
1016 friend class MediaStreamGraphImpl; |
|
1017 |
|
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; } |
|
1054 |
|
1055 /** |
|
1056 * Forward SetTrackEnabled() to the input MediaStream(s) and translate the ID |
|
1057 */ |
|
1058 virtual void ForwardTrackEnabled(TrackID aOutputID, bool aEnabled) {}; |
|
1059 |
|
1060 bool InCycle() const { return mInCycle; } |
|
1061 |
|
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 } |
|
1070 |
|
1071 virtual size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const MOZ_OVERRIDE |
|
1072 { |
|
1073 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); |
|
1074 } |
|
1075 |
|
1076 protected: |
|
1077 // This state is all accessed only on the media graph thread. |
|
1078 |
|
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 }; |
|
1086 |
|
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. |
|
1098 |
|
1099 // Main thread only |
|
1100 static MediaStreamGraph* GetInstance(); |
|
1101 static MediaStreamGraph* CreateNonRealtimeInstance(TrackRate aSampleRate); |
|
1102 // Idempotent |
|
1103 static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph); |
|
1104 |
|
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); |
|
1139 |
|
1140 AudioNodeExternalInputStream* |
|
1141 CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine, |
|
1142 TrackRate aSampleRate = 0); |
|
1143 |
|
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); |
|
1149 |
|
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 } |
|
1161 |
|
1162 protected: |
|
1163 MediaStreamGraph() |
|
1164 : mNextGraphUpdateIndex(1) |
|
1165 { |
|
1166 MOZ_COUNT_CTOR(MediaStreamGraph); |
|
1167 } |
|
1168 virtual ~MediaStreamGraph() |
|
1169 { |
|
1170 MOZ_COUNT_DTOR(MediaStreamGraph); |
|
1171 } |
|
1172 |
|
1173 // Media graph thread only |
|
1174 nsTArray<nsCOMPtr<nsIRunnable> > mPendingUpdateRunnables; |
|
1175 |
|
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 }; |
|
1181 |
|
1182 } |
|
1183 |
|
1184 #endif /* MOZILLA_MEDIASTREAMGRAPH_H_ */ |