From 1b78f72fe719099c5b76e7fd565ed79fd7662f3d Mon Sep 17 00:00:00 2001 From: Andreas Pehrson Date: Fri, 1 Mar 2019 10:11:16 +0000 Subject: [PATCH] Bug 1473469 - Make MediaStreamGraph run on a single thread with AudioWorklets enabled. r=padenot Differential Revision: https://phabricator.services.mozilla.com/D20828 --HG-- extra : moz-landing-system : lando --- dom/media/GraphDriver.cpp | 38 ++++--- dom/media/GraphDriver.h | 20 ++-- dom/media/GraphRunner.cpp | 105 ++++++++++++++++++++ dom/media/GraphRunner.h | 85 ++++++++++++++++ dom/media/MediaStreamGraph.cpp | 55 +++++++--- dom/media/MediaStreamGraph.h | 12 ++- dom/media/MediaStreamGraphImpl.h | 37 ++++++- dom/media/moz.build | 1 + dom/media/webrtc/MediaEngineWebRTCAudio.cpp | 10 +- 9 files changed, 316 insertions(+), 47 deletions(-) create mode 100644 dom/media/GraphRunner.cpp create mode 100644 dom/media/GraphRunner.h diff --git a/dom/media/GraphDriver.cpp b/dom/media/GraphDriver.cpp index 58d3e461ca36..a99f777bc37a 100644 --- a/dom/media/GraphDriver.cpp +++ b/dom/media/GraphDriver.cpp @@ -41,7 +41,7 @@ GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl) void GraphDriver::SetGraphTime(GraphDriver* aPreviousDriver, GraphTime aLastSwitchNextIterationStart, GraphTime aLastSwitchNextIterationEnd) { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); // We set mIterationEnd here, because the first thing a driver do when it // does an iteration is to update graph times, so we are in fact setting @@ -63,7 +63,7 @@ void GraphDriver::SetGraphTime(GraphDriver* aPreviousDriver, } void GraphDriver::SwitchAtNextIteration(GraphDriver* aNextDriver) { - MOZ_ASSERT(OnThread()); + MOZ_ASSERT(OnGraphThread()); MOZ_ASSERT(aNextDriver); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); @@ -87,14 +87,20 @@ GraphTime GraphDriver::StateComputedTime() const { void GraphDriver::EnsureNextIteration() { GraphImpl()->EnsureNextIteration(); } +#ifdef DEBUG +bool GraphDriver::OnGraphThread() { + return GraphImpl()->RunByGraphDriver(this); +} +#endif + bool GraphDriver::Switching() { - MOZ_ASSERT(OnThread()); + MOZ_ASSERT(OnGraphThread()); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); return mNextDriver || mPreviousDriver; } void GraphDriver::SwitchToNextDriver() { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); MOZ_ASSERT(NextDriver()); @@ -105,19 +111,19 @@ void GraphDriver::SwitchToNextDriver() { } GraphDriver* GraphDriver::NextDriver() { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); return mNextDriver; } GraphDriver* GraphDriver::PreviousDriver() { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); return mPreviousDriver; } void GraphDriver::SetNextDriver(GraphDriver* aNextDriver) { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); MOZ_ASSERT(aNextDriver != this); MOZ_ASSERT(aNextDriver != mNextDriver); @@ -133,7 +139,7 @@ void GraphDriver::SetNextDriver(GraphDriver* aNextDriver) { } void GraphDriver::SetPreviousDriver(GraphDriver* aPreviousDriver) { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); GraphImpl()->GetMonitor().AssertCurrentThreadOwns(); mPreviousDriver = aPreviousDriver; } @@ -659,7 +665,7 @@ bool AudioCallbackDriver::Init() { void AudioCallbackDriver::Start() { MOZ_ASSERT(!IsStarted()); MOZ_ASSERT(NS_IsMainThread() || OnCubebOperationThread() || - (PreviousDriver() && PreviousDriver()->OnThread())); + (PreviousDriver() && PreviousDriver()->OnGraphThread())); if (mPreviousDriver) { if (mPreviousDriver->AsAudioCallbackDriver()) { LOG(LogLevel::Debug, ("Releasing audio driver off main thread.")); @@ -723,7 +729,7 @@ void AudioCallbackDriver::Revive() { } void AudioCallbackDriver::RemoveMixerCallback() { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); if (mAddedMixer) { GraphImpl()->mMixer.RemoveCallback(this); @@ -732,7 +738,7 @@ void AudioCallbackDriver::RemoveMixerCallback() { } void AudioCallbackDriver::AddMixerCallback() { - MOZ_ASSERT(OnThread()); + MOZ_ASSERT(OnGraphThread()); if (!mAddedMixer) { mGraphImpl->mMixer.AddCallback(this); @@ -949,7 +955,7 @@ long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer, } void AudioCallbackDriver::StateCallback(cubeb_state aState) { - MOZ_ASSERT(!OnThread()); + MOZ_ASSERT(!OnGraphThread()); LOG(LogLevel::Debug, ("AudioCallbackDriver State: %d", aState)); // Clear the flag for the not running @@ -972,7 +978,7 @@ void AudioCallbackDriver::MixerCallback(AudioDataValue* aMixedBuffer, AudioSampleFormat aFormat, uint32_t aChannels, uint32_t aFrames, uint32_t aSampleRate) { - MOZ_ASSERT(OnThread()); + MOZ_ASSERT(OnGraphThread()); uint32_t toWrite = mBuffer.Available(); if (!mBuffer.Available()) { @@ -1028,7 +1034,7 @@ void AudioCallbackDriver::PanOutputIfNeeded(bool aMicrophoneActive) { } void AudioCallbackDriver::DeviceChangedCallback() { - MOZ_ASSERT(!OnThread()); + MOZ_ASSERT(!OnGraphThread()); // Tell the audio engine the device has changed, it might want to reset some // state. MonitorAutoLock mon(mGraphImpl->GetMonitor()); @@ -1039,7 +1045,7 @@ void AudioCallbackDriver::DeviceChangedCallback() { } uint32_t AudioCallbackDriver::IterationDuration() { - MOZ_ASSERT(OnThread()); + MOZ_ASSERT(OnGraphThread()); // The real fix would be to have an API in cubeb to give us the number. Short // of that, we approximate it here. bug 1019507 return mIterationDurationMS; @@ -1050,7 +1056,7 @@ bool AudioCallbackDriver::IsStarted() { return mStarted; } void AudioCallbackDriver::EnqueueStreamAndPromiseForOperation( MediaStream* aStream, void* aPromise, dom::AudioContextOperation aOperation) { - MOZ_ASSERT(OnThread() || !ThreadRunning()); + MOZ_ASSERT(OnGraphThread() || !ThreadRunning()); MonitorAutoLock mon(mGraphImpl->GetMonitor()); mPromisesForOperation.AppendElement( StreamAndPromiseForOperation(aStream, aPromise, aOperation)); diff --git a/dom/media/GraphDriver.h b/dom/media/GraphDriver.h index 68c69dca1b17..2b9f9f030086 100644 --- a/dom/media/GraphDriver.h +++ b/dom/media/GraphDriver.h @@ -55,12 +55,17 @@ static const int SCHEDULE_SAFETY_MARGIN_MS = 10; static const int AUDIO_TARGET_MS = 2 * MEDIA_GRAPH_TARGET_PERIOD_MS + SCHEDULE_SAFETY_MARGIN_MS; +class MediaStream; class MediaStreamGraphImpl; class AudioCallbackDriver; class OfflineClockDriver; class SystemClockDriver; +namespace dom { +enum class AudioContextOperation; +} + /** * A driver is responsible for the scheduling of the processing, the thread * management, and give the different clocks to a MediaStreamGraph. This is an @@ -180,18 +185,15 @@ class GraphDriver { */ void EnsureNextIteration(); - /** - * Same thing, but not locked. - */ - void EnsureNextIterationLocked(); - MediaStreamGraphImpl* GraphImpl() const { return mGraphImpl; } +#ifdef DEBUG + // True if the current thread is driving the MSG. + bool OnGraphThread(); +#endif // True if the current thread is the GraphDriver's thread. - // This is the thread that drives the MSG. virtual bool OnThread() = 0; // GraphDriver's thread has started and the thread is running. - // This is the thread that drives the MSG. virtual bool ThreadRunning() = 0; protected: @@ -251,6 +253,8 @@ class ThreadedDriver : public GraphDriver { friend class MediaStreamGraphInitThreadRunnable; uint32_t IterationDuration() override { return MEDIA_GRAPH_TARGET_PERIOD_MS; } + nsIThread* Thread() { return mThread; } + bool OnThread() override { return !mThread || mThread->EventTarget()->IsOnCurrentThread(); } @@ -412,6 +416,8 @@ class AudioCallbackDriver : public GraphDriver, MediaStream* aStream, void* aPromise, dom::AudioContextOperation aOperation); + std::thread::id ThreadId() { return mAudioThreadId.load(); } + bool OnThread() override { return mAudioThreadId.load() == std::this_thread::get_id(); } diff --git a/dom/media/GraphRunner.cpp b/dom/media/GraphRunner.cpp new file mode 100644 index 000000000000..ad5cc6327dcb --- /dev/null +++ b/dom/media/GraphRunner.cpp @@ -0,0 +1,105 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#include "GraphRunner.h" + +#include "GraphDriver.h" +#include "MediaStreamGraph.h" +#include "MediaStreamGraphImpl.h" +#include "nsISupportsImpl.h" +#include "prthread.h" +#include "Tracing.h" + +namespace mozilla { + +static void Start(void* aArg) { + GraphRunner* th = static_cast(aArg); + th->Run(); +} + +GraphRunner::GraphRunner(MediaStreamGraphImpl* aGraph) + : mMonitor("GraphRunner::mMonitor"), + mGraph(aGraph), + mThread(PR_CreateThread(PR_SYSTEM_THREAD, &Start, this, + PR_PRIORITY_URGENT, PR_GLOBAL_THREAD, + PR_JOINABLE_THREAD, 0)) { + MOZ_COUNT_CTOR(GraphRunner); +} + +GraphRunner::~GraphRunner() { + MOZ_COUNT_DTOR(GraphRunner); + PR_JoinThread(mThread); +} + +void GraphRunner::Shutdown() { + MonitorAutoLock lock(mMonitor); + mShutdown = true; + mMonitor.Notify(); +} + +bool GraphRunner::OneIteration(GraphTime aStateEnd) { + TRACE_AUDIO_CALLBACK(); + + MonitorAutoLock lock(mMonitor); + MOZ_ASSERT(!mShutdown); + mStateEnd = aStateEnd; + +#ifdef DEBUG + if (auto audioDriver = mGraph->CurrentDriver()->AsAudioCallbackDriver()) { + mAudioDriverThreadId = audioDriver->ThreadId(); + } else if (auto clockDriver = + mGraph->CurrentDriver()->AsSystemClockDriver()) { + mClockDriverThread = clockDriver->Thread(); + } else { + MOZ_CRASH("Unknown GraphDriver"); + } +#endif + + mMonitor.Notify(); // Signal that mStateEnd was updated + mMonitor.Wait(); // Wait for mStillProcessing to update + +#ifdef DEBUG + mAudioDriverThreadId = std::thread::id(); + mClockDriverThread = nullptr; +#endif + + return mStillProcessing; +} + +void GraphRunner::Run() { + MonitorAutoLock lock(mMonitor); + while (true) { + mMonitor.Wait(); // Wait for mStateEnd or mShutdown to update + if (mShutdown) { + break; + } + TRACE(); + mStillProcessing = mGraph->OneIterationImpl(mStateEnd); + mMonitor.Notify(); // Signal that mStillProcessing was updated + } +} + +bool GraphRunner::OnThread() { return PR_GetCurrentThread() == mThread; } + +#ifdef DEBUG +bool GraphRunner::RunByGraphDriver(GraphDriver* aDriver) { + if (!OnThread()) { + return false; + } + + if (auto audioDriver = aDriver->AsAudioCallbackDriver()) { + return audioDriver->ThreadId() == mAudioDriverThreadId; + } + + if (auto clockDriver = aDriver->AsSystemClockDriver()) { + return clockDriver->Thread() == mClockDriverThread; + } + + MOZ_CRASH("Unknown driver"); +} +#endif + +} // namespace mozilla diff --git a/dom/media/GraphRunner.h b/dom/media/GraphRunner.h new file mode 100644 index 000000000000..eeb03b116670 --- /dev/null +++ b/dom/media/GraphRunner.h @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_GraphRunner_h +#define mozilla_GraphRunner_h + +#include "MediaSegment.h" +#include "mozilla/Monitor.h" + +#include + +struct PRThread; + +namespace mozilla { + +class GraphDriver; +class MediaStreamGraphImpl; + +class GraphRunner { + public: + explicit GraphRunner(MediaStreamGraphImpl* aGraph); + ~GraphRunner(); + + /** + * Marks us as shut down and signals mThread, so that it runs until the end. + */ + void Shutdown(); + + /** + * Signals one iteration of mGraph. Hands aStateEnd over to mThread and runs + * the iteration there. + */ + bool OneIteration(GraphTime aStateEnd); + + /** + * Runs mGraph until it shuts down. + */ + void Run(); + + /** + * Returns true if called on mThread. + */ + bool OnThread(); + +#ifdef DEBUG + /** + * Returns true if called on mThread, and aDriver was the driver that called + * OneIteration() last. + */ + bool RunByGraphDriver(GraphDriver* aDriver); +#endif + + private: + // Monitor used for yielding mThread through Wait(), and scheduling mThread + // through Signal() from a GraphDriver. + Monitor mMonitor; + // The MediaStreamGraph we're running. Weakptr beecause this graph owns us and + // guarantees that our lifetime will not go beyond that of itself. + MediaStreamGraphImpl* const mGraph; + // The thread running mGraph. + PRThread* const mThread; + // GraphTime being handed over to the graph through OneIteration. Protected by + // mMonitor. + GraphTime mStateEnd = 0; + // Reply from mGraph's OneIteration. Protected by mMonitor. + bool mStillProcessing = true; + // True after Shutdown(). Protected by mMonitor. + bool mShutdown = false; + +#ifdef DEBUG + // Set to mGraph's audio callback driver's thread id, if run by an + // AudioCallbackDriver, while OneIteration() is running. + std::thread::id mAudioDriverThreadId = std::thread::id(); + // Set to mGraph's system clock driver's thread, if run by a + // SystemClockDriver, while OneIteration() is running. + nsIThread* mClockDriverThread = nullptr; +#endif +}; + +} // namespace mozilla + +#endif diff --git a/dom/media/MediaStreamGraph.cpp b/dom/media/MediaStreamGraph.cpp index e3493118187d..fe931e190119 100644 --- a/dom/media/MediaStreamGraph.cpp +++ b/dom/media/MediaStreamGraph.cpp @@ -32,6 +32,7 @@ #include "mozilla/Unused.h" #include "mtransport/runnable_utils.h" #include "VideoUtils.h" +#include "GraphRunner.h" #include "Tracing.h" #include "webaudio/blink/DenormalDisabler.h" @@ -1011,22 +1012,20 @@ void MediaStreamGraphImpl::ReevaluateInputDevice() { } } -bool MediaStreamGraph::OnGraphThreadOrNotRunning() const { +bool MediaStreamGraphImpl::OnGraphThreadOrNotRunning() const { // either we're on the right thread (and calling CurrentDriver() is safe), // or we're going to fail the assert anyway, so don't cross-check // via CurrentDriver(). - MediaStreamGraphImpl const* graph = - static_cast(this); - return graph->mDetectedNotRunning ? NS_IsMainThread() - : graph->mDriver->OnThread(); + return mDetectedNotRunning ? NS_IsMainThread() : OnGraphThread(); } -bool MediaStreamGraph::OnGraphThread() const { +bool MediaStreamGraphImpl::OnGraphThread() const { // we're on the right thread (and calling mDriver is safe), - MediaStreamGraphImpl const* graph = - static_cast(this); - MOZ_ASSERT(graph->mDriver); - return graph->mDriver->OnThread(); + MOZ_ASSERT(mDriver); + if (mGraphRunner && mGraphRunner->OnThread()) { + return true; + } + return mDriver->OnThread(); } bool MediaStreamGraphImpl::ShouldUpdateMainThread() { @@ -1379,13 +1378,23 @@ bool MediaStreamGraphImpl::UpdateMainThreadState() { } bool MediaStreamGraphImpl::OneIteration(GraphTime aStateEnd) { + if (mGraphRunner) { + return mGraphRunner->OneIteration(aStateEnd); + } + + return OneIterationImpl(aStateEnd); +} + +bool MediaStreamGraphImpl::OneIterationImpl(GraphTime aStateEnd) { TRACE_AUDIO_CALLBACK(); + // Changes to LIFECYCLE_RUNNING occur before starting or reviving the graph // thread, and so the monitor need not be held to check mLifecycleState. // LIFECYCLE_THREAD_NOT_STARTED is possible when shutting down offline // graphs that have not started. MOZ_DIAGNOSTIC_ASSERT(mLifecycleState <= LIFECYCLE_RUNNING); MOZ_ASSERT(OnGraphThread()); + WebCore::DenormalDisabler disabler; // Process graph message from the main thread for this iteration. @@ -1499,6 +1508,10 @@ class MediaStreamGraphShutDownRunnable : public Runnable { } #endif + if (mGraph->mGraphRunner) { + mGraph->mGraphRunner->Shutdown(); + } + mGraph->mDriver ->Shutdown(); // This will wait until it's shutdown since // we'll start tearing down the graph after this @@ -3160,9 +3173,12 @@ void ProcessedMediaStream::DestroyImpl() { } MediaStreamGraphImpl::MediaStreamGraphImpl(GraphDriverType aDriverRequested, + GraphRunType aRunTypeRequested, TrackRate aSampleRate, AbstractThread* aMainThread) : MediaStreamGraph(aSampleRate), + mGraphRunner(aRunTypeRequested == SINGLE_THREAD ? new GraphRunner(this) + : nullptr), mFirstCycleBreaker(0) // An offline graph is not initially processing. , @@ -3217,6 +3233,13 @@ AbstractThread* MediaStreamGraph::AbstractMainThread() { return static_cast(this)->mAbstractMainThread; } +#ifdef DEBUG +bool MediaStreamGraphImpl::RunByGraphDriver(GraphDriver* aDriver) { + return aDriver->OnThread() || + (mGraphRunner && mGraphRunner->RunByGraphDriver(aDriver)); +} +#endif + void MediaStreamGraphImpl::Destroy() { // First unregister from memory reporting. UnregisterWeakMemoryReporter(this); @@ -3298,8 +3321,14 @@ MediaStreamGraph* MediaStreamGraph::GetInstance( // Uncommon case, only for some old configuration of webspeech. mainThread = AbstractThread::MainThread(); } - graph = - new MediaStreamGraphImpl(aGraphDriverRequested, sampleRate, mainThread); + + GraphRunType runType = DIRECT_DRIVER; + if (aGraphDriverRequested != OFFLINE_THREAD_DRIVER && + Preferences::GetBool("dom.audioworklet.enabled", false)) { + runType = SINGLE_THREAD; + } + graph = new MediaStreamGraphImpl(aGraphDriverRequested, runType, sampleRate, + mainThread); uint32_t hashkey = WindowToHash(aWindow, sampleRate); gGraphs.Put(hashkey, graph); @@ -3316,7 +3345,7 @@ MediaStreamGraph* MediaStreamGraph::CreateNonRealtimeInstance( MOZ_ASSERT(NS_IsMainThread(), "Main thread only"); MediaStreamGraphImpl* graph = new MediaStreamGraphImpl( - OFFLINE_THREAD_DRIVER, aSampleRate, + OFFLINE_THREAD_DRIVER, DIRECT_DRIVER, aSampleRate, aWindow->AsGlobal()->AbstractMainThreadFor(TaskCategory::Other)); LOG(LogLevel::Debug, ("Starting up Offline MediaStreamGraph %p", graph)); diff --git a/dom/media/MediaStreamGraph.h b/dom/media/MediaStreamGraph.h index c71e6bf8203b..8f6ff62b4a27 100644 --- a/dom/media/MediaStreamGraph.h +++ b/dom/media/MediaStreamGraph.h @@ -1205,6 +1205,14 @@ class MediaStreamGraph { SYSTEM_THREAD_DRIVER, OFFLINE_THREAD_DRIVER }; + // A MediaStreamGraph running an AudioWorklet must always be run from the + // same thread, in order to run js. To acheive this, create the graph with + // a SINGLE_THREAD RunType. DIRECT_DRIVER will run the graph directly off + // the GraphDriver's thread. + enum GraphRunType { + DIRECT_DRIVER, + SINGLE_THREAD, + }; static const uint32_t AUDIO_CALLBACK_DRIVER_SHUTDOWN_TIMEOUT = 20 * 1000; static const TrackRate REQUEST_DEFAULT_SAMPLE_RATE = 0; @@ -1324,8 +1332,8 @@ class MediaStreamGraph { // Intended only for assertions, either on graph thread or not running (in // which case we must be on the main thread). - bool OnGraphThreadOrNotRunning() const; - bool OnGraphThread() const; + virtual bool OnGraphThreadOrNotRunning() const = 0; + virtual bool OnGraphThread() const = 0; /** * Sample rate at which this graph runs. For real time graphs, this is diff --git a/dom/media/MediaStreamGraphImpl.h b/dom/media/MediaStreamGraphImpl.h index 93a317622a7b..e46d729d7e6b 100644 --- a/dom/media/MediaStreamGraphImpl.h +++ b/dom/media/MediaStreamGraphImpl.h @@ -31,6 +31,7 @@ class ShutdownTicket; template class LinkedList; +class GraphRunner; /** * A per-stream update message passed from the media graph thread to the @@ -109,8 +110,22 @@ class MediaStreamGraphImpl : public MediaStreamGraph, * implement OfflineAudioContext. They do not support MediaStream inputs. */ explicit MediaStreamGraphImpl(GraphDriverType aGraphDriverRequested, + GraphRunType aRunTypeRequested, TrackRate aSampleRate, AbstractThread* aWindow); + // Intended only for assertions, either on graph thread or not running (in + // which case we must be on the main thread). + bool OnGraphThreadOrNotRunning() const override; + bool OnGraphThread() const override; + +#ifdef DEBUG + /** + * True if we're on aDriver's thread, or if we're on mGraphRunner's thread + * and mGraphRunner is currently run by aDriver. + */ + bool RunByGraphDriver(GraphDriver* aDriver); +#endif + /** * Unregisters memory reporting and deletes this instance. This should be * called instead of calling the destructor directly. @@ -180,10 +195,18 @@ class MediaStreamGraphImpl : public MediaStreamGraph, bool UpdateMainThreadState(); /** - * Returns true if this MediaStreamGraph should keep running + * Proxy method called by GraphDriver to iterate the graph. + * If this graph was created with GraphRunType SINGLE_THREAD, mGraphRunner + * will take care of calling OneIterationImpl from its thread. Otherwise, + * OneIterationImpl is called directly. */ bool OneIteration(GraphTime aStateEnd); + /** + * Returns true if this MediaStreamGraph should keep running + */ + bool OneIterationImpl(GraphTime aStateEnd); + /** * Called from the driver, when the graph thread is about to stop, to tell * the main thread to attempt to begin cleanup. The main thread may either @@ -242,7 +265,7 @@ class MediaStreamGraphImpl : public MediaStreamGraph, void UpdateGraph(GraphTime aEndBlockingDecisions); void SwapMessageQueues() { - MOZ_ASSERT(CurrentDriver()->OnThread()); + MOZ_ASSERT(OnGraphThread()); MOZ_ASSERT(mFrontMessageQueue.IsEmpty()); mMonitor.AssertCurrentThreadOwns(); mFrontMessageQueue.SwapElements(mBackMessageQueue); @@ -504,7 +527,7 @@ class MediaStreamGraphImpl : public MediaStreamGraph, * We can also switch from Revive() (on MainThread). Monitor must be held. */ void SetCurrentDriver(GraphDriver* aDriver) { - MOZ_ASSERT(mDriver->OnThread() || !mDriver->ThreadRunning()); + MOZ_ASSERT(RunByGraphDriver(mDriver) || !mDriver->ThreadRunning()); #ifdef DEBUG mMonitor.AssertCurrentThreadOwns(); #endif @@ -588,7 +611,13 @@ class MediaStreamGraphImpl : public MediaStreamGraph, StreamSet AllStreams() { return StreamSet(*this); } // Data members - // + + /* + * If set, the GraphRunner class handles handing over data from audio + * callbacks to a common single thread, shared across GraphDrivers. + */ + const UniquePtr mGraphRunner; + /** * Graphs own owning references to their driver, until shutdown. When a driver * switch occur, previous driver is either deleted, or it's ownership is diff --git a/dom/media/moz.build b/dom/media/moz.build index bfd48f9c700d..45110e12279f 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -240,6 +240,7 @@ UNIFIED_SOURCES += [ 'FileMediaResource.cpp', 'GetUserMediaRequest.cpp', 'GraphDriver.cpp', + 'GraphRunner.cpp', 'ImageToI420.cpp', 'MediaCache.cpp', 'MediaContainerType.cpp', diff --git a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp index 6f038ca8e434..60a47bfe260e 100644 --- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp +++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp @@ -654,7 +654,7 @@ AudioInputProcessing::AudioInputProcessing( void AudioInputProcessing::Disconnect(MediaStreamGraphImpl* aGraph) { // This method is just for asserts. - MOZ_ASSERT(aGraph->CurrentDriver()->OnThread()); + MOZ_ASSERT(aGraph->OnGraphThread()); } void MediaEngineWebRTCMicrophoneSource::Shutdown() { @@ -671,7 +671,7 @@ void MediaEngineWebRTCMicrophoneSource::Shutdown() { } bool AudioInputProcessing::PassThrough(MediaStreamGraphImpl* aGraph) const { - MOZ_ASSERT(aGraph->CurrentDriver()->OnThread()); + MOZ_ASSERT(aGraph->OnGraphThread()); return mSkipProcessing; } @@ -851,7 +851,7 @@ void AudioInputProcessing::NotifyOutputData(MediaStreamGraphImpl* aGraph, AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) { - MOZ_ASSERT(aGraph->CurrentDriver()->OnThread()); + MOZ_ASSERT(aGraph->OnGraphThread()); MOZ_ASSERT(mEnabled); if (!mPacketizerOutput || mPacketizerOutput->PacketSize() != aRate / 100u || @@ -1096,7 +1096,7 @@ void AudioInputProcessing::NotifyInputData(MediaStreamGraphImpl* aGraph, const AudioDataValue* aBuffer, size_t aFrames, TrackRate aRate, uint32_t aChannels) { - MOZ_ASSERT(aGraph->CurrentDriver()->OnThread()); + MOZ_ASSERT(aGraph->OnGraphThread()); TRACE_AUDIO_CALLBACK(); MOZ_ASSERT(mEnabled); @@ -1132,7 +1132,7 @@ void AudioInputProcessing::NotifyInputData(MediaStreamGraphImpl* aGraph, } while (0) void AudioInputProcessing::DeviceChanged(MediaStreamGraphImpl* aGraph) { - MOZ_ASSERT(aGraph->CurrentDriver()->OnThread()); + MOZ_ASSERT(aGraph->OnGraphThread()); // Reset some processing ResetProcessingIfNeeded(gain_control); ResetProcessingIfNeeded(echo_cancellation);