diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:37 +0000 |
commit | a90a5cba08fdf6c0ceb95101c275108a152a3aed (patch) | |
tree | 532507288f3defd7f4dcf1af49698bcb76034855 /dom/media/gtest | |
parent | Adding debian version 126.0.1-1. (diff) | |
download | firefox-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.h | 4 | ||||
-rw-r--r-- | dom/media/gtest/GMPTestMonitor.h | 4 | ||||
-rw-r--r-- | dom/media/gtest/MockCubeb.cpp | 301 | ||||
-rw-r--r-- | dom/media/gtest/MockCubeb.h | 184 | ||||
-rw-r--r-- | dom/media/gtest/TestAudioCallbackDriver.cpp | 457 | ||||
-rw-r--r-- | dom/media/gtest/TestAudioInputProcessing.cpp | 175 | ||||
-rw-r--r-- | dom/media/gtest/TestAudioInputSource.cpp | 114 | ||||
-rw-r--r-- | dom/media/gtest/TestAudioRingBuffer.cpp | 50 | ||||
-rw-r--r-- | dom/media/gtest/TestAudioTrackGraph.cpp | 740 | ||||
-rw-r--r-- | dom/media/gtest/TestCDMStorage.cpp | 24 | ||||
-rw-r--r-- | dom/media/gtest/TestDeviceInputTrack.cpp | 18 | ||||
-rw-r--r-- | dom/media/gtest/TestMP4Demuxer.cpp | 4 | ||||
-rw-r--r-- | dom/media/gtest/TestMediaDataEncoder.cpp | 2 | ||||
-rw-r--r-- | dom/media/gtest/TestWebMWriter.cpp | 6 |
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; } |