/src/mozilla-central/dom/media/MediaStreamGraphImpl.h
Line | Count | Source (jump to first uncovered line) |
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_MEDIASTREAMGRAPHIMPL_H_ |
7 | | #define MOZILLA_MEDIASTREAMGRAPHIMPL_H_ |
8 | | |
9 | | #include "MediaStreamGraph.h" |
10 | | |
11 | | #include "AudioMixer.h" |
12 | | #include "GraphDriver.h" |
13 | | #include "Latency.h" |
14 | | #include "mozilla/Atomics.h" |
15 | | #include "mozilla/Monitor.h" |
16 | | #include "mozilla/Services.h" |
17 | | #include "mozilla/TimeStamp.h" |
18 | | #include "mozilla/UniquePtr.h" |
19 | | #include "mozilla/WeakPtr.h" |
20 | | #include "nsClassHashtable.h" |
21 | | #include "nsIMemoryReporter.h" |
22 | | #include "nsINamed.h" |
23 | | #include "nsIRunnable.h" |
24 | | #include "nsIThread.h" |
25 | | #include "nsITimer.h" |
26 | | #include "AsyncLogger.h" |
27 | | |
28 | | namespace mozilla { |
29 | | |
30 | | namespace media { |
31 | | class ShutdownTicket; |
32 | | } |
33 | | |
34 | | template <typename T> |
35 | | class LinkedList; |
36 | | |
37 | | /** |
38 | | * A per-stream update message passed from the media graph thread to the |
39 | | * main thread. |
40 | | */ |
41 | | struct StreamUpdate |
42 | | { |
43 | | RefPtr<MediaStream> mStream; |
44 | | StreamTime mNextMainThreadCurrentTime; |
45 | | bool mNextMainThreadFinished; |
46 | | }; |
47 | | |
48 | | /** |
49 | | * This represents a message run on the graph thread to modify stream or graph |
50 | | * state. These are passed from main thread to graph thread through |
51 | | * AppendMessage(), or scheduled on the graph thread with |
52 | | * RunMessageAfterProcessing(). A ControlMessage |
53 | | * always has a weak reference to a particular affected stream. |
54 | | */ |
55 | | class ControlMessage |
56 | | { |
57 | | public: |
58 | | explicit ControlMessage(MediaStream* aStream) : mStream(aStream) |
59 | 0 | { |
60 | 0 | MOZ_COUNT_CTOR(ControlMessage); |
61 | 0 | } |
62 | | // All these run on the graph thread |
63 | | virtual ~ControlMessage() |
64 | 0 | { |
65 | 0 | MOZ_COUNT_DTOR(ControlMessage); |
66 | 0 | } |
67 | | // Do the action of this message on the MediaStreamGraph thread. Any actions |
68 | | // affecting graph processing should take effect at mProcessedTime. |
69 | | // All stream data for times < mProcessedTime has already been |
70 | | // computed. |
71 | | virtual void Run() = 0; |
72 | | // RunDuringShutdown() is only relevant to messages generated on the main |
73 | | // thread (for AppendMessage()). |
74 | | // When we're shutting down the application, most messages are ignored but |
75 | | // some cleanup messages should still be processed (on the main thread). |
76 | | // This must not add new control messages to the graph. |
77 | 0 | virtual void RunDuringShutdown() {} |
78 | 0 | MediaStream* GetStream() { return mStream; } |
79 | | |
80 | | protected: |
81 | | // We do not hold a reference to mStream. The graph will be holding |
82 | | // a reference to the stream until the Destroy message is processed. The |
83 | | // last message referencing a stream is the Destroy message for that stream. |
84 | | MediaStream* mStream; |
85 | | }; |
86 | | |
87 | | class MessageBlock |
88 | | { |
89 | | public: |
90 | | nsTArray<UniquePtr<ControlMessage>> mMessages; |
91 | | }; |
92 | | |
93 | | /** |
94 | | * The implementation of a media stream graph. This class is private to this |
95 | | * file. It's not in the anonymous namespace because MediaStream needs to |
96 | | * be able to friend it. |
97 | | * |
98 | | * There can be multiple MediaStreamGraph per process: one per document. |
99 | | * Additionaly, each OfflineAudioContext object creates its own MediaStreamGraph |
100 | | * object too. |
101 | | */ |
102 | | class MediaStreamGraphImpl : public MediaStreamGraph, |
103 | | public nsIMemoryReporter, |
104 | | public nsITimerCallback, |
105 | | public nsINamed |
106 | | { |
107 | | public: |
108 | | NS_DECL_THREADSAFE_ISUPPORTS |
109 | | NS_DECL_NSIMEMORYREPORTER |
110 | | NS_DECL_NSITIMERCALLBACK |
111 | | NS_DECL_NSINAMED |
112 | | |
113 | | /** |
114 | | * Use aGraphDriverRequested with SYSTEM_THREAD_DRIVER or AUDIO_THREAD_DRIVER |
115 | | * to create a MediaStreamGraph which provides support for real-time audio |
116 | | * and/or video. Set it to OFFLINE_THREAD_DRIVER in order to create a |
117 | | * non-realtime instance which just churns through its inputs and produces |
118 | | * output. Those objects currently only support audio, and are used to |
119 | | * implement OfflineAudioContext. They do not support MediaStream inputs. |
120 | | */ |
121 | | explicit MediaStreamGraphImpl(GraphDriverType aGraphDriverRequested, |
122 | | TrackRate aSampleRate, |
123 | | AbstractThread* aWindow); |
124 | | |
125 | | /** |
126 | | * Unregisters memory reporting and deletes this instance. This should be |
127 | | * called instead of calling the destructor directly. |
128 | | */ |
129 | | void Destroy(); |
130 | | |
131 | | // Main thread only. |
132 | | /** |
133 | | * This runs every time we need to sync state from the media graph thread |
134 | | * to the main thread while the main thread is not in the middle |
135 | | * of a script. It runs during a "stable state" (per HTML5) or during |
136 | | * an event posted to the main thread. |
137 | | * The boolean affects which boolean controlling runnable dispatch is cleared |
138 | | */ |
139 | | void RunInStableState(bool aSourceIsMSG); |
140 | | /** |
141 | | * Ensure a runnable to run RunInStableState is posted to the appshell to |
142 | | * run at the next stable state (per HTML5). |
143 | | * See EnsureStableStateEventPosted. |
144 | | */ |
145 | | void EnsureRunInStableState(); |
146 | | /** |
147 | | * Called to apply a StreamUpdate to its stream. |
148 | | */ |
149 | | void ApplyStreamUpdate(StreamUpdate* aUpdate); |
150 | | /** |
151 | | * Append a ControlMessage to the message queue. This queue is drained |
152 | | * during RunInStableState; the messages will run on the graph thread. |
153 | | */ |
154 | | void AppendMessage(UniquePtr<ControlMessage> aMessage); |
155 | | |
156 | | /** |
157 | | * Dispatches a runnable from any thread to the correct main thread for this |
158 | | * MediaStreamGraph. |
159 | | */ |
160 | | void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable); |
161 | | |
162 | | /** |
163 | | * Make this MediaStreamGraph enter forced-shutdown state. This state |
164 | | * will be noticed by the media graph thread, which will shut down all streams |
165 | | * and other state controlled by the media graph thread. |
166 | | * This is called during application shutdown. |
167 | | */ |
168 | | void ForceShutDown(media::ShutdownTicket* aShutdownTicket); |
169 | | |
170 | | /** |
171 | | * Called before the thread runs. |
172 | | */ |
173 | | void Init(); |
174 | | |
175 | | /** |
176 | | * Respond to CollectReports with sizes collected on the graph thread. |
177 | | */ |
178 | | static void |
179 | | FinishCollectReports(nsIHandleReportCallback* aHandleReport, |
180 | | nsISupports* aData, |
181 | | const nsTArray<AudioNodeSizes>& aAudioStreamSizes); |
182 | | |
183 | | // The following methods run on the graph thread (or possibly the main thread |
184 | | // if mLifecycleState > LIFECYCLE_RUNNING) |
185 | | void CollectSizesForMemoryReport( |
186 | | already_AddRefed<nsIHandleReportCallback> aHandleReport, |
187 | | already_AddRefed<nsISupports> aHandlerData); |
188 | | |
189 | | /** |
190 | | * Returns true if this MediaStreamGraph should keep running |
191 | | */ |
192 | | bool UpdateMainThreadState(); |
193 | | |
194 | | /** |
195 | | * Returns true if this MediaStreamGraph should keep running |
196 | | */ |
197 | | bool OneIteration(GraphTime aStateEnd); |
198 | | |
199 | | /** |
200 | | * Called from the driver, when the graph thread is about to stop, to tell |
201 | | * the main thread to attempt to begin cleanup. The main thread may either |
202 | | * shutdown or revive the graph depending on whether it receives new |
203 | | * messages. |
204 | | */ |
205 | | void SignalMainThreadCleanup(); |
206 | | |
207 | | bool Running() const |
208 | 0 | { |
209 | 0 | return LifecycleStateRef() == LIFECYCLE_RUNNING; |
210 | 0 | } |
211 | | |
212 | | /* This is the end of the current iteration, that is, the current time of the |
213 | | * graph. */ |
214 | | GraphTime IterationEnd() const; |
215 | | |
216 | | /** |
217 | | * Ensure there is an event posted to the main thread to run RunInStableState. |
218 | | * mMonitor must be held. |
219 | | * See EnsureRunInStableState |
220 | | */ |
221 | | void EnsureStableStateEventPosted(); |
222 | | /** |
223 | | * Generate messages to the main thread to update it for all state changes. |
224 | | * mMonitor must be held. |
225 | | */ |
226 | | void PrepareUpdatesToMainThreadState(bool aFinalUpdate); |
227 | | /** |
228 | | * Returns false if there is any stream that has finished but not yet finished |
229 | | * playing out. |
230 | | */ |
231 | | bool AllFinishedStreamsNotified(); |
232 | | /** |
233 | | * If we are rendering in non-realtime mode, we don't want to send messages to |
234 | | * the main thread at each iteration for performance reasons. We instead |
235 | | * notify the main thread at the same rate |
236 | | */ |
237 | | bool ShouldUpdateMainThread(); |
238 | | // The following methods are the various stages of RunThread processing. |
239 | | /** |
240 | | * Advance all stream state to mStateComputedTime. |
241 | | */ |
242 | | void UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime); |
243 | | /** |
244 | | * Process chunks for all streams and raise events for properties that have |
245 | | * changed, such as principalId. |
246 | | */ |
247 | | void ProcessChunkMetadata(GraphTime aPrevCurrentTime); |
248 | | /** |
249 | | * Process chunks for the given stream and interval, and raise events for |
250 | | * properties that have changed, such as principalId. |
251 | | */ |
252 | | template<typename C, typename Chunk> |
253 | | void ProcessChunkMetadataForInterval(MediaStream* aStream, |
254 | | TrackID aTrackID, |
255 | | C& aSegment, |
256 | | StreamTime aStart, |
257 | | StreamTime aEnd); |
258 | | /** |
259 | | * Process graph messages in mFrontMessageQueue. |
260 | | */ |
261 | | void RunMessagesInQueue(); |
262 | | /** |
263 | | * Update stream processing order and recompute stream blocking until |
264 | | * aEndBlockingDecisions. |
265 | | */ |
266 | | void UpdateGraph(GraphTime aEndBlockingDecisions); |
267 | | |
268 | | void SwapMessageQueues() |
269 | 0 | { |
270 | 0 | MOZ_ASSERT(CurrentDriver()->OnThread()); |
271 | 0 | MOZ_ASSERT(mFrontMessageQueue.IsEmpty()); |
272 | 0 | mMonitor.AssertCurrentThreadOwns(); |
273 | 0 | mFrontMessageQueue.SwapElements(mBackMessageQueue); |
274 | 0 | } |
275 | | /** |
276 | | * Do all the processing and play the audio and video, from |
277 | | * mProcessedTime to mStateComputedTime. |
278 | | */ |
279 | | void Process(); |
280 | | |
281 | | /** |
282 | | * For use during ProcessedMediaStream::ProcessInput() or |
283 | | * MediaStreamListener callbacks, when graph state cannot be changed. |
284 | | * Schedules |aMessage| to run after processing, at a time when graph state |
285 | | * can be changed. Graph thread. |
286 | | */ |
287 | | void RunMessageAfterProcessing(UniquePtr<ControlMessage> aMessage); |
288 | | |
289 | | /** |
290 | | * Called when a suspend/resume/close operation has been completed, on the |
291 | | * graph thread. |
292 | | */ |
293 | | void AudioContextOperationCompleted(MediaStream* aStream, |
294 | | void* aPromise, |
295 | | dom::AudioContextOperation aOperation); |
296 | | |
297 | | /** |
298 | | * Apply and AudioContext operation (suspend/resume/closed), on the graph |
299 | | * thread. |
300 | | */ |
301 | | void ApplyAudioContextOperationImpl(MediaStream* aDestinationStream, |
302 | | const nsTArray<MediaStream*>& aStreams, |
303 | | dom::AudioContextOperation aOperation, |
304 | | void* aPromise); |
305 | | |
306 | | /** |
307 | | * Increment suspend count on aStream and move it to mSuspendedStreams if |
308 | | * necessary. |
309 | | */ |
310 | | void IncrementSuspendCount(MediaStream* aStream); |
311 | | /** |
312 | | * Increment suspend count on aStream and move it to mStreams if |
313 | | * necessary. |
314 | | */ |
315 | | void DecrementSuspendCount(MediaStream* aStream); |
316 | | |
317 | | /* |
318 | | * Move streams from the mStreams to mSuspendedStream if suspending/closing an |
319 | | * AudioContext, or the inverse when resuming an AudioContext. |
320 | | */ |
321 | | void SuspendOrResumeStreams(dom::AudioContextOperation aAudioContextOperation, |
322 | | const nsTArray<MediaStream*>& aStreamSet); |
323 | | |
324 | | /** |
325 | | * Determine if we have any audio tracks, or are about to add any audiotracks. |
326 | | */ |
327 | | bool AudioTrackPresent(); |
328 | | |
329 | | /** |
330 | | * Sort mStreams so that every stream not in a cycle is after any streams |
331 | | * it depends on, and every stream in a cycle is marked as being in a cycle. |
332 | | * Also sets mIsConsumed on every stream. |
333 | | */ |
334 | | void UpdateStreamOrder(); |
335 | | |
336 | | /** |
337 | | * Returns smallest value of t such that t is a multiple of |
338 | | * WEBAUDIO_BLOCK_SIZE and t >= aTime. |
339 | | */ |
340 | | GraphTime RoundUpToEndOfAudioBlock(GraphTime aTime); |
341 | | /** |
342 | | * Returns smallest value of t such that t is a multiple of |
343 | | * WEBAUDIO_BLOCK_SIZE and t > aTime. |
344 | | */ |
345 | | GraphTime RoundUpToNextAudioBlock(GraphTime aTime); |
346 | | /** |
347 | | * Produce data for all streams >= aStreamIndex for the current time interval. |
348 | | * Advances block by block, each iteration producing data for all streams |
349 | | * for a single block. |
350 | | * This is called whenever we have an AudioNodeStream in the graph. |
351 | | */ |
352 | | void ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex, |
353 | | TrackRate aSampleRate); |
354 | | /** |
355 | | * If aStream will underrun between aTime, and aEndBlockingDecisions, returns |
356 | | * the time at which the underrun will start. Otherwise return |
357 | | * aEndBlockingDecisions. |
358 | | */ |
359 | | GraphTime WillUnderrun(MediaStream* aStream, GraphTime aEndBlockingDecisions); |
360 | | |
361 | | /** |
362 | | * Given a graph time aTime, convert it to a stream time taking into |
363 | | * account the time during which aStream is scheduled to be blocked. |
364 | | */ |
365 | | StreamTime GraphTimeToStreamTimeWithBlocking(const MediaStream* aStream, GraphTime aTime) const; |
366 | | |
367 | | /** |
368 | | * Call NotifyHaveCurrentData on aStream's listeners. |
369 | | */ |
370 | | void NotifyHasCurrentData(MediaStream* aStream); |
371 | | /** |
372 | | * If aStream needs an audio stream but doesn't have one, create it. |
373 | | * If aStream doesn't need an audio stream but has one, destroy it. |
374 | | */ |
375 | | void CreateOrDestroyAudioStreams(MediaStream* aStream); |
376 | | /** |
377 | | * Queue audio (mix of stream audio and silence for blocked intervals) |
378 | | * to the audio output stream. Returns the number of frames played. |
379 | | */ |
380 | | StreamTime PlayAudio(MediaStream* aStream); |
381 | | /* Runs off a message on the graph thread when something requests audio from |
382 | | * an input audio device of ID aID, and delivers the input audio frames to |
383 | | * aListener. */ |
384 | | void OpenAudioInputImpl(CubebUtils::AudioDeviceID aID, |
385 | | AudioDataListener* aListener); |
386 | | /* Called on the main thread when something requests audio from an input |
387 | | * audio device aID. */ |
388 | | virtual nsresult OpenAudioInput(CubebUtils::AudioDeviceID aID, |
389 | | AudioDataListener* aListener) override; |
390 | | /* Runs off a message on the graph when input audio from aID is not needed |
391 | | * anymore, for a particular stream. It can be that other streams still need |
392 | | * audio from this audio input device. */ |
393 | | void CloseAudioInputImpl(Maybe<CubebUtils::AudioDeviceID>& aID, |
394 | | AudioDataListener* aListener); |
395 | | /* Called on the main thread when input audio from aID is not needed |
396 | | * anymore, for a particular stream. It can be that other streams still need |
397 | | * audio from this audio input device. */ |
398 | | virtual void CloseAudioInput(Maybe<CubebUtils::AudioDeviceID>& aID, |
399 | | AudioDataListener* aListener) override; |
400 | | /* Called on the graph thread when the input device settings should be |
401 | | * reevaluated, for example, if the channel count of the input stream should |
402 | | * be changed. */ |
403 | | void ReevaluateInputDevice(); |
404 | | /* Called on the graph thread when there is new output data for listeners. |
405 | | * This is the mixed audio output of this MediaStreamGraph. */ |
406 | | void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames, |
407 | | TrackRate aRate, uint32_t aChannels); |
408 | | /* Called on the graph thread when there is new input data for listeners. This |
409 | | * is the raw audio input for this MediaStreamGraph. */ |
410 | | void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames, |
411 | | TrackRate aRate, uint32_t aChannels); |
412 | | /* Called every time there are changes to input/output audio devices like |
413 | | * plug/unplug etc. This can be called on any thread, and posts a message to |
414 | | * the main thread so that it can post a message to the graph thread. */ |
415 | | void DeviceChanged(); |
416 | | /* Called every time there are changes to input/output audio devices. This is |
417 | | * called on the graph thread. */ |
418 | | void DeviceChangedImpl(); |
419 | | |
420 | | /** |
421 | | * Compute how much stream data we would like to buffer for aStream. |
422 | | */ |
423 | | StreamTime GetDesiredBufferEnd(MediaStream* aStream); |
424 | | /** |
425 | | * Returns true when there are no active streams. |
426 | | */ |
427 | | bool IsEmpty() const |
428 | 0 | { |
429 | 0 | MOZ_ASSERT(OnGraphThreadOrNotRunning() || |
430 | 0 | (NS_IsMainThread() && |
431 | 0 | LifecycleStateRef() >= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP)); |
432 | 0 | return mStreams.IsEmpty() && mSuspendedStreams.IsEmpty() && mPortCount == 0; |
433 | 0 | } |
434 | | |
435 | | /** |
436 | | * Add aStream to the graph and initializes its graph-specific state. |
437 | | */ |
438 | | void AddStreamGraphThread(MediaStream* aStream); |
439 | | /** |
440 | | * Remove aStream from the graph. Ensures that pending messages about the |
441 | | * stream back to the main thread are flushed. |
442 | | */ |
443 | | void RemoveStreamGraphThread(MediaStream* aStream); |
444 | | /** |
445 | | * Remove aPort from the graph and release it. |
446 | | */ |
447 | | void DestroyPort(MediaInputPort* aPort); |
448 | | /** |
449 | | * Mark the media stream order as dirty. |
450 | | */ |
451 | | void SetStreamOrderDirty() |
452 | 0 | { |
453 | 0 | MOZ_ASSERT(OnGraphThreadOrNotRunning()); |
454 | 0 | mStreamOrderDirty = true; |
455 | 0 | } |
456 | | |
457 | | uint32_t AudioOutputChannelCount() const |
458 | 0 | { |
459 | 0 | return mOutputChannels; |
460 | 0 | } |
461 | | |
462 | | /** |
463 | | * The audio input channel count for a MediaStreamGraph is the max of all the |
464 | | * channel counts requested by the listeners. The max channel count is |
465 | | * delivered to the listeners themselves, and they take care of downmixing. |
466 | | */ |
467 | | uint32_t AudioInputChannelCount() |
468 | 0 | { |
469 | 0 | MOZ_ASSERT(OnGraphThreadOrNotRunning()); |
470 | 0 |
|
471 | | #ifdef ANDROID |
472 | | if (!mInputDeviceUsers.GetValue(mInputDeviceID)) { |
473 | | return 0; |
474 | | } |
475 | | #else |
476 | 0 | if (!mInputDeviceID) { |
477 | 0 | MOZ_ASSERT(mInputDeviceUsers.Count() == 0, |
478 | 0 | "If running on a platform other than android," |
479 | 0 | "an explicit device id should be present"); |
480 | 0 | return 0; |
481 | 0 | } |
482 | 0 | #endif |
483 | 0 | uint32_t maxInputChannels = 0; |
484 | 0 | // When/if we decide to support multiple input device per graph, this needs |
485 | 0 | // loop over them. |
486 | 0 | nsTArray<RefPtr<AudioDataListener>>* listeners = |
487 | 0 | mInputDeviceUsers.GetValue(mInputDeviceID); |
488 | 0 | MOZ_ASSERT(listeners); |
489 | 0 | for (const auto& listener : *listeners) { |
490 | 0 | maxInputChannels = |
491 | 0 | std::max(maxInputChannels, listener->RequestedInputChannelCount(this)); |
492 | 0 | } |
493 | 0 | return maxInputChannels; |
494 | 0 | } |
495 | | |
496 | | CubebUtils::AudioDeviceID InputDeviceID() |
497 | 0 | { |
498 | 0 | return mInputDeviceID; |
499 | 0 | } |
500 | | |
501 | | double MediaTimeToSeconds(GraphTime aTime) const |
502 | 0 | { |
503 | 0 | NS_ASSERTION(aTime > -STREAM_TIME_MAX && aTime <= STREAM_TIME_MAX, |
504 | 0 | "Bad time"); |
505 | 0 | return static_cast<double>(aTime)/GraphRate(); |
506 | 0 | } |
507 | | |
508 | | GraphTime SecondsToMediaTime(double aS) const |
509 | 0 | { |
510 | 0 | NS_ASSERTION(0 <= aS && aS <= TRACK_TICKS_MAX/TRACK_RATE_MAX, |
511 | 0 | "Bad seconds"); |
512 | 0 | return GraphRate() * aS; |
513 | 0 | } |
514 | | |
515 | | GraphTime MillisecondsToMediaTime(int32_t aMS) const |
516 | 0 | { |
517 | 0 | return RateConvertTicksRoundDown(GraphRate(), 1000, aMS); |
518 | 0 | } |
519 | | |
520 | | /** |
521 | | * Signal to the graph that the thread has paused indefinitly, |
522 | | * or resumed. |
523 | | */ |
524 | | void PausedIndefinitly(); |
525 | | void ResumedFromPaused(); |
526 | | |
527 | | /** |
528 | | * Not safe to call off the MediaStreamGraph thread unless monitor is held! |
529 | | */ |
530 | | GraphDriver* CurrentDriver() const |
531 | 0 | { |
532 | | #ifdef DEBUG |
533 | | if (!OnGraphThreadOrNotRunning()) { |
534 | | mMonitor.AssertCurrentThreadOwns(); |
535 | | } |
536 | | #endif |
537 | | return mDriver; |
538 | 0 | } |
539 | | |
540 | | /** |
541 | | * Effectively set the new driver, while we are switching. |
542 | | * It is only safe to call this at the very end of an iteration, when there |
543 | | * has been a SwitchAtNextIteration call during the iteration. The driver |
544 | | * should return and pass the control to the new driver shortly after. |
545 | | * We can also switch from Revive() (on MainThread). Monitor must be held. |
546 | | */ |
547 | | void SetCurrentDriver(GraphDriver* aDriver) |
548 | 0 | { |
549 | 0 | MOZ_ASSERT(mDriver->OnThread() || !mDriver->ThreadRunning()); |
550 | | #ifdef DEBUG |
551 | | mMonitor.AssertCurrentThreadOwns(); |
552 | | #endif |
553 | | mDriver = aDriver; |
554 | 0 | } |
555 | | |
556 | | Monitor& GetMonitor() |
557 | 0 | { |
558 | 0 | return mMonitor; |
559 | 0 | } |
560 | | |
561 | | void EnsureNextIteration() |
562 | 0 | { |
563 | 0 | mNeedAnotherIteration = true; // atomic |
564 | 0 | // Note: GraphDriver must ensure that there's no race on setting |
565 | 0 | // mNeedAnotherIteration and mGraphDriverAsleep -- see WaitForNextIteration() |
566 | 0 | if (mGraphDriverAsleep) { // atomic |
567 | 0 | MonitorAutoLock mon(mMonitor); |
568 | 0 | CurrentDriver()->WakeUp(); // Might not be the same driver; might have woken already |
569 | 0 | } |
570 | 0 | } |
571 | | |
572 | | void EnsureNextIterationLocked() |
573 | 0 | { |
574 | 0 | mNeedAnotherIteration = true; // atomic |
575 | 0 | if (mGraphDriverAsleep) { // atomic |
576 | 0 | CurrentDriver()->WakeUp(); // Might not be the same driver; might have woken already |
577 | 0 | } |
578 | 0 | } |
579 | | |
580 | | // Capture Stream API. This allows to get a mixed-down output for a window. |
581 | | void RegisterCaptureStreamForWindow(uint64_t aWindowId, |
582 | | ProcessedMediaStream* aCaptureStream); |
583 | | void UnregisterCaptureStreamForWindow(uint64_t aWindowId); |
584 | | already_AddRefed<MediaInputPort> |
585 | | ConnectToCaptureStream(uint64_t aWindowId, MediaStream* aMediaStream); |
586 | | |
587 | | class StreamSet { |
588 | | public: |
589 | | class iterator { |
590 | | public: |
591 | | explicit iterator(MediaStreamGraphImpl& aGraph) |
592 | | : mGraph(&aGraph), mArrayNum(-1), mArrayIndex(0) |
593 | 0 | { |
594 | 0 | ++(*this); |
595 | 0 | } |
596 | 0 | iterator() : mGraph(nullptr), mArrayNum(2), mArrayIndex(0) {} |
597 | | MediaStream* operator*() |
598 | 0 | { |
599 | 0 | return Array()->ElementAt(mArrayIndex); |
600 | 0 | } |
601 | | iterator operator++() |
602 | 0 | { |
603 | 0 | ++mArrayIndex; |
604 | 0 | while (mArrayNum < 2 && |
605 | 0 | (mArrayNum < 0 || mArrayIndex >= Array()->Length())) { |
606 | 0 | ++mArrayNum; |
607 | 0 | mArrayIndex = 0; |
608 | 0 | } |
609 | 0 | return *this; |
610 | 0 | } |
611 | | bool operator==(const iterator& aOther) const |
612 | 0 | { |
613 | 0 | return mArrayNum == aOther.mArrayNum && mArrayIndex == aOther.mArrayIndex; |
614 | 0 | } |
615 | | bool operator!=(const iterator& aOther) const |
616 | 0 | { |
617 | 0 | return !(*this == aOther); |
618 | 0 | } |
619 | | private: |
620 | | nsTArray<MediaStream*>* Array() |
621 | 0 | { |
622 | 0 | return mArrayNum == 0 ? &mGraph->mStreams : &mGraph->mSuspendedStreams; |
623 | 0 | } |
624 | | MediaStreamGraphImpl* mGraph; |
625 | | int mArrayNum; |
626 | | uint32_t mArrayIndex; |
627 | | }; |
628 | | |
629 | 0 | explicit StreamSet(MediaStreamGraphImpl& aGraph) : mGraph(aGraph) {} |
630 | 0 | iterator begin() { return iterator(mGraph); } |
631 | 0 | iterator end() { return iterator(); } |
632 | | private: |
633 | | MediaStreamGraphImpl& mGraph; |
634 | | }; |
635 | 0 | StreamSet AllStreams() { return StreamSet(*this); } |
636 | | |
637 | | // Data members |
638 | | // |
639 | | /** |
640 | | * Graphs own owning references to their driver, until shutdown. When a driver |
641 | | * switch occur, previous driver is either deleted, or it's ownership is |
642 | | * passed to a event that will take care of the asynchronous cleanup, as |
643 | | * audio stream can take some time to shut down. |
644 | | * Accessed on both the main thread and the graph thread; both read and write. |
645 | | * Must hold monitor to access it. |
646 | | */ |
647 | | RefPtr<GraphDriver> mDriver; |
648 | | |
649 | | // The following state is managed on the graph thread only, unless |
650 | | // mLifecycleState > LIFECYCLE_RUNNING in which case the graph thread |
651 | | // is not running and this state can be used from the main thread. |
652 | | |
653 | | /** |
654 | | * The graph keeps a reference to each stream. |
655 | | * References are maintained manually to simplify reordering without |
656 | | * unnecessary thread-safe refcount changes. |
657 | | * Must satisfy OnGraphThreadOrNotRunning(). |
658 | | */ |
659 | | nsTArray<MediaStream*> mStreams; |
660 | | /** |
661 | | * This stores MediaStreams that are part of suspended AudioContexts. |
662 | | * mStreams and mSuspendStream are disjoint sets: a stream is either suspended |
663 | | * or not suspended. Suspended streams are not ordered in UpdateStreamOrder, |
664 | | * and are therefore not doing any processing. |
665 | | * Must satisfy OnGraphThreadOrNotRunning(). |
666 | | */ |
667 | | nsTArray<MediaStream*> mSuspendedStreams; |
668 | | /** |
669 | | * Streams from mFirstCycleBreaker to the end of mStreams produce output |
670 | | * before they receive input. They correspond to DelayNodes that are in |
671 | | * cycles. |
672 | | */ |
673 | | uint32_t mFirstCycleBreaker; |
674 | | /** |
675 | | * Blocking decisions have been computed up to this time. |
676 | | * Between each iteration, this is the same as mProcessedTime. |
677 | | */ |
678 | | GraphTime mStateComputedTime = 0; |
679 | | /** |
680 | | * All stream contents have been computed up to this time. |
681 | | * The next batch of updates from the main thread will be processed |
682 | | * at this time. This is behind mStateComputedTime during processing. |
683 | | */ |
684 | | GraphTime mProcessedTime = 0; |
685 | | /** |
686 | | * Date of the last time we updated the main thread with the graph state. |
687 | | */ |
688 | | TimeStamp mLastMainThreadUpdate; |
689 | | /** |
690 | | * Number of active MediaInputPorts |
691 | | */ |
692 | | int32_t mPortCount; |
693 | | |
694 | | /** |
695 | | * Devices to use for cubeb input & output, or nullptr for default device. |
696 | | * A MediaStreamGraph always has an output (even if silent). |
697 | | * If `mInputDeviceUsers.Count() != 0`, this MediaStreamGraph wants audio |
698 | | * input. |
699 | | * |
700 | | * In any case, the number of channels to use can be queried (on the graph |
701 | | * thread) by AudioInputChannelCount() and AudioOutputChannelCount(). |
702 | | */ |
703 | | CubebUtils::AudioDeviceID mInputDeviceID; |
704 | | CubebUtils::AudioDeviceID mOutputDeviceID; |
705 | | // Maps AudioDeviceID to an array of their users (that are listeners). This is |
706 | | // used to deliver audio input frames and to notify the listeners that the |
707 | | // audio device that delivers the audio frames has changed. |
708 | | // This is only touched on the graph thread. |
709 | | nsDataHashtable<nsVoidPtrHashKey, |
710 | | nsTArray<RefPtr<AudioDataListener>>> mInputDeviceUsers; |
711 | | |
712 | | // True if the graph needs another iteration after the current iteration. |
713 | | Atomic<bool> mNeedAnotherIteration; |
714 | | // GraphDriver may need a WakeUp() if something changes |
715 | | Atomic<bool> mGraphDriverAsleep; |
716 | | |
717 | | // mMonitor guards the data below. |
718 | | // MediaStreamGraph normally does its work without holding mMonitor, so it is |
719 | | // not safe to just grab mMonitor from some thread and start monkeying with |
720 | | // the graph. Instead, communicate with the graph thread using provided |
721 | | // mechanisms such as the ControlMessage queue. |
722 | | Monitor mMonitor; |
723 | | |
724 | | // Data guarded by mMonitor (must always be accessed with mMonitor held, |
725 | | // regardless of the value of mLifecycleState). |
726 | | |
727 | | /** |
728 | | * State to copy to main thread |
729 | | */ |
730 | | nsTArray<StreamUpdate> mStreamUpdates; |
731 | | /** |
732 | | * Runnables to run after the next update to main thread state. |
733 | | */ |
734 | | nsTArray<nsCOMPtr<nsIRunnable> > mUpdateRunnables; |
735 | | /** |
736 | | * A list of batches of messages to process. Each batch is processed |
737 | | * as an atomic unit. |
738 | | */ |
739 | | /* |
740 | | * Message queue processed by the MSG thread during an iteration. |
741 | | * Accessed on graph thread only. |
742 | | */ |
743 | | nsTArray<MessageBlock> mFrontMessageQueue; |
744 | | /* |
745 | | * Message queue in which the main thread appends messages. |
746 | | * Access guarded by mMonitor. |
747 | | */ |
748 | | nsTArray<MessageBlock> mBackMessageQueue; |
749 | | |
750 | | /* True if there will messages to process if we swap the message queues. */ |
751 | | bool MessagesQueued() const |
752 | 0 | { |
753 | 0 | mMonitor.AssertCurrentThreadOwns(); |
754 | 0 | return !mBackMessageQueue.IsEmpty(); |
755 | 0 | } |
756 | | /** |
757 | | * This enum specifies where this graph is in its lifecycle. This is used |
758 | | * to control shutdown. |
759 | | * Shutdown is tricky because it can happen in two different ways: |
760 | | * 1) Shutdown due to inactivity. RunThread() detects that it has no |
761 | | * pending messages and no streams, and exits. The next RunInStableState() |
762 | | * checks if there are new pending messages from the main thread (true only |
763 | | * if new stream creation raced with shutdown); if there are, it revives |
764 | | * RunThread(), otherwise it commits to shutting down the graph. New stream |
765 | | * creation after this point will create a new graph. An async event is |
766 | | * dispatched to Shutdown() the graph's threads and then delete the graph |
767 | | * object. |
768 | | * 2) Forced shutdown at application shutdown, or completion of a |
769 | | * non-realtime graph. A flag is set, RunThread() detects the flag and |
770 | | * exits, the next RunInStableState() detects the flag, and dispatches the |
771 | | * async event to Shutdown() the graph's threads. However the graph object |
772 | | * is not deleted. New messages for the graph are processed synchronously on |
773 | | * the main thread if necessary. When the last stream is destroyed, the |
774 | | * graph object is deleted. |
775 | | * |
776 | | * This should be kept in sync with the LifecycleState_str array in |
777 | | * MediaStreamGraph.cpp |
778 | | */ |
779 | | enum LifecycleState |
780 | | { |
781 | | // The graph thread hasn't started yet. |
782 | | LIFECYCLE_THREAD_NOT_STARTED, |
783 | | // RunThread() is running normally. |
784 | | LIFECYCLE_RUNNING, |
785 | | // In the following states, the graph thread is not running so |
786 | | // all "graph thread only" state in this class can be used safely |
787 | | // on the main thread. |
788 | | // RunThread() has exited and we're waiting for the next |
789 | | // RunInStableState(), at which point we can clean up the main-thread |
790 | | // side of the graph. |
791 | | LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP, |
792 | | // RunInStableState() posted a ShutdownRunnable, and we're waiting for it |
793 | | // to shut down the graph thread(s). |
794 | | LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN, |
795 | | // Graph threads have shut down but we're waiting for remaining streams |
796 | | // to be destroyed. Only happens during application shutdown and on |
797 | | // completed non-realtime graphs, since normally we'd only shut down a |
798 | | // realtime graph when it has no streams. |
799 | | LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION |
800 | | }; |
801 | | |
802 | | /** |
803 | | * Modified only in mMonitor. Transitions to |
804 | | * LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP occur on the graph thread at |
805 | | * the end of an iteration. All other transitions occur on the main thread. |
806 | | */ |
807 | | LifecycleState mLifecycleState; |
808 | | LifecycleState& LifecycleStateRef() |
809 | 0 | { |
810 | | #if DEBUG |
811 | | if (!mDetectedNotRunning) { |
812 | | mMonitor.AssertCurrentThreadOwns(); |
813 | | } |
814 | | #endif |
815 | | return mLifecycleState; |
816 | 0 | } |
817 | | const LifecycleState& LifecycleStateRef() const |
818 | 0 | { |
819 | 0 | #if DEBUG |
820 | 0 | if (!mDetectedNotRunning) { |
821 | 0 | mMonitor.AssertCurrentThreadOwns(); |
822 | 0 | } |
823 | 0 | #endif |
824 | 0 | return mLifecycleState; |
825 | 0 | } |
826 | | /** |
827 | | * The graph should stop processing at or after this time. |
828 | | * Only set on main thread. Read on both main and MSG thread. |
829 | | */ |
830 | | Atomic<GraphTime> mEndTime; |
831 | | |
832 | | /** |
833 | | * True when we need to do a forced shutdown during application shutdown. |
834 | | * Only set on main thread. |
835 | | * Can be read safely on the main thread, on all other threads mMonitor must |
836 | | * be held. |
837 | | */ |
838 | | bool mForceShutDown; |
839 | | |
840 | | /** |
841 | | * Drop this reference during shutdown to unblock shutdown. |
842 | | * Only accessed on the main thread. |
843 | | **/ |
844 | | RefPtr<media::ShutdownTicket> mForceShutdownTicket; |
845 | | |
846 | | /** |
847 | | * True when we have posted an event to the main thread to run |
848 | | * RunInStableState() and the event hasn't run yet. |
849 | | * Accessed on both main and MSG thread, mMonitor must be held. |
850 | | */ |
851 | | bool mPostedRunInStableStateEvent; |
852 | | |
853 | | // Main thread only |
854 | | |
855 | | /** |
856 | | * Messages posted by the current event loop task. These are forwarded to |
857 | | * the media graph thread during RunInStableState. We can't forward them |
858 | | * immediately because we want all messages between stable states to be |
859 | | * processed as an atomic batch. |
860 | | */ |
861 | | nsTArray<UniquePtr<ControlMessage>> mCurrentTaskMessageQueue; |
862 | | /** |
863 | | * True when RunInStableState has determined that mLifecycleState is > |
864 | | * LIFECYCLE_RUNNING. Since only the main thread can reset mLifecycleState to |
865 | | * LIFECYCLE_RUNNING, this can be relied on to not change unexpectedly. |
866 | | */ |
867 | | Atomic<bool> mDetectedNotRunning; |
868 | | /** |
869 | | * True when a stable state runner has been posted to the appshell to run |
870 | | * RunInStableState at the next stable state. |
871 | | * Only accessed on the main thread. |
872 | | */ |
873 | | bool mPostedRunInStableState; |
874 | | /** |
875 | | * True when processing real-time audio/video. False when processing non-realtime |
876 | | * audio. |
877 | | */ |
878 | | const bool mRealtime; |
879 | | /** |
880 | | * True when a non-realtime MediaStreamGraph has started to process input. This |
881 | | * value is only accessed on the main thread. |
882 | | */ |
883 | | bool mNonRealtimeProcessing; |
884 | | /** |
885 | | * True when a change has happened which requires us to recompute the stream |
886 | | * blocking order. |
887 | | */ |
888 | | bool mStreamOrderDirty; |
889 | | /** |
890 | | * Hold a ref to the Latency logger |
891 | | */ |
892 | | RefPtr<AsyncLatencyLogger> mLatencyLog; |
893 | | AudioMixer mMixer; |
894 | | const RefPtr<AbstractThread> mAbstractMainThread; |
895 | | |
896 | | // used to limit graph shutdown time |
897 | | // Only accessed on the main thread. |
898 | | nsCOMPtr<nsITimer> mShutdownTimer; |
899 | | |
900 | | private: |
901 | | virtual ~MediaStreamGraphImpl(); |
902 | | |
903 | | MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) |
904 | | |
905 | | /** |
906 | | * This class uses manual memory management, and all pointers to it are raw |
907 | | * pointers. However, in order for it to implement nsIMemoryReporter, it needs |
908 | | * to implement nsISupports and so be ref-counted. So it maintains a single |
909 | | * nsRefPtr to itself, giving it a ref-count of 1 during its entire lifetime, |
910 | | * and Destroy() nulls this self-reference in order to trigger self-deletion. |
911 | | */ |
912 | | RefPtr<MediaStreamGraphImpl> mSelfRef; |
913 | | |
914 | | struct WindowAndStream |
915 | | { |
916 | | uint64_t mWindowId; |
917 | | RefPtr<ProcessedMediaStream> mCaptureStreamSink; |
918 | | }; |
919 | | /** |
920 | | * Stream for window audio capture. |
921 | | */ |
922 | | nsTArray<WindowAndStream> mWindowCaptureStreams; |
923 | | |
924 | | /** |
925 | | * Number of channels on output. |
926 | | */ |
927 | | const uint32_t mOutputChannels; |
928 | | |
929 | | /** |
930 | | * Global volume scale. Used when running tests so that the output is not too |
931 | | * loud. |
932 | | */ |
933 | | const float mGlobalVolume; |
934 | | |
935 | | #ifdef DEBUG |
936 | | /** |
937 | | * Used to assert when AppendMessage() runs ControlMessages synchronously. |
938 | | */ |
939 | | bool mCanRunMessagesSynchronously; |
940 | | #endif |
941 | | }; |
942 | | |
943 | | } // namespace mozilla |
944 | | |
945 | | #endif /* MEDIASTREAMGRAPHIMPL_H_ */ |