/src/mozilla-central/dom/media/GraphDriver.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 GRAPHDRIVER_H_ |
7 | | #define GRAPHDRIVER_H_ |
8 | | |
9 | | #include "nsAutoRef.h" |
10 | | #include "AudioBufferUtils.h" |
11 | | #include "AudioMixer.h" |
12 | | #include "AudioSegment.h" |
13 | | #include "SelfRef.h" |
14 | | #include "mozilla/Atomics.h" |
15 | | #include "mozilla/SharedThreadPool.h" |
16 | | #include "mozilla/StaticPtr.h" |
17 | | |
18 | | #include <thread> |
19 | | |
20 | | #if defined(XP_WIN) |
21 | | #include "mozilla/audio/AudioNotificationReceiver.h" |
22 | | #endif |
23 | | |
24 | | struct cubeb_stream; |
25 | | |
26 | | template <> |
27 | | class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream> |
28 | | { |
29 | | public: |
30 | 0 | static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); } |
31 | | }; |
32 | | |
33 | | namespace mozilla { |
34 | | |
35 | | /** |
36 | | * Assume we can run an iteration of the MediaStreamGraph loop in this much time |
37 | | * or less. |
38 | | * We try to run the control loop at this rate. |
39 | | */ |
40 | | static const int MEDIA_GRAPH_TARGET_PERIOD_MS = 10; |
41 | | |
42 | | /** |
43 | | * Assume that we might miss our scheduled wakeup of the MediaStreamGraph by |
44 | | * this much. |
45 | | */ |
46 | | static const int SCHEDULE_SAFETY_MARGIN_MS = 10; |
47 | | |
48 | | /** |
49 | | * Try have this much audio buffered in streams and queued to the hardware. |
50 | | * The maximum delay to the end of the next control loop |
51 | | * is 2*MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS. |
52 | | * There is no point in buffering more audio than this in a stream at any |
53 | | * given time (until we add processing). |
54 | | * This is not optimal yet. |
55 | | */ |
56 | | static const int AUDIO_TARGET_MS = 2*MEDIA_GRAPH_TARGET_PERIOD_MS + |
57 | | SCHEDULE_SAFETY_MARGIN_MS; |
58 | | |
59 | | class MediaStreamGraphImpl; |
60 | | |
61 | | class AudioCallbackDriver; |
62 | | class OfflineClockDriver; |
63 | | class SystemClockDriver; |
64 | | |
65 | | /** |
66 | | * A driver is responsible for the scheduling of the processing, the thread |
67 | | * management, and give the different clocks to a MediaStreamGraph. This is an |
68 | | * abstract base class. A MediaStreamGraph can be driven by an |
69 | | * OfflineClockDriver, if the graph is offline, or a SystemClockDriver, if the |
70 | | * graph is real time. |
71 | | * A MediaStreamGraph holds an owning reference to its driver. |
72 | | * |
73 | | * The lifetime of drivers is a complicated affair. Here are the different |
74 | | * scenarii that can happen: |
75 | | * |
76 | | * Starting a MediaStreamGraph with an AudioCallbackDriver |
77 | | * - A new thread T is created, from the main thread. |
78 | | * - On this thread T, cubeb is initialized if needed, and a cubeb_stream is |
79 | | * created and started |
80 | | * - The thread T posts a message to the main thread to terminate itself. |
81 | | * - The graph runs off the audio thread |
82 | | * |
83 | | * Starting a MediaStreamGraph with a SystemClockDriver: |
84 | | * - A new thread T is created from the main thread. |
85 | | * - The graph runs off this thread. |
86 | | * |
87 | | * Switching from a SystemClockDriver to an AudioCallbackDriver: |
88 | | * - A new AudioCallabackDriver is created and initialized on the graph thread |
89 | | * - At the end of the MSG iteration, the SystemClockDriver transfers its timing |
90 | | * info and a reference to itself to the AudioCallbackDriver. It then starts |
91 | | * the AudioCallbackDriver. |
92 | | * - When the AudioCallbackDriver starts, it checks if it has been switched from |
93 | | * a SystemClockDriver, and if that is the case, sends a message to the main |
94 | | * thread to shut the SystemClockDriver thread down. |
95 | | * - The graph now runs off an audio callback |
96 | | * |
97 | | * Switching from an AudioCallbackDriver to a SystemClockDriver: |
98 | | * - A new SystemClockDriver is created, and set as mNextDriver. |
99 | | * - At the end of the MSG iteration, the AudioCallbackDriver transfers its |
100 | | * timing info and a reference to itself to the SystemClockDriver. A new |
101 | | * SystemClockDriver is started from the current audio thread. |
102 | | * - When starting, the SystemClockDriver checks if it has been switched from an |
103 | | * AudioCallbackDriver. If yes, it creates a new temporary thread to release |
104 | | * the cubeb_streams. This temporary thread closes the cubeb_stream, and |
105 | | * then dispatches a message to the main thread to be terminated. |
106 | | * - The graph now runs off a normal thread. |
107 | | * |
108 | | * Two drivers cannot run at the same time for the same graph. The thread safety |
109 | | * of the different attributes of drivers, and they access pattern is documented |
110 | | * next to the members themselves. |
111 | | * |
112 | | */ |
113 | | class GraphDriver |
114 | | { |
115 | | public: |
116 | | explicit GraphDriver(MediaStreamGraphImpl* aGraphImpl); |
117 | | |
118 | | NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GraphDriver); |
119 | | /* For real-time graphs, this waits until it's time to process more data. For |
120 | | * offline graphs, this is a no-op. */ |
121 | | virtual void WaitForNextIteration() = 0; |
122 | | /* Wakes up the graph if it is waiting. */ |
123 | | virtual void WakeUp() = 0; |
124 | | /* Start the graph, init the driver, start the thread. |
125 | | * A driver cannot be started twice, it must be shutdown |
126 | | * before being started again. */ |
127 | | virtual void Start() = 0; |
128 | | /* Revive this driver, as more messages just arrived. */ |
129 | | virtual void Revive() = 0; |
130 | | /* Shutdown GraphDriver (synchronously) */ |
131 | | virtual void Shutdown() = 0; |
132 | | /* Rate at which the GraphDriver runs, in ms. This can either be user |
133 | | * controlled (because we are using a {System,Offline}ClockDriver, and decide |
134 | | * how often we want to wakeup/how much we want to process per iteration), or |
135 | | * it can be indirectly set by the latency of the audio backend, and the |
136 | | * number of buffers of this audio backend: say we have four buffers, and 40ms |
137 | | * latency, we will get a callback approximately every 10ms. */ |
138 | | virtual uint32_t IterationDuration() = 0; |
139 | | |
140 | | /* Return whether we are switching or not. */ |
141 | | bool Switching(); |
142 | | /* Implement the switching of the driver and the necessary updates */ |
143 | | void SwitchToNextDriver(); |
144 | | |
145 | | // Those are simply or setting the associated pointer, but assert that the |
146 | | // lock is held. |
147 | | GraphDriver* NextDriver(); |
148 | | GraphDriver* PreviousDriver(); |
149 | | void SetPreviousDriver(GraphDriver* aPreviousDriver); |
150 | | |
151 | | /** |
152 | | * If we are running a real time graph, get the current time stamp to schedule |
153 | | * video frames. This has to be reimplemented by real time drivers. |
154 | | */ |
155 | 0 | virtual TimeStamp GetCurrentTimeStamp() { |
156 | 0 | return mCurrentTimeStamp; |
157 | 0 | } |
158 | | |
159 | 0 | GraphTime IterationEnd() { |
160 | 0 | return mIterationEnd; |
161 | 0 | } |
162 | | |
163 | 0 | virtual AudioCallbackDriver* AsAudioCallbackDriver() { |
164 | 0 | return nullptr; |
165 | 0 | } |
166 | | |
167 | 0 | virtual OfflineClockDriver* AsOfflineClockDriver() { |
168 | 0 | return nullptr; |
169 | 0 | } |
170 | | |
171 | 0 | virtual SystemClockDriver* AsSystemClockDriver() { |
172 | 0 | return nullptr; |
173 | 0 | } |
174 | | |
175 | | /** |
176 | | * Tell the driver it has to stop and return the current time of the graph, so |
177 | | * another driver can start from the right point in time. |
178 | | */ |
179 | | virtual void SwitchAtNextIteration(GraphDriver* aDriver); |
180 | | |
181 | | /** |
182 | | * Set the time for a graph, on a driver. This is used so a new driver just |
183 | | * created can start at the right point in time. |
184 | | */ |
185 | | void SetGraphTime(GraphDriver* aPreviousDriver, |
186 | | GraphTime aLastSwitchNextIterationStart, |
187 | | GraphTime aLastSwitchNextIterationEnd); |
188 | | /** |
189 | | * Call this to indicate that another iteration of the control loop is |
190 | | * required on its regular schedule. The monitor must not be held. |
191 | | * This function has to be idempotent. |
192 | | */ |
193 | | void EnsureNextIteration(); |
194 | | |
195 | | /** |
196 | | * Same thing, but not locked. |
197 | | */ |
198 | | void EnsureNextIterationLocked(); |
199 | | |
200 | 0 | MediaStreamGraphImpl* GraphImpl() const { |
201 | 0 | return mGraphImpl; |
202 | 0 | } |
203 | | |
204 | | // True if the current thread is the GraphDriver's thread. |
205 | | // This is the thread that drives the MSG. |
206 | | virtual bool OnThread() = 0; |
207 | | // GraphDriver's thread has started and the thread is running. |
208 | | // This is the thread that drives the MSG. |
209 | | virtual bool ThreadRunning() = 0; |
210 | | |
211 | | protected: |
212 | | GraphTime StateComputedTime() const; |
213 | | // Sets the associated pointer, asserting that the lock is held |
214 | | void SetNextDriver(GraphDriver* aNextDriver); |
215 | | |
216 | | // Time of the start of this graph iteration. This must be accessed while |
217 | | // having the monitor. |
218 | | GraphTime mIterationStart; |
219 | | // Time of the end of this graph iteration. This must be accessed while having |
220 | | // the monitor. |
221 | | GraphTime mIterationEnd; |
222 | | // The MediaStreamGraphImpl associated with this driver. |
223 | | const RefPtr<MediaStreamGraphImpl> mGraphImpl; |
224 | | |
225 | | // This is used on the main thread (during initialization), and the graph |
226 | | // thread. No monitor needed because we know the graph thread does not run |
227 | | // during the initialization. |
228 | | TimeStamp mCurrentTimeStamp; |
229 | | // This is non-null only when this driver has recently switched from an other |
230 | | // driver, and has not cleaned it up yet (for example because the audio stream |
231 | | // is currently calling the callback during initialization). |
232 | | // |
233 | | // This is written to when changing driver, from the previous driver's thread, |
234 | | // or a thread created for the occasion. This is read each time we need to |
235 | | // check whether we're changing driver (in Switching()), from the graph |
236 | | // thread. |
237 | | // This must be accessed using the {Set,Get}PreviousDriver methods. |
238 | | RefPtr<GraphDriver> mPreviousDriver; |
239 | | // This is non-null only when this driver is going to switch to an other |
240 | | // driver at the end of this iteration. |
241 | | // This must be accessed using the {Set,Get}NextDriver methods. |
242 | | RefPtr<GraphDriver> mNextDriver; |
243 | | virtual ~GraphDriver() |
244 | 0 | { } |
245 | | }; |
246 | | |
247 | | class MediaStreamGraphInitThreadRunnable; |
248 | | |
249 | | /** |
250 | | * This class is a driver that manages its own thread. |
251 | | */ |
252 | | class ThreadedDriver : public GraphDriver |
253 | | { |
254 | | public: |
255 | | explicit ThreadedDriver(MediaStreamGraphImpl* aGraphImpl); |
256 | | virtual ~ThreadedDriver(); |
257 | | void Start() override; |
258 | | void Revive() override; |
259 | | void Shutdown() override; |
260 | | /** |
261 | | * Runs main control loop on the graph thread. Normally a single invocation |
262 | | * of this runs for the entire lifetime of the graph thread. |
263 | | */ |
264 | | void RunThread(); |
265 | | friend class MediaStreamGraphInitThreadRunnable; |
266 | 0 | uint32_t IterationDuration() override { |
267 | 0 | return MEDIA_GRAPH_TARGET_PERIOD_MS; |
268 | 0 | } |
269 | | |
270 | | bool OnThread() override |
271 | 0 | { |
272 | 0 | return !mThread || mThread->EventTarget()->IsOnCurrentThread(); |
273 | 0 | } |
274 | | |
275 | | bool ThreadRunning() override |
276 | 0 | { |
277 | 0 | return mThreadRunning; |
278 | 0 | } |
279 | | |
280 | | /* When the graph wakes up to do an iteration, implementations return the |
281 | | * range of time that will be processed. This is called only once per |
282 | | * iteration; it may determine the interval from state in a previous |
283 | | * call. */ |
284 | | virtual MediaTime GetIntervalForIteration() = 0; |
285 | | protected: |
286 | | nsCOMPtr<nsIThread> mThread; |
287 | | private: |
288 | | // This is true if the thread is running. It is false |
289 | | // before starting the thread and after stopping it. |
290 | | Atomic<bool> mThreadRunning; |
291 | | }; |
292 | | |
293 | | /** |
294 | | * A SystemClockDriver drives a MediaStreamGraph using a system clock, and waits |
295 | | * using a monitor, between each iteration. |
296 | | */ |
297 | | class SystemClockDriver : public ThreadedDriver |
298 | | { |
299 | | public: |
300 | | explicit SystemClockDriver(MediaStreamGraphImpl* aGraphImpl); |
301 | | virtual ~SystemClockDriver(); |
302 | | MediaTime GetIntervalForIteration() override; |
303 | | void WaitForNextIteration() override; |
304 | | void WakeUp() override; |
305 | | void MarkAsFallback(); |
306 | | bool IsFallback(); |
307 | 0 | SystemClockDriver* AsSystemClockDriver() override { |
308 | 0 | return this; |
309 | 0 | } |
310 | | |
311 | | private: |
312 | | // Those are only modified (after initialization) on the graph thread. The |
313 | | // graph thread does not run during the initialization. |
314 | | TimeStamp mInitialTimeStamp; |
315 | | TimeStamp mLastTimeStamp; |
316 | | |
317 | | // This enum specifies the wait state of the driver. |
318 | | enum WaitState { |
319 | | // RunThread() is running normally |
320 | | WAITSTATE_RUNNING, |
321 | | // RunThread() is paused waiting for its next iteration, which will |
322 | | // happen soon |
323 | | WAITSTATE_WAITING_FOR_NEXT_ITERATION, |
324 | | // RunThread() is paused indefinitely waiting for something to change |
325 | | WAITSTATE_WAITING_INDEFINITELY, |
326 | | // Something has signaled RunThread() to wake up immediately, |
327 | | // but it hasn't done so yet |
328 | | WAITSTATE_WAKING_UP |
329 | | }; |
330 | | // This must be access with the monitor. |
331 | | WaitState mWaitState; |
332 | | |
333 | | // This is true if this SystemClockDriver runs the graph because we could not |
334 | | // open an audio stream. |
335 | | bool mIsFallback; |
336 | | }; |
337 | | |
338 | | /** |
339 | | * An OfflineClockDriver runs the graph as fast as possible, without waiting |
340 | | * between iteration. |
341 | | */ |
342 | | class OfflineClockDriver : public ThreadedDriver |
343 | | { |
344 | | public: |
345 | | OfflineClockDriver(MediaStreamGraphImpl* aGraphImpl, GraphTime aSlice); |
346 | | virtual ~OfflineClockDriver(); |
347 | | MediaTime GetIntervalForIteration() override; |
348 | | void WaitForNextIteration() override; |
349 | | void WakeUp() override; |
350 | | TimeStamp GetCurrentTimeStamp() override; |
351 | 0 | OfflineClockDriver* AsOfflineClockDriver() override { |
352 | 0 | return this; |
353 | 0 | } |
354 | | |
355 | | private: |
356 | | // Time, in GraphTime, for each iteration |
357 | | GraphTime mSlice; |
358 | | }; |
359 | | |
360 | | struct StreamAndPromiseForOperation |
361 | | { |
362 | | StreamAndPromiseForOperation(MediaStream* aStream, |
363 | | void* aPromise, |
364 | | dom::AudioContextOperation aOperation); |
365 | | RefPtr<MediaStream> mStream; |
366 | | void* mPromise; |
367 | | dom::AudioContextOperation mOperation; |
368 | | }; |
369 | | |
370 | | enum AsyncCubebOperation { |
371 | | INIT, |
372 | | SHUTDOWN |
373 | | }; |
374 | | |
375 | | /** |
376 | | * This is a graph driver that is based on callback functions called by the |
377 | | * audio api. This ensures minimal audio latency, because it means there is no |
378 | | * buffering happening: the audio is generated inside the callback. |
379 | | * |
380 | | * This design is less flexible than running our own thread: |
381 | | * - We have no control over the thread: |
382 | | * - It cannot block, and it has to run for a shorter amount of time than the |
383 | | * buffer it is going to fill, or an under-run is going to occur (short burst |
384 | | * of silence in the final audio output). |
385 | | * - We can't know for sure when the callback function is going to be called |
386 | | * (although we compute an estimation so we can schedule video frames) |
387 | | * - Creating and shutting the thread down is a blocking operation, that can |
388 | | * take _seconds_ in some cases (because IPC has to be set up, and |
389 | | * sometimes hardware components are involved and need to be warmed up) |
390 | | * - We have no control on how much audio we generate, we have to return exactly |
391 | | * the number of frames asked for by the callback. Since for the Web Audio |
392 | | * API, we have to do block processing at 128 frames per block, we need to |
393 | | * keep a little spill buffer to store the extra frames. |
394 | | */ |
395 | | class AudioCallbackDriver : public GraphDriver, |
396 | | public MixerCallbackReceiver |
397 | | #if defined(XP_WIN) |
398 | | , public audio::DeviceChangeListener |
399 | | #endif |
400 | | { |
401 | | public: |
402 | | /** If aInputChannelCount is zero, then this driver is output-only. */ |
403 | | AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl, uint32_t aInputChannelCount); |
404 | | virtual ~AudioCallbackDriver(); |
405 | | |
406 | | void Start() override; |
407 | | void Revive() override; |
408 | | void WaitForNextIteration() override; |
409 | | void WakeUp() override; |
410 | | void Shutdown() override; |
411 | | #if defined(XP_WIN) |
412 | | void ResetDefaultDevice() override; |
413 | | #endif |
414 | | |
415 | | /* Static wrapper function cubeb calls back. */ |
416 | | static long DataCallback_s(cubeb_stream * aStream, |
417 | | void * aUser, |
418 | | const void * aInputBuffer, |
419 | | void * aOutputBuffer, |
420 | | long aFrames); |
421 | | static void StateCallback_s(cubeb_stream* aStream, void * aUser, cubeb_state aState); |
422 | | static void DeviceChangedCallback_s(void * aUser); |
423 | | /* This function is called by the underlying audio backend when a refill is |
424 | | * needed. This is what drives the whole graph when it is used to output |
425 | | * audio. If the return value is exactly aFrames, this function will get |
426 | | * called again. If it is less than aFrames, the stream will go in draining |
427 | | * mode, and this function will not be called again. */ |
428 | | long DataCallback(const AudioDataValue* aInputBuffer, |
429 | | AudioDataValue* aOutputBuffer, |
430 | | long aFrames); |
431 | | /* This function is called by the underlying audio backend, but is only used |
432 | | * for informational purposes at the moment. */ |
433 | | void StateCallback(cubeb_state aState); |
434 | | /* This is an approximation of the number of millisecond there are between two |
435 | | * iterations of the graph. */ |
436 | | uint32_t IterationDuration() override; |
437 | | |
438 | | /* This function gets called when the graph has produced the audio frames for |
439 | | * this iteration. */ |
440 | | void MixerCallback(AudioDataValue* aMixedBuffer, |
441 | | AudioSampleFormat aFormat, |
442 | | uint32_t aChannels, |
443 | | uint32_t aFrames, |
444 | | uint32_t aSampleRate) override; |
445 | | |
446 | 0 | AudioCallbackDriver* AsAudioCallbackDriver() override { |
447 | 0 | return this; |
448 | 0 | } |
449 | | |
450 | | uint32_t OutputChannelCount() |
451 | 0 | { |
452 | 0 | MOZ_ASSERT(mOutputChannels != 0 && mOutputChannels <= 8); |
453 | 0 | return mOutputChannels; |
454 | 0 | } |
455 | | |
456 | | uint32_t InputChannelCount() |
457 | 0 | { |
458 | 0 | return mInputChannelCount; |
459 | 0 | } |
460 | | |
461 | | /* Enqueue a promise that is going to be resolved when a specific operation |
462 | | * occurs on the cubeb stream. */ |
463 | | void EnqueueStreamAndPromiseForOperation(MediaStream* aStream, |
464 | | void* aPromise, |
465 | | dom::AudioContextOperation aOperation); |
466 | | |
467 | | bool OnThread() override |
468 | 0 | { |
469 | 0 | return mAudioThreadId.load() == std::this_thread::get_id(); |
470 | 0 | } |
471 | | |
472 | | bool ThreadRunning() override |
473 | 0 | { |
474 | 0 | return mAudioThreadRunning; |
475 | 0 | } |
476 | | |
477 | | /* Whether the underlying cubeb stream has been started. See comment for |
478 | | * mStarted for details. */ |
479 | | bool IsStarted(); |
480 | | |
481 | | void CompleteAudioContextOperations(AsyncCubebOperation aOperation); |
482 | | |
483 | | private: |
484 | | /* Remove Mixer callbacks when switching */ |
485 | | void RemoveMixerCallback(); |
486 | | /* Add this driver in Mixer callbacks. */ |
487 | | void AddMixerCallback(); |
488 | | /** |
489 | | * On certain MacBookPro, the microphone is located near the left speaker. |
490 | | * We need to pan the sound output to the right speaker if we are using the |
491 | | * mic and the built-in speaker, or we will have terrible echo. */ |
492 | | void PanOutputIfNeeded(bool aMicrophoneActive); |
493 | | /** |
494 | | * This is called when the output device used by the cubeb stream changes. */ |
495 | | void DeviceChangedCallback(); |
496 | | /* Start the cubeb stream */ |
497 | | bool StartStream(); |
498 | | friend class AsyncCubebTask; |
499 | | bool Init(); |
500 | | void Stop(); |
501 | | /** |
502 | | * Fall back to a SystemClockDriver using a normal thread. If needed, |
503 | | * the graph will try to re-open an audio stream later. */ |
504 | | void FallbackToSystemClockDriver(); |
505 | | |
506 | | /* This is true when the method is executed on CubebOperation thread pool. */ |
507 | | bool OnCubebOperationThread() |
508 | 0 | { |
509 | 0 | return mInitShutdownThread->IsOnCurrentThreadInfallible(); |
510 | 0 | } |
511 | | |
512 | | /* MediaStreamGraphs are always down/up mixed to output channels. */ |
513 | | uint32_t mOutputChannels; |
514 | | /* The size of this buffer comes from the fact that some audio backends can |
515 | | * call back with a number of frames lower than one block (128 frames), so we |
516 | | * need to keep at most two block in the SpillBuffer, because we always round |
517 | | * up to block boundaries during an iteration. |
518 | | * This is only ever accessed on the audio callback thread. */ |
519 | | SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2> mScratchBuffer; |
520 | | /* Wrapper to ensure we write exactly the number of frames we need in the |
521 | | * audio buffer cubeb passes us. This is only ever accessed on the audio |
522 | | * callback thread. */ |
523 | | AudioCallbackBufferWrapper<AudioDataValue> mBuffer; |
524 | | /* cubeb stream for this graph. This is guaranteed to be non-null after Init() |
525 | | * has been called, and is synchronized internaly. */ |
526 | | nsAutoRef<cubeb_stream> mAudioStream; |
527 | | /* The sample rate for the aforementionned cubeb stream. This is set on |
528 | | * initialization and can be read safely afterwards. */ |
529 | | uint32_t mSampleRate; |
530 | | /* The number of input channels from cubeb. Set before opening cubeb. If it is |
531 | | * zero then the driver is output-only. */ |
532 | | const uint32_t mInputChannelCount; |
533 | | /* Approximation of the time between two callbacks. This is used to schedule |
534 | | * video frames. This is in milliseconds. Only even used (after |
535 | | * inizatialization) on the audio callback thread. */ |
536 | | uint32_t mIterationDurationMS; |
537 | | /* cubeb_stream_init calls the audio callback to prefill the buffers. The |
538 | | * previous driver has to be kept alive until the audio stream has been |
539 | | * started, because it is responsible to call cubeb_stream_start, so we delay |
540 | | * the cleanup of the previous driver until it has started the audio stream. |
541 | | * Otherwise, there is a race where we kill the previous driver thread |
542 | | * between cubeb_stream_init and cubeb_stream_start, |
543 | | * and callbacks after the prefill never get called. |
544 | | * This is written on the previous driver's thread (if switching) or main |
545 | | * thread (if this driver is the first one). |
546 | | * This is read on previous driver's thread (during callbacks from |
547 | | * cubeb_stream_init) and the audio thread (when switching away from this |
548 | | * driver back to a SystemClockDriver). |
549 | | * This is synchronized by the Graph's monitor. |
550 | | * */ |
551 | | Atomic<bool> mStarted; |
552 | | |
553 | | struct AutoInCallback |
554 | | { |
555 | | explicit AutoInCallback(AudioCallbackDriver* aDriver); |
556 | | ~AutoInCallback(); |
557 | | AudioCallbackDriver* mDriver; |
558 | | }; |
559 | | |
560 | | /* Shared thread pool with up to one thread for off-main-thread |
561 | | * initialization and shutdown of the audio stream via AsyncCubebTask. */ |
562 | | const RefPtr<SharedThreadPool> mInitShutdownThread; |
563 | | /* This must be accessed with the graph monitor held. */ |
564 | | AutoTArray<StreamAndPromiseForOperation, 1> mPromisesForOperation; |
565 | | /* This is used to signal adding the mixer callback on first run |
566 | | * of audio callback. This is atomic because it is touched from different |
567 | | * threads, the audio callback thread and the state change thread. However, |
568 | | * the order of the threads does not allow concurent access. */ |
569 | | Atomic<bool> mAddedMixer; |
570 | | |
571 | | /* Contains the id of the audio thread for as long as the callback |
572 | | * is taking place, after that it is reseted to an invalid value. */ |
573 | | std::atomic<std::thread::id> mAudioThreadId; |
574 | | /* True when audio thread is running. False before |
575 | | * starting and after stopping it the audio thread. */ |
576 | | Atomic<bool> mAudioThreadRunning; |
577 | | /* Indication of whether a fallback SystemClockDriver should be started if |
578 | | * StateCallback() receives an error. No mutex need be held during access. |
579 | | * The transition to true happens before cubeb_stream_start() is called. |
580 | | * After transitioning to false on the last DataCallback(), the stream is |
581 | | * not accessed from another thread until the graph thread either signals |
582 | | * main thread cleanup or dispatches an event to switch to another |
583 | | * driver. */ |
584 | | bool mShouldFallbackIfError; |
585 | | /* True if this driver was created from a driver created because of a previous |
586 | | * AudioCallbackDriver failure. */ |
587 | | bool mFromFallback; |
588 | | }; |
589 | | |
590 | | class AsyncCubebTask : public Runnable |
591 | | { |
592 | | public: |
593 | | |
594 | | AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation); |
595 | | |
596 | | nsresult Dispatch(uint32_t aFlags = NS_DISPATCH_NORMAL) |
597 | 0 | { |
598 | 0 | return mDriver->mInitShutdownThread->Dispatch(this, aFlags); |
599 | 0 | } |
600 | | |
601 | | protected: |
602 | | virtual ~AsyncCubebTask(); |
603 | | |
604 | | private: |
605 | | NS_IMETHOD Run() final; |
606 | | |
607 | | RefPtr<AudioCallbackDriver> mDriver; |
608 | | AsyncCubebOperation mOperation; |
609 | | RefPtr<MediaStreamGraphImpl> mShutdownGrip; |
610 | | }; |
611 | | |
612 | | } // namespace mozilla |
613 | | |
614 | | #endif // GRAPHDRIVER_H_ |