зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1586370 - Make GraphDriver iterate the Graph through an interface instead of directly. r=padenot
This lets us iterate more things than MediaTrackGraphImpl, e.g., audio drivers (from the fallback driver) and unit test classes. Differential Revision: https://phabricator.services.mozilla.com/D56082 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
7502376eec
Коммит
638c69d019
|
@ -4,7 +4,6 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <MediaTrackGraphImpl.h>
|
||||
#include "mozilla/dom/AudioContext.h"
|
||||
#include "mozilla/dom/AudioDeviceInfo.h"
|
||||
#include "mozilla/dom/BaseAudioContextBinding.h"
|
||||
|
@ -32,9 +31,9 @@ extern mozilla::LazyLogModule gMediaTrackGraphLog;
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
GraphDriver::GraphDriver(MediaTrackGraphImpl* aGraphImpl,
|
||||
GraphDriver::GraphDriver(GraphInterface* aGraphInterface,
|
||||
GraphDriver* aPreviousDriver, uint32_t aSampleRate)
|
||||
: mGraphImpl(aGraphImpl),
|
||||
: mGraphInterface(aGraphInterface),
|
||||
mSampleRate(aSampleRate),
|
||||
mPreviousDriver(aPreviousDriver) {}
|
||||
|
||||
|
@ -48,7 +47,7 @@ void GraphDriver::SetState(GraphTime aIterationStart, GraphTime aIterationEnd,
|
|||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
bool GraphDriver::InIteration() { return GraphImpl()->InDriverIteration(this); }
|
||||
bool GraphDriver::InIteration() { return Graph()->InDriverIteration(this); }
|
||||
#endif
|
||||
|
||||
GraphDriver* GraphDriver::PreviousDriver() {
|
||||
|
@ -61,10 +60,10 @@ void GraphDriver::SetPreviousDriver(GraphDriver* aPreviousDriver) {
|
|||
mPreviousDriver = aPreviousDriver;
|
||||
}
|
||||
|
||||
ThreadedDriver::ThreadedDriver(MediaTrackGraphImpl* aGraphImpl,
|
||||
ThreadedDriver::ThreadedDriver(GraphInterface* aGraphInterface,
|
||||
GraphDriver* aPreviousDriver,
|
||||
uint32_t aSampleRate)
|
||||
: GraphDriver(aGraphImpl, aPreviousDriver, aSampleRate),
|
||||
: GraphDriver(aGraphInterface, aPreviousDriver, aSampleRate),
|
||||
mThreadRunning(false) {}
|
||||
|
||||
class MediaTrackGraphShutdownThreadRunnable : public Runnable {
|
||||
|
@ -100,12 +99,12 @@ class MediaTrackGraphInitThreadRunnable : public Runnable {
|
|||
NS_IMETHOD Run() override {
|
||||
MOZ_ASSERT(!mDriver->ThreadRunning());
|
||||
LOG(LogLevel::Debug, ("Starting a new system driver for graph %p",
|
||||
mDriver->mGraphImpl.get()));
|
||||
mDriver->mGraphInterface.get()));
|
||||
|
||||
if (GraphDriver* previousDriver = mDriver->PreviousDriver()) {
|
||||
LOG(LogLevel::Debug,
|
||||
("%p releasing an AudioCallbackDriver(%p), for graph %p",
|
||||
mDriver.get(), previousDriver, mDriver->GraphImpl()));
|
||||
mDriver.get(), previousDriver, mDriver->Graph()));
|
||||
MOZ_ASSERT(!mDriver->AsAudioCallbackDriver());
|
||||
RefPtr<AsyncCubebTask> releaseEvent =
|
||||
new AsyncCubebTask(previousDriver->AsAudioCallbackDriver(),
|
||||
|
@ -125,7 +124,7 @@ class MediaTrackGraphInitThreadRunnable : public Runnable {
|
|||
void ThreadedDriver::Start() {
|
||||
MOZ_ASSERT(!ThreadRunning());
|
||||
LOG(LogLevel::Debug,
|
||||
("Starting thread for a SystemClockDriver %p", mGraphImpl.get()));
|
||||
("Starting thread for a SystemClockDriver %p", mGraphInterface.get()));
|
||||
Unused << NS_WARN_IF(mThread);
|
||||
MOZ_ASSERT(!mThread); // Ensure we haven't already started it
|
||||
|
||||
|
@ -145,17 +144,17 @@ void ThreadedDriver::Shutdown() {
|
|||
|
||||
if (mThread) {
|
||||
LOG(LogLevel::Debug,
|
||||
("%p: Stopping ThreadedDriver's %p thread", GraphImpl(), this));
|
||||
("%p: Stopping ThreadedDriver's %p thread", Graph(), this));
|
||||
mThread->Shutdown();
|
||||
mThread = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
SystemClockDriver::SystemClockDriver(MediaTrackGraphImpl* aGraphImpl,
|
||||
SystemClockDriver::SystemClockDriver(GraphInterface* aGraphInterface,
|
||||
GraphDriver* aPreviousDriver,
|
||||
uint32_t aSampleRate,
|
||||
FallbackMode aFallback)
|
||||
: ThreadedDriver(aGraphImpl, aPreviousDriver, aSampleRate),
|
||||
: ThreadedDriver(aGraphInterface, aPreviousDriver, aSampleRate),
|
||||
mInitialTimeStamp(TimeStamp::Now()),
|
||||
mCurrentTimeStamp(TimeStamp::Now()),
|
||||
mLastTimeStamp(TimeStamp::Now()),
|
||||
|
@ -172,7 +171,7 @@ void ThreadedDriver::RunThread() {
|
|||
mIterationEnd += GetIntervalForIteration();
|
||||
|
||||
if (mStateComputedTime < mIterationEnd) {
|
||||
LOG(LogLevel::Warning, ("%p: Global underrun detected", GraphImpl()));
|
||||
LOG(LogLevel::Warning, ("%p: Global underrun detected", Graph()));
|
||||
mIterationEnd = mStateComputedTime;
|
||||
}
|
||||
|
||||
|
@ -180,7 +179,7 @@ void ThreadedDriver::RunThread() {
|
|||
NS_ASSERTION(mIterationStart == mIterationEnd,
|
||||
"Time can't go backwards!");
|
||||
// This could happen due to low clock resolution, maybe?
|
||||
LOG(LogLevel::Debug, ("%p: Time did not advance", GraphImpl()));
|
||||
LOG(LogLevel::Debug, ("%p: Time did not advance", Graph()));
|
||||
}
|
||||
|
||||
GraphTime nextStateComputedTime =
|
||||
|
@ -193,18 +192,17 @@ void ThreadedDriver::RunThread() {
|
|||
("%p: Prevent state from going backwards. interval[%ld; %ld] "
|
||||
"state[%ld; "
|
||||
"%ld]",
|
||||
GraphImpl(), (long)mIterationStart, (long)mIterationEnd,
|
||||
Graph(), (long)mIterationStart, (long)mIterationEnd,
|
||||
(long)mStateComputedTime, (long)nextStateComputedTime));
|
||||
nextStateComputedTime = mStateComputedTime;
|
||||
}
|
||||
LOG(LogLevel::Verbose,
|
||||
("%p: interval[%ld; %ld] state[%ld; %ld]", GraphImpl(),
|
||||
("%p: interval[%ld; %ld] state[%ld; %ld]", Graph(),
|
||||
(long)mIterationStart, (long)mIterationEnd, (long)mStateComputedTime,
|
||||
(long)nextStateComputedTime));
|
||||
|
||||
mStateComputedTime = nextStateComputedTime;
|
||||
IterationResult result =
|
||||
GraphImpl()->OneIteration(mStateComputedTime, nullptr);
|
||||
IterationResult result = Graph()->OneIteration(mStateComputedTime, nullptr);
|
||||
|
||||
if (result.IsStop()) {
|
||||
// Signal that we're done stopping.
|
||||
|
@ -214,8 +212,7 @@ void ThreadedDriver::RunThread() {
|
|||
}
|
||||
WaitForNextIteration();
|
||||
if (GraphDriver* nextDriver = result.NextDriver()) {
|
||||
LOG(LogLevel::Debug,
|
||||
("%p: Switching to AudioCallbackDriver", GraphImpl()));
|
||||
LOG(LogLevel::Debug, ("%p: Switching to AudioCallbackDriver", Graph()));
|
||||
result.Switched();
|
||||
nextDriver->SetState(mIterationStart, mIterationEnd, mStateComputedTime);
|
||||
nextDriver->Start();
|
||||
|
@ -234,7 +231,7 @@ MediaTime SystemClockDriver::GetIntervalForIteration() {
|
|||
|
||||
MOZ_LOG(gMediaTrackGraphLog, LogLevel::Verbose,
|
||||
("%p: Updating current time to %f (real %f, StateComputedTime() %f)",
|
||||
GraphImpl(), MediaTimeToSeconds(IterationEnd() + interval),
|
||||
Graph(), MediaTimeToSeconds(IterationEnd() + interval),
|
||||
(now - mInitialTimeStamp).ToSeconds(),
|
||||
MediaTimeToSeconds(mStateComputedTime)));
|
||||
|
||||
|
@ -261,15 +258,15 @@ TimeDuration SystemClockDriver::WaitInterval() {
|
|||
// least once a minute, if we need to wake up at all
|
||||
timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60 * 1000));
|
||||
LOG(LogLevel::Verbose,
|
||||
("%p: Waiting for next iteration; at %f, timeout=%f", GraphImpl(),
|
||||
("%p: Waiting for next iteration; at %f, timeout=%f", Graph(),
|
||||
(now - mInitialTimeStamp).ToSeconds(), timeoutMS / 1000.0));
|
||||
|
||||
return TimeDuration::FromMilliseconds(timeoutMS);
|
||||
}
|
||||
|
||||
OfflineClockDriver::OfflineClockDriver(MediaTrackGraphImpl* aGraphImpl,
|
||||
OfflineClockDriver::OfflineClockDriver(GraphInterface* aGraphInterface,
|
||||
uint32_t aSampleRate, GraphTime aSlice)
|
||||
: ThreadedDriver(aGraphImpl, nullptr, aSampleRate), mSlice(aSlice) {}
|
||||
: ThreadedDriver(aGraphInterface, nullptr, aSampleRate), mSlice(aSlice) {}
|
||||
|
||||
OfflineClockDriver::~OfflineClockDriver() {}
|
||||
|
||||
|
@ -282,7 +279,7 @@ AsyncCubebTask::AsyncCubebTask(AudioCallbackDriver* aDriver,
|
|||
: Runnable("AsyncCubebTask"),
|
||||
mDriver(aDriver),
|
||||
mOperation(aOperation),
|
||||
mShutdownGrip(aDriver->GraphImpl()) {
|
||||
mShutdownGrip(aDriver->Graph()) {
|
||||
NS_WARNING_ASSERTION(
|
||||
mDriver->mAudioStream || aOperation == AsyncCubebOperation::INIT,
|
||||
"No audio stream!");
|
||||
|
@ -297,7 +294,7 @@ AsyncCubebTask::Run() {
|
|||
switch (mOperation) {
|
||||
case AsyncCubebOperation::INIT: {
|
||||
LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::INIT driver=%p",
|
||||
mDriver->GraphImpl(), mDriver.get()));
|
||||
mDriver->Graph(), mDriver.get()));
|
||||
if (!mDriver->Init()) {
|
||||
LOG(LogLevel::Warning,
|
||||
("AsyncCubebOperation::INIT failed for driver=%p", mDriver.get()));
|
||||
|
@ -308,7 +305,7 @@ AsyncCubebTask::Run() {
|
|||
}
|
||||
case AsyncCubebOperation::SHUTDOWN: {
|
||||
LOG(LogLevel::Debug, ("%p: AsyncCubebOperation::SHUTDOWN driver=%p",
|
||||
mDriver->GraphImpl(), mDriver.get()));
|
||||
mDriver->Graph(), mDriver.get()));
|
||||
mDriver->Stop();
|
||||
|
||||
mDriver->CompleteAudioContextOperations(mOperation);
|
||||
|
@ -342,11 +339,11 @@ TrackAndPromiseForOperation::TrackAndPromiseForOperation(
|
|||
mHolder(std::move(aOther.mHolder)) {}
|
||||
|
||||
AudioCallbackDriver::AudioCallbackDriver(
|
||||
MediaTrackGraphImpl* aGraphImpl, GraphDriver* aPreviousDriver,
|
||||
GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
|
||||
uint32_t aSampleRate, uint32_t aOutputChannelCount,
|
||||
uint32_t aInputChannelCount, CubebUtils::AudioDeviceID aOutputDeviceID,
|
||||
CubebUtils::AudioDeviceID aInputDeviceID, AudioInputType aAudioInputType)
|
||||
: GraphDriver(aGraphImpl, aPreviousDriver, aSampleRate),
|
||||
: GraphDriver(aGraphInterface, aPreviousDriver, aSampleRate),
|
||||
mOutputChannels(aOutputChannelCount),
|
||||
mInputChannelCount(aInputChannelCount),
|
||||
mOutputDeviceID(aOutputDeviceID),
|
||||
|
@ -360,7 +357,7 @@ AudioCallbackDriver::AudioCallbackDriver(
|
|||
mAudioThreadRunning(false),
|
||||
mShouldFallbackIfError(false),
|
||||
mFromFallback(false) {
|
||||
LOG(LogLevel::Debug, ("%p: AudioCallbackDriver ctor", GraphImpl()));
|
||||
LOG(LogLevel::Debug, ("%p: AudioCallbackDriver ctor", Graph()));
|
||||
|
||||
const uint32_t kIdleThreadTimeoutMs = 2000;
|
||||
mInitShutdownThread->SetIdleThreadTimeout(
|
||||
|
@ -551,12 +548,11 @@ bool AudioCallbackDriver::Init() {
|
|||
|
||||
if (!StartStream()) {
|
||||
LOG(LogLevel::Warning,
|
||||
("%p: AudioCallbackDriver couldn't start a cubeb stream.",
|
||||
GraphImpl()));
|
||||
("%p: AudioCallbackDriver couldn't start a cubeb stream.", Graph()));
|
||||
return false;
|
||||
}
|
||||
|
||||
LOG(LogLevel::Debug, ("%p: AudioCallbackDriver started.", GraphImpl()));
|
||||
LOG(LogLevel::Debug, ("%p: AudioCallbackDriver started.", Graph()));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -615,7 +611,7 @@ void AudioCallbackDriver::Shutdown() {
|
|||
MOZ_ASSERT(NS_IsMainThread());
|
||||
LOG(LogLevel::Debug,
|
||||
("%p: Releasing audio driver off main thread (GraphDriver::Shutdown).",
|
||||
GraphImpl()));
|
||||
Graph()));
|
||||
RefPtr<AsyncCubebTask> releaseEvent =
|
||||
new AsyncCubebTask(this, AsyncCubebOperation::SHUTDOWN);
|
||||
releaseEvent->Dispatch(NS_DISPATCH_SYNC);
|
||||
|
@ -711,27 +707,26 @@ long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
|
|||
LOG(LogLevel::Verbose,
|
||||
("%p: interval[%ld; %ld] state[%ld; %ld] (frames: %ld) (durationMS: %u) "
|
||||
"(duration ticks: %ld)",
|
||||
GraphImpl(), (long)mIterationStart, (long)mIterationEnd,
|
||||
Graph(), (long)mIterationStart, (long)mIterationEnd,
|
||||
(long)mStateComputedTime, (long)nextStateComputedTime, (long)aFrames,
|
||||
(uint32_t)durationMS,
|
||||
(long)(nextStateComputedTime - mStateComputedTime)));
|
||||
|
||||
if (mStateComputedTime < mIterationEnd) {
|
||||
LOG(LogLevel::Error,
|
||||
("%p: Media graph global underrun detected", GraphImpl()));
|
||||
LOG(LogLevel::Error, ("%p: Media graph global underrun detected", Graph()));
|
||||
MOZ_ASSERT_UNREACHABLE("We should not underrun in full duplex");
|
||||
mIterationEnd = mStateComputedTime;
|
||||
}
|
||||
|
||||
// Process mic data if any/needed
|
||||
if (aInputBuffer && mInputChannelCount > 0) {
|
||||
GraphImpl()->NotifyInputData(aInputBuffer, static_cast<size_t>(aFrames),
|
||||
mSampleRate, mInputChannelCount);
|
||||
Graph()->NotifyInputData(aInputBuffer, static_cast<size_t>(aFrames),
|
||||
mSampleRate, mInputChannelCount);
|
||||
}
|
||||
|
||||
bool iterate = mBuffer.Available();
|
||||
IterationResult result =
|
||||
iterate ? GraphImpl()->OneIteration(nextStateComputedTime, &mMixer)
|
||||
iterate ? Graph()->OneIteration(nextStateComputedTime, &mMixer)
|
||||
: IterationResult::CreateStillProcessing();
|
||||
if (iterate) {
|
||||
// We totally filled the buffer (and mScratchBuffer isn't empty).
|
||||
|
@ -741,7 +736,7 @@ long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
|
|||
LOG(LogLevel::Verbose,
|
||||
("%p: DataCallback buffer filled entirely from scratch "
|
||||
"buffer, skipping iteration.",
|
||||
GraphImpl()));
|
||||
Graph()));
|
||||
result = IterationResult::CreateStillProcessing();
|
||||
}
|
||||
|
||||
|
@ -752,8 +747,8 @@ long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
|
|||
// data off separate cubeb callbacks. Take care with how stuff is
|
||||
// removed/added to this list and TSAN issues, but input and output will
|
||||
// use separate callback methods.
|
||||
GraphImpl()->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
|
||||
mSampleRate, mOutputChannels);
|
||||
Graph()->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
|
||||
mSampleRate, mOutputChannels);
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// This only happens when the output is on a macbookpro's external speaker,
|
||||
|
@ -780,7 +775,7 @@ long AudioCallbackDriver::DataCallback(const AudioDataValue* aInputBuffer,
|
|||
}
|
||||
|
||||
if (GraphDriver* nextDriver = result.NextDriver()) {
|
||||
LOG(LogLevel::Debug, ("%p: Switching to system driver.", GraphImpl()));
|
||||
LOG(LogLevel::Debug, ("%p: Switching to system driver.", Graph()));
|
||||
result.Switched();
|
||||
mShouldFallbackIfError = false;
|
||||
mAudioThreadRunning = false;
|
||||
|
@ -885,8 +880,7 @@ void AudioCallbackDriver::DeviceChangedCallback() {
|
|||
MOZ_ASSERT(!InIteration());
|
||||
// Tell the audio engine the device has changed, it might want to reset some
|
||||
// state.
|
||||
MonitorAutoLock mon(mGraphImpl->GetMonitor());
|
||||
GraphImpl()->DeviceChanged();
|
||||
Graph()->DeviceChanged();
|
||||
#ifdef XP_MACOSX
|
||||
RefPtr<AudioCallbackDriver> self(this);
|
||||
bool hasInput = mInputChannelCount;
|
||||
|
|
|
@ -57,10 +57,9 @@ 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 MediaTrack;
|
||||
class MediaTrackGraphImpl;
|
||||
|
||||
class AudioCallbackDriver;
|
||||
class GraphDriver;
|
||||
class MediaTrack;
|
||||
class OfflineClockDriver;
|
||||
class SystemClockDriver;
|
||||
|
||||
|
@ -68,56 +67,7 @@ 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 MediaTrackGraph. This is an
|
||||
* abstract base class. A MediaTrackGraph can be driven by an
|
||||
* OfflineClockDriver, if the graph is offline, or a SystemClockDriver, if the
|
||||
* graph is real time.
|
||||
* A MediaTrackGraph holds an owning reference to its driver.
|
||||
*
|
||||
* The lifetime of drivers is a complicated affair. Here are the different
|
||||
* scenarii that can happen:
|
||||
*
|
||||
* Starting a MediaTrackGraph with an AudioCallbackDriver
|
||||
* - A new thread T is created, from the main thread.
|
||||
* - On this thread T, cubeb is initialized if needed, and a cubeb_stream is
|
||||
* created and started
|
||||
* - The thread T posts a message to the main thread to terminate itself.
|
||||
* - The graph runs off the audio thread
|
||||
*
|
||||
* Starting a MediaTrackGraph with a SystemClockDriver:
|
||||
* - A new thread T is created from the main thread.
|
||||
* - The graph runs off this thread.
|
||||
*
|
||||
* Switching from a SystemClockDriver to an AudioCallbackDriver:
|
||||
* - A new AudioCallabackDriver is created and initialized on the graph thread
|
||||
* - At the end of the MTG iteration, the SystemClockDriver transfers its timing
|
||||
* info and a reference to itself to the AudioCallbackDriver. It then starts
|
||||
* the AudioCallbackDriver.
|
||||
* - When the AudioCallbackDriver starts, it checks if it has been switched from
|
||||
* a SystemClockDriver, and if that is the case, sends a message to the main
|
||||
* thread to shut the SystemClockDriver thread down.
|
||||
* - The graph now runs off an audio callback
|
||||
*
|
||||
* Switching from an AudioCallbackDriver to a SystemClockDriver:
|
||||
* - A new SystemClockDriver is created, and set as mNextDriver.
|
||||
* - At the end of the MTG iteration, the AudioCallbackDriver transfers its
|
||||
* timing info and a reference to itself to the SystemClockDriver. A new
|
||||
* SystemClockDriver is started from the current audio thread.
|
||||
* - When starting, the SystemClockDriver checks if it has been switched from an
|
||||
* AudioCallbackDriver. If yes, it creates a new temporary thread to release
|
||||
* the cubeb_streams. This temporary thread closes the cubeb_stream, and
|
||||
* then dispatches a message to the main thread to be terminated.
|
||||
* - The graph now runs off a normal thread.
|
||||
*
|
||||
* Two drivers cannot run at the same time for the same graph. The thread safety
|
||||
* of the different attributes of drivers, and they access pattern is documented
|
||||
* next to the members themselves.
|
||||
*
|
||||
*/
|
||||
class GraphDriver {
|
||||
public:
|
||||
struct GraphInterface {
|
||||
/**
|
||||
* Object returned from OneIteration() instructing the iterating GraphDriver
|
||||
* what to do.
|
||||
|
@ -212,7 +162,84 @@ class GraphDriver {
|
|||
}
|
||||
};
|
||||
|
||||
GraphDriver(MediaTrackGraphImpl* aGraphImpl, GraphDriver* aPreviousDriver,
|
||||
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||
|
||||
/* Called on the graph thread when there is new output data for listeners.
|
||||
* This is the mixed audio output of this MediaTrackGraph. */
|
||||
virtual void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels) = 0;
|
||||
/* Called on the graph thread when there is new input data for listeners. This
|
||||
* is the raw audio input for this MediaTrackGraph. */
|
||||
virtual void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels) = 0;
|
||||
/* Called every time there are changes to input/output audio devices like
|
||||
* plug/unplug etc. This can be called on any thread, and posts a message to
|
||||
* the main thread so that it can post a message to the graph thread. */
|
||||
virtual void DeviceChanged() = 0;
|
||||
/* Called by GraphDriver to iterate the graph. Output from the graph gets
|
||||
* mixed into aMixer, if it is non-null. */
|
||||
virtual IterationResult OneIteration(GraphTime aStateEnd,
|
||||
AudioMixer* aMixer) = 0;
|
||||
#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. */
|
||||
virtual bool InDriverIteration(GraphDriver* aDriver) = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* A driver is responsible for the scheduling of the processing, the thread
|
||||
* management, and give the different clocks to a MediaTrackGraph. This is an
|
||||
* abstract base class. A MediaTrackGraph can be driven by an
|
||||
* OfflineClockDriver, if the graph is offline, or a SystemClockDriver, if the
|
||||
* graph is real time.
|
||||
* A MediaTrackGraph holds an owning reference to its driver.
|
||||
*
|
||||
* The lifetime of drivers is a complicated affair. Here are the different
|
||||
* scenarii that can happen:
|
||||
*
|
||||
* Starting a MediaTrackGraph with an AudioCallbackDriver
|
||||
* - A new thread T is created, from the main thread.
|
||||
* - On this thread T, cubeb is initialized if needed, and a cubeb_stream is
|
||||
* created and started
|
||||
* - The thread T posts a message to the main thread to terminate itself.
|
||||
* - The graph runs off the audio thread
|
||||
*
|
||||
* Starting a MediaTrackGraph with a SystemClockDriver:
|
||||
* - A new thread T is created from the main thread.
|
||||
* - The graph runs off this thread.
|
||||
*
|
||||
* Switching from a SystemClockDriver to an AudioCallbackDriver:
|
||||
* - A new AudioCallabackDriver is created and initialized on the graph thread
|
||||
* - At the end of the MTG iteration, the SystemClockDriver transfers its timing
|
||||
* info and a reference to itself to the AudioCallbackDriver. It then starts
|
||||
* the AudioCallbackDriver.
|
||||
* - When the AudioCallbackDriver starts, it checks if it has been switched from
|
||||
* a SystemClockDriver, and if that is the case, sends a message to the main
|
||||
* thread to shut the SystemClockDriver thread down.
|
||||
* - The graph now runs off an audio callback
|
||||
*
|
||||
* Switching from an AudioCallbackDriver to a SystemClockDriver:
|
||||
* - A new SystemClockDriver is created, and set as mNextDriver.
|
||||
* - At the end of the MTG iteration, the AudioCallbackDriver transfers its
|
||||
* timing info and a reference to itself to the SystemClockDriver. A new
|
||||
* SystemClockDriver is started from the current audio thread.
|
||||
* - When starting, the SystemClockDriver checks if it has been switched from an
|
||||
* AudioCallbackDriver. If yes, it creates a new temporary thread to release
|
||||
* the cubeb_streams. This temporary thread closes the cubeb_stream, and
|
||||
* then dispatches a message to the main thread to be terminated.
|
||||
* - The graph now runs off a normal thread.
|
||||
*
|
||||
* Two drivers cannot run at the same time for the same graph. The thread safety
|
||||
* of the different attributes of drivers, and they access pattern is documented
|
||||
* next to the members themselves.
|
||||
*
|
||||
*/
|
||||
class GraphDriver {
|
||||
public:
|
||||
using IterationResult = GraphInterface::IterationResult;
|
||||
|
||||
GraphDriver(GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
|
||||
uint32_t aSampleRate);
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GraphDriver);
|
||||
|
@ -259,7 +286,7 @@ class GraphDriver {
|
|||
void SetState(GraphTime aIterationStart, GraphTime aIterationEnd,
|
||||
GraphTime aStateComputedTime);
|
||||
|
||||
MediaTrackGraphImpl* GraphImpl() const { return mGraphImpl; }
|
||||
GraphInterface* Graph() const { return mGraphInterface; }
|
||||
|
||||
#ifdef DEBUG
|
||||
// True if the current thread is currently iterating the MTG.
|
||||
|
@ -295,8 +322,8 @@ class GraphDriver {
|
|||
GraphTime mIterationEnd = 0;
|
||||
// Time until which the graph has processed data.
|
||||
GraphTime mStateComputedTime = 0;
|
||||
// The MediaTrackGraphImpl associated with this driver.
|
||||
const RefPtr<MediaTrackGraphImpl> mGraphImpl;
|
||||
// The GraphInterface this driver is currently iterating.
|
||||
const RefPtr<GraphInterface> mGraphInterface;
|
||||
// The sample rate for the graph, and in case of an audio driver, also for the
|
||||
// cubeb stream.
|
||||
const uint32_t mSampleRate;
|
||||
|
@ -366,7 +393,7 @@ class ThreadedDriver : public GraphDriver {
|
|||
};
|
||||
|
||||
public:
|
||||
ThreadedDriver(MediaTrackGraphImpl* aGraphImpl, GraphDriver* aPreviousDriver,
|
||||
ThreadedDriver(GraphInterface* aGraphInterface, GraphDriver* aPreviousDriver,
|
||||
uint32_t aSampleRate);
|
||||
virtual ~ThreadedDriver();
|
||||
|
||||
|
@ -413,13 +440,13 @@ class ThreadedDriver : public GraphDriver {
|
|||
};
|
||||
|
||||
/**
|
||||
* A SystemClockDriver drives a MediaTrackGraph using a system clock, and waits
|
||||
* A SystemClockDriver drives a GraphInterface using a system clock, and waits
|
||||
* using a monitor, between each iteration.
|
||||
*/
|
||||
enum class FallbackMode { Regular, Fallback };
|
||||
class SystemClockDriver : public ThreadedDriver {
|
||||
public:
|
||||
SystemClockDriver(MediaTrackGraphImpl* aGraphImpl,
|
||||
SystemClockDriver(GraphInterface* aGraphInterface,
|
||||
GraphDriver* aPreviousDriver, uint32_t aSampleRate,
|
||||
FallbackMode aFallback = FallbackMode::Regular);
|
||||
virtual ~SystemClockDriver();
|
||||
|
@ -449,7 +476,7 @@ class SystemClockDriver : public ThreadedDriver {
|
|||
*/
|
||||
class OfflineClockDriver : public ThreadedDriver {
|
||||
public:
|
||||
OfflineClockDriver(MediaTrackGraphImpl* aGraphImpl, uint32_t aSampleRate,
|
||||
OfflineClockDriver(GraphInterface* aGraphInterface, uint32_t aSampleRate,
|
||||
GraphTime aSlice);
|
||||
virtual ~OfflineClockDriver();
|
||||
OfflineClockDriver* AsOfflineClockDriver() override { return this; }
|
||||
|
@ -508,7 +535,7 @@ class AudioCallbackDriver : public GraphDriver,
|
|||
{
|
||||
public:
|
||||
/** If aInputChannelCount is zero, then this driver is output-only. */
|
||||
AudioCallbackDriver(MediaTrackGraphImpl* aGraphImpl,
|
||||
AudioCallbackDriver(GraphInterface* aGraphInterface,
|
||||
GraphDriver* aPreviousDriver, uint32_t aSampleRate,
|
||||
uint32_t aOutputChannelCount, uint32_t aInputChannelCount,
|
||||
CubebUtils::AudioDeviceID aOutputDeviceID,
|
||||
|
@ -712,7 +739,7 @@ class AsyncCubebTask : public Runnable {
|
|||
|
||||
RefPtr<AudioCallbackDriver> mDriver;
|
||||
AsyncCubebOperation mOperation;
|
||||
RefPtr<MediaTrackGraphImpl> mShutdownGrip;
|
||||
RefPtr<GraphInterface> mShutdownGrip;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -21,7 +21,7 @@ class AudioMixer;
|
|||
class MediaTrackGraphImpl;
|
||||
|
||||
class GraphRunner final : public Runnable {
|
||||
using IterationResult = GraphDriver::IterationResult;
|
||||
using IterationResult = GraphInterface::IterationResult;
|
||||
|
||||
public:
|
||||
static already_AddRefed<GraphRunner> Create(MediaTrackGraphImpl* aGraph);
|
||||
|
|
|
@ -91,11 +91,10 @@ class MessageBlock {
|
|||
* object too.
|
||||
*/
|
||||
class MediaTrackGraphImpl : public MediaTrackGraph,
|
||||
public GraphInterface,
|
||||
public nsIMemoryReporter,
|
||||
public nsITimerCallback,
|
||||
public nsINamed {
|
||||
using IterationResult = GraphDriver::IterationResult;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIMEMORYREPORTER
|
||||
|
@ -127,7 +126,7 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
|
|||
* True if we're on aDriver's thread, or if we're on mGraphRunner's thread
|
||||
* and mGraphRunner is currently run by aDriver.
|
||||
*/
|
||||
bool InDriverIteration(GraphDriver* aDriver);
|
||||
bool InDriverIteration(GraphDriver* aDriver) override;
|
||||
#endif
|
||||
|
||||
/**
|
||||
|
@ -217,7 +216,8 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
|
|||
* OneIterationImpl is called directly. Output from the graph gets mixed into
|
||||
* aMixer, if it is non-null.
|
||||
*/
|
||||
IterationResult OneIteration(GraphTime aStateEnd, AudioMixer* aMixer);
|
||||
IterationResult OneIteration(GraphTime aStateEnd,
|
||||
AudioMixer* aMixer) override;
|
||||
|
||||
/**
|
||||
* Returns true if this MediaTrackGraph should keep running
|
||||
|
@ -428,15 +428,15 @@ class MediaTrackGraphImpl : public MediaTrackGraph,
|
|||
/* Called on the graph thread when there is new output data for listeners.
|
||||
* This is the mixed audio output of this MediaTrackGraph. */
|
||||
void NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels);
|
||||
TrackRate aRate, uint32_t aChannels) override;
|
||||
/* Called on the graph thread when there is new input data for listeners. This
|
||||
* is the raw audio input for this MediaTrackGraph. */
|
||||
void NotifyInputData(const AudioDataValue* aBuffer, size_t aFrames,
|
||||
TrackRate aRate, uint32_t aChannels);
|
||||
TrackRate aRate, uint32_t aChannels) override;
|
||||
/* Called every time there are changes to input/output audio devices like
|
||||
* plug/unplug etc. This can be called on any thread, and posts a message to
|
||||
* the main thread so that it can post a message to the graph thread. */
|
||||
void DeviceChanged();
|
||||
void DeviceChanged() override;
|
||||
/* Called every time there are changes to input/output audio devices. This is
|
||||
* called on the graph thread. */
|
||||
void DeviceChangedImpl();
|
||||
|
|
Загрузка…
Ссылка в новой задаче