MediaTrackGraphImpl.h (45348B)
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_MEDIATRACKGRAPHIMPL_H_ 7 #define MOZILLA_MEDIATRACKGRAPHIMPL_H_ 8 9 #include <atomic> 10 11 #include "AsyncLogger.h" 12 #include "AudioMixer.h" 13 #include "DeviceInputTrack.h" 14 #include "GraphDriver.h" 15 #include "MediaEventSource.h" 16 #include "MediaTrackGraph.h" 17 #include "mozilla/Atomics.h" 18 #include "mozilla/Monitor.h" 19 #include "mozilla/TimeStamp.h" 20 #include "mozilla/UniquePtr.h" 21 #include "nsClassHashtable.h" 22 #include "nsIMemoryReporter.h" 23 #include "nsINamed.h" 24 #include "nsIRunnable.h" 25 #include "nsIThreadInternal.h" 26 #include "nsITimer.h" 27 28 namespace mozilla { 29 30 namespace media { 31 class ShutdownBlocker; 32 } 33 34 class AudioContextOperationControlMessage; 35 class CubebDeviceEnumerator; 36 template <typename T> 37 class LinkedList; 38 class GraphRunner; 39 40 class DeviceInputTrackManager { 41 public: 42 DeviceInputTrackManager() = default; 43 44 // Returns the current NativeInputTrack. 45 NativeInputTrack* GetNativeInputTrack(); 46 // Returns the DeviceInputTrack paired with the device of aID if it exists. 47 // Otherwise, returns nullptr. 48 DeviceInputTrack* GetDeviceInputTrack(CubebUtils::AudioDeviceID aID); 49 // Returns the first added NonNativeInputTrack if any. Otherwise, returns 50 // nullptr. 51 NonNativeInputTrack* GetFirstNonNativeInputTrack(); 52 // Adds DeviceInputTrack to the managing list. 53 void Add(DeviceInputTrack* aTrack); 54 // Removes DeviceInputTrack from the managing list. 55 void Remove(DeviceInputTrack* aTrack); 56 57 private: 58 RefPtr<NativeInputTrack> mNativeInputTrack; 59 nsTArray<RefPtr<NonNativeInputTrack>> mNonNativeInputTracks; 60 }; 61 62 /** 63 * A per-track update message passed from the media graph thread to the 64 * main thread. 65 */ 66 struct TrackUpdate { 67 RefPtr<MediaTrack> mTrack; 68 TrackTime mNextMainThreadCurrentTime; 69 bool mNextMainThreadEnded; 70 }; 71 72 /** 73 * This represents a message run on the graph thread to modify track or graph 74 * state. These are passed from main thread to graph thread through 75 * AppendMessage(). A ControlMessage often has a weak reference to a 76 * particular affected track. 77 */ 78 class ControlMessage : public MediaTrack::ControlMessageInterface { 79 public: 80 explicit ControlMessage(MediaTrack* aTrack) : mTrack(aTrack) { 81 MOZ_ASSERT(NS_IsMainThread()); 82 MOZ_RELEASE_ASSERT(!aTrack || !aTrack->IsDestroyed()); 83 } 84 85 MediaTrack* GetTrack() { return mTrack; } 86 87 protected: 88 // We do not hold a reference to mTrack. The graph will be holding a reference 89 // to the track until the Destroy message is processed. The last message 90 // referencing a track is the Destroy message for that track. 91 MediaTrack* const mTrack; 92 }; 93 94 class MessageBlock { 95 public: 96 nsTArray<UniquePtr<MediaTrack::ControlMessageInterface>> mMessages; 97 }; 98 99 /** 100 * The implementation of a media track graph. This class is private to this 101 * file. It's not in the anonymous namespace because MediaTrack needs to 102 * be able to friend it. 103 * 104 * There can be multiple MediaTrackGraph per process: one per document. 105 * Additionaly, each OfflineAudioContext object creates its own MediaTrackGraph 106 * object too. 107 */ 108 class MediaTrackGraphImpl : public MediaTrackGraph, 109 public GraphInterface, 110 public nsIMemoryReporter, 111 public nsIObserver, 112 public nsIThreadObserver, 113 public nsITimerCallback, 114 public nsINamed { 115 public: 116 using ControlMessageInterface = MediaTrack::ControlMessageInterface; 117 118 NS_DECL_THREADSAFE_ISUPPORTS 119 NS_DECL_NSIMEMORYREPORTER 120 NS_DECL_NSIOBSERVER 121 NS_DECL_NSITHREADOBSERVER 122 NS_DECL_NSITIMERCALLBACK 123 NS_DECL_NSINAMED 124 125 /** 126 * Use aGraphDriverRequested with SYSTEM_THREAD_DRIVER or AUDIO_THREAD_DRIVER 127 * to create a MediaTrackGraph which provides support for real-time audio 128 * and/or video. Set it to OFFLINE_THREAD_DRIVER in order to create a 129 * non-realtime instance which just churns through its inputs and produces 130 * output. Those objects currently only support audio, and are used to 131 * implement OfflineAudioContext. They do not support MediaTrack inputs. 132 */ 133 explicit MediaTrackGraphImpl(uint64_t aWindowID, TrackRate aSampleRate, 134 CubebUtils::AudioDeviceID aOutputDeviceID, 135 nsISerialEventTarget* aMainThread); 136 137 static MediaTrackGraphImpl* GetInstance( 138 GraphDriverType aGraphDriverRequested, uint64_t aWindowID, 139 TrackRate aSampleRate, CubebUtils::AudioDeviceID aPrimaryOutputDeviceID, 140 nsISerialEventTarget* aMainThread); 141 static MediaTrackGraphImpl* GetInstanceIfExists( 142 uint64_t aWindowID, TrackRate aSampleRate, 143 CubebUtils::AudioDeviceID aPrimaryOutputDeviceID); 144 static MediaTrackGraph* CreateNonRealtimeInstance(TrackRate aSampleRate); 145 // For GraphHashSet: 146 struct Lookup; 147 operator Lookup() const; 148 149 // Intended only for assertions, either on graph thread or not running (in 150 // which case we must be on the main thread). 151 bool OnGraphThreadOrNotRunning() const override; 152 bool OnGraphThread() const override; 153 154 bool Destroyed() const override; 155 156 #ifdef DEBUG 157 /** 158 * True if we're on aDriver's thread, or if we're on mGraphRunner's thread 159 * and mGraphRunner is currently run by aDriver. 160 */ 161 bool InDriverIteration(const GraphDriver* aDriver) const override; 162 #endif 163 164 /** 165 * Unregisters memory reporting and deletes this instance. This should be 166 * called instead of calling the destructor directly. 167 */ 168 void Destroy(); 169 170 // Main thread only. 171 /** 172 * This runs every time we need to sync state from the media graph thread 173 * to the main thread while the main thread is not in the middle 174 * of a script. It runs during a "stable state" (per HTML5) or during 175 * an event posted to the main thread. 176 * The boolean affects which boolean controlling runnable dispatch is cleared 177 */ 178 void RunInStableState(bool aSourceIsMTG); 179 /** 180 * Ensure a runnable to run RunInStableState is posted to the appshell to 181 * run at the next stable state (per HTML5). 182 * See EnsureStableStateEventPosted. 183 */ 184 void EnsureRunInStableState(); 185 /** 186 * Called to apply a TrackUpdate to its track. 187 */ 188 void ApplyTrackUpdate(TrackUpdate* aUpdate) MOZ_REQUIRES(mMonitor); 189 /** 190 * Append a control message to the message queue. This queue is drained 191 * during RunInStableState; the messages will run on the graph thread. 192 */ 193 virtual void AppendMessage(UniquePtr<ControlMessageInterface> aMessage); 194 /** 195 * Append to the message queue a control message to execute a given lambda 196 * function with no parameters. The lambda will be executed on the graph 197 * thread. The lambda will not be executed if the graph has been forced to 198 * shut down. 199 **/ 200 template <typename Function> 201 void QueueControlMessageWithNoShutdown(Function&& aFunction) { 202 AppendMessage(WrapUnique(new MediaTrack::ControlMessageWithNoShutdown( 203 std::forward<Function>(aFunction)))); 204 } 205 /** 206 * Append to the message queue a control message to execute a given lambda 207 * function with a single IsInShutdown parameter. A No argument indicates 208 * execution on the thread of a graph that is still running. A Yes argument 209 * indicates execution on the main thread when the graph has been forced to 210 * shut down. 211 **/ 212 template <typename Function> 213 void QueueControlOrShutdownMessage(Function&& aFunction) { 214 AppendMessage(WrapUnique(new MediaTrack::ControlOrShutdownMessage( 215 std::forward<Function>(aFunction)))); 216 } 217 /* Add or remove an audio output for this track. At most one output may be 218 * registered per key. aPreferredSampleRate is the rate preferred by the 219 * output device; it may be zero to indicate the preferred rate for the 220 * default device; it is unused when aDeviceID is the graph's primary output. 221 */ 222 void RegisterAudioOutput(MediaTrack* aTrack, void* aKey, 223 CubebUtils::AudioDeviceID aDeviceID, 224 TrackRate aPreferredSampleRate); 225 void UnregisterAudioOutput(MediaTrack* aTrack, void* aKey); 226 227 void SetAudioOutputVolume(MediaTrack* aTrack, void* aKey, float aVolume); 228 /* Manage the creation and destruction of CrossGraphReceivers. 229 * aPreferredSampleRate is the rate preferred by the output device. */ 230 void IncrementOutputDeviceRefCnt(CubebUtils::AudioDeviceID aDeviceID, 231 TrackRate aPreferredSampleRate); 232 void DecrementOutputDeviceRefCnt(CubebUtils::AudioDeviceID aDeviceID); 233 /* Send a control message to update mOutputDevices for main thread changes to 234 * mAudioOutputParams. */ 235 void UpdateAudioOutput(MediaTrack* aTrack, 236 CubebUtils::AudioDeviceID aDeviceID); 237 /** 238 * Dispatches a runnable from any thread to the correct main thread for this 239 * MediaTrackGraph. 240 */ 241 void Dispatch(already_AddRefed<nsIRunnable>&& aRunnable); 242 243 /** 244 * Make this MediaTrackGraph enter forced-shutdown state. This state 245 * will be noticed by the media graph thread, which will shut down all tracks 246 * and other state controlled by the media graph thread. 247 * This is called during application shutdown, and on document unload if an 248 * AudioContext is using the graph. 249 */ 250 void ForceShutDown(); 251 252 /** 253 * Sets mShutdownBlocker and makes it block shutdown. 254 * Main thread only. Not idempotent. Returns true if a blocker was added, 255 * false if this failed. 256 */ 257 bool AddShutdownBlocker(); 258 259 /** 260 * Removes mShutdownBlocker and unblocks shutdown. 261 * Main thread only. Idempotent. 262 */ 263 void RemoveShutdownBlocker(); 264 265 /** 266 * Called before the thread runs. 267 */ 268 void Init(GraphDriverType aDriverRequested, GraphRunType aRunTypeRequested, 269 uint32_t aChannelCount); 270 271 /** 272 * Respond to CollectReports with sizes collected on the graph thread. 273 */ 274 static void FinishCollectReports( 275 nsIHandleReportCallback* aHandleReport, nsISupports* aData, 276 const nsTArray<AudioNodeSizes>& aAudioTrackSizes); 277 278 // The following methods run on the graph thread (or possibly the main thread 279 // if mLifecycleState > LIFECYCLE_RUNNING) 280 void CollectSizesForMemoryReport( 281 already_AddRefed<nsIHandleReportCallback> aHandleReport, 282 already_AddRefed<nsISupports> aHandlerData); 283 284 /** 285 * Returns true if this MediaTrackGraph should keep running 286 */ 287 bool UpdateMainThreadState(); 288 289 /** 290 * Proxy method called by GraphDriver to iterate the graph. 291 * If this graph was created with GraphRunType SINGLE_THREAD, mGraphRunner 292 * will take care of calling OneIterationImpl from its thread. Otherwise, 293 * OneIterationImpl is called directly. Mixed audio output from the graph is 294 * passed into aMixerReceiver, if it is non-null. 295 */ 296 IterationResult OneIteration(GraphTime aStateTime, 297 MixerCallbackReceiver* aMixerReceiver) override; 298 299 /** 300 * Returns true if this MediaTrackGraph should keep running 301 */ 302 IterationResult OneIterationImpl(GraphTime aStateTime, 303 MixerCallbackReceiver* aMixerReceiver); 304 305 /** 306 * Called from the driver, when the graph thread is about to stop, to tell 307 * the main thread to attempt to begin cleanup. The main thread may either 308 * shutdown or revive the graph depending on whether it receives new 309 * messages. 310 */ 311 void SignalMainThreadCleanup(); 312 313 /** 314 * Ensure there is an event posted to the main thread to run RunInStableState. 315 * mMonitor must be held. 316 * See EnsureRunInStableState 317 */ 318 void EnsureStableStateEventPosted() MOZ_REQUIRES(mMonitor); 319 /** 320 * Generate messages to the main thread to update it for all state changes. 321 * mMonitor must be held. 322 */ 323 void PrepareUpdatesToMainThreadState(bool aFinalUpdate) 324 MOZ_REQUIRES(mMonitor); 325 /** 326 * If we are rendering in non-realtime mode, we don't want to send messages to 327 * the main thread at each iteration for performance reasons. We instead 328 * notify the main thread at the same rate 329 */ 330 bool ShouldUpdateMainThread(); 331 // The following methods are the various stages of RunThread processing. 332 /** 333 * Advance all track state to mStateComputedTime. 334 */ 335 void UpdateCurrentTimeForTracks(GraphTime aPrevCurrentTime); 336 /** 337 * Process chunks for all tracks and raise events for properties that have 338 * changed, such as principalId. 339 */ 340 void ProcessChunkMetadata(GraphTime aPrevCurrentTime); 341 /** 342 * Process chunks for the given track and interval, and raise events for 343 * properties that have changed, such as principalHandle. 344 */ 345 template <typename C, typename Chunk> 346 void ProcessChunkMetadataForInterval(MediaTrack* aTrack, C& aSegment, 347 TrackTime aStart, TrackTime aEnd); 348 /** 349 * Process graph messages in mFrontMessageQueue. 350 */ 351 void RunMessagesInQueue(); 352 /** 353 * Update track processing order and recompute track blocking until 354 * aEndBlockingDecisions. 355 */ 356 void UpdateGraph(GraphTime aEndBlockingDecisions); 357 358 void SwapMessageQueues() { 359 MonitorAutoLock lock(mMonitor); 360 MOZ_ASSERT(OnGraphThreadOrNotRunning()); 361 MOZ_ASSERT(mFrontMessageQueue.IsEmpty()); 362 mFrontMessageQueue.SwapElements(mBackMessageQueue); 363 } 364 /** 365 * Do all the processing and play the audio and video, from 366 * mProcessedTime to mStateComputedTime. 367 */ 368 void Process(MixerCallbackReceiver* aMixerReceiver); 369 370 /** 371 * For use during ProcessedMediaTrack::ProcessInput() or 372 * MediaTrackListener callbacks, when graph state cannot be changed. 373 * Schedules |aMessage| to run after processing, at a time when graph state 374 * can be changed. Graph thread. 375 */ 376 void RunMessageAfterProcessing(UniquePtr<ControlMessageInterface> aMessage); 377 378 /* From the main thread, ask the MTG to resolve the returned promise when 379 * the device specified has started. 380 * A null aDeviceID indicates the default audio output device. 381 * The promise is rejected with NS_ERROR_INVALID_ARG if aDeviceID does not 382 * correspond to any output devices used by the graph, or 383 * NS_ERROR_NOT_AVAILABLE if outputs to the device are removed or 384 * NS_ERROR_ILLEGAL_DURING_SHUTDOWN if the graph is force shut down 385 * before the promise could be resolved. 386 */ 387 using GraphStartedPromise = GenericPromise; 388 RefPtr<GraphStartedPromise> NotifyWhenDeviceStarted( 389 CubebUtils::AudioDeviceID aDeviceID) override; 390 391 /** 392 * Resolve the GraphStartedPromise when the driver has started processing on 393 * the audio thread after the device has started. 394 * (Audio is initially processed in the FallbackDriver's thread while the 395 * device is starting up.) 396 */ 397 void NotifyWhenPrimaryDeviceStarted( 398 MozPromiseHolder<GraphStartedPromise>&& aHolder); 399 400 /** 401 * Apply an AudioContext operation (suspend/resume/close), on the graph 402 * thread. 403 */ 404 void ApplyAudioContextOperationImpl( 405 AudioContextOperationControlMessage* aMessage); 406 407 /** 408 * Determine if we have any audio tracks, or are about to add any audiotracks. 409 */ 410 bool AudioTrackPresent(); 411 412 /** 413 * Schedules a replacement GraphDriver in mNextDriver, if necessary. 414 */ 415 void CheckDriver(); 416 417 /** 418 * Sort mTracks so that every track not in a cycle is after any tracks 419 * it depends on, and every track in a cycle is marked as being in a cycle. 420 */ 421 void UpdateTrackOrder(); 422 423 /** 424 * Returns smallest value of t such that t is a multiple of 425 * WEBAUDIO_BLOCK_SIZE and t >= aTime. 426 */ 427 static GraphTime RoundUpToEndOfAudioBlock(GraphTime aTime); 428 /** 429 * Returns smallest value of t such that t is a multiple of 430 * WEBAUDIO_BLOCK_SIZE and t > aTime. 431 */ 432 static GraphTime RoundUpToNextAudioBlock(GraphTime aTime); 433 /** 434 * Produce data for all tracks >= aTrackIndex for the current time interval. 435 * Advances block by block, each iteration producing data for all tracks 436 * for a single block. 437 * This is called whenever we have an AudioNodeTrack in the graph. 438 */ 439 void ProduceDataForTracksBlockByBlock(uint32_t aTrackIndex, 440 TrackRate aSampleRate); 441 /** 442 * If aTrack will underrun between aTime, and aEndBlockingDecisions, returns 443 * the time at which the underrun will start. Otherwise return 444 * aEndBlockingDecisions. 445 */ 446 GraphTime WillUnderrun(MediaTrack* aTrack, GraphTime aEndBlockingDecisions); 447 448 /** 449 * Given a graph time aTime, convert it to a track time taking into 450 * account the time during which aTrack is scheduled to be blocked. 451 */ 452 TrackTime GraphTimeToTrackTimeWithBlocking(const MediaTrack* aTrack, 453 GraphTime aTime) const; 454 455 protected: 456 /** 457 * Set mOutputDeviceForAEC to indicate the audio output to be passed as the 458 * reverse stream for audio echo cancellation. Graph thread. 459 */ 460 void SelectOutputDeviceForAEC(); 461 /** 462 * Queue audio (mix of track audio and silence for blocked intervals) 463 * to the audio output track. Returns the number of frames played. 464 */ 465 struct TrackAndVolume; 466 TrackTime PlayAudio(const TrackAndVolume& aOutput, GraphTime aPlayedTime, 467 uint32_t aOutputChannelCount); 468 469 public: 470 /* Runs off a message on the graph thread when something requests audio from 471 * an input audio device of ID aID, and delivers the input audio frames to 472 * aListener. */ 473 void OpenAudioInputImpl(DeviceInputTrack* aTrack); 474 /* Called on the main thread when something requests audio from an input 475 * audio device aID. */ 476 virtual void OpenAudioInput(DeviceInputTrack* aTrack) override; 477 478 /* Runs off a message on the graph when input audio from aID is not needed 479 * anymore, for a particular track. It can be that other tracks still need 480 * audio from this audio input device. */ 481 void CloseAudioInputImpl(DeviceInputTrack* aTrack); 482 /* Called on the main thread when input audio from aID is not needed 483 * anymore, for a particular track. It can be that other tracks still need 484 * audio from this audio input device. */ 485 virtual void CloseAudioInput(DeviceInputTrack* aTrack) override; 486 487 void UnregisterAllAudioOutputs(MediaTrack* aTrack); 488 489 /* Called on the graph thread when the input device settings should be 490 * reevaluated, for example, if the channel count of the input track should 491 * be changed. */ 492 void ReevaluateInputDevice(CubebUtils::AudioDeviceID aID); 493 494 /* Called on the graph thread when there is new output data for listeners. 495 * This is the mixed audio output of this MediaTrackGraph. */ 496 void NotifyOutputData(const AudioChunk& aChunk); 497 /* Called on the graph thread after an AudioCallbackDriver with an input 498 * stream has stopped. */ 499 void NotifyInputStopped() override; 500 /* Called on the graph thread when there is new input data for listeners. This 501 * is the raw audio input for this MediaTrackGraph. */ 502 void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames, 503 TrackRate aRate, uint32_t aChannels, 504 uint32_t aAlreadyBuffered) override; 505 /* Called on the main thread after an AudioCallbackDriver has attempted an 506 * operation to set aRequestedParams on the cubeb stream. */ 507 void NotifySetRequestedInputProcessingParamsResult( 508 AudioCallbackDriver* aDriver, int aGeneration, 509 Result<cubeb_input_processing_params, int>&& aResult) override; 510 /* Called every time there are changes to input/output audio devices like 511 * plug/unplug etc. This can be called on any thread, and posts a message to 512 * the main thread so that it can post a message to the graph thread. */ 513 void DeviceChanged() override; 514 /* Called every time there are changes to input/output audio devices. This is 515 * called on the graph thread. */ 516 void DeviceChangedImpl(); 517 518 /** 519 * Compute how much track data we would like to buffer for aTrack. 520 */ 521 TrackTime GetDesiredBufferEnd(MediaTrack* aTrack); 522 /** 523 * Returns true when there are no active tracks. 524 */ 525 bool IsEmpty() const { 526 MOZ_ASSERT( 527 OnGraphThreadOrNotRunning() || 528 (NS_IsMainThread() && 529 LifecycleStateRef() >= LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP)); 530 return mTracks.IsEmpty() && mSuspendedTracks.IsEmpty() && mPortCount == 0; 531 } 532 533 /** 534 * Add aTrack to the graph and initializes its graph-specific state. 535 */ 536 void AddTrackGraphThread(MediaTrack* aTrack); 537 /** 538 * Remove aTrack from the graph. Ensures that pending messages about the 539 * track back to the main thread are flushed. 540 */ 541 void RemoveTrackGraphThread(MediaTrack* aTrack); 542 /** 543 * Remove a track from the graph. Main thread. 544 */ 545 void RemoveTrack(MediaTrack* aTrack); 546 /** 547 * Remove aPort from the graph and release it. 548 */ 549 void DestroyPort(MediaInputPort* aPort); 550 /** 551 * Mark the media track order as dirty. 552 */ 553 void SetTrackOrderDirty() { 554 MOZ_ASSERT(OnGraphThreadOrNotRunning()); 555 mTrackOrderDirty = true; 556 } 557 558 protected: 559 // Get the current maximum channel count required for a device. 560 // aDevice is an element of mOutputDevices. Graph thread only. 561 struct OutputDeviceEntry; 562 uint32_t AudioOutputChannelCount(const OutputDeviceEntry& aDevice) const; 563 // Get the current maximum channel count for audio output through an 564 // AudioCallbackDriver. Graph thread only. 565 uint32_t PrimaryOutputChannelCount() const; 566 567 public: 568 // Set a new maximum channel count. Graph thread only. 569 void SetMaxOutputChannelCount(uint32_t aMaxChannelCount); 570 571 double AudioOutputLatency(); 572 /* Return whether the clock for the audio output device used for the AEC 573 * reverse stream might drift from the clock for this MediaTrackGraph. */ 574 bool OutputForAECMightDrift() { 575 AssertOnGraphThread(); 576 return mOutputDeviceForAEC != PrimaryOutputDeviceID(); 577 } 578 /* Return whether the audio output device used for the aec reverse stream 579 * corresponds to the primary output device, explicitly or implicitly. 580 * Implicitly meaning when the primary output device is the system default 581 * output device, and the output device used for the aec reverse stream is 582 * explicit and matches the current system default output device. */ 583 bool OutputForAECIsPrimary() { 584 AssertOnGraphThread(); 585 if (mOutputDeviceForAEC == PrimaryOutputDeviceID()) { 586 // Output device for AEC is explicitly the primary output device, which is 587 // used for the duplex stream of the graph driver. 588 return true; 589 } 590 // The output device for AEC is still considered primary if the primary 591 // output device is set to follow the system default output device, and the 592 // output device for AEC is explicitly the current system default output 593 // device. 594 return PrimaryOutputDeviceID() == DEFAULT_OUTPUT_DEVICE && 595 mOutputDeviceForAEC == mDefaultOutputDeviceID; 596 } 597 CubebUtils::AudioDeviceID DefaultOutputDeviceID() const { 598 return mDefaultOutputDeviceID.load(std::memory_order_relaxed); 599 } 600 /** 601 * Update whether the enumerator is set up for default output device tracking, 602 * based on presence of input devices. 603 * Marked virtual for unittests. Main thread only. 604 */ 605 virtual void UpdateEnumeratorDefaultDeviceTracking(); 606 /** 607 * Update the tracked default output device from the enumerator. 608 * Main thread only. 609 */ 610 void UpdateDefaultDevice(); 611 /** 612 * The audio input channel count for a MediaTrackGraph is the max of all the 613 * channel counts requested by the listeners. The max channel count is 614 * delivered to the listeners themselves, and they take care of downmixing. 615 */ 616 uint32_t AudioInputChannelCount(CubebUtils::AudioDeviceID aID); 617 618 AudioInputType AudioInputDevicePreference(CubebUtils::AudioDeviceID aID); 619 620 double MediaTimeToSeconds(GraphTime aTime) const { 621 NS_ASSERTION(aTime > -TRACK_TIME_MAX && aTime <= TRACK_TIME_MAX, 622 "Bad time"); 623 return static_cast<double>(aTime) / GraphRate(); 624 } 625 626 /** 627 * Signal to the graph that the thread has paused indefinitly, 628 * or resumed. 629 */ 630 void PausedIndefinitly(); 631 void ResumedFromPaused(); 632 633 /** 634 * Not safe to call off the MediaTrackGraph thread unless monitor is held! 635 */ 636 GraphDriver* CurrentDriver() const MOZ_NO_THREAD_SAFETY_ANALYSIS { 637 #ifdef DEBUG 638 if (!OnGraphThreadOrNotRunning()) { 639 mMonitor.AssertCurrentThreadOwns(); 640 } 641 #endif 642 return mDriver; 643 } 644 645 /** 646 * Effectively set the new driver, while we are switching. 647 * It is only safe to call this at the very end of an iteration, when there 648 * has been a SwitchAtNextIteration call during the iteration. The driver 649 * should return and pass the control to the new driver shortly after. 650 * Monitor must be held. 651 */ 652 void SetCurrentDriver(GraphDriver* aDriver) { 653 MOZ_ASSERT_IF(mGraphDriverRunning, InDriverIteration(mDriver)); 654 MOZ_ASSERT_IF(!mGraphDriverRunning, NS_IsMainThread()); 655 MonitorAutoLock lock(GetMonitor()); 656 mDriver = aDriver; 657 } 658 659 GraphDriver* NextDriver() const { 660 MOZ_ASSERT(OnGraphThread()); 661 return mNextDriver; 662 } 663 664 bool Switching() const { return NextDriver(); } 665 666 void SwitchAtNextIteration(GraphDriver* aNextDriver); 667 668 Monitor& GetMonitor() { return mMonitor; } 669 670 void EnsureNextIteration() { CurrentDriver()->EnsureNextIteration(); } 671 672 // Capture API. This allows to get a mixed-down output for a window. 673 void RegisterCaptureTrackForWindow(uint64_t aWindowId, 674 ProcessedMediaTrack* aCaptureTrack); 675 void UnregisterCaptureTrackForWindow(uint64_t aWindowId); 676 already_AddRefed<MediaInputPort> ConnectToCaptureTrack( 677 uint64_t aWindowId, MediaTrack* aMediaTrack); 678 679 Watchable<GraphTime>& CurrentTime() override; 680 681 /** 682 * Interrupt any JS running on the graph thread. 683 * Called on the main thread when shutting down the graph. 684 */ 685 void InterruptJS(); 686 687 class TrackSet { 688 public: 689 class iterator { 690 public: 691 explicit iterator(MediaTrackGraphImpl& aGraph) 692 : mGraph(&aGraph), mArrayNum(-1), mArrayIndex(0) { 693 ++(*this); 694 } 695 iterator() : mGraph(nullptr), mArrayNum(2), mArrayIndex(0) {} 696 MediaTrack* operator*() { return Array()->ElementAt(mArrayIndex); } 697 iterator operator++() { 698 ++mArrayIndex; 699 while (mArrayNum < 2 && 700 (mArrayNum < 0 || mArrayIndex >= Array()->Length())) { 701 ++mArrayNum; 702 mArrayIndex = 0; 703 } 704 return *this; 705 } 706 bool operator==(const iterator& aOther) const { 707 return mArrayNum == aOther.mArrayNum && 708 mArrayIndex == aOther.mArrayIndex; 709 } 710 bool operator!=(const iterator& aOther) const { 711 return !(*this == aOther); 712 } 713 714 private: 715 nsTArray<MediaTrack*>* Array() { 716 return mArrayNum == 0 ? &mGraph->mTracks : &mGraph->mSuspendedTracks; 717 } 718 MediaTrackGraphImpl* mGraph; 719 int mArrayNum; 720 uint32_t mArrayIndex; 721 }; 722 723 explicit TrackSet(MediaTrackGraphImpl& aGraph) : mGraph(aGraph) {} 724 iterator begin() { return iterator(mGraph); } 725 iterator end() { return iterator(); } 726 727 private: 728 MediaTrackGraphImpl& mGraph; 729 }; 730 TrackSet AllTracks() { return TrackSet(*this); } 731 732 // Data members 733 734 /** 735 * The ID of the inner Window which uses this graph, or zero for offline 736 * graphs. 737 */ 738 const uint64_t mWindowID; 739 /* 740 * If set, the GraphRunner class handles handing over data from audio 741 * callbacks to a common single thread, shared across GraphDrivers. 742 */ 743 RefPtr<GraphRunner> mGraphRunner; 744 745 /** 746 * Main-thread view of the number of tracks in this graph, for lifetime 747 * management. 748 * 749 * When this becomes zero, the graph is marked as forbidden to add more 750 * tracks to. It will be shut down shortly after. 751 */ 752 size_t mMainThreadTrackCount = 0; 753 754 /** 755 * Main-thread view of the number of ports in this graph, to catch bugs. 756 * 757 * When this becomes zero, and mMainThreadTrackCount is 0, the graph is 758 * marked as forbidden to add more control messages to. It will be shut down 759 * shortly after. 760 */ 761 size_t mMainThreadPortCount = 0; 762 763 /** 764 * Graphs own owning references to their driver, until shutdown. When a driver 765 * switch occur, previous driver is either deleted, or it's ownership is 766 * passed to a event that will take care of the asynchronous cleanup, as 767 * audio track can take some time to shut down. 768 * Accessed on both the main thread and the graph thread; both read and write. 769 * Must hold monitor to access it. 770 */ 771 RefPtr<GraphDriver> mDriver; 772 773 // Set during an iteration to switch driver after the iteration has finished. 774 // Should the current iteration be the last iteration, the next driver will be 775 // discarded. Access through SwitchAtNextIteration()/NextDriver(). Graph 776 // thread only. 777 RefPtr<GraphDriver> mNextDriver; 778 779 // The following state is managed on the graph thread only, unless 780 // mLifecycleState > LIFECYCLE_RUNNING in which case the graph thread 781 // is not running and this state can be used from the main thread. 782 783 /** 784 * The graph keeps a reference to each track. 785 * References are maintained manually to simplify reordering without 786 * unnecessary thread-safe refcount changes. 787 * Must satisfy OnGraphThreadOrNotRunning(). 788 */ 789 nsTArray<MediaTrack*> mTracks; 790 /** 791 * This stores MediaTracks that are part of suspended AudioContexts. 792 * mTracks and mSuspendTracks are disjoint sets: a track is either suspended 793 * or not suspended. Suspended tracks are not ordered in UpdateTrackOrder, 794 * and are therefore not doing any processing. 795 * Must satisfy OnGraphThreadOrNotRunning(). 796 */ 797 nsTArray<MediaTrack*> mSuspendedTracks; 798 /** 799 * Tracks from mFirstCycleBreaker to the end of mTracks produce output before 800 * they receive input. They correspond to DelayNodes that are in cycles. 801 */ 802 uint32_t mFirstCycleBreaker; 803 /** 804 * Blocking decisions have been computed up to this time. 805 * Between each iteration, this is the same as mProcessedTime. 806 */ 807 GraphTime mStateComputedTime = 0; 808 /** 809 * All track contents have been computed up to this time. 810 * The next batch of updates from the main thread will be processed 811 * at this time. This is behind mStateComputedTime during processing. 812 */ 813 GraphTime mProcessedTime = 0; 814 /** 815 * The graph should stop processing at this time. 816 */ 817 GraphTime mEndTime; 818 /** 819 * Date of the last time we updated the main thread with the graph state. 820 */ 821 TimeStamp mLastMainThreadUpdate; 822 /** 823 * Number of active MediaInputPorts 824 */ 825 int32_t mPortCount; 826 /** 827 * Runnables to run after the next update to main thread state, but that are 828 * still waiting for the next iteration to finish. 829 */ 830 nsTArray<nsCOMPtr<nsIRunnable>> mPendingUpdateRunnables; 831 832 /** 833 * List of resume operations waiting for a switch to an AudioCallbackDriver. 834 */ 835 class PendingResumeOperation { 836 public: 837 explicit PendingResumeOperation( 838 AudioContextOperationControlMessage* aMessage); 839 void Apply(MediaTrackGraphImpl* aGraph); 840 void Abort(); 841 MediaTrack* DestinationTrack() const { return mDestinationTrack; } 842 843 private: 844 RefPtr<MediaTrack> mDestinationTrack; 845 nsTArray<RefPtr<MediaTrack>> mTracks; 846 MozPromiseHolder<AudioContextOperationPromise> mHolder; 847 }; 848 AutoTArray<PendingResumeOperation, 1> mPendingResumeOperations; 849 850 // mMonitor guards the data below. 851 // MediaTrackGraph normally does its work without holding mMonitor, so it is 852 // not safe to just grab mMonitor from some thread and start monkeying with 853 // the graph. Instead, communicate with the graph thread using provided 854 // mechanisms such as the control message queue. 855 Monitor mMonitor; 856 857 // Data guarded by mMonitor (must always be accessed with mMonitor held, 858 // regardless of the value of mLifecycleState). 859 860 /** 861 * State to copy to main thread 862 */ 863 nsTArray<TrackUpdate> mTrackUpdates MOZ_GUARDED_BY(mMonitor); 864 /** 865 * Runnables to run after the next update to main thread state. 866 */ 867 nsTArray<nsCOMPtr<nsIRunnable>> mUpdateRunnables MOZ_GUARDED_BY(mMonitor); 868 /** 869 * A list of batches of messages to process. Each batch is processed 870 * as an atomic unit. 871 */ 872 /* 873 * Message queue processed by the MTG thread during an iteration. 874 * Accessed on graph thread only. 875 */ 876 nsTArray<MessageBlock> mFrontMessageQueue; 877 /* 878 * Message queue in which the main thread appends messages. 879 * Access guarded by mMonitor. 880 */ 881 nsTArray<MessageBlock> mBackMessageQueue MOZ_GUARDED_BY(mMonitor); 882 883 /* True if there will messages to process if we swap the message queues. */ 884 bool MessagesQueued() const MOZ_REQUIRES(mMonitor) { 885 mMonitor.AssertCurrentThreadOwns(); 886 return !mBackMessageQueue.IsEmpty(); 887 } 888 /** 889 * This enum specifies where this graph is in its lifecycle. This is used 890 * to control shutdown. 891 * Shutdown is tricky because it can happen in two different ways: 892 * 1) Shutdown due to inactivity. RunThread() detects that it has no 893 * pending messages and no tracks, and exits. The next RunInStableState() 894 * checks if there are new pending messages from the main thread (true only 895 * if new track creation raced with shutdown); if there are, it revives 896 * RunThread(), otherwise it commits to shutting down the graph. New track 897 * creation after this point will create a new graph. An async event is 898 * dispatched to Shutdown() the graph's threads and then delete the graph 899 * object. 900 * 2) Forced shutdown at application shutdown, completion of a non-realtime 901 * graph, or document unload. A flag is set, RunThread() detects the flag 902 * and exits, the next RunInStableState() detects the flag, and dispatches 903 * the async event to Shutdown() the graph's threads. However the graph 904 * object is not deleted. New messages for the graph are processed 905 * synchronously on the main thread if necessary. When the last track is 906 * destroyed, the graph object is deleted. 907 * 908 * This should be kept in sync with the LifecycleState_str array in 909 * MediaTrackGraph.cpp 910 */ 911 enum LifecycleState { 912 // The graph thread hasn't started yet. 913 LIFECYCLE_THREAD_NOT_STARTED, 914 // RunThread() is running normally. 915 LIFECYCLE_RUNNING, 916 // In the following states, the graph thread is not running so 917 // all "graph thread only" state in this class can be used safely 918 // on the main thread. 919 // RunThread() has exited and we're waiting for the next 920 // RunInStableState(), at which point we can clean up the main-thread 921 // side of the graph. 922 LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP, 923 // RunInStableState() posted a ShutdownRunnable, and we're waiting for it 924 // to shut down the graph thread(s). 925 LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN, 926 // Graph threads have shut down but we're waiting for remaining tracks 927 // to be destroyed. Only happens during application shutdown and on 928 // completed non-realtime graphs, since normally we'd only shut down a 929 // realtime graph when it has no tracks. 930 LIFECYCLE_WAITING_FOR_TRACK_DESTRUCTION 931 }; 932 933 /** 934 * Modified only in mMonitor. Transitions to 935 * LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP occur on the graph thread at 936 * the end of an iteration. All other transitions occur on the main thread. 937 */ 938 LifecycleState mLifecycleState MOZ_GUARDED_BY(mMonitor); 939 LifecycleState& LifecycleStateRef() MOZ_NO_THREAD_SAFETY_ANALYSIS { 940 #if DEBUG 941 if (mGraphDriverRunning) { 942 mMonitor.AssertCurrentThreadOwns(); 943 } else { 944 MOZ_ASSERT(NS_IsMainThread()); 945 } 946 #endif 947 return mLifecycleState; 948 } 949 const LifecycleState& LifecycleStateRef() const 950 MOZ_NO_THREAD_SAFETY_ANALYSIS { 951 #if DEBUG 952 if (mGraphDriverRunning) { 953 mMonitor.AssertCurrentThreadOwns(); 954 } else { 955 MOZ_ASSERT(NS_IsMainThread()); 956 } 957 #endif 958 return mLifecycleState; 959 } 960 961 /** 962 * True once the graph thread has received the message from ForceShutDown(). 963 * This is checked in the decision to shut down the 964 * graph thread so that control messages dispatched before forced shutdown 965 * are processed on the graph thread. 966 * Only set on the graph thread. 967 * Can be read safely on the thread currently owning the graph, as indicated 968 * by mLifecycleState. 969 */ 970 bool mForceShutDownReceived = false; 971 /** 972 * true when InterruptJS() has been called, because shutdown (normal or 973 * forced) has commenced. Set on the main thread under mMonitor and read on 974 * the graph thread under mMonitor. 975 **/ 976 bool mInterruptJSCalled MOZ_GUARDED_BY(mMonitor) = false; 977 978 /** 979 * Remove this blocker to unblock shutdown. 980 * Only accessed on the main thread. 981 **/ 982 RefPtr<media::ShutdownBlocker> mShutdownBlocker; 983 984 /** 985 * True when we have posted an event to the main thread to run 986 * RunInStableState() and the event hasn't run yet. 987 * Accessed on both main and MTG thread, mMonitor must be held. 988 */ 989 bool mPostedRunInStableStateEvent MOZ_GUARDED_BY(mMonitor); 990 991 /** 992 * The JSContext of the graph thread. Set under mMonitor on only the graph 993 * or GraphRunner thread. Once set this does not change until reset when 994 * the thread is about to exit. Read under mMonitor on the main thread to 995 * interrupt running JS for forced shutdown. 996 **/ 997 JSContext* mJSContext MOZ_GUARDED_BY(mMonitor) = nullptr; 998 999 // Main thread only 1000 1001 /** 1002 * Messages posted by the current event loop task. These are forwarded to 1003 * the media graph thread during RunInStableState. We can't forward them 1004 * immediately because we want all messages between stable states to be 1005 * processed as an atomic batch. 1006 */ 1007 nsTArray<UniquePtr<ControlMessageInterface>> mCurrentTaskMessageQueue; 1008 /** 1009 * True from when RunInStableState sets mLifecycleState to LIFECYCLE_RUNNING, 1010 * until RunInStableState has determined that mLifecycleState is > 1011 * LIFECYCLE_RUNNING. 1012 */ 1013 Atomic<bool> mGraphDriverRunning; 1014 /** 1015 * True when a stable state runner has been posted to the appshell to run 1016 * RunInStableState at the next stable state. 1017 * Only accessed on the main thread. 1018 */ 1019 bool mPostedRunInStableState; 1020 /** 1021 * True when processing real-time audio/video. False when processing 1022 * non-realtime audio. 1023 */ 1024 bool mRealtime; 1025 /** 1026 * True when a change has happened which requires us to recompute the track 1027 * blocking order. 1028 */ 1029 bool mTrackOrderDirty; 1030 const RefPtr<nsISerialEventTarget> mMainThread; 1031 1032 // used to limit graph shutdown time 1033 // Only accessed on the main thread. 1034 nsCOMPtr<nsITimer> mShutdownTimer; 1035 1036 protected: 1037 virtual ~MediaTrackGraphImpl(); 1038 1039 MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) 1040 1041 // Set a new native iput device when the current native input device is close. 1042 // Main thread only. 1043 void SetNewNativeInput(); 1044 1045 /** 1046 * This class uses manual memory management, and all pointers to it are raw 1047 * pointers. However, in order for it to implement nsIMemoryReporter, it needs 1048 * to implement nsISupports and so be ref-counted. So it maintains a single 1049 * nsRefPtr to itself, giving it a ref-count of 1 during its entire lifetime, 1050 * and Destroy() nulls this self-reference in order to trigger self-deletion. 1051 */ 1052 RefPtr<MediaTrackGraphImpl> mSelfRef; 1053 1054 struct WindowAndTrack { 1055 uint64_t mWindowId; 1056 RefPtr<ProcessedMediaTrack> mCaptureTrackSink; 1057 }; 1058 /** 1059 * Track for window audio capture. 1060 */ 1061 nsTArray<WindowAndTrack> mWindowCaptureTracks; 1062 1063 /** 1064 * Main thread unordered record of audio outputs, keyed by Track and output 1065 * key. Used to determine when an output device is no longer in use and to 1066 * record the volumes corresponding to each key. An array is used as a 1067 * simple hash table, on the assumption that the number of outputs is small. 1068 */ 1069 struct TrackAndKey { 1070 MOZ_UNSAFE_REF("struct exists only if track exists") MediaTrack* mTrack; 1071 void* mKey; 1072 }; 1073 struct TrackKeyDeviceAndVolume { 1074 MOZ_UNSAFE_REF("struct exists only if track exists") 1075 MediaTrack* const mTrack; 1076 void* const mKey; 1077 const CubebUtils::AudioDeviceID mDeviceID; 1078 float mVolume; 1079 1080 bool operator==(const TrackAndKey& aTrackAndKey) const { 1081 return mTrack == aTrackAndKey.mTrack && mKey == aTrackAndKey.mKey; 1082 } 1083 }; 1084 nsTArray<TrackKeyDeviceAndVolume> mAudioOutputParams; 1085 /** 1086 * Main thread record of which audio output devices are active, keyed by 1087 * AudioDeviceID, and their CrossGraphReceivers if any. 1088 * mOutputDeviceRefCnts[0] always exists and corresponds to the primary 1089 * audio output device, which an AudioCallbackDriver will use if active. 1090 * mCount may be zero for the first entry only. */ 1091 struct DeviceReceiverAndCount { 1092 const CubebUtils::AudioDeviceID mDeviceID; 1093 // For secondary devices, mReceiver receives audio output. 1094 // Null for the primary output device, fed by an AudioCallbackDriver. 1095 const RefPtr<CrossGraphReceiver> mReceiver; 1096 size_t mRefCnt; // number of mAudioOutputParams entries with this device 1097 1098 bool operator==(CubebUtils::AudioDeviceID aDeviceID) const { 1099 return mDeviceID == aDeviceID; 1100 } 1101 }; 1102 nsTArray<DeviceReceiverAndCount> mOutputDeviceRefCnts; 1103 /** 1104 * Graph thread record of devices to which audio outputs are mixed, keyed by 1105 * AudioDeviceID. All tracks that have an audio output to each device are 1106 * grouped for mixing their outputs to a single stream. 1107 * mOutputDevices[0] always exists and corresponds to the primary audio 1108 * output device, which an AudioCallbackDriver will use if active. 1109 * An AudioCallbackDriver may be active when no audio outputs have audio 1110 * outputs. 1111 */ 1112 struct TrackAndVolume { 1113 MOZ_UNSAFE_REF("struct exists only if track exists") 1114 MediaTrack* const mTrack; 1115 float mVolume; 1116 1117 bool operator==(const MediaTrack* aTrack) const { return mTrack == aTrack; } 1118 }; 1119 struct OutputDeviceEntry { 1120 const CubebUtils::AudioDeviceID mDeviceID; 1121 // For secondary devices, mReceiver receives audio output. 1122 // Null for the primary output device, fed by an AudioCallbackDriver. 1123 const RefPtr<CrossGraphReceiver> mReceiver; 1124 /** 1125 * Mapping from MediaTrack to volume for all tracks that have their audio 1126 * output mixed and written to this output device. 1127 */ 1128 nsTArray<TrackAndVolume> mTrackOutputs; 1129 1130 bool operator==(CubebUtils::AudioDeviceID aDeviceID) const { 1131 return mDeviceID == aDeviceID; 1132 } 1133 }; 1134 nsTArray<OutputDeviceEntry> mOutputDevices; 1135 /** 1136 * mOutputDeviceForAEC identifies the audio output to be passed as the 1137 * reverse stream for audio echo cancellation. Used only if a microphone is 1138 * active. Graph thread. 1139 */ 1140 CubebUtils::AudioDeviceID mOutputDeviceForAEC = nullptr; 1141 1142 /** 1143 * Global volume scale. Used when running tests so that the output is not too 1144 * loud. 1145 */ 1146 const float mGlobalVolume; 1147 1148 #ifdef DEBUG 1149 /** 1150 * Used to assert when AppendMessage() runs control messages synchronously. 1151 */ 1152 bool mCanRunMessagesSynchronously; 1153 #endif 1154 1155 /** 1156 * The graph's main-thread observable graph time. 1157 * Updated by the stable state runnable after each iteration. 1158 */ 1159 Watchable<GraphTime> mMainThreadGraphTime; 1160 1161 /** 1162 * Set based on mProcessedTime at end of iteration. 1163 * Read by stable state runnable on main thread. Protected by mMonitor. 1164 */ 1165 GraphTime mNextMainThreadGraphTime MOZ_GUARDED_BY(mMonitor) = 0; 1166 1167 /** 1168 * Cached audio output latency, in seconds. Main thread only. This is reset 1169 * whenever the audio device running this MediaTrackGraph changes. 1170 */ 1171 double mAudioOutputLatency; 1172 1173 /** 1174 * The max audio output channel count the default audio output device 1175 * supports. This is cached here because it can be expensive to query. The 1176 * cache is invalidated when the device is changed. This is initialized in the 1177 * ctor, and the read/write only on the graph thread. 1178 */ 1179 uint32_t mMaxOutputChannelCount; 1180 1181 public: 1182 /** 1183 * Manage the native or non-native input device in graph. Main thread only. 1184 */ 1185 DeviceInputTrackManager mDeviceInputTrackManagerMainThread; 1186 1187 /** 1188 * An enumerator for tracking the system default output device. Set only while 1189 * an input track is present in the graph, as the system default output device 1190 * is used for AEC decisions. Main thread only. 1191 */ 1192 RefPtr<CubebDeviceEnumerator> mEnumeratorMainThread; 1193 1194 protected: 1195 /** 1196 * Manage the native or non-native input device in graph. Graph thread only. 1197 */ 1198 DeviceInputTrackManager mDeviceInputTrackManagerGraphThread; 1199 MediaEventListener mOutputDevicesChangedListener; 1200 /** 1201 * The system's current default device. When PrimaryOutputDeviceID() is 1202 * nullptr, this is what it maps to. There will be a delay between a user 1203 * changing their default device, to this device ID being up to date. 1204 */ 1205 std::atomic<CubebUtils::AudioDeviceID> mDefaultOutputDeviceID = {nullptr}; 1206 /** 1207 * The mixer that the graph mixes into during an iteration. This is here 1208 * rather than on the stack so that its buffer is not allocated each 1209 * iteration. Graph thread only. 1210 */ 1211 AudioMixer mMixer; 1212 }; 1213 1214 } // namespace mozilla 1215 1216 #endif /* MEDIATRACKGRAPHIMPL_H_ */