summaryrefslogtreecommitdiffstats
path: root/dom/media/gtest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-12 05:35:37 +0000
commita90a5cba08fdf6c0ceb95101c275108a152a3aed (patch)
tree532507288f3defd7f4dcf1af49698bcb76034855 /dom/media/gtest
parentAdding debian version 126.0.1-1. (diff)
downloadfirefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.tar.xz
firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.zip
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/media/gtest')
-rw-r--r--dom/media/gtest/AudioVerifier.h4
-rw-r--r--dom/media/gtest/GMPTestMonitor.h4
-rw-r--r--dom/media/gtest/MockCubeb.cpp301
-rw-r--r--dom/media/gtest/MockCubeb.h184
-rw-r--r--dom/media/gtest/TestAudioCallbackDriver.cpp457
-rw-r--r--dom/media/gtest/TestAudioInputProcessing.cpp175
-rw-r--r--dom/media/gtest/TestAudioInputSource.cpp114
-rw-r--r--dom/media/gtest/TestAudioRingBuffer.cpp50
-rw-r--r--dom/media/gtest/TestAudioTrackGraph.cpp740
-rw-r--r--dom/media/gtest/TestCDMStorage.cpp24
-rw-r--r--dom/media/gtest/TestDeviceInputTrack.cpp18
-rw-r--r--dom/media/gtest/TestMP4Demuxer.cpp4
-rw-r--r--dom/media/gtest/TestMediaDataEncoder.cpp2
-rw-r--r--dom/media/gtest/TestWebMWriter.cpp6
14 files changed, 1582 insertions, 501 deletions
diff --git a/dom/media/gtest/AudioVerifier.h b/dom/media/gtest/AudioVerifier.h
index ba67f6e489..2ff8ed9269 100644
--- a/dom/media/gtest/AudioVerifier.h
+++ b/dom/media/gtest/AudioVerifier.h
@@ -99,7 +99,7 @@ class AudioVerifier {
void CountZeroCrossing(Sample aCurrentSample) {
if (mPrevious > 0 && aCurrentSample <= 0) {
if (mZeroCrossCount++) {
- MOZ_ASSERT(mZeroCrossCount > 1);
+ MOZ_RELEASE_ASSERT(mZeroCrossCount > 1);
mSumPeriodInSamples += mTotalFramesSoFar - mLastZeroCrossPosition;
}
mLastZeroCrossPosition = mTotalFramesSoFar;
@@ -120,7 +120,7 @@ class AudioVerifier {
return;
}
- MOZ_ASSERT(mCurrentDiscontinuityFrameCount == 0);
+ MOZ_RELEASE_ASSERT(mCurrentDiscontinuityFrameCount == 0);
if (!discontinuity) {
return;
}
diff --git a/dom/media/gtest/GMPTestMonitor.h b/dom/media/gtest/GMPTestMonitor.h
index 27477b6a42..9f4e8f0a84 100644
--- a/dom/media/gtest/GMPTestMonitor.h
+++ b/dom/media/gtest/GMPTestMonitor.h
@@ -15,7 +15,7 @@ class GMPTestMonitor {
GMPTestMonitor() : mFinished(false) {}
void AwaitFinished() {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
mozilla::SpinEventLoopUntil("GMPTestMonitor::AwaitFinished"_ns,
[&]() { return mFinished; });
mFinished = false;
@@ -23,7 +23,7 @@ class GMPTestMonitor {
private:
void MarkFinished() {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
mFinished = true;
}
diff --git a/dom/media/gtest/MockCubeb.cpp b/dom/media/gtest/MockCubeb.cpp
index ae3a676ac8..9d61ccf4b5 100644
--- a/dom/media/gtest/MockCubeb.cpp
+++ b/dom/media/gtest/MockCubeb.cpp
@@ -168,79 +168,93 @@ MockCubebStream::MockCubebStream(
mAudioVerifier(aInputStreamParams ? aInputStreamParams->rate
: aOutputStreamParams->rate,
100 /* aFrequency */) {
- MOZ_ASSERT(mAudioGenerator.ChannelCount() <= MAX_INPUT_CHANNELS,
- "mInputBuffer has no enough space to hold generated data");
- MOZ_ASSERT_IF(mFrozenStart, mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mAudioGenerator.ChannelCount() <= MAX_INPUT_CHANNELS,
+ "mInputBuffer has no enough space to hold generated data");
+ if (mFrozenStart) {
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
+ }
if (aInputStreamParams) {
mInputParams = *aInputStreamParams;
}
if (aOutputStreamParams) {
mOutputParams = *aOutputStreamParams;
- MOZ_ASSERT(SampleRate() == mOutputParams.rate);
+ MOZ_RELEASE_ASSERT(SampleRate() == mOutputParams.rate);
}
}
MockCubebStream::~MockCubebStream() = default;
int MockCubebStream::Start() {
- NotifyState(CUBEB_STATE_STARTED);
- mStreamStop = false;
- if (mFrozenStart) {
- // We need to grab mFrozenStartMonitor before returning to avoid races in
- // the calling code -- it controls when to mFrozenStartMonitor.Notify().
- // TempData helps facilitate this by holding what's needed to block the
- // calling thread until the background thread has grabbed the lock.
- struct TempData {
- NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TempData)
- static_assert(HasThreadSafeRefCnt::value,
- "Silence a -Wunused-local-typedef warning");
- Monitor mMonitor{"MockCubebStream::Start::TempData::mMonitor"};
- bool mFinished = false;
-
- private:
- ~TempData() = default;
- };
- auto temp = MakeRefPtr<TempData>();
- MonitorAutoLock lock(temp->mMonitor);
- NS_DispatchBackgroundTask(NS_NewRunnableFunction(
- "MockCubebStream::WaitForThawBeforeStart",
- [temp, this, self = RefPtr<SmartMockCubebStream>(mSelf)]() mutable {
- MonitorAutoLock lock(mFrozenStartMonitor);
- {
- // Unblock MockCubebStream::Start now that we have locked the frozen
- // start monitor.
- MonitorAutoLock tempLock(temp->mMonitor);
- temp->mFinished = true;
- temp->mMonitor.Notify();
- temp = nullptr;
- }
- while (mFrozenStart) {
- mFrozenStartMonitor.Wait();
- }
- if (!mStreamStop) {
+ {
+ MutexAutoLock l(mMutex);
+ NotifyState(CUBEB_STATE_STARTED);
+ }
+ {
+ MonitorAutoLock lock(mFrozenStartMonitor);
+ if (mFrozenStart) {
+ // We need to grab mFrozenStartMonitor before returning to avoid races in
+ // the calling code -- it controls when to mFrozenStartMonitor.Notify().
+ // TempData helps facilitate this by holding what's needed to block the
+ // calling thread until the background thread has grabbed the lock.
+ struct TempData {
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TempData)
+ static_assert(HasThreadSafeRefCnt::value,
+ "Silence a -Wunused-local-typedef warning");
+ Monitor mMonitor{"MockCubebStream::Start::TempData::mMonitor"};
+ bool mFinished = false;
+
+ private:
+ ~TempData() = default;
+ };
+ auto temp = MakeRefPtr<TempData>();
+ MonitorAutoLock lock(temp->mMonitor);
+ NS_DispatchBackgroundTask(NS_NewRunnableFunction(
+ "MockCubebStream::WaitForThawBeforeStart",
+ [temp, this, self = RefPtr<SmartMockCubebStream>(mSelf)]() mutable {
+ {
+ // Unblock MockCubebStream::Start now that we have locked the
+ // frozen start monitor.
+ MonitorAutoLock tempLock(temp->mMonitor);
+ temp->mFinished = true;
+ temp->mMonitor.Notify();
+ temp = nullptr;
+ }
+ {
+ MonitorAutoLock lock(mFrozenStartMonitor);
+ while (mFrozenStart) {
+ mFrozenStartMonitor.Wait();
+ }
+ }
+ if (MutexAutoLock l(mMutex);
+ !mState || *mState != CUBEB_STATE_STARTED) {
+ return;
+ }
MockCubeb::AsMock(context)->StartStream(mSelf);
- }
- }));
- while (!temp->mFinished) {
- temp->mMonitor.Wait();
+ }));
+ while (!temp->mFinished) {
+ temp->mMonitor.Wait();
+ }
+ return CUBEB_OK;
}
- return CUBEB_OK;
}
MockCubeb::AsMock(context)->StartStream(this);
return CUBEB_OK;
}
int MockCubebStream::Stop() {
+ MockCubeb::AsMock(context)->StopStream(this);
+ MutexAutoLock l(mMutex);
mOutputVerificationEvent.Notify(std::make_tuple(
mAudioVerifier.PreSilenceSamples(), mAudioVerifier.EstimatedFreq(),
mAudioVerifier.CountDiscontinuities()));
- MockCubeb::AsMock(context)->StopStream(this);
- mStreamStop = true;
NotifyState(CUBEB_STATE_STOPPED);
return CUBEB_OK;
}
-uint64_t MockCubebStream::Position() { return mPosition; }
+uint64_t MockCubebStream::Position() {
+ MutexAutoLock l(mMutex);
+ return mPosition;
+}
void MockCubebStream::Destroy() {
// Stop() even if cubeb_stream_stop() has already been called, as with
@@ -248,11 +262,15 @@ void MockCubebStream::Destroy() {
// This provides an extra STOPPED state callback as with audioipc.
// It also ensures that this stream is removed from MockCubeb::mLiveStreams.
Stop();
- mDestroyed = true;
+ {
+ MutexAutoLock l(mMutex);
+ mDestroyed = true;
+ }
MockCubeb::AsMock(context)->StreamDestroy(this);
}
int MockCubebStream::SetName(char const* aName) {
+ MutexAutoLock l(mMutex);
mName = aName;
mNameSetEvent.Notify(mName);
return CUBEB_OK;
@@ -260,6 +278,7 @@ int MockCubebStream::SetName(char const* aName) {
int MockCubebStream::RegisterDeviceChangedCallback(
cubeb_device_changed_callback aDeviceChangedCallback) {
+ MutexAutoLock l(mMutex);
if (mDeviceChangedCallback && aDeviceChangedCallback) {
return CUBEB_ERROR_INVALID_PARAMETER;
}
@@ -267,14 +286,41 @@ int MockCubebStream::RegisterDeviceChangedCallback(
return CUBEB_OK;
}
+int MockCubebStream::SetInputProcessingParams(
+ cubeb_input_processing_params aParams) {
+ MockCubeb* mock = MockCubeb::AsMock(context);
+ auto res = mock->SupportedInputProcessingParams();
+ if (res.isErr()) {
+ return CUBEB_ERROR_NOT_SUPPORTED;
+ }
+ cubeb_input_processing_params supported = res.unwrap();
+ if ((supported & aParams) != aParams) {
+ return CUBEB_ERROR_INVALID_PARAMETER;
+ }
+ return mock->InputProcessingApplyRv();
+}
+
cubeb_stream* MockCubebStream::AsCubebStream() {
- MOZ_ASSERT(!mDestroyed);
+ MutexAutoLock l(mMutex);
+ return AsCubebStreamLocked();
+}
+
+cubeb_stream* MockCubebStream::AsCubebStreamLocked() {
+ MOZ_RELEASE_ASSERT(!mDestroyed);
+ mMutex.AssertCurrentThreadOwns();
return reinterpret_cast<cubeb_stream*>(this);
}
MockCubebStream* MockCubebStream::AsMock(cubeb_stream* aStream) {
auto* mockStream = reinterpret_cast<MockCubebStream*>(aStream);
- MOZ_ASSERT(!mockStream->mDestroyed);
+ MutexAutoLock l(mockStream->mMutex);
+ return AsMockLocked(aStream);
+}
+
+MockCubebStream* MockCubebStream::AsMockLocked(cubeb_stream* aStream) {
+ auto* mockStream = reinterpret_cast<MockCubebStream*>(aStream);
+ mockStream->mMutex.AssertCurrentThreadOwns();
+ MOZ_RELEASE_ASSERT(!mockStream->mDestroyed);
return mockStream;
}
@@ -285,58 +331,96 @@ cubeb_devid MockCubebStream::GetOutputDeviceID() const {
}
uint32_t MockCubebStream::InputChannels() const {
+ MutexAutoLock l(mMutex);
+ return InputChannelsLocked();
+}
+
+uint32_t MockCubebStream::InputChannelsLocked() const {
+ mMutex.AssertCurrentThreadOwns();
return mAudioGenerator.ChannelCount();
}
uint32_t MockCubebStream::OutputChannels() const {
+ MutexAutoLock l(mMutex);
+ return OutputChannelsLocked();
+}
+
+uint32_t MockCubebStream::OutputChannelsLocked() const {
+ mMutex.AssertCurrentThreadOwns();
return mOutputParams.channels;
}
uint32_t MockCubebStream::SampleRate() const {
+ MutexAutoLock l(mMutex);
+ return SampleRateLocked();
+}
+
+uint32_t MockCubebStream::SampleRateLocked() const {
+ mMutex.AssertCurrentThreadOwns();
return mAudioGenerator.mSampleRate;
}
uint32_t MockCubebStream::InputFrequency() const {
+ MutexAutoLock l(mMutex);
+ return InputFrequencyLocked();
+}
+
+uint32_t MockCubebStream::InputFrequencyLocked() const {
+ mMutex.AssertCurrentThreadOwns();
return mAudioGenerator.mFrequency;
}
+Maybe<cubeb_state> MockCubebStream::State() const {
+ MutexAutoLock l(mMutex);
+ return mState;
+}
+
nsTArray<AudioDataValue>&& MockCubebStream::TakeRecordedOutput() {
+ MutexAutoLock l(mMutex);
return std::move(mRecordedOutput);
}
nsTArray<AudioDataValue>&& MockCubebStream::TakeRecordedInput() {
+ MutexAutoLock l(mMutex);
return std::move(mRecordedInput);
}
void MockCubebStream::SetDriftFactor(float aDriftFactor) {
- MOZ_ASSERT(mRunningMode == MockCubeb::RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == MockCubeb::RunningMode::Automatic);
+ MutexAutoLock l(mMutex);
mDriftFactor = aDriftFactor;
}
-void MockCubebStream::ForceError() { mForceErrorState = true; }
+void MockCubebStream::ForceError() {
+ MutexAutoLock l(mMutex);
+ mForceErrorState = true;
+}
void MockCubebStream::ForceDeviceChanged() {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
+ MutexAutoLock l(mMutex);
mForceDeviceChanged = true;
};
void MockCubebStream::NotifyDeviceChangedNow() {
- MOZ_ASSERT(mRunningMode == RunningMode::Manual);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Manual);
NotifyDeviceChanged();
}
void MockCubebStream::Thaw() {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
MonitorAutoLock l(mFrozenStartMonitor);
mFrozenStart = false;
mFrozenStartMonitor.Notify();
}
void MockCubebStream::SetOutputRecordingEnabled(bool aEnabled) {
+ MutexAutoLock l(mMutex);
mOutputRecordingEnabled = aEnabled;
}
void MockCubebStream::SetInputRecordingEnabled(bool aEnabled) {
+ MutexAutoLock l(mMutex);
mInputRecordingEnabled = aEnabled;
}
@@ -370,33 +454,43 @@ MediaEventSource<void>& MockCubebStream::DeviceChangeForcedEvent() {
}
KeepProcessing MockCubebStream::ManualDataCallback(long aNrFrames) {
- MOZ_ASSERT(mRunningMode == RunningMode::Manual);
- MOZ_ASSERT(aNrFrames <= kMaxNrFrames);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Manual);
+ MOZ_RELEASE_ASSERT(aNrFrames <= kMaxNrFrames);
+ MutexAutoLock l(mMutex);
return Process(aNrFrames);
}
KeepProcessing MockCubebStream::Process(long aNrFrames) {
+ mMutex.AssertCurrentThreadOwns();
+ if (!mState || *mState != CUBEB_STATE_STARTED) {
+ return KeepProcessing::InvalidState;
+ }
if (mInputParams.rate) {
mAudioGenerator.GenerateInterleaved(mInputBuffer, aNrFrames);
}
- cubeb_stream* stream = AsCubebStream();
+ cubeb_stream* stream = AsCubebStreamLocked();
const long outframes =
mDataCallback(stream, mUserPtr, mHasInput ? mInputBuffer : nullptr,
mHasOutput ? mOutputBuffer : nullptr, aNrFrames);
- if (mInputRecordingEnabled && mHasInput) {
- mRecordedInput.AppendElements(mInputBuffer, outframes * InputChannels());
- }
- if (mOutputRecordingEnabled && mHasOutput) {
- mRecordedOutput.AppendElements(mOutputBuffer, outframes * OutputChannels());
- }
- mAudioVerifier.AppendDataInterleaved(mOutputBuffer, outframes,
- MAX_OUTPUT_CHANNELS);
- mPosition += outframes;
+ if (outframes > 0) {
+ if (mInputRecordingEnabled && mHasInput) {
+ mRecordedInput.AppendElements(mInputBuffer,
+ outframes * InputChannelsLocked());
+ }
+ if (mOutputRecordingEnabled && mHasOutput) {
+ mRecordedOutput.AppendElements(mOutputBuffer,
+
+ outframes * OutputChannelsLocked());
+ }
+ mAudioVerifier.AppendDataInterleaved(mOutputBuffer, outframes,
+ MAX_OUTPUT_CHANNELS);
+ mPosition += outframes;
- mFramesProcessedEvent.Notify(outframes);
- if (mAudioVerifier.PreSilenceEnded()) {
- mFramesVerifiedEvent.Notify(outframes);
+ mFramesProcessedEvent.Notify(outframes);
+ if (mAudioVerifier.PreSilenceEnded()) {
+ mFramesVerifiedEvent.Notify(outframes);
+ }
}
if (outframes < aNrFrames) {
@@ -422,8 +516,9 @@ KeepProcessing MockCubebStream::Process(long aNrFrames) {
}
KeepProcessing MockCubebStream::Process10Ms() {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
- uint32_t rate = SampleRate();
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
+ MutexAutoLock l(mMutex);
+ uint32_t rate = SampleRateLocked();
const long nrFrames =
static_cast<long>(static_cast<float>(rate * 10) * mDriftFactor) /
PR_MSEC_PER_SEC;
@@ -431,11 +526,14 @@ KeepProcessing MockCubebStream::Process10Ms() {
}
void MockCubebStream::NotifyState(cubeb_state aState) {
- mStateCallback(AsCubebStream(), mUserPtr, aState);
+ mMutex.AssertCurrentThreadOwns();
+ mState = Some(aState);
+ mStateCallback(AsCubebStreamLocked(), mUserPtr, aState);
mStateEvent.Notify(aState);
}
void MockCubebStream::NotifyDeviceChanged() {
+ MutexAutoLock l(mMutex);
mDeviceChangedCallback(this->mUserPtr);
mDeviceChangedForcedEvent.Notify();
}
@@ -445,20 +543,20 @@ MockCubeb::MockCubeb() : MockCubeb(MockCubeb::RunningMode::Automatic) {}
MockCubeb::MockCubeb(RunningMode aRunningMode)
: ops(&mock_ops), mRunningMode(aRunningMode) {}
-MockCubeb::~MockCubeb() { MOZ_ASSERT(!mFakeAudioThread); };
+MockCubeb::~MockCubeb() { MOZ_RELEASE_ASSERT(!mFakeAudioThread); };
void MockCubeb::Destroy() {
- MOZ_ASSERT(mHasCubebContext);
+ MOZ_RELEASE_ASSERT(mHasCubebContext);
{
auto streams = mLiveStreams.Lock();
- MOZ_ASSERT(streams->IsEmpty());
+ MOZ_RELEASE_ASSERT(streams->IsEmpty());
}
mDestroyed = true;
Release();
}
cubeb* MockCubeb::AsCubebContext() {
- MOZ_ASSERT(!mDestroyed);
+ MOZ_RELEASE_ASSERT(!mDestroyed);
if (mHasCubebContext.compareExchange(false, true)) {
AddRef();
}
@@ -467,7 +565,7 @@ cubeb* MockCubeb::AsCubebContext() {
MockCubeb* MockCubeb::AsMock(cubeb* aContext) {
auto* mockCubeb = reinterpret_cast<MockCubeb*>(aContext);
- MOZ_ASSERT(!mockCubeb->mDestroyed);
+ MOZ_RELEASE_ASSERT(!mockCubeb->mDestroyed);
return mockCubeb;
}
@@ -528,6 +626,28 @@ int MockCubeb::RegisterDeviceCollectionChangeCallback(
return CUBEB_OK;
}
+Result<cubeb_input_processing_params, int>
+MockCubeb::SupportedInputProcessingParams() const {
+ const auto& [params, rv] = mSupportedInputProcessingParams;
+ if (rv != CUBEB_OK) {
+ return Err(rv);
+ }
+ return params;
+}
+
+void MockCubeb::SetSupportedInputProcessingParams(
+ cubeb_input_processing_params aParams, int aRv) {
+ mSupportedInputProcessingParams = std::make_pair(aParams, aRv);
+}
+
+void MockCubeb::SetInputProcessingApplyRv(int aRv) {
+ mInputProcessingParamsApplyRv = aRv;
+}
+
+int MockCubeb::InputProcessingApplyRv() const {
+ return mInputProcessingParamsApplyRv;
+}
+
void MockCubeb::AddDevice(cubeb_device_info aDevice) {
if (aDevice.type == CUBEB_DEVICE_TYPE_INPUT) {
mInputDevices.AppendElement(aDevice);
@@ -613,12 +733,12 @@ void MockCubeb::SetSupportDeviceChangeCallback(bool aSupports) {
void MockCubeb::ForceStreamInitError() { mStreamInitErrorState = true; }
void MockCubeb::SetStreamStartFreezeEnabled(bool aEnabled) {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
mStreamStartFreezeEnabled = aEnabled;
}
auto MockCubeb::ForceAudioThread() -> RefPtr<ForcedAudioThreadPromise> {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
RefPtr<ForcedAudioThreadPromise> p =
mForcedAudioThreadPromise.Ensure(__func__);
mForcedAudioThread = true;
@@ -627,7 +747,7 @@ auto MockCubeb::ForceAudioThread() -> RefPtr<ForcedAudioThreadPromise> {
}
void MockCubeb::UnforceAudioThread() {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
mForcedAudioThread = false;
}
@@ -660,12 +780,12 @@ void MockCubeb::StreamDestroy(MockCubebStream* aStream) {
}
void MockCubeb::GoFaster() {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
mFastMode = true;
}
void MockCubeb::DontGoFaster() {
- MOZ_ASSERT(mRunningMode == RunningMode::Automatic);
+ MOZ_RELEASE_ASSERT(mRunningMode == RunningMode::Automatic);
mFastMode = false;
}
@@ -679,13 +799,17 @@ MockCubeb::StreamDestroyEvent() {
}
void MockCubeb::StartStream(MockCubebStream* aStream) {
+ if (aStream) {
+ aStream->mMutex.AssertNotCurrentThreadOwns();
+ }
auto streams = mLiveStreams.Lock();
- MOZ_ASSERT_IF(!aStream, mForcedAudioThread);
- // Forcing an audio thread must happen before starting streams
- MOZ_ASSERT_IF(!aStream, streams->IsEmpty());
if (aStream) {
- MOZ_ASSERT(!streams->Contains(aStream->mSelf));
+ MOZ_RELEASE_ASSERT(!streams->Contains(aStream->mSelf));
streams->AppendElement(aStream->mSelf);
+ } else {
+ MOZ_RELEASE_ASSERT(mForcedAudioThread);
+ // Forcing an audio thread must happen before starting streams
+ MOZ_RELEASE_ASSERT(streams->IsEmpty());
}
if (!mFakeAudioThread && mRunningMode == RunningMode::Automatic) {
AddRef(); // released when the thread exits
@@ -694,6 +818,7 @@ void MockCubeb::StartStream(MockCubebStream* aStream) {
}
void MockCubeb::StopStream(MockCubebStream* aStream) {
+ aStream->mMutex.AssertNotCurrentThreadOwns();
{
auto streams = mLiveStreams.Lock();
if (!streams->Contains(aStream->mSelf)) {
@@ -719,7 +844,7 @@ void MockCubeb::ThreadFunction() {
}
}
streams->RemoveElementsBy([](const auto& stream) { return !stream; });
- MOZ_ASSERT(mFakeAudioThread);
+ MOZ_RELEASE_ASSERT(mFakeAudioThread);
if (streams->IsEmpty() && !mForcedAudioThread) {
// This leaks the std::thread if Gecko's main thread has already been
// shut down.
diff --git a/dom/media/gtest/MockCubeb.h b/dom/media/gtest/MockCubeb.h
index ed6342a779..15689a4a4e 100644
--- a/dom/media/gtest/MockCubeb.h
+++ b/dom/media/gtest/MockCubeb.h
@@ -11,12 +11,15 @@
#include "MediaEventSource.h"
#include "mozilla/DataMutex.h"
#include "mozilla/MozPromise.h"
+#include "mozilla/Result.h"
+#include "mozilla/ResultVariant.h"
#include "mozilla/ThreadSafeWeakPtr.h"
#include "nsTArray.h"
#include <thread>
#include <atomic>
#include <chrono>
+#include <utility>
namespace mozilla {
const uint32_t MAX_OUTPUT_CHANNELS = 2;
@@ -68,6 +71,8 @@ struct cubeb_ops {
// Keep those and the struct definition in sync with cubeb.h and
// cubeb-internal.h
void cubeb_mock_destroy(cubeb* context);
+static int cubeb_mock_get_supported_input_processing_params(
+ cubeb* context, cubeb_input_processing_params* params);
static int cubeb_mock_enumerate_devices(cubeb* context, cubeb_device_type type,
cubeb_device_collection* out);
@@ -101,6 +106,9 @@ static int cubeb_mock_stream_set_volume(cubeb_stream* stream, float volume);
static int cubeb_mock_stream_set_name(cubeb_stream* stream,
char const* stream_name);
+static int cubeb_mock_stream_set_input_processing_params(
+ cubeb_stream* stream, cubeb_input_processing_params);
+
static int cubeb_mock_stream_register_device_changed_callback(
cubeb_stream* stream,
cubeb_device_changed_callback device_changed_callback);
@@ -122,7 +130,7 @@ cubeb_ops const mock_ops = {
/*.get_min_latency =*/cubeb_mock_get_min_latency,
/*.get_preferred_sample_rate =*/cubeb_mock_get_preferred_sample_rate,
/*.get_supported_input_processing_params =*/
- NULL,
+ cubeb_mock_get_supported_input_processing_params,
/*.enumerate_devices =*/cubeb_mock_enumerate_devices,
/*.device_collection_destroy =*/cubeb_mock_device_collection_destroy,
/*.destroy =*/cubeb_mock_destroy,
@@ -139,7 +147,7 @@ cubeb_ops const mock_ops = {
/*.stream_set_input_mute =*/NULL,
/*.stream_set_input_processing_params =*/
- NULL,
+ cubeb_mock_stream_set_input_processing_params,
/*.stream_device_destroy =*/NULL,
/*.stream_register_device_changed_callback =*/
cubeb_mock_stream_register_device_changed_callback,
@@ -160,7 +168,7 @@ class MockCubebStream {
void* mUserPtr;
public:
- enum class KeepProcessing { No, Yes };
+ enum class KeepProcessing { No, Yes, InvalidState };
enum class RunningMode { Automatic, Manual };
MockCubebStream(cubeb* aContext, char const* aStreamName,
@@ -175,50 +183,56 @@ class MockCubebStream {
~MockCubebStream();
- int Start();
- int Stop();
- uint64_t Position();
- void Destroy();
- int SetName(char const* aName);
+ int Start() MOZ_EXCLUDES(mMutex);
+ int Stop() MOZ_EXCLUDES(mMutex);
+ uint64_t Position() MOZ_EXCLUDES(mMutex);
+ void Destroy() MOZ_EXCLUDES(mMutex);
+ int SetName(char const* aName) MOZ_EXCLUDES(mMutex);
int RegisterDeviceChangedCallback(
- cubeb_device_changed_callback aDeviceChangedCallback);
+ cubeb_device_changed_callback aDeviceChangedCallback)
+ MOZ_EXCLUDES(mMutex);
+ int SetInputProcessingParams(cubeb_input_processing_params aParams);
- cubeb_stream* AsCubebStream();
+ cubeb_stream* AsCubebStream() MOZ_EXCLUDES(mMutex);
static MockCubebStream* AsMock(cubeb_stream* aStream);
- char const* StreamName() const { return mName.get(); }
+ char const* StreamName() const MOZ_EXCLUDES(mMutex) {
+ MutexAutoLock l(mMutex);
+ return mName.get();
+ }
cubeb_devid GetInputDeviceID() const;
cubeb_devid GetOutputDeviceID() const;
- uint32_t InputChannels() const;
- uint32_t OutputChannels() const;
- uint32_t SampleRate() const;
- uint32_t InputFrequency() const;
+ uint32_t InputChannels() const MOZ_EXCLUDES(mMutex);
+ uint32_t OutputChannels() const MOZ_EXCLUDES(mMutex);
+ uint32_t SampleRate() const MOZ_EXCLUDES(mMutex);
+ uint32_t InputFrequency() const MOZ_EXCLUDES(mMutex);
+ Maybe<cubeb_state> State() const MOZ_EXCLUDES(mMutex);
- void SetDriftFactor(float aDriftFactor);
- void ForceError();
- void ForceDeviceChanged();
- void Thaw();
+ void SetDriftFactor(float aDriftFactor) MOZ_EXCLUDES(mMutex);
+ void ForceError() MOZ_EXCLUDES(mMutex);
+ void ForceDeviceChanged() MOZ_EXCLUDES(mMutex);
+ void Thaw() MOZ_EXCLUDES(mMutex);
// For RunningMode::Manual, drive this MockCubebStream forward.
- KeepProcessing ManualDataCallback(long aNrFrames);
+ KeepProcessing ManualDataCallback(long aNrFrames) MOZ_EXCLUDES(mMutex);
// For RunningMode::Manual, notify the client of a DeviceChanged event
// synchronously.
- void NotifyDeviceChangedNow();
+ void NotifyDeviceChangedNow() MOZ_EXCLUDES(mMutex);
// Enable input recording for this driver. This is best called before
// the thread is running, but is safe to call whenever.
- void SetOutputRecordingEnabled(bool aEnabled);
+ void SetOutputRecordingEnabled(bool aEnabled) MOZ_EXCLUDES(mMutex);
// Enable input recording for this driver. This is best called before
// the thread is running, but is safe to call whenever.
- void SetInputRecordingEnabled(bool aEnabled);
+ void SetInputRecordingEnabled(bool aEnabled) MOZ_EXCLUDES(mMutex);
// Get the recorded output from this stream. This doesn't copy, and therefore
// only works once.
- nsTArray<AudioDataValue>&& TakeRecordedOutput();
+ nsTArray<AudioDataValue>&& TakeRecordedOutput() MOZ_EXCLUDES(mMutex);
// Get the recorded input from this stream. This doesn't copy, and therefore
// only works once.
- nsTArray<AudioDataValue>&& TakeRecordedInput();
+ nsTArray<AudioDataValue>&& TakeRecordedInput() MOZ_EXCLUDES(mMutex);
MediaEventSource<nsCString>& NameSetEvent();
MediaEventSource<cubeb_state>& StateEvent();
@@ -232,7 +246,13 @@ class MockCubebStream {
MediaEventSource<void>& DeviceChangeForcedEvent();
private:
- KeepProcessing Process(long aNrFrames);
+ cubeb_stream* AsCubebStreamLocked() MOZ_REQUIRES(mMutex);
+ static MockCubebStream* AsMockLocked(cubeb_stream* aStream);
+ uint32_t InputChannelsLocked() const MOZ_REQUIRES(mMutex);
+ uint32_t OutputChannelsLocked() const MOZ_REQUIRES(mMutex);
+ uint32_t SampleRateLocked() const MOZ_REQUIRES(mMutex);
+ uint32_t InputFrequencyLocked() const MOZ_REQUIRES(mMutex);
+ KeepProcessing Process(long aNrFrames) MOZ_REQUIRES(mMutex);
KeepProcessing Process10Ms();
public:
@@ -242,52 +262,58 @@ class MockCubebStream {
SmartMockCubebStream* const mSelf;
private:
- void NotifyState(cubeb_state aState);
- void NotifyDeviceChanged();
+ void NotifyState(cubeb_state aState) MOZ_REQUIRES(mMutex);
+ void NotifyDeviceChanged() MOZ_EXCLUDES(mMutex);
static constexpr long kMaxNrFrames = 1920;
+ // Mutex guarding most members to ensure state is in sync.
+ mutable Mutex mMutex{"MockCubebStream::mMutex"};
// Monitor used to block start until mFrozenStart is false.
- Monitor mFrozenStartMonitor MOZ_UNANNOTATED;
+ Monitor mFrozenStartMonitor MOZ_ACQUIRED_BEFORE(mMutex);
// Whether this stream should wait for an explicit start request before
- // starting. Protected by FrozenStartMonitor.
- bool mFrozenStart;
+ // starting.
+ bool mFrozenStart MOZ_GUARDED_BY(mFrozenStartMonitor);
+ // The stream's most recently issued state change, if any has occurred.
// Used to abort a frozen start if cubeb_stream_start() is called currently
// with a blocked cubeb_stream_start() call.
- std::atomic_bool mStreamStop{true};
+ Maybe<cubeb_state> mState MOZ_GUARDED_BY(mMutex);
// Whether or not the output-side of this stream (what is written from the
// callback output buffer) is recorded in an internal buffer. The data is then
// available via `GetRecordedOutput`.
- std::atomic_bool mOutputRecordingEnabled{false};
+ bool mOutputRecordingEnabled MOZ_GUARDED_BY(mMutex) = false;
// Whether or not the input-side of this stream (what is written from the
// callback input buffer) is recorded in an internal buffer. The data is then
// available via `TakeRecordedInput`.
- std::atomic_bool mInputRecordingEnabled{false};
+ bool mInputRecordingEnabled MOZ_GUARDED_BY(mMutex) = false;
// The audio buffer used on data callback.
- AudioDataValue mOutputBuffer[MAX_OUTPUT_CHANNELS * kMaxNrFrames] = {};
- AudioDataValue mInputBuffer[MAX_INPUT_CHANNELS * kMaxNrFrames] = {};
+ AudioDataValue mOutputBuffer[MAX_OUTPUT_CHANNELS *
+ kMaxNrFrames] MOZ_GUARDED_BY(mMutex) = {};
+ AudioDataValue mInputBuffer[MAX_INPUT_CHANNELS * kMaxNrFrames] MOZ_GUARDED_BY(
+ mMutex) = {};
// The audio callback
- cubeb_data_callback mDataCallback = nullptr;
+ const cubeb_data_callback mDataCallback = nullptr;
// The stream state callback
- cubeb_state_callback mStateCallback = nullptr;
+ const cubeb_state_callback mStateCallback = nullptr;
// The device changed callback
- cubeb_device_changed_callback mDeviceChangedCallback = nullptr;
+ cubeb_device_changed_callback mDeviceChangedCallback MOZ_GUARDED_BY(mMutex) =
+ nullptr;
// A name for this stream
- nsCString mName;
+ nsCString mName MOZ_GUARDED_BY(mMutex);
// The stream params
- cubeb_stream_params mOutputParams = {};
- cubeb_stream_params mInputParams = {};
+ cubeb_stream_params mOutputParams MOZ_GUARDED_BY(mMutex) = {};
+ cubeb_stream_params mInputParams MOZ_GUARDED_BY(mMutex) = {};
/* Device IDs */
- cubeb_devid mInputDeviceID;
- cubeb_devid mOutputDeviceID;
-
- std::atomic<float> mDriftFactor{1.0};
- std::atomic_bool mFastMode{false};
- std::atomic_bool mForceErrorState{false};
- std::atomic_bool mForceDeviceChanged{false};
- std::atomic_bool mDestroyed{false};
- std::atomic<uint64_t> mPosition{0};
- AudioGenerator<AudioDataValue> mAudioGenerator;
- AudioVerifier<AudioDataValue> mAudioVerifier;
+ const cubeb_devid mInputDeviceID;
+ const cubeb_devid mOutputDeviceID;
+
+ float mDriftFactor MOZ_GUARDED_BY(mMutex) = 1.0;
+ bool mFastMode MOZ_GUARDED_BY(mMutex) = false;
+ bool mForceErrorState MOZ_GUARDED_BY(mMutex) = false;
+ bool mForceDeviceChanged MOZ_GUARDED_BY(mMutex) = false;
+ bool mDestroyed MOZ_GUARDED_BY(mMutex) = false;
+ uint64_t mPosition MOZ_GUARDED_BY(mMutex) = 0;
+ AudioGenerator<AudioDataValue> mAudioGenerator MOZ_GUARDED_BY(mMutex);
+ AudioVerifier<AudioDataValue> mAudioVerifier MOZ_GUARDED_BY(mMutex);
MediaEventProducer<nsCString> mNameSetEvent;
MediaEventProducer<cubeb_state> mStateEvent;
@@ -299,12 +325,29 @@ class MockCubebStream {
MediaEventProducer<void> mDeviceChangedForcedEvent;
// The recorded data, copied from the output_buffer of the callback.
// Interleaved.
- nsTArray<AudioDataValue> mRecordedOutput;
+ nsTArray<AudioDataValue> mRecordedOutput MOZ_GUARDED_BY(mMutex);
// The recorded data, copied from the input buffer of the callback.
// Interleaved.
- nsTArray<AudioDataValue> mRecordedInput;
+ nsTArray<AudioDataValue> mRecordedInput MOZ_GUARDED_BY(mMutex);
};
+inline std::ostream& operator<<(std::ostream& aStream,
+ const MockCubebStream::KeepProcessing& aVal) {
+ switch (aVal) {
+ case MockCubebStream::KeepProcessing::Yes:
+ aStream << "KeepProcessing::Yes";
+ return aStream;
+ case MockCubebStream::KeepProcessing::No:
+ aStream << "KeepProcessing::No";
+ return aStream;
+ case MockCubebStream::KeepProcessing::InvalidState:
+ aStream << "KeepProcessing::InvalidState";
+ return aStream;
+ }
+ aStream << "KeepProcessing(invalid " << static_cast<uint32_t>(aVal) << ")";
+ return aStream;
+}
+
class SmartMockCubebStream
: public MockCubebStream,
public SupportsThreadSafeWeakPtr<SmartMockCubebStream> {
@@ -362,6 +405,16 @@ class MockCubeb {
cubeb_device_type aDevType,
cubeb_device_collection_changed_callback aCallback, void* aUserPtr);
+ Result<cubeb_input_processing_params, int> SupportedInputProcessingParams()
+ const;
+ void SetSupportedInputProcessingParams(cubeb_input_processing_params aParams,
+ int aRv);
+ // Set the rv to be returned when SetInputProcessingParams for any stream of
+ // this context would apply the params to the stream, i.e. after passing the
+ // supported-params check.
+ void SetInputProcessingApplyRv(int aRv);
+ int InputProcessingApplyRv() const;
+
// Control API
// Add an input or output device to this backend. This calls the device
@@ -455,6 +508,10 @@ class MockCubeb {
// notification via a system callback. If not, Gecko is expected to re-query
// the list every time.
bool mSupportsDeviceCollectionChangedCallback = true;
+ std::pair<cubeb_input_processing_params, int>
+ mSupportedInputProcessingParams = std::make_pair(
+ CUBEB_INPUT_PROCESSING_PARAM_NONE, CUBEB_ERROR_NOT_SUPPORTED);
+ int mInputProcessingParamsApplyRv = CUBEB_OK;
const RunningMode mRunningMode;
Atomic<bool> mStreamInitErrorState;
// Whether new MockCubebStreams should be frozen on start.
@@ -502,6 +559,18 @@ int cubeb_mock_register_device_collection_changed(
devtype, callback, user_ptr);
}
+int cubeb_mock_get_supported_input_processing_params(
+ cubeb* context, cubeb_input_processing_params* params) {
+ Result<cubeb_input_processing_params, int> res =
+ MockCubeb::AsMock(context)->SupportedInputProcessingParams();
+ if (res.isErr()) {
+ *params = CUBEB_INPUT_PROCESSING_PARAM_NONE;
+ return res.unwrapErr();
+ }
+ *params = res.unwrap();
+ return CUBEB_OK;
+}
+
int cubeb_mock_stream_init(
cubeb* context, cubeb_stream** stream, char const* stream_name,
cubeb_devid input_device, cubeb_stream_params* input_stream_params,
@@ -562,6 +631,11 @@ int cubeb_mock_stream_register_device_changed_callback(
device_changed_callback);
}
+static int cubeb_mock_stream_set_input_processing_params(
+ cubeb_stream* stream, cubeb_input_processing_params params) {
+ return MockCubebStream::AsMock(stream)->SetInputProcessingParams(params);
+}
+
int cubeb_mock_get_min_latency(cubeb* context, cubeb_stream_params params,
uint32_t* latency_ms) {
*latency_ms = 10;
diff --git a/dom/media/gtest/TestAudioCallbackDriver.cpp b/dom/media/gtest/TestAudioCallbackDriver.cpp
index 050395fa44..9c8c9bd107 100644
--- a/dom/media/gtest/TestAudioCallbackDriver.cpp
+++ b/dom/media/gtest/TestAudioCallbackDriver.cpp
@@ -9,34 +9,39 @@
#include "GraphDriver.h"
#include "gmock/gmock.h"
-#include "gtest/gtest-printers.h"
#include "gtest/gtest.h"
#include "MediaTrackGraphImpl.h"
#include "mozilla/gtest/WaitFor.h"
#include "mozilla/Attributes.h"
#include "mozilla/SyncRunnable.h"
-#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
#include "MockCubeb.h"
-using namespace mozilla;
+namespace mozilla {
+
using IterationResult = GraphInterface::IterationResult;
using ::testing::_;
using ::testing::AnyNumber;
+using ::testing::AtMost;
+using ::testing::Eq;
+using ::testing::InSequence;
using ::testing::NiceMock;
class MockGraphInterface : public GraphInterface {
NS_DECL_THREADSAFE_ISUPPORTS
explicit MockGraphInterface(TrackRate aSampleRate)
: mSampleRate(aSampleRate) {}
- MOCK_METHOD0(NotifyInputStopped, void());
- MOCK_METHOD5(NotifyInputData, void(const AudioDataValue*, size_t, TrackRate,
- uint32_t, uint32_t));
- MOCK_METHOD0(DeviceChanged, void());
+ MOCK_METHOD(void, NotifyInputStopped, ());
+ MOCK_METHOD(void, NotifyInputData,
+ (const AudioDataValue*, size_t, TrackRate, uint32_t, uint32_t));
+ MOCK_METHOD(void, NotifySetRequestedInputProcessingParamsResult,
+ (AudioCallbackDriver*, cubeb_input_processing_params,
+ (Result<cubeb_input_processing_params, int>&&)));
+ MOCK_METHOD(void, DeviceChanged, ());
#ifdef DEBUG
- MOCK_CONST_METHOD1(InDriverIteration, bool(const GraphDriver*));
+ MOCK_METHOD(bool, InDriverIteration, (const GraphDriver*), (const));
#endif
/* OneIteration cannot be mocked because IterationResult is non-memmovable and
* cannot be passed as a parameter, which GMock does internally. */
@@ -81,7 +86,7 @@ class MockGraphInterface : public GraphInterface {
RefPtr<Runnable> aSwitchedRunnable = NS_NewRunnableFunction(
"DefaultNoopSwitchedRunnable", [] {})) {
auto guard = mNextDriver.Lock();
- MOZ_ASSERT(guard->isNothing());
+ MOZ_RELEASE_ASSERT(guard->isNothing());
*guard =
Some(std::make_tuple(std::move(aDriver), std::move(aSwitchedRunnable)));
}
@@ -108,7 +113,7 @@ class MockGraphInterface : public GraphInterface {
NS_IMPL_ISUPPORTS0(MockGraphInterface)
TEST(TestAudioCallbackDriver, StartStop)
-MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+MOZ_CAN_RUN_SCRIPT_BOUNDARY {
const TrackRate rate = 44100;
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
@@ -118,7 +123,8 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
driver = MakeRefPtr<AudioCallbackDriver>(graph, nullptr, rate, 2, 0, nullptr,
- nullptr, AudioInputType::Unknown);
+ nullptr, AudioInputType::Unknown,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
@@ -135,7 +141,7 @@ MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
}
-void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_BOUNDARY {
std::cerr << "TestSlowStart with rate " << aRate << std::endl;
MockCubeb* cubeb = new MockCubeb();
@@ -177,15 +183,17 @@ void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
});
driver = MakeRefPtr<AudioCallbackDriver>(graph, nullptr, aRate, 2, 2, nullptr,
- (void*)1, AudioInputType::Voice);
+ (void*)1, AudioInputType::Voice,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
graph->SetCurrentDriver(driver);
graph->SetEnsureNextIteration(true);
+ auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
driver->Start();
- RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ auto [stream] = WaitFor(initPromise).unwrap()[0];
cubeb->SetStreamStartFreezeEnabled(false);
const size_t fallbackIterations = 3;
@@ -234,7 +242,7 @@ void TestSlowStart(const TrackRate aRate) MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
}
TEST(TestAudioCallbackDriver, SlowStart)
-MOZ_CAN_RUN_SCRIPT_FOR_DEFINITION {
+{
TestSlowStart(1000); // 10ms = 10 <<< 128 samples
TestSlowStart(8000); // 10ms = 80 < 128 samples
TestSlowStart(44100); // 10ms = 441 > 128 samples
@@ -252,7 +260,7 @@ class MOZ_STACK_CLASS AutoSetter {
: mVal(aVal), mNew(aNew), mOld(mVal.exchange(aNew)) {}
~AutoSetter() {
DebugOnly<T> oldNew = mVal.exchange(mOld);
- MOZ_ASSERT(oldNew == mNew);
+ MOZ_RELEASE_ASSERT(oldNew == mNew);
}
};
#endif
@@ -265,12 +273,13 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY {
auto graph = MakeRefPtr<MockGraphInterface>(rate);
auto driver = MakeRefPtr<AudioCallbackDriver>(
- graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice);
+ graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
#ifdef DEBUG
- std::atomic<std::thread::id> threadInDriverIteration((std::thread::id()));
+ std::atomic<std::thread::id> threadInDriverIteration{std::thread::id()};
EXPECT_CALL(*graph, InDriverIteration(driver.get())).WillRepeatedly([&] {
return std::this_thread::get_id() == threadInDriverIteration;
});
@@ -279,17 +288,24 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY {
EXPECT_CALL(*graph, NotifyInputData(_, 0, rate, 1, _)).Times(AnyNumber());
EXPECT_CALL(*graph, NotifyInputData(_, ignoredFrameCount, _, _, _)).Times(0);
EXPECT_CALL(*graph, DeviceChanged);
+ Result<cubeb_input_processing_params, int> expected =
+ Err(CUBEB_ERROR_NOT_SUPPORTED);
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(), CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ Eq(std::ref(expected))));
graph->SetCurrentDriver(driver);
graph->SetEnsureNextIteration(true);
// This starts the fallback driver.
+ auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
driver->Start();
- RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ auto [stream] = WaitFor(initPromise).unwrap()[0];
// Wait for the audio driver to have started the stream before running data
// callbacks. driver->Start() does a dispatch to the cubeb operation thread
// and starts the stream there.
- nsCOMPtr<nsIEventTarget> cubebOpThread = CUBEB_TASK_THREAD;
+ nsCOMPtr<nsIEventTarget> cubebOpThread =
+ CubebUtils::GetCubebOperationThread();
MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
@@ -393,85 +409,412 @@ MOZ_CAN_RUN_SCRIPT_BOUNDARY {
auto graph = MakeRefPtr<MockGraphInterface>(rate);
auto driver = MakeRefPtr<AudioCallbackDriver>(
- graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice);
+ graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
auto newDriver = MakeRefPtr<AudioCallbackDriver>(
- graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice);
+ graph, nullptr, rate, 2, 1, nullptr, (void*)1, AudioInputType::Voice,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
EXPECT_FALSE(newDriver->ThreadRunning()) << "Verify thread is not running";
EXPECT_FALSE(newDriver->IsStarted()) << "Verify thread is not started";
#ifdef DEBUG
- std::atomic<std::thread::id> threadInDriverIteration(
- (std::this_thread::get_id()));
+ std::atomic<std::thread::id> threadInDriverIteration{
+ std::this_thread::get_id()};
EXPECT_CALL(*graph, InDriverIteration(_)).WillRepeatedly([&] {
return std::this_thread::get_id() == threadInDriverIteration;
});
#endif
EXPECT_CALL(*graph, NotifyInputData(_, 0, rate, 1, _)).Times(AnyNumber());
- EXPECT_CALL(*graph, DeviceChanged);
+ // This only happens if the first fallback driver is stopped by the audio
+ // driver handover rather than the driver switch. It happens when the
+ // subsequent audio callback performs the switch.
+ EXPECT_CALL(*graph, NotifyInputStopped()).Times(AtMost(1));
+ Result<cubeb_input_processing_params, int> expected =
+ Err(CUBEB_ERROR_NOT_SUPPORTED);
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(), CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ Eq(std::ref(expected))));
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ newDriver.get(), CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ Eq(std::ref(expected))));
graph->SetCurrentDriver(driver);
graph->SetEnsureNextIteration(true);
+ auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
// This starts the fallback driver.
driver->Start();
- RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ RefPtr<SmartMockCubebStream> stream;
+ std::tie(stream) = WaitFor(initPromise).unwrap()[0];
// Wait for the audio driver to have started or the DeviceChanged event will
// be ignored. driver->Start() does a dispatch to the cubeb operation thread
// and starts the stream there.
- nsCOMPtr<nsIEventTarget> cubebOpThread = CUBEB_TASK_THREAD;
+ nsCOMPtr<nsIEventTarget> cubebOpThread =
+ CubebUtils::GetCubebOperationThread();
MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
-#ifdef DEBUG
- AutoSetter as(threadInDriverIteration, std::this_thread::get_id());
-#endif
+ initPromise = TakeN(cubeb->StreamInitEvent(), 1);
+ Monitor mon(__func__);
+ bool canContinueToStartNextDriver = false;
+ bool continued = false;
// This marks the audio driver as running.
EXPECT_EQ(stream->ManualDataCallback(0),
MockCubebStream::KeepProcessing::Yes);
- // If a fallback driver callback happens between the audio callback above, and
- // the SwitchTo below, the audio driver will perform the switch instead of the
- // fallback since the fallback will have stopped. This test may therefore
- // intermittently take different code paths.
-
- // Stop the fallback driver by switching audio driver in the graph.
- {
- Monitor mon(__func__);
- MonitorAutoLock lock(mon);
- bool switched = false;
- graph->SwitchTo(newDriver, NS_NewRunnableFunction(__func__, [&] {
- MonitorAutoLock lock(mon);
- switched = true;
- lock.Notify();
- }));
- while (!switched) {
- lock.Wait();
- }
+ // To satisfy TSAN's lock-order-inversion checking we avoid locking stream's
+ // mMutex (by calling ManualDataCallback) under mon. The SwitchTo runnable
+ // below already locks mon under stream's mMutex.
+ MonitorAutoLock lock(mon);
+
+ // If a fallback driver callback happens between the audio callback
+ // above, and the SwitchTo below, the driver will enter
+ // `FallbackDriverState::None`, relying on the audio driver to
+ // iterate the graph, including performing the driver switch. This
+ // test may therefore intermittently take different code paths.
+ // Note however that the fallback driver runs every ~10ms while the
+ // time from the manual callback above to telling the mock graph to
+ // switch drivers below is much much shorter. The vast majority of
+ // test runs will exercise the intended code path.
+
+ // Make the fallback driver enter FallbackDriverState::Stopped by
+ // switching audio driver in the graph.
+ graph->SwitchTo(newDriver, NS_NewRunnableFunction(__func__, [&] {
+ MonitorAutoLock lock(mon);
+ // Block the fallback driver on its thread until
+ // the test on main thread has finished testing
+ // what it needs.
+ while (!canContinueToStartNextDriver) {
+ lock.Wait();
+ }
+ // Notify the test that it can take these
+ // variables off the stack now.
+ continued = true;
+ lock.Notify();
+ }));
+
+ // Wait for the fallback driver to stop running.
+ while (driver->OnFallback()) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
- {
+ if (driver->HasFallback()) {
+ // Driver entered FallbackDriverState::Stopped as desired.
+ // Proceed with a DeviceChangedCallback.
+
+ EXPECT_CALL(*graph, DeviceChanged);
+
+ {
#ifdef DEBUG
- AutoSetter as(threadInDriverIteration, std::thread::id());
+ AutoSetter as(threadInDriverIteration, std::thread::id());
#endif
- // After stopping the fallback driver, but before newDriver has stopped the
- // old audio driver, fire a DeviceChanged event to ensure it is handled
- // properly.
- AudioCallbackDriver::DeviceChangedCallback_s(driver);
+ // After stopping the fallback driver, but before newDriver has
+ // stopped the old audio driver, fire a DeviceChanged event to
+ // ensure it is handled properly.
+ AudioCallbackDriver::DeviceChangedCallback_s(driver);
+ }
+
+ EXPECT_FALSE(driver->OnFallback())
+ << "DeviceChangedCallback after stopping must not start the "
+ "fallback driver again";
}
+ // Iterate the audio driver on a background thread in case the fallback
+ // driver completed the handover to the audio driver before the switch
+ // above. Doing the switch would deadlock as the switch runnable waits on
+ // mon.
+ NS_DispatchBackgroundTask(NS_NewRunnableFunction(
+ "DeviceChangeAfterStop::postSwitchManualAudioCallback", [stream] {
+ // An audio callback after switching must tell the stream to stop.
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::No);
+ }));
+
+ // Unblock the fallback driver.
+ canContinueToStartNextDriver = true;
+ lock.Notify();
+
+ // Wait for the fallback driver to continue, so we can clear the
+ // stack.
+ while (!continued) {
+ lock.Wait();
+ }
+
+ // Wait for newDriver's cubeb stream to init.
+ std::tie(stream) = WaitFor(initPromise).unwrap()[0];
+
graph->StopIterating();
newDriver->EnsureNextIteration();
while (newDriver->OnFallback()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
- // This will block until all events have been queued.
- MOZ_KnownLive(driver)->Shutdown();
- MOZ_KnownLive(newDriver)->Shutdown();
+ {
+#ifdef DEBUG
+ AutoSetter as(threadInDriverIteration, std::thread::id());
+#endif
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::No);
+ }
+
// Drain the event queue.
NS_ProcessPendingEvents(nullptr);
}
+
+void TestInputProcessingOnStart(
+ MockCubeb* aCubeb, cubeb_input_processing_params aRequested,
+ const Result<cubeb_input_processing_params, int>& aExpected)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY {
+ const TrackRate rate = 44100;
+
+ auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate);
+ auto driver = MakeRefPtr<AudioCallbackDriver>(
+ graph, nullptr, rate, 2, 1, nullptr, nullptr, AudioInputType::Voice,
+ aRequested);
+ EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
+ EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
+
+#ifdef DEBUG
+ std::atomic_bool inGraphIteration{false};
+ ON_CALL(*graph, InDriverIteration(_)).WillByDefault([&] {
+ return inGraphIteration.load() && NS_IsMainThread();
+ });
+#endif
+ bool notified = false;
+ EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(), aRequested, Eq(std::ref(aExpected))))
+ .WillOnce([&] { notified = true; });
+
+ graph->SetCurrentDriver(driver);
+ auto initPromise = TakeN(aCubeb->StreamInitEvent(), 1);
+ driver->Start();
+ auto [stream] = WaitFor(initPromise).unwrap()[0];
+
+ // Wait for the audio driver to have started the stream before running data
+ // callbacks. driver->Start() does a dispatch to the cubeb operation thread
+ // and starts the stream there.
+ nsCOMPtr<nsIEventTarget> cubebOpThread =
+ CubebUtils::GetCubebOperationThread();
+ MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
+ cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
+
+ // This makes the fallback driver stop on its next callback.
+ {
+#ifdef DEBUG
+ AutoSetter as(inGraphIteration, true);
+#endif
+ while (driver->OnFallback()) {
+ stream->ManualDataCallback(0);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+
+ while (!notified) {
+ NS_ProcessNextEvent();
+ }
+
+ // This will block untill all events have been executed.
+ MOZ_KnownLive(driver)->Shutdown();
+ EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
+ EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
+}
+
+TEST(TestAudioCallbackDriver, InputProcessingOnStart)
+{
+ constexpr cubeb_input_processing_params allParams =
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
+ CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
+ CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
+
+ MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
+ CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
+
+ // Not supported by backend.
+ cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_ERROR_NOT_SUPPORTED);
+ TestInputProcessingOnStart(cubeb,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ Err(CUBEB_ERROR_NOT_SUPPORTED));
+
+ // Not supported by params.
+ cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_OK);
+ TestInputProcessingOnStart(cubeb,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+
+ // Successful all.
+ cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
+ TestInputProcessingOnStart(cubeb, allParams, allParams);
+
+ // Successful partial.
+ TestInputProcessingOnStart(cubeb,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+
+ // Not supported by stream.
+ cubeb->SetInputProcessingApplyRv(CUBEB_ERROR);
+ TestInputProcessingOnStart(
+ cubeb, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, Err(CUBEB_ERROR));
+}
+
+TEST(TestAudioCallbackDriver, InputProcessingWhileRunning)
+MOZ_CAN_RUN_SCRIPT_BOUNDARY {
+ constexpr TrackRate rate = 44100;
+ constexpr cubeb_input_processing_params allParams =
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
+ CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
+ CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
+ constexpr int applyError = 99;
+
+ int numNotifications = 0;
+ const auto signal = [&]() mutable {
+ MOZ_ASSERT(NS_IsMainThread());
+ ++numNotifications;
+ };
+ const auto waitForSignal = [&](int aNotification) {
+ while (numNotifications < aNotification) {
+ NS_ProcessNextEvent();
+ }
+ };
+ MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
+ CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
+
+ auto graph = MakeRefPtr<NiceMock<MockGraphInterface>>(rate);
+ auto driver = MakeRefPtr<AudioCallbackDriver>(
+ graph, nullptr, rate, 2, 1, nullptr, nullptr, AudioInputType::Voice,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+ EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
+ EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
+
+ EXPECT_CALL(*graph, NotifyInputStopped).Times(0);
+ // Expectations
+ const Result<cubeb_input_processing_params, int> noneResult =
+ CUBEB_INPUT_PROCESSING_PARAM_NONE;
+ const Result<cubeb_input_processing_params, int> aecResult =
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION;
+ const Result<cubeb_input_processing_params, int> allResult = allParams;
+ const Result<cubeb_input_processing_params, int> notSupportedResult =
+ Err(CUBEB_ERROR_NOT_SUPPORTED);
+ const Result<cubeb_input_processing_params, int> applyErrorResult =
+ Err(applyError);
+ {
+ InSequence s;
+
+ // Notified on start.
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(), CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ Eq(std::ref(notSupportedResult))))
+ .WillOnce(signal);
+ // Not supported by backend.
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(),
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
+ Eq(std::ref(notSupportedResult))))
+ .WillOnce(signal);
+ // Not supported by params.
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(),
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ Eq(std::ref(noneResult))))
+ .WillOnce(signal);
+ // Successful all.
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(), allParams, Eq(std::ref(allResult))))
+ .WillOnce(signal);
+ // Successful partial.
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(),
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ Eq(std::ref(aecResult))))
+ .WillOnce(signal);
+ // Not supported by stream.
+ EXPECT_CALL(*graph, NotifySetRequestedInputProcessingParamsResult(
+ driver.get(),
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
+ Eq(std::ref(applyErrorResult))))
+ .WillOnce(signal);
+ }
+
+#ifdef DEBUG
+ std::atomic_bool inGraphIteration{false};
+ ON_CALL(*graph, InDriverIteration(_)).WillByDefault([&] {
+ return inGraphIteration.load() && NS_IsMainThread();
+ });
+#endif
+
+ const auto setParams = [&](cubeb_input_processing_params aParams) {
+ {
+#ifdef DEBUG
+ AutoSetter as(inGraphIteration, true);
+#endif
+ driver->SetRequestedInputProcessingParams(aParams);
+ }
+ };
+
+ graph->SetCurrentDriver(driver);
+ auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
+ driver->Start();
+ auto [stream] = WaitFor(initPromise).unwrap()[0];
+
+ // Wait for the audio driver to have started the stream before running data
+ // callbacks. driver->Start() does a dispatch to the cubeb operation thread
+ // and starts the stream there.
+ nsCOMPtr<nsIEventTarget> cubebOpThread =
+ CubebUtils::GetCubebOperationThread();
+ MOZ_ALWAYS_SUCCEEDS(SyncRunnable::DispatchToThread(
+ cubebOpThread, NS_NewRunnableFunction(__func__, [] {})));
+
+ // This makes the fallback driver stop on its next callback.
+
+ {
+#ifdef DEBUG
+ AutoSetter as(inGraphIteration, true);
+#endif
+ while (driver->OnFallback()) {
+ stream->ManualDataCallback(0);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ }
+ waitForSignal(1);
+
+ // Not supported by backend.
+ cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_ERROR_NOT_SUPPORTED);
+ setParams(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
+ waitForSignal(2);
+
+ // Not supported by params.
+ cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_OK);
+ setParams(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+ waitForSignal(3);
+
+ // Successful all.
+ cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
+ setParams(allParams);
+ waitForSignal(4);
+
+ // Successful partial.
+ setParams(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+ waitForSignal(5);
+
+ // Not supported by stream.
+ cubeb->SetInputProcessingApplyRv(applyError);
+ setParams(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
+ waitForSignal(6);
+
+ // This will block untill all events have been executed.
+ MOZ_KnownLive(driver)->Shutdown();
+ EXPECT_FALSE(driver->ThreadRunning()) << "Verify thread is not running";
+ EXPECT_FALSE(driver->IsStarted()) << "Verify thread is not started";
+}
+
+} // namespace mozilla
diff --git a/dom/media/gtest/TestAudioInputProcessing.cpp b/dom/media/gtest/TestAudioInputProcessing.cpp
index d21c37a900..e357839768 100644
--- a/dom/media/gtest/TestAudioInputProcessing.cpp
+++ b/dom/media/gtest/TestAudioInputProcessing.cpp
@@ -428,3 +428,178 @@ TEST(TestAudioInputProcessing, Downmixing)
aip->Stop(graph);
track->Destroy();
}
+
+TEST(TestAudioInputProcessing, DisabledPlatformProcessing)
+{
+ const TrackRate rate = 44100;
+ const uint32_t channels = 1;
+ auto graph = MakeRefPtr<NiceMock<MockGraph>>(rate);
+ graph->Init(channels);
+
+ auto aip = MakeRefPtr<AudioInputProcessing>(channels);
+
+ MediaEnginePrefs settings;
+ settings.mUsePlatformProcessing = false;
+ settings.mAecOn = true;
+ aip->ApplySettings(graph, nullptr, settings);
+ aip->Start(graph);
+
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+
+ aip->Stop(graph);
+ graph->Destroy();
+}
+
+TEST(TestAudioInputProcessing, EnabledPlatformProcessing)
+{
+ const TrackRate rate = 44100;
+ const uint32_t channels = 1;
+ auto graph = MakeRefPtr<NiceMock<MockGraph>>(rate);
+ graph->Init(channels);
+
+ auto aip = MakeRefPtr<AudioInputProcessing>(channels);
+
+ MediaEnginePrefs settings;
+ settings.mUsePlatformProcessing = true;
+ settings.mAecOn = true;
+ aip->ApplySettings(graph, nullptr, settings);
+ aip->Start(graph);
+
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+
+ aip->Stop(graph);
+ graph->Destroy();
+}
+
+namespace webrtc {
+bool operator==(const AudioProcessing::Config& aLhs,
+ const AudioProcessing::Config& aRhs) {
+ return aLhs.echo_canceller.enabled == aRhs.echo_canceller.enabled &&
+ (aLhs.gain_controller1.enabled == aRhs.gain_controller1.enabled ||
+ aLhs.gain_controller2.enabled == aRhs.gain_controller2.enabled) &&
+ aLhs.noise_suppression.enabled == aRhs.noise_suppression.enabled;
+}
+
+static std::ostream& operator<<(
+ std::ostream& aStream, const webrtc::AudioProcessing::Config& aConfig) {
+ aStream << "webrtc::AudioProcessing::Config[";
+ bool hadPrior = false;
+ if (aConfig.echo_canceller.enabled) {
+ aStream << "AEC";
+ hadPrior = true;
+ }
+ if (aConfig.gain_controller1.enabled || aConfig.gain_controller2.enabled) {
+ if (hadPrior) {
+ aStream << ", ";
+ }
+ aStream << "AGC";
+ }
+ if (aConfig.noise_suppression.enabled) {
+ if (hadPrior) {
+ aStream << ", ";
+ }
+ aStream << "NS";
+ }
+ aStream << "]";
+ return aStream;
+}
+} // namespace webrtc
+
+TEST(TestAudioInputProcessing, PlatformProcessing)
+{
+ const TrackRate rate = 44100;
+ const uint32_t channels = 1;
+ auto graph = MakeRefPtr<NiceMock<MockGraph>>(rate);
+ graph->Init(channels);
+
+ auto aip = MakeRefPtr<AudioInputProcessing>(channels);
+
+ MediaEnginePrefs settings;
+ settings.mUsePlatformProcessing = true;
+ settings.mAecOn = true;
+ aip->ApplySettings(graph, nullptr, settings);
+ aip->Start(graph);
+
+ webrtc::AudioProcessing::Config echoOnlyConfig;
+ echoOnlyConfig.echo_canceller.enabled = true;
+ webrtc::AudioProcessing::Config echoNoiseConfig = echoOnlyConfig;
+ echoNoiseConfig.noise_suppression.enabled = true;
+
+ // Config is applied, and platform processing requested.
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+ EXPECT_EQ(aip->AppliedConfig(graph), echoOnlyConfig);
+ EXPECT_FALSE(aip->IsPassThrough(graph));
+
+ // Platform processing params successfully applied.
+ aip->NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+ // Turns off the equivalent APM config.
+ EXPECT_EQ(aip->AppliedConfig(graph), webrtc::AudioProcessing::Config());
+ EXPECT_TRUE(aip->IsPassThrough(graph));
+
+ // Simulate an error after a driver switch.
+ aip->NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, Err(CUBEB_ERROR));
+ // The APM config is turned back on, and platform processing is requested to
+ // be turned off.
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+ EXPECT_EQ(aip->AppliedConfig(graph), echoOnlyConfig);
+ EXPECT_FALSE(aip->IsPassThrough(graph));
+
+ // Pretend there was a response for an old request.
+ aip->NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+ // It does nothing since we are requesting NONE now.
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+ EXPECT_EQ(aip->AppliedConfig(graph), echoOnlyConfig);
+ EXPECT_FALSE(aip->IsPassThrough(graph));
+
+ // Turn it off as requested.
+ aip->NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+ EXPECT_EQ(aip->AppliedConfig(graph), echoOnlyConfig);
+ EXPECT_FALSE(aip->IsPassThrough(graph));
+
+ // Test partial support for the requested params.
+ settings.mNoiseOn = true;
+ aip->ApplySettings(graph, nullptr, settings);
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
+ EXPECT_EQ(aip->AppliedConfig(graph), echoNoiseConfig);
+ EXPECT_FALSE(aip->IsPassThrough(graph));
+ // Only noise suppression was supported in the platform.
+ aip->NotifySetRequestedInputProcessingParamsResult(
+ graph,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
+ // In the APM only echo cancellation is applied.
+ EXPECT_EQ(aip->AppliedConfig(graph), echoOnlyConfig);
+ EXPECT_FALSE(aip->IsPassThrough(graph));
+
+ // Test error for partial support.
+ aip->NotifySetRequestedInputProcessingParamsResult(
+ graph,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
+ Err(CUBEB_ERROR));
+ // The full config is applied in the APM, and NONE is requested.
+ EXPECT_EQ(aip->RequestedInputProcessingParams(graph),
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+ EXPECT_EQ(aip->AppliedConfig(graph), echoNoiseConfig);
+ EXPECT_FALSE(aip->IsPassThrough(graph));
+
+ aip->Stop(graph);
+ graph->Destroy();
+}
diff --git a/dom/media/gtest/TestAudioInputSource.cpp b/dom/media/gtest/TestAudioInputSource.cpp
index f3f18b26a9..5defd1d053 100644
--- a/dom/media/gtest/TestAudioInputSource.cpp
+++ b/dom/media/gtest/TestAudioInputSource.cpp
@@ -10,16 +10,22 @@
#include "gtest/gtest.h"
#include "MockCubeb.h"
+#include "mozilla/Result.h"
#include "mozilla/gtest/WaitFor.h"
#include "nsContentUtils.h"
using namespace mozilla;
using testing::ContainerEq;
-namespace {
+// Short-hand for DispatchToCurrentThread with a function.
#define DispatchFunction(f) \
NS_DispatchToCurrentThread(NS_NewRunnableFunction(__func__, f))
-} // namespace
+
+// Short-hand for draining the current threads event queue, i.e. processing
+// those runnables dispatched per above.
+#define ProcessEventQueue() \
+ while (NS_ProcessNextEvent(nullptr, false)) { \
+ }
class MockEventListener : public AudioInputSource::EventListener {
public:
@@ -63,7 +69,10 @@ TEST(TestAudioInputSource, StartAndStop)
// Make sure start and stop works.
{
- DispatchFunction([&] { ais->Start(); });
+ DispatchFunction([&] {
+ ais->Init();
+ ais->Start();
+ });
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream->mHasInput);
EXPECT_FALSE(stream->mHasOutput);
@@ -79,7 +88,10 @@ TEST(TestAudioInputSource, StartAndStop)
// Make sure restart is ok.
{
- DispatchFunction([&] { ais->Start(); });
+ DispatchFunction([&] {
+ ais->Init();
+ ais->Start();
+ });
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream->mHasInput);
EXPECT_FALSE(stream->mHasOutput);
@@ -133,7 +145,10 @@ TEST(TestAudioInputSource, DataOutputBeforeStartAndAfterStop)
EXPECT_TRUE(data.IsNull());
}
- DispatchFunction([&] { ais->Start(); });
+ DispatchFunction([&] {
+ ais->Init();
+ ais->Start();
+ });
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream->mHasInput);
EXPECT_FALSE(stream->mHasOutput);
@@ -206,7 +221,10 @@ TEST(TestAudioInputSource, ErrorCallback)
sourceRate, targetRate);
ASSERT_TRUE(ais);
- DispatchFunction([&] { ais->Start(); });
+ DispatchFunction([&] {
+ ais->Init();
+ ais->Start();
+ });
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream->mHasInput);
EXPECT_FALSE(stream->mHasOutput);
@@ -251,7 +269,10 @@ TEST(TestAudioInputSource, DeviceChangedCallback)
sourceRate, targetRate);
ASSERT_TRUE(ais);
- DispatchFunction([&] { ais->Start(); });
+ DispatchFunction([&] {
+ ais->Init();
+ ais->Start();
+ });
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream->mHasInput);
EXPECT_FALSE(stream->mHasOutput);
@@ -267,3 +288,82 @@ TEST(TestAudioInputSource, DeviceChangedCallback)
ais = nullptr; // Drop the SharedThreadPool here.
}
+
+TEST(TestAudioInputSource, InputProcessing)
+{
+ MockCubeb* cubeb = new MockCubeb();
+ CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
+
+ const AudioInputSource::Id sourceId = 1;
+ const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
+ const uint32_t channels = 2;
+ const PrincipalHandle testPrincipal =
+ MakePrincipalHandle(nsContentUtils::GetSystemPrincipal());
+ const TrackRate sourceRate = 44100;
+ const TrackRate targetRate = 48000;
+ using ProcessingPromise =
+ AudioInputSource::SetRequestedProcessingParamsPromise;
+
+ auto listener = MakeRefPtr<MockEventListener>();
+ EXPECT_CALL(*listener,
+ AudioStateCallback(
+ sourceId, AudioInputSource::EventListener::State::Started))
+ .Times(0);
+ EXPECT_CALL(*listener,
+ AudioStateCallback(
+ sourceId, AudioInputSource::EventListener::State::Stopped))
+ .Times(10);
+
+ RefPtr<AudioInputSource> ais = MakeRefPtr<AudioInputSource>(
+ std::move(listener), sourceId, deviceId, channels, true, testPrincipal,
+ sourceRate, targetRate);
+
+ const auto test =
+ [&](cubeb_input_processing_params aRequested,
+ const Result<cubeb_input_processing_params, int>& aExpected) {
+ RefPtr<ProcessingPromise> p;
+ DispatchFunction([&] {
+ ais->Init();
+ p = ais->SetRequestedProcessingParams(aRequested);
+ });
+ ProcessEventQueue();
+ EXPECT_EQ(WaitFor(p), aExpected);
+
+ DispatchFunction([&] { ais->Stop(); });
+ Unused << WaitFor(cubeb->StreamDestroyEvent());
+ };
+
+ // Not supported by backend.
+ cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_ERROR_NOT_SUPPORTED);
+ test(CUBEB_INPUT_PROCESSING_PARAM_NONE, Err(CUBEB_ERROR_NOT_SUPPORTED));
+
+ // Not supported by params.
+ cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_OK);
+ test(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+
+ constexpr cubeb_input_processing_params allParams =
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
+ CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
+ CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
+
+ // Successful all.
+ cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
+ test(allParams, allParams);
+
+ // Successful partial.
+ test(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+
+ // Not supported by stream.
+ // Note this also tests that AudioInputSource resets its configured params
+ // state from the previous successful test.
+ constexpr int propagatedError = 99;
+ cubeb->SetInputProcessingApplyRv(propagatedError);
+ test(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION, Err(propagatedError));
+
+ ais = nullptr; // Drop the SharedThreadPool here.
+}
diff --git a/dom/media/gtest/TestAudioRingBuffer.cpp b/dom/media/gtest/TestAudioRingBuffer.cpp
index 082323efd1..3da39780f1 100644
--- a/dom/media/gtest/TestAudioRingBuffer.cpp
+++ b/dom/media/gtest/TestAudioRingBuffer.cpp
@@ -1094,7 +1094,7 @@ TEST(TestAudioRingBuffer, PrependSilenceNoWrapShort)
EXPECT_THAT(out, ElementsAre(2, 3, 4, 5, 0, 0, 6, 7));
}
-TEST(TestAudioRingBuffer, SetLengthBytesNoWrapFloat)
+TEST(TestAudioRingBuffer, EnsureLengthBytesNoWrapFloat)
{
AudioRingBuffer rb(6 * sizeof(float));
rb.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
@@ -1106,7 +1106,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesNoWrapFloat)
EXPECT_EQ(rb.AvailableWrite(), 0u);
EXPECT_EQ(rb.Capacity(), 6u);
- EXPECT_TRUE(rb.SetLengthBytes(11 * sizeof(float)));
+ EXPECT_TRUE(rb.EnsureLengthBytes(11 * sizeof(float)));
float out[10] = {};
rv = rb.Read(Span(out, 10));
EXPECT_EQ(rv, 5u);
@@ -1116,7 +1116,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesNoWrapFloat)
EXPECT_THAT(out, ElementsAre(.1, .2, .3, .4, .5, 0, 0, 0, 0, 0));
}
-TEST(TestAudioRingBuffer, SetLengthBytesNoWrapShort)
+TEST(TestAudioRingBuffer, EnsureLengthBytesNoWrapShort)
{
AudioRingBuffer rb(6 * sizeof(short));
rb.SetSampleFormat(AUDIO_FORMAT_S16);
@@ -1128,7 +1128,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesNoWrapShort)
EXPECT_EQ(rb.AvailableWrite(), 0u);
EXPECT_EQ(rb.Capacity(), 6u);
- EXPECT_TRUE(rb.SetLengthBytes(11 * sizeof(short)));
+ EXPECT_TRUE(rb.EnsureLengthBytes(11 * sizeof(short)));
short out[10] = {};
rv = rb.Read(Span(out, 10));
EXPECT_EQ(rv, 5u);
@@ -1138,7 +1138,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesNoWrapShort)
EXPECT_THAT(out, ElementsAre(1, 2, 3, 4, 5, 0, 0, 0, 0, 0));
}
-TEST(TestAudioRingBuffer, SetLengthBytesWrap1PartFloat)
+TEST(TestAudioRingBuffer, EnsureLengthBytesWrap1PartFloat)
{
AudioRingBuffer rb(6 * sizeof(float));
rb.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
@@ -1158,7 +1158,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap1PartFloat)
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 0u);
- EXPECT_TRUE(rb.SetLengthBytes(11 * sizeof(float)));
+ EXPECT_TRUE(rb.EnsureLengthBytes(11 * sizeof(float)));
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 5u);
@@ -1175,7 +1175,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap1PartFloat)
EXPECT_THAT(out, ElementsAre(.1, .2, .3, .4, .5, .6, .7, 0, 0, 0));
}
-TEST(TestAudioRingBuffer, SetLengthBytesWrap1PartShort)
+TEST(TestAudioRingBuffer, EnsureLengthBytesWrap1PartShort)
{
AudioRingBuffer rb(6 * sizeof(short));
rb.SetSampleFormat(AUDIO_FORMAT_S16);
@@ -1195,7 +1195,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap1PartShort)
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 0u);
- EXPECT_TRUE(rb.SetLengthBytes(11 * sizeof(short)));
+ EXPECT_TRUE(rb.EnsureLengthBytes(11 * sizeof(short)));
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 5u);
@@ -1212,7 +1212,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap1PartShort)
EXPECT_THAT(out, ElementsAre(1, 2, 3, 4, 5, 6, 7, 0, 0, 0));
}
-TEST(TestAudioRingBuffer, SetLengthBytesWrap2PartsFloat)
+TEST(TestAudioRingBuffer, EnsureLengthBytesWrap2PartsFloat)
{
AudioRingBuffer rb(6 * sizeof(float));
rb.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
@@ -1232,7 +1232,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap2PartsFloat)
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 0u);
- EXPECT_TRUE(rb.SetLengthBytes(8 * sizeof(float)));
+ EXPECT_TRUE(rb.EnsureLengthBytes(8 * sizeof(float)));
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 2u);
@@ -1249,7 +1249,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap2PartsFloat)
EXPECT_THAT(out, ElementsAre(.1, .2, .3, .4, .5, .6, .7, 0));
}
-TEST(TestAudioRingBuffer, SetLengthBytesWrap2PartsShort)
+TEST(TestAudioRingBuffer, EnsureLengthBytesWrap2PartsShort)
{
AudioRingBuffer rb(6 * sizeof(short));
rb.SetSampleFormat(AUDIO_FORMAT_S16);
@@ -1269,7 +1269,7 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap2PartsShort)
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 0u);
- EXPECT_TRUE(rb.SetLengthBytes(8 * sizeof(short)));
+ EXPECT_TRUE(rb.EnsureLengthBytes(8 * sizeof(short)));
EXPECT_EQ(rb.AvailableRead(), 5u);
EXPECT_EQ(rb.AvailableWrite(), 2u);
@@ -1285,3 +1285,29 @@ TEST(TestAudioRingBuffer, SetLengthBytesWrap2PartsShort)
EXPECT_EQ(rb.Capacity(), 8u);
EXPECT_THAT(out, ElementsAre(1, 2, 3, 4, 5, 6, 7, 0));
}
+
+TEST(TestAudioRingBuffer, EnsureLengthShorter)
+{
+ AudioRingBuffer rb(5 * sizeof(float));
+ rb.SetSampleFormat(AUDIO_FORMAT_FLOAT32);
+
+ float in[5] = {.1, .2, .3, .4, .5};
+ EXPECT_EQ(rb.Write(Span(in, 5)), 4u);
+ EXPECT_EQ(rb.AvailableRead(), 4u);
+ EXPECT_EQ(rb.AvailableWrite(), 0u);
+ EXPECT_EQ(rb.Capacity(), 5u);
+
+ float out[5] = {};
+ EXPECT_EQ(rb.Read(Span(out, 3)), 3u);
+ EXPECT_THAT(out, ElementsAre(.1, .2, .3, 0, 0));
+ EXPECT_EQ(rb.AvailableRead(), 1u);
+ EXPECT_EQ(rb.AvailableWrite(), 3u);
+
+ EXPECT_TRUE(rb.EnsureLengthBytes(3 * sizeof(float)));
+ EXPECT_EQ(rb.AvailableRead(), 1u);
+ EXPECT_EQ(rb.AvailableWrite(), 3u);
+ EXPECT_EQ(rb.Capacity(), 5u);
+ EXPECT_EQ(rb.Write(Span(in, 5)), 3u);
+ EXPECT_EQ(rb.Read(Span(out, 5)), 4u);
+ EXPECT_THAT(out, ElementsAre(.4, .1, .2, .3, 0));
+}
diff --git a/dom/media/gtest/TestAudioTrackGraph.cpp b/dom/media/gtest/TestAudioTrackGraph.cpp
index 7be1224ab9..b1202277ce 100644
--- a/dom/media/gtest/TestAudioTrackGraph.cpp
+++ b/dom/media/gtest/TestAudioTrackGraph.cpp
@@ -21,9 +21,13 @@
#include "WavDumper.h"
using namespace mozilla;
+using testing::Eq;
+using testing::InSequence;
+using testing::Return;
+using testing::StrictMock;
// Short-hand for InvokeAsync on the current thread.
-#define Invoke(f) InvokeAsync(GetCurrentSerialEventTarget(), __func__, f)
+#define InvokeAsync(f) InvokeAsync(GetCurrentSerialEventTarget(), __func__, f)
// Short-hand for DispatchToCurrentThread with a function.
#define DispatchFunction(f) \
@@ -33,6 +37,12 @@ using namespace mozilla;
#define DispatchMethod(t, m, args...) \
NS_DispatchToCurrentThread(NewRunnableMethod(__func__, t, m, ##args))
+// Short-hand for draining the current threads event queue, i.e. processing
+// those runnables dispatched per above.
+#define ProcessEventQueue() \
+ while (NS_ProcessNextEvent(nullptr, false)) { \
+ }
+
namespace {
#ifdef MOZ_WEBRTC
/*
@@ -111,6 +121,44 @@ struct StopNonNativeInput : public ControlMessage {
void Run() override { mInputTrack->StopAudio(); }
};
+// Helper for detecting when fallback driver has been switched away, for use
+// with RunningMode::Manual.
+class OnFallbackListener : public MediaTrackListener {
+ const RefPtr<MediaTrack> mTrack;
+ Atomic<bool> mOnFallback{true};
+
+ public:
+ explicit OnFallbackListener(MediaTrack* aTrack) : mTrack(aTrack) {}
+
+ void Reset() { mOnFallback = true; }
+ bool OnFallback() { return mOnFallback; }
+
+ void NotifyOutput(MediaTrackGraph*, TrackTime) override {
+ if (auto* ad =
+ mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver()) {
+ mOnFallback = ad->OnFallback();
+ }
+ }
+};
+
+class MockAudioDataListener : public AudioDataListener {
+ protected:
+ ~MockAudioDataListener() = default;
+
+ public:
+ MockAudioDataListener() = default;
+
+ MOCK_METHOD(uint32_t, RequestedInputChannelCount, (MediaTrackGraph*),
+ (const));
+ MOCK_METHOD(cubeb_input_processing_params, RequestedInputProcessingParams,
+ (MediaTrackGraph*), (const));
+ MOCK_METHOD(bool, IsVoiceInput, (MediaTrackGraph*), (const));
+ MOCK_METHOD(void, DeviceChanged, (MediaTrackGraph*));
+ MOCK_METHOD(void, Disconnect, (MediaTrackGraph*));
+ MOCK_METHOD(void, NotifySetRequestedInputProcessingParamsResult,
+ (MediaTrackGraph*, cubeb_input_processing_params,
+ (const Result<cubeb_input_processing_params, int>&)));
+};
} // namespace
/*
@@ -164,7 +212,7 @@ TEST(TestAudioTrackGraph, DifferentDeviceIDs)
// graph from the global hash table and let it shutdown.
using SourceTrackPromise = MozPromise<SourceMediaTrack*, nsresult, true>;
- auto p = Invoke([g] {
+ auto p = InvokeAsync([g] {
return SourceTrackPromise::CreateAndResolve(
g->CreateSourceTrack(MediaSegment::AUDIO), __func__);
});
@@ -256,7 +304,7 @@ TEST(TestAudioTrackGraph, NotifyDeviceStarted)
nullptr, GetMainThreadSerialEventTarget());
RefPtr<SourceMediaTrack> dummySource;
- Unused << WaitFor(Invoke([&] {
+ Unused << WaitFor(InvokeAsync([&] {
// Dummy track to make graph rolling. Add it and remove it to remove the
// graph from the global hash table and let it shutdown.
dummySource = graph->CreateSourceTrack(MediaSegment::AUDIO);
@@ -522,7 +570,7 @@ TEST(TestAudioTrackGraph, NonNativeInputTrackErrorCallback)
class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
public:
static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
TestDeviceInputConsumerTrack* track =
new TestDeviceInputConsumerTrack(aGraph->GraphRate());
aGraph->AddTrack(track);
@@ -530,7 +578,7 @@ class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
}
void Destroy() {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
DisconnectDeviceInput();
DeviceInputConsumerTrack::Destroy();
}
@@ -543,7 +591,7 @@ class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
if (mInputs.IsEmpty()) {
GetData<AudioSegment>()->AppendNullData(aTo - aFrom);
} else {
- MOZ_ASSERT(mInputs.Length() == 1);
+ MOZ_RELEASE_ASSERT(mInputs.Length() == 1);
AudioSegment data;
DeviceInputConsumerTrack::GetInputSourceData(data, aFrom, aTo);
GetData<AudioSegment>()->AppendFrom(&data);
@@ -555,7 +603,7 @@ class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
return 0;
}
DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
- MOZ_ASSERT(t);
+ MOZ_RELEASE_ASSERT(t);
return t->NumberOfChannels();
}
@@ -574,30 +622,23 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback)
CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
nullptr, GetMainThreadSerialEventTarget());
- class TestAudioDataListener : public AudioDataListener {
+ class TestAudioDataListener : public StrictMock<MockAudioDataListener> {
public:
- TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
- : mChannelCount(aChannelCount),
- mIsVoice(aIsVoice),
- mDeviceChangedCount(0) {}
-
- uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
- return mChannelCount;
- }
- bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
- return mIsVoice;
- };
- void DeviceChanged(MediaTrackGraph* aGraph) override {
- ++mDeviceChangedCount;
+ TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) {
+ EXPECT_CALL(*this, RequestedInputChannelCount)
+ .WillRepeatedly(Return(aChannelCount));
+ EXPECT_CALL(*this, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
+ EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice));
+ {
+ InSequence s;
+ EXPECT_CALL(*this, DeviceChanged);
+ EXPECT_CALL(*this, Disconnect);
+ }
}
- void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
- uint32_t DeviceChangedCount() { return mDeviceChangedCount; }
private:
~TestAudioDataListener() = default;
- const uint32_t mChannelCount;
- const bool mIsVoice;
- std::atomic<uint32_t> mDeviceChangedCount;
};
// Create a full-duplex AudioCallbackDriver by creating a NativeInputTrack.
@@ -610,7 +651,7 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback)
EXPECT_TRUE(track1->ConnectedToNativeDevice());
EXPECT_FALSE(track1->ConnectedToNonNativeDevice());
auto started =
- Invoke([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
+ InvokeAsync([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream1->mHasInput);
EXPECT_TRUE(stream1->mHasOutput);
@@ -648,9 +689,6 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback)
WaitFor(cubeb->StreamDestroyEvent());
EXPECT_EQ(destroyedStream.get(), stream2.get());
- // Make sure we only have one device-changed event for the NativeInputTrack.
- EXPECT_EQ(listener2->DeviceChangedCount(), 1U);
-
// Destroy the NativeInputTrack.
DispatchFunction([&] {
track1->DisconnectDeviceInput();
@@ -658,9 +696,6 @@ TEST(TestAudioTrackGraph, DeviceChangedCallback)
});
destroyedStream = WaitFor(cubeb->StreamDestroyEvent());
EXPECT_EQ(destroyedStream.get(), stream1.get());
-
- // Make sure we only have one device-changed event for the NativeInputTrack.
- EXPECT_EQ(listener1->DeviceChangedCount(), 1U);
}
// The native audio stream (a.k.a. GraphDriver) and the non-native audio stream
@@ -692,57 +727,32 @@ TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged)
// A test-only AudioDataListener that simulates AudioInputProcessing's setter
// and getter for the input channel count.
- class TestAudioDataListener : public AudioDataListener {
+ class TestAudioDataListener : public StrictMock<MockAudioDataListener> {
public:
- TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
- : mChannelCount(aChannelCount), mIsVoice(aIsVoice) {}
+ TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) {
+ EXPECT_CALL(*this, RequestedInputChannelCount)
+ .WillRepeatedly(Return(aChannelCount));
+ EXPECT_CALL(*this, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
+ EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice));
+ EXPECT_CALL(*this, Disconnect);
+ }
// Main thread API
void SetInputChannelCount(MediaTrackGraph* aGraph,
CubebUtils::AudioDeviceID aDevice,
uint32_t aChannelCount) {
- MOZ_ASSERT(NS_IsMainThread());
-
- struct Message : public ControlMessage {
- MediaTrackGraph* mGraph;
- TestAudioDataListener* mListener;
- CubebUtils::AudioDeviceID mDevice;
- uint32_t mChannelCount;
-
- Message(MediaTrackGraph* aGraph, TestAudioDataListener* aListener,
- CubebUtils::AudioDeviceID aDevice, uint32_t aChannelCount)
- : ControlMessage(nullptr),
- mGraph(aGraph),
- mListener(aListener),
- mDevice(aDevice),
- mChannelCount(aChannelCount) {}
- void Run() override {
- mListener->mChannelCount = mChannelCount;
- mGraph->ReevaluateInputDevice(mDevice);
- }
- };
-
- static_cast<MediaTrackGraphImpl*>(aGraph)->AppendMessage(
- MakeUnique<Message>(aGraph, this, aDevice, aChannelCount));
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ static_cast<MediaTrackGraphImpl*>(aGraph)
+ ->QueueControlMessageWithNoShutdown(
+ [this, self = RefPtr(this), aGraph, aDevice, aChannelCount] {
+ EXPECT_CALL(*this, RequestedInputChannelCount)
+ .WillRepeatedly(Return(aChannelCount));
+ aGraph->ReevaluateInputDevice(aDevice);
+ });
}
- // Graph thread APIs: AudioDataListenerInterface implementations.
- uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
- aGraph->AssertOnGraphThread();
- return mChannelCount;
- }
- bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
- return mIsVoice;
- };
- void DeviceChanged(MediaTrackGraph* aGraph) override { /* Ignored */
- }
- void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
private:
~TestAudioDataListener() = default;
-
- // Graph thread-only.
- uint32_t mChannelCount;
- // Any thread.
- const bool mIsVoice;
};
// Request a new input channel count and expect to have a new stream.
@@ -841,8 +851,8 @@ TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged)
EXPECT_TRUE(track1->ConnectedToNativeDevice());
EXPECT_FALSE(track1->ConnectedToNonNativeDevice());
- auto started =
- Invoke([&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
+ auto started = InvokeAsync(
+ [&] { return graphImpl->NotifyWhenDeviceStarted(nullptr); });
nativeStream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(nativeStream->mHasInput);
EXPECT_TRUE(nativeStream->mHasOutput);
@@ -947,30 +957,18 @@ TEST(TestAudioTrackGraph, RestartAudioIfMaxChannelCountChanged)
// AudioDataListener. However, it only tests when MOZ_WEBRTC is defined.
TEST(TestAudioTrackGraph, SwitchNativeInputDevice)
{
- class TestAudioDataListener : public AudioDataListener {
+ class TestAudioDataListener : public StrictMock<MockAudioDataListener> {
public:
- TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
- : mChannelCount(aChannelCount),
- mIsVoice(aIsVoice),
- mDeviceChangedCount(0) {}
-
- uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
- return mChannelCount;
- }
- bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
- return mIsVoice;
- };
- void DeviceChanged(MediaTrackGraph* aGraph) override {
- ++mDeviceChangedCount;
+ TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice) {
+ EXPECT_CALL(*this, RequestedInputChannelCount)
+ .WillRepeatedly(Return(aChannelCount));
+ EXPECT_CALL(*this, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
+ EXPECT_CALL(*this, IsVoiceInput).WillRepeatedly(Return(aIsVoice));
}
- void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
- uint32_t DeviceChangedCount() { return mDeviceChangedCount; }
private:
~TestAudioDataListener() = default;
- const uint32_t mChannelCount;
- const bool mIsVoice;
- std::atomic<uint32_t> mDeviceChangedCount;
};
MockCubeb* cubeb = new MockCubeb();
@@ -1049,11 +1047,12 @@ TEST(TestAudioTrackGraph, SwitchNativeInputDevice)
RefPtr<TestDeviceInputConsumerTrack> track1 =
TestDeviceInputConsumerTrack::Create(graph);
RefPtr<TestAudioDataListener> listener1 = new TestAudioDataListener(1, false);
+ EXPECT_CALL(*listener1, Disconnect);
track1->ConnectDeviceInput(device1, listener1, PRINCIPAL_HANDLE_NONE);
EXPECT_EQ(track1->DeviceId().value(), device1);
auto started =
- Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
+ InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream1->mHasInput);
@@ -1069,6 +1068,7 @@ TEST(TestAudioTrackGraph, SwitchNativeInputDevice)
RefPtr<TestDeviceInputConsumerTrack> track2 =
TestDeviceInputConsumerTrack::Create(graph);
RefPtr<TestAudioDataListener> listener2 = new TestAudioDataListener(2, false);
+ EXPECT_CALL(*listener2, Disconnect).Times(2);
track2->ConnectDeviceInput(device2, listener2, PRINCIPAL_HANDLE_NONE);
EXPECT_EQ(track2->DeviceId().value(), device2);
@@ -1085,6 +1085,7 @@ TEST(TestAudioTrackGraph, SwitchNativeInputDevice)
RefPtr<TestDeviceInputConsumerTrack> track3 =
TestDeviceInputConsumerTrack::Create(graph);
RefPtr<TestAudioDataListener> listener3 = new TestAudioDataListener(1, false);
+ EXPECT_CALL(*listener3, Disconnect).Times(2);
track3->ConnectDeviceInput(device3, listener3, PRINCIPAL_HANDLE_NONE);
EXPECT_EQ(track3->DeviceId().value(), device3);
@@ -1160,7 +1161,7 @@ TEST(TestAudioTrackGraph, ErrorCallback)
// output from the graph.
RefPtr<AudioProcessingTrack> processingTrack;
RefPtr<AudioInputProcessing> listener;
- auto started = Invoke([&] {
+ auto started = InvokeAsync([&] {
processingTrack = AudioProcessingTrack::Create(graph);
listener = new AudioInputProcessing(2);
QueueExpectIsPassThrough(processingTrack, listener);
@@ -1225,7 +1226,7 @@ TEST(TestAudioTrackGraph, AudioProcessingTrack)
RefPtr<ProcessedMediaTrack> outputTrack;
RefPtr<MediaInputPort> port;
RefPtr<AudioInputProcessing> listener;
- auto p = Invoke([&] {
+ auto p = InvokeAsync([&] {
processingTrack = AudioProcessingTrack::Create(graph);
outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
outputTrack->QueueSetAutoend(false);
@@ -1282,7 +1283,7 @@ TEST(TestAudioTrackGraph, AudioProcessingTrack)
EXPECT_EQ(estimatedFreq, inputFrequency);
std::cerr << "PreSilence: " << preSilenceSamples << std::endl;
- // We buffer 128 frames. See DeviceInputTrack::ProcessInput.
+ // We buffer 128 frames. See NativeInputTrack::NotifyInputData.
EXPECT_GE(preSilenceSamples, 128U);
// If the fallback system clock driver is doing a graph iteration before the
// first audio driver iteration comes in, that iteration is ignored and
@@ -1297,13 +1298,17 @@ TEST(TestAudioTrackGraph, AudioProcessingTrack)
TEST(TestAudioTrackGraph, ReConnectDeviceInput)
{
- MockCubeb* cubeb = new MockCubeb();
+ MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
// 48k is a native processing rate, and avoids a resampling pass compared
// to 44.1k. The resampler may add take a few frames to stabilize, which show
// as unexected discontinuities in the test.
const TrackRate rate = 48000;
+ // Use a drift factor so that we don't dont produce perfect 10ms-chunks.
+ // This will exercise whatever buffers are in the audio processing pipeline,
+ // and the bookkeeping surrounding them.
+ const long step = 10 * rate * 1111 / 1000 / PR_MSEC_PER_SEC;
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1, rate, nullptr,
@@ -1315,7 +1320,8 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput)
RefPtr<ProcessedMediaTrack> outputTrack;
RefPtr<MediaInputPort> port;
RefPtr<AudioInputProcessing> listener;
- auto p = Invoke([&] {
+ RefPtr<OnFallbackListener> fallbackListener;
+ DispatchFunction([&] {
processingTrack = AudioProcessingTrack::Create(graph);
outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
outputTrack->QueueSetAutoend(false);
@@ -1337,54 +1343,65 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput)
settings.mAgc2Forced = true;
QueueApplySettings(processingTrack, listener, settings);
- return graph->NotifyWhenDeviceStarted(nullptr);
+ fallbackListener = new OnFallbackListener(processingTrack);
+ processingTrack->AddListener(fallbackListener);
});
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream->mHasInput);
- Unused << WaitFor(p);
- // Set a drift factor so that we don't dont produce perfect 10ms-chunks. This
- // will exercise whatever buffers are in the audio processing pipeline, and
- // the bookkeeping surrounding them.
- stream->SetDriftFactor(1.111);
+ while (
+ stream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
- // Wait for a second worth of audio data. GoFaster is dispatched through a
- // ControlMessage so that it is called in the first audio driver iteration.
- // Otherwise the audio driver might be going very fast while the fallback
- // system clock driver is still in an iteration.
- DispatchFunction([&] {
- processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
- });
- {
- uint32_t totalFrames = 0;
- WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
- totalFrames += aFrames;
- return totalFrames > static_cast<uint32_t>(graph->GraphRate());
- });
+ // Wait for the AudioCallbackDriver to come into effect.
+ while (fallbackListener->OnFallback()) {
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Iterate for a second worth of audio data.
+ for (long frames = 0; frames < graph->GraphRate(); frames += step) {
+ stream->ManualDataCallback(step);
}
- cubeb->DontGoFaster();
// Close the input to see that no asserts go off due to bad state.
DispatchFunction([&] { processingTrack->DisconnectDeviceInput(); });
- stream = WaitFor(cubeb->StreamInitEvent());
+ // Dispatch the disconnect message.
+ ProcessEventQueue();
+ // Run the disconnect message.
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ // Switch driver.
+ auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
+ EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
+ std::tie(stream) = WaitFor(initPromise).unwrap()[0];
EXPECT_FALSE(stream->mHasInput);
- Unused << WaitFor(
- Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
- // Output-only. Wait for another second before unmuting.
- DispatchFunction([&] {
- processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
- });
- {
- uint32_t totalFrames = 0;
- WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
- totalFrames += aFrames;
- return totalFrames > static_cast<uint32_t>(graph->GraphRate());
- });
+ while (
+ stream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Wait for the new AudioCallbackDriver to come into effect.
+ fallbackListener->Reset();
+ while (fallbackListener->OnFallback()) {
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Output-only. Iterate for another second before unmuting.
+ for (long frames = 0; frames < graph->GraphRate(); frames += step) {
+ stream->ManualDataCallback(step);
}
- cubeb->DontGoFaster();
// Re-open the input to again see that no asserts go off due to bad state.
DispatchFunction([&] {
@@ -1392,27 +1409,40 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput)
processingTrack->ConnectDeviceInput(deviceId, listener,
PRINCIPAL_HANDLE_NONE);
});
-
- stream = WaitFor(cubeb->StreamInitEvent());
+ // Dispatch the connect message.
+ ProcessEventQueue();
+ // Run the connect message.
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ // Switch driver.
+ initPromise = TakeN(cubeb->StreamInitEvent(), 1);
+ EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
+ std::tie(stream) = WaitFor(initPromise).unwrap()[0];
EXPECT_TRUE(stream->mHasInput);
- Unused << WaitFor(
- Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
- // Full-duplex. Wait for another second before finishing.
- DispatchFunction([&] {
- processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
- });
- {
- uint32_t totalFrames = 0;
- WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
- totalFrames += aFrames;
- return totalFrames > static_cast<uint32_t>(graph->GraphRate());
- });
+ while (
+ stream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Wait for the new AudioCallbackDriver to come into effect.
+ fallbackListener->Reset();
+ while (fallbackListener->OnFallback()) {
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Full-duplex. Iterate for another second before finishing.
+ for (long frames = 0; frames < graph->GraphRate(); frames += step) {
+ stream->ManualDataCallback(step);
}
- cubeb->DontGoFaster();
// Clean up.
DispatchFunction([&] {
+ processingTrack->RemoveListener(fallbackListener);
outputTrack->RemoveAudioOutput((void*)1);
outputTrack->Destroy();
port->Destroy();
@@ -1422,7 +1452,14 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput)
processingTrack->Destroy();
});
- uint32_t inputRate = stream->SampleRate();
+ // Dispatch the clean-up messages.
+ ProcessEventQueue();
+ // Run the clean-up messages.
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ // Shut down driver.
+ EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
+
uint32_t inputFrequency = stream->InputFrequency();
uint64_t preSilenceSamples;
uint32_t estimatedFreq;
@@ -1431,17 +1468,12 @@ TEST(TestAudioTrackGraph, ReConnectDeviceInput)
WaitFor(stream->OutputVerificationEvent());
EXPECT_EQ(estimatedFreq, inputFrequency);
- std::cerr << "PreSilence: " << preSilenceSamples << std::endl;
- // We buffer 10ms worth of frames in non-passthrough mode, plus up to 128
- // frames as we round up to the nearest block. See
- // AudioInputProcessing::Process and DeviceInputTrack::PrcoessInput.
- EXPECT_GE(preSilenceSamples, 128U + inputRate / 100);
- // If the fallback system clock driver is doing a graph iteration before the
- // first audio driver iteration comes in, that iteration is ignored and
- // results in zeros. It takes one fallback driver iteration *after* the audio
- // driver has started to complete the switch, *usually* resulting two
- // 10ms-iterations of silence; sometimes only one.
- EXPECT_LE(preSilenceSamples, 128U + 3 * inputRate / 100 /* 3*10ms */);
+ std::cerr << "PreSilence: " << preSilenceSamples << "\n";
+ // We buffer 128 frames. See NativeInputTrack::NotifyInputData.
+ // When not in passthrough the AudioInputProcessing packetizer also buffers
+ // 10ms of silence, pulled in from NativeInputTrack when being run by the
+ // fallback SystemClockDriver.
+ EXPECT_EQ(preSilenceSamples, WEBAUDIO_BLOCK_SIZE + rate / 100);
// The waveform from AudioGenerator starts at 0, but we don't control its
// ending, so we expect a discontinuity there. Note that this check is only
// for the waveform on the stream *after* re-opening the input.
@@ -1467,7 +1499,7 @@ float rmsf32(AudioDataValue* aSamples, uint32_t aChannels, uint32_t aFrames) {
TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
{
- MockCubeb* cubeb = new MockCubeb();
+ MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
@@ -1481,7 +1513,8 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
RefPtr<ProcessedMediaTrack> outputTrack;
RefPtr<MediaInputPort> port;
RefPtr<AudioInputProcessing> listener;
- auto p = Invoke([&] {
+ RefPtr<OnFallbackListener> fallbackListener;
+ DispatchFunction([&] {
processingTrack = AudioProcessingTrack::Create(graph);
outputTrack = graph->CreateForwardedInputTrack(MediaSegment::AUDIO);
outputTrack->QueueSetAutoend(false);
@@ -1495,32 +1528,36 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
PRINCIPAL_HANDLE_NONE);
processingTrack->GraphImpl()->AppendMessage(
MakeUnique<StartInputProcessing>(processingTrack, listener));
- return graph->NotifyWhenDeviceStarted(nullptr);
+ fallbackListener = new OnFallbackListener(processingTrack);
+ processingTrack->AddListener(fallbackListener);
});
+ ProcessEventQueue();
+
RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream->mHasInput);
- Unused << WaitFor(p);
+
+ while (
+ stream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Wait for the AudioCallbackDriver to come into effect.
+ while (fallbackListener->OnFallback()) {
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
stream->SetOutputRecordingEnabled(true);
// Wait for a second worth of audio data.
- uint64_t targetPosition = graph->GraphRate();
- auto AdvanceToTargetPosition = [&] {
- DispatchFunction([&] {
- processingTrack->GraphImpl()->AppendMessage(MakeUnique<GoFaster>(cubeb));
- });
- WaitUntil(stream->FramesProcessedEvent(), [&](uint32_t aFrames) {
- // Position() gives a more up-to-date indication than summing aFrames if
- // multiple events are queued.
- if (stream->Position() < targetPosition) {
- return false;
- }
- cubeb->DontGoFaster();
- return true;
- });
- };
- AdvanceToTargetPosition();
+ const long step = graph->GraphRate() / 100; // 10ms
+ for (long frames = 0; frames < graph->GraphRate(); frames += step) {
+ stream->ManualDataCallback(step);
+ }
const uint32_t ITERATION_COUNT = 5;
uint32_t iterations = ITERATION_COUNT;
@@ -1537,8 +1574,11 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
}
});
- targetPosition += graph->GraphRate();
- AdvanceToTargetPosition();
+ ProcessEventQueue();
+
+ for (long frames = 0; frames < graph->GraphRate(); frames += step) {
+ stream->ManualDataCallback(step);
+ }
}
// Clean up.
@@ -1548,10 +1588,18 @@ TEST(TestAudioTrackGraph, AudioProcessingTrackDisabling)
port->Destroy();
processingTrack->GraphImpl()->AppendMessage(
MakeUnique<StopInputProcessing>(processingTrack, listener));
+ processingTrack->RemoveListener(fallbackListener);
processingTrack->DisconnectDeviceInput();
processingTrack->Destroy();
});
+ ProcessEventQueue();
+
+ // Close the input and switch driver.
+ while (stream->ManualDataCallback(0) != MockCubebStream::KeepProcessing::No) {
+ std::cerr << "Waiting for switch...\n";
+ }
+
uint64_t preSilenceSamples;
uint32_t estimatedFreq;
uint32_t nrDiscontinuities;
@@ -1604,7 +1652,7 @@ TEST(TestAudioTrackGraph, SetRequestedInputChannelCount)
EXPECT_EQ(track1->DeviceId().value(), device1);
auto started =
- Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
+ InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream1->mHasInput);
@@ -1829,7 +1877,7 @@ TEST(TestAudioTrackGraph, RestartAudioIfProcessingMaxChannelCountChanged)
EXPECT_EQ(track1->DeviceId().value(), nativeDevice);
auto started =
- Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
+ InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
nativeStream = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(nativeStream->mHasInput);
@@ -1961,44 +2009,23 @@ TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)
const CubebUtils::AudioDeviceID deviceId = (CubebUtils::AudioDeviceID)1;
RefPtr<AudioProcessingTrack> track;
RefPtr<AudioInputProcessing> listener;
- {
- MozPromiseHolder<GenericPromise> h;
- RefPtr<GenericPromise> p = h.Ensure(__func__);
-
- struct GuardMessage : public ControlMessage {
- MozPromiseHolder<GenericPromise> mHolder;
-
- GuardMessage(MediaTrack* aTrack,
- MozPromiseHolder<GenericPromise>&& aHolder)
- : ControlMessage(aTrack), mHolder(std::move(aHolder)) {}
- void Run() override {
- mTrack->GraphImpl()->Dispatch(NS_NewRunnableFunction(
- "TestAudioTrackGraph::SetInputChannel::Message::Resolver",
- [holder = std::move(mHolder)]() mutable {
- holder.Resolve(true, __func__);
- }));
- }
- };
-
- DispatchFunction([&] {
- track = AudioProcessingTrack::Create(graph);
- listener = new AudioInputProcessing(2);
- QueueExpectIsPassThrough(track, listener);
- track->SetInputProcessing(listener);
-
- MediaEnginePrefs settings;
- settings.mChannels = 1;
- QueueApplySettings(track, listener, settings);
+ DispatchFunction([&] {
+ track = AudioProcessingTrack::Create(graph);
+ listener = new AudioInputProcessing(2);
+ QueueExpectIsPassThrough(track, listener);
+ track->SetInputProcessing(listener);
- track->GraphImpl()->AppendMessage(
- MakeUnique<GuardMessage>(track, std::move(h)));
- });
+ MediaEnginePrefs settings;
+ settings.mChannels = 1;
+ QueueApplySettings(track, listener, settings);
+ });
- Unused << WaitFor(p);
- }
+ // Wait for AudioCallbackDriver to init output-only stream.
+ RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ EXPECT_FALSE(stream->mHasInput);
+ EXPECT_TRUE(stream->mHasOutput);
// Open a full-duplex AudioCallbackDriver.
-
RefPtr<MediaInputPort> port;
DispatchFunction([&] {
track->GraphImpl()->AppendMessage(
@@ -2006,22 +2033,13 @@ TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)
track->ConnectDeviceInput(deviceId, listener, PRINCIPAL_HANDLE_NONE);
});
- // MediaTrackGraph will create a output-only AudioCallbackDriver in
- // CheckDriver before we open an audio input above, since AudioProcessingTrack
- // is a audio-type MediaTrack, so we need to wait here until the duplex
- // AudioCallbackDriver is created.
- RefPtr<SmartMockCubebStream> stream;
- SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
- "TEST(TestAudioTrackGraph, SetInputChannelCountBeforeAudioCallbackDriver)"_ns,
- [&] {
- stream = WaitFor(cubeb->StreamInitEvent());
- EXPECT_TRUE(stream->mHasOutput);
- return stream->mHasInput;
- });
+ stream = WaitFor(cubeb->StreamInitEvent());
+ EXPECT_TRUE(stream->mHasInput);
+ EXPECT_TRUE(stream->mHasOutput);
EXPECT_EQ(stream->InputChannels(), 1U);
Unused << WaitFor(
- Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
+ InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); }));
// Clean up.
DispatchFunction([&] {
@@ -2257,7 +2275,7 @@ TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack)
EXPECT_EQ(track1->DeviceId().value(), device1);
auto started =
- Invoke([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
+ InvokeAsync([&] { return graph->NotifyWhenDeviceStarted(nullptr); });
RefPtr<SmartMockCubebStream> stream1 = WaitFor(cubeb->StreamInitEvent());
EXPECT_TRUE(stream1->mHasInput);
@@ -2350,23 +2368,6 @@ TEST(TestAudioTrackGraph, SwitchNativeAudioProcessingTrack)
std::cerr << "No native input now" << std::endl;
}
-class OnFallbackListener : public MediaTrackListener {
- const RefPtr<MediaTrack> mTrack;
- Atomic<bool> mOnFallback{true};
-
- public:
- explicit OnFallbackListener(MediaTrack* aTrack) : mTrack(aTrack) {}
-
- bool OnFallback() { return mOnFallback; }
-
- void NotifyOutput(MediaTrackGraph*, TrackTime) override {
- if (auto* ad =
- mTrack->GraphImpl()->CurrentDriver()->AsAudioCallbackDriver()) {
- mOnFallback = ad->OnFallback();
- }
- }
-};
-
void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
float aDriftFactor, uint32_t aRunTimeSeconds = 10,
uint32_t aNumExpectedUnderruns = 0) {
@@ -2409,6 +2410,13 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
RefPtr<SmartMockCubebStream> inputStream = WaitFor(cubeb->StreamInitEvent());
+ while (
+ inputStream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
// Wait for the primary AudioCallbackDriver to come into effect.
while (primaryFallbackListener->OnFallback()) {
EXPECT_EQ(inputStream->ManualDataCallback(0),
@@ -2441,6 +2449,13 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
RefPtr<SmartMockCubebStream> partnerStream =
WaitFor(cubeb->StreamInitEvent());
+ while (
+ partnerStream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
// Process the CrossGraphTransmitter on the primary graph.
EXPECT_EQ(inputStream->ManualDataCallback(0),
MockCubebStream::KeepProcessing::Yes);
@@ -2453,8 +2468,7 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
}
DispatchFunction([&] { receiver->RemoveListener(partnerFallbackListener); });
- while (NS_ProcessNextEvent(nullptr, false)) {
- }
+ ProcessEventQueue();
nsIThread* currentThread = NS_GetCurrentThread();
cubeb_state inputState = CUBEB_STATE_STARTED;
@@ -2499,8 +2513,7 @@ void TestCrossGraphPort(uint32_t aInputRate, uint32_t aOutputRate,
processingTrack->Destroy();
});
- while (NS_ProcessNextEvent(nullptr, false)) {
- }
+ ProcessEventQueue();
EXPECT_EQ(inputStream->ManualDataCallback(0),
MockCubebStream::KeepProcessing::Yes);
@@ -2816,6 +2829,221 @@ TEST(TestAudioInputProcessing, ClockDriftExpectation)
}
#endif // MOZ_WEBRTC
+TEST(TestAudioTrackGraph, PlatformProcessing)
+{
+ constexpr cubeb_input_processing_params allParams =
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION |
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
+ CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
+ CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION;
+ MockCubeb* cubeb = new MockCubeb(MockCubeb::RunningMode::Manual);
+ cubeb->SetSupportedInputProcessingParams(allParams, CUBEB_OK);
+ CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
+
+ MediaTrackGraph* graph = MediaTrackGraphImpl::GetInstance(
+ MediaTrackGraph::SYSTEM_THREAD_DRIVER, /*Window ID*/ 1,
+ CubebUtils::PreferredSampleRate(/* aShouldResistFingerprinting */ false),
+ nullptr, GetMainThreadSerialEventTarget());
+
+ const CubebUtils::AudioDeviceID device = (CubebUtils::AudioDeviceID)1;
+
+ // Set up mock listener.
+ RefPtr<MockAudioDataListener> listener = MakeRefPtr<MockAudioDataListener>();
+ EXPECT_CALL(*listener, IsVoiceInput).WillRepeatedly(Return(true));
+ EXPECT_CALL(*listener, RequestedInputChannelCount).WillRepeatedly(Return(1));
+ EXPECT_CALL(*listener, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
+ EXPECT_CALL(*listener, Disconnect);
+
+ // Expectations.
+ const Result<cubeb_input_processing_params, int> notSupportedResult(
+ Err(CUBEB_ERROR_NOT_SUPPORTED));
+ const Result<cubeb_input_processing_params, int> errorResult(
+ Err(CUBEB_ERROR));
+ const Result<cubeb_input_processing_params, int> noneResult(
+ CUBEB_INPUT_PROCESSING_PARAM_NONE);
+ const Result<cubeb_input_processing_params, int> echoResult(
+ CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION);
+ const Result<cubeb_input_processing_params, int> noiseResult(
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION);
+ Atomic<int> numProcessingParamsResults(0);
+ {
+ InSequence s;
+ // On first driver start.
+ EXPECT_CALL(*listener,
+ NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ Eq(std::ref(echoResult))))
+ .WillOnce([&] { ++numProcessingParamsResults; });
+ // After requesting something else.
+ EXPECT_CALL(*listener,
+ NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
+ Eq(std::ref(noiseResult))))
+ .WillOnce([&] { ++numProcessingParamsResults; });
+ // After error request.
+ EXPECT_CALL(*listener,
+ NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ Eq(std::ref(errorResult))))
+ .WillOnce([&] { ++numProcessingParamsResults; });
+ // After requesting None.
+ EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ Eq(std::ref(noneResult))))
+ .WillOnce([&] { ++numProcessingParamsResults; });
+ // After driver switch.
+ EXPECT_CALL(*listener, NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ Eq(std::ref(noneResult))))
+ .WillOnce([&] { ++numProcessingParamsResults; });
+ // After requesting something not supported.
+ EXPECT_CALL(*listener,
+ NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION,
+ Eq(std::ref(noneResult))))
+ .WillOnce([&] { ++numProcessingParamsResults; });
+ // After requesting something with backend not supporting processing params.
+ EXPECT_CALL(*listener,
+ NotifySetRequestedInputProcessingParamsResult(
+ graph, CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION,
+ Eq(std::ref(notSupportedResult))))
+ .WillOnce([&] { ++numProcessingParamsResults; });
+ }
+
+ // Open a device.
+ RefPtr<TestDeviceInputConsumerTrack> track;
+ RefPtr<OnFallbackListener> fallbackListener;
+ DispatchFunction([&] {
+ track = TestDeviceInputConsumerTrack::Create(graph);
+ track->ConnectDeviceInput(device, listener, PRINCIPAL_HANDLE_NONE);
+ fallbackListener = new OnFallbackListener(track);
+ track->AddListener(fallbackListener);
+ });
+
+ RefPtr<SmartMockCubebStream> stream = WaitFor(cubeb->StreamInitEvent());
+ EXPECT_TRUE(stream->mHasInput);
+ EXPECT_TRUE(stream->mHasOutput);
+ EXPECT_EQ(stream->InputChannels(), 1U);
+ EXPECT_EQ(stream->GetInputDeviceID(), device);
+
+ while (
+ stream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ const auto waitForResult = [&](int aNumResult) {
+ while (numProcessingParamsResults < aNumResult) {
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ NS_ProcessNextEvent();
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+ };
+
+ // Wait for the AudioCallbackDriver to come into effect.
+ while (fallbackListener->OnFallback()) {
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Wait for the first result after driver creation.
+ waitForResult(1);
+
+ // Request new processing params.
+ EXPECT_CALL(*listener, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
+ waitForResult(2);
+
+ // Test with returning error on new request.
+ cubeb->SetInputProcessingApplyRv(CUBEB_ERROR);
+ EXPECT_CALL(*listener, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
+ waitForResult(3);
+
+ // Test unsetting all params.
+ cubeb->SetInputProcessingApplyRv(CUBEB_OK);
+ EXPECT_CALL(*listener, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NONE));
+ waitForResult(4);
+
+ // Switch driver.
+ EXPECT_CALL(*listener, RequestedInputChannelCount).WillRepeatedly(Return(2));
+ DispatchFunction([&] {
+ track->QueueControlMessageWithNoShutdown(
+ [&] { graph->ReevaluateInputDevice(device); });
+ });
+ ProcessEventQueue();
+ // Process the reevaluation message.
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ // Perform the switch.
+ auto initPromise = TakeN(cubeb->StreamInitEvent(), 1);
+ EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
+ std::tie(stream) = WaitFor(initPromise).unwrap()[0];
+ EXPECT_TRUE(stream->mHasInput);
+ EXPECT_TRUE(stream->mHasOutput);
+ EXPECT_EQ(stream->InputChannels(), 2U);
+ EXPECT_EQ(stream->GetInputDeviceID(), device);
+
+ while (
+ stream->State()
+ .map([](cubeb_state aState) { return aState != CUBEB_STATE_STARTED; })
+ .valueOr(true)) {
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Wait for the new AudioCallbackDriver to come into effect.
+ fallbackListener->Reset();
+ while (fallbackListener->OnFallback()) {
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ }
+
+ // Wait for the first result after driver creation.
+ waitForResult(5);
+
+ // Test requesting something not supported.
+ cubeb->SetSupportedInputProcessingParams(
+ CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION |
+ CUBEB_INPUT_PROCESSING_PARAM_AUTOMATIC_GAIN_CONTROL |
+ CUBEB_INPUT_PROCESSING_PARAM_VOICE_ISOLATION,
+ CUBEB_OK);
+ EXPECT_CALL(*listener, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_ECHO_CANCELLATION));
+ waitForResult(6);
+
+ // Test requesting something when unsupported by backend.
+ cubeb->SetSupportedInputProcessingParams(CUBEB_INPUT_PROCESSING_PARAM_NONE,
+ CUBEB_ERROR_NOT_SUPPORTED);
+ EXPECT_CALL(*listener, RequestedInputProcessingParams)
+ .WillRepeatedly(Return(CUBEB_INPUT_PROCESSING_PARAM_NOISE_SUPPRESSION));
+ waitForResult(7);
+
+ // Clean up.
+ DispatchFunction([&] {
+ track->RemoveListener(fallbackListener);
+ track->Destroy();
+ });
+ ProcessEventQueue();
+ // Process the destroy message.
+ EXPECT_EQ(stream->ManualDataCallback(0),
+ MockCubebStream::KeepProcessing::Yes);
+ // Shut down.
+ EXPECT_EQ(stream->ManualDataCallback(0), MockCubebStream::KeepProcessing::No);
+ RefPtr<SmartMockCubebStream> destroyedStream =
+ WaitFor(cubeb->StreamDestroyEvent());
+ EXPECT_EQ(destroyedStream.get(), stream.get());
+ {
+ NativeInputTrack* native = graph->GetNativeInputTrackMainThread();
+ ASSERT_TRUE(!native);
+ }
+}
+
#undef Invoke
#undef DispatchFunction
#undef DispatchMethod
diff --git a/dom/media/gtest/TestCDMStorage.cpp b/dom/media/gtest/TestCDMStorage.cpp
index d6249cc95f..57bbbd9298 100644
--- a/dom/media/gtest/TestCDMStorage.cpp
+++ b/dom/media/gtest/TestCDMStorage.cpp
@@ -58,7 +58,7 @@ template <typename T>
static nsresult EnumerateCDMStorageDir(const nsACString& aDir, T&& aDirIter) {
RefPtr<GeckoMediaPluginServiceParent> service =
GeckoMediaPluginServiceParent::GetSingleton();
- MOZ_ASSERT(service);
+ MOZ_RELEASE_ASSERT(service);
// $profileDir/gmp/$platform/
nsCOMPtr<nsIFile> path;
@@ -94,7 +94,7 @@ class GMPShutdownObserver : public nsIRunnable, public nsIObserver {
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD Run() override {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
EXPECT_TRUE(observerService);
@@ -133,7 +133,7 @@ class NotifyObserversTask : public Runnable {
explicit NotifyObserversTask(const char* aTopic)
: mozilla::Runnable("NotifyObserversTask"), mTopic(aTopic) {}
NS_IMETHOD Run() override {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
if (observerService) {
@@ -153,7 +153,7 @@ class ClearCDMStorageTask : public nsIRunnable, public nsIObserver {
NS_DECL_THREADSAFE_ISUPPORTS
NS_IMETHOD Run() override {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService();
EXPECT_TRUE(observerService);
@@ -272,7 +272,7 @@ static nsCString GetNodeId(const nsAString& aOrigin,
static bool IsCDMStorageIsEmpty() {
RefPtr<GeckoMediaPluginServiceParent> service =
GeckoMediaPluginServiceParent::GetSingleton();
- MOZ_ASSERT(service);
+ MOZ_RELEASE_ASSERT(service);
nsCOMPtr<nsIFile> storage;
nsresult rv = service->GetStorageDir(getter_AddRefs(storage));
EXPECT_NS_SUCCEEDED(rv);
@@ -286,14 +286,14 @@ static bool IsCDMStorageIsEmpty() {
static void AssertIsOnGMPThread() {
RefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService();
- MOZ_ASSERT(service);
+ MOZ_RELEASE_ASSERT(service);
nsCOMPtr<nsIThread> thread;
service->GetThread(getter_AddRefs(thread));
- MOZ_ASSERT(thread);
+ MOZ_RELEASE_ASSERT(thread);
nsCOMPtr<nsIThread> currentThread;
- DebugOnly<nsresult> rv = NS_GetCurrentThread(getter_AddRefs(currentThread));
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- MOZ_ASSERT(currentThread == thread);
+ nsresult rv = NS_GetCurrentThread(getter_AddRefs(currentThread));
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ MOZ_RELEASE_ASSERT(currentThread == thread);
}
class CDMStorageTest {
@@ -1049,8 +1049,8 @@ class CDMStorageTest {
constexpr auto data = "Just_some_arbitrary_data."_ns;
- MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE);
- MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH
+ MOZ_RELEASE_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE);
+ MOZ_RELEASE_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH
nsCString response("stored ");
response.Append(longRecordName);
diff --git a/dom/media/gtest/TestDeviceInputTrack.cpp b/dom/media/gtest/TestDeviceInputTrack.cpp
index 14b5227f9d..fca06d5e4a 100644
--- a/dom/media/gtest/TestDeviceInputTrack.cpp
+++ b/dom/media/gtest/TestDeviceInputTrack.cpp
@@ -87,7 +87,7 @@ TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) {
class TestDeviceInputConsumerTrack : public DeviceInputConsumerTrack {
public:
static TestDeviceInputConsumerTrack* Create(MediaTrackGraph* aGraph) {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
TestDeviceInputConsumerTrack* track =
new TestDeviceInputConsumerTrack(aGraph->GraphRate());
aGraph->AddTrack(track);
@@ -95,7 +95,7 @@ TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) {
}
void Destroy() {
- MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
DisconnectDeviceInput();
DeviceInputConsumerTrack::Destroy();
}
@@ -108,7 +108,7 @@ TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) {
return 0;
}
DeviceInputTrack* t = mInputs[0]->GetSource()->AsDeviceInputTrack();
- MOZ_ASSERT(t);
+ MOZ_RELEASE_ASSERT(t);
return t->NumberOfChannels();
}
@@ -122,16 +122,26 @@ TEST_F(TestDeviceInputTrack, DeviceInputConsumerTrack) {
TestAudioDataListener(uint32_t aChannelCount, bool aIsVoice)
: mChannelCount(aChannelCount), mIsVoice(aIsVoice) {}
// Graph thread APIs: AudioDataListenerInterface implementations.
- uint32_t RequestedInputChannelCount(MediaTrackGraph* aGraph) override {
+ uint32_t RequestedInputChannelCount(
+ MediaTrackGraph* aGraph) const override {
aGraph->AssertOnGraphThread();
return mChannelCount;
}
+ cubeb_input_processing_params RequestedInputProcessingParams(
+ MediaTrackGraph*) const override {
+ return CUBEB_INPUT_PROCESSING_PARAM_NONE;
+ }
bool IsVoiceInput(MediaTrackGraph* aGraph) const override {
return mIsVoice;
};
void DeviceChanged(MediaTrackGraph* aGraph) override { /* Ignored */
}
void Disconnect(MediaTrackGraph* aGraph) override{/* Ignored */};
+ void NotifySetRequestedInputProcessingParamsResult(
+ MediaTrackGraph* aGraph, cubeb_input_processing_params aRequestedParams,
+ const Result<cubeb_input_processing_params, int>& aResult) override {
+ /* Ignored */
+ }
private:
~TestAudioDataListener() = default;
diff --git a/dom/media/gtest/TestMP4Demuxer.cpp b/dom/media/gtest/TestMP4Demuxer.cpp
index 43dfdf19a4..1a1bde8035 100644
--- a/dom/media/gtest/TestMP4Demuxer.cpp
+++ b/dom/media/gtest/TestMP4Demuxer.cpp
@@ -56,7 +56,7 @@ class MP4DemuxerBinding {
}
RefPtr<GenericPromise> CheckTrackKeyFrame(MediaTrackDemuxer* aTrackDemuxer) {
- MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ MOZ_RELEASE_ASSERT(mTaskQueue->IsCurrentThreadIn());
RefPtr<MediaTrackDemuxer> track = aTrackDemuxer;
RefPtr<MP4DemuxerBinding> binding = this;
@@ -97,7 +97,7 @@ class MP4DemuxerBinding {
}
RefPtr<GenericPromise> CheckTrackSamples(MediaTrackDemuxer* aTrackDemuxer) {
- MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+ MOZ_RELEASE_ASSERT(mTaskQueue->IsCurrentThreadIn());
RefPtr<MediaTrackDemuxer> track = aTrackDemuxer;
RefPtr<MP4DemuxerBinding> binding = this;
diff --git a/dom/media/gtest/TestMediaDataEncoder.cpp b/dom/media/gtest/TestMediaDataEncoder.cpp
index 39c92fb19c..fefedcce43 100644
--- a/dom/media/gtest/TestMediaDataEncoder.cpp
+++ b/dom/media/gtest/TestMediaDataEncoder.cpp
@@ -193,7 +193,7 @@ static already_AddRefed<MediaDataEncoder> CreateH264Encoder(
}
void WaitForShutdown(const RefPtr<MediaDataEncoder>& aEncoder) {
- MOZ_ASSERT(aEncoder);
+ MOZ_RELEASE_ASSERT(aEncoder);
Maybe<bool> result;
// media::Await() supports exclusive promises only, but ShutdownPromise is
diff --git a/dom/media/gtest/TestWebMWriter.cpp b/dom/media/gtest/TestWebMWriter.cpp
index 837ee6a2c6..5384fd7c99 100644
--- a/dom/media/gtest/TestWebMWriter.cpp
+++ b/dom/media/gtest/TestWebMWriter.cpp
@@ -223,7 +223,7 @@ struct WebMioData {
};
static int webm_read(void* aBuffer, size_t aLength, void* aUserData) {
- NS_ASSERTION(aUserData, "aUserData must point to a valid WebMioData");
+ MOZ_RELEASE_ASSERT(aUserData, "aUserData must point to a valid WebMioData");
WebMioData* ioData = static_cast<WebMioData*>(aUserData);
// Check the read length.
@@ -247,7 +247,7 @@ static int webm_read(void* aBuffer, size_t aLength, void* aUserData) {
}
static int webm_seek(int64_t aOffset, int aWhence, void* aUserData) {
- NS_ASSERTION(aUserData, "aUserData must point to a valid WebMioData");
+ MOZ_RELEASE_ASSERT(aUserData, "aUserData must point to a valid WebMioData");
WebMioData* ioData = static_cast<WebMioData*>(aUserData);
if (Abs(aOffset) > ioData->data.Length()) {
@@ -281,7 +281,7 @@ static int webm_seek(int64_t aOffset, int aWhence, void* aUserData) {
}
static int64_t webm_tell(void* aUserData) {
- NS_ASSERTION(aUserData, "aUserData must point to a valid WebMioData");
+ MOZ_RELEASE_ASSERT(aUserData, "aUserData must point to a valid WebMioData");
WebMioData* ioData = static_cast<WebMioData*>(aUserData);
return ioData->offset.isValid() ? ioData->offset.value() : -1;
}