Coverage Report

Created: 2018-09-25 14:53

/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_