Bug 1656438 - Create a unitest for SourceMediaTrack and AudioInputProcessing. r=padenot

Use the newly added functionality in MockCubeb to verify that the input is
forwarded to the output.

Depends on D85556

Differential Revision: https://phabricator.services.mozilla.com/D85557
This commit is contained in:
Alex Chronopoulos 2020-09-15 14:43:46 +00:00
parent 332eacb86a
commit 0d36f710e8
3 changed files with 140 additions and 0 deletions

View File

@ -4,6 +4,7 @@
#include "AudioDeviceInfo.h"
#include "AudioGenerator.h"
#include "AudioVerifier.h"
#include "MediaEventSource.h"
#include "nsTArray.h"
#include <thread>
@ -194,6 +195,10 @@ class MockCubebStream {
void ForceError() { mForceErrorState = true; }
void VerifyOutput() { mVerifyOutput = true; }
MediaEventSource<uint32_t>& FramesProcessedEvent() {
return mFramesProcessedEvent;
}
private:
// Simulates the audio thread. The thread is created at Start anda destroyed
// at Stop. At next StreamStart a new thread is created.
@ -214,6 +219,8 @@ class MockCubebStream {
mAudioVerifier.AppendDataInterleaved(mOutputBuffer, outframes,
NUM_OF_CHANNELS);
mFramesProcessedEvent.Notify(outframes);
if (outframes < NUM_OF_FRAMES) {
mStateCallback(stream, mUserPtr, CUBEB_STATE_DRAINED);
break;
@ -269,6 +276,8 @@ class MockCubebStream {
std::atomic_bool mVerifyOutput{false};
AudioGenerator<AudioDataValue> mAudioGenerator;
AudioVerifier<AudioDataValue> mAudioVerifier;
MediaEventProducer<uint32_t> mFramesProcessedEvent;
};
// This class has two facets: it is both a fake cubeb backend that is intended
@ -440,16 +449,23 @@ class MockCubeb {
aOutputStreamParams, aDataCallback, aStateCallback, aUserPtr);
*aStream = reinterpret_cast<cubeb_stream*>(mockStream);
mCurrentMockStream = mockStream;
mStreamInitEvent.Notify(mockStream);
return CUBEB_OK;
}
void StreamDestroy(cubeb_stream* aStream) {
mStreamDestroyEvent.Notify();
MockCubebStream* mockStream = reinterpret_cast<MockCubebStream*>(aStream);
delete mockStream;
}
MockCubebStream* CurrentStream() { return mCurrentMockStream; }
MediaEventSource<MockCubebStream*>& StreamInitEvent() {
return mStreamInitEvent;
}
MediaEventSource<void>& StreamDestroyEvent() { return mStreamDestroyEvent; }
private:
// This needs to have the exact same memory layout as a real cubeb backend.
// It's very important for this `ops` member to be the very first member of
@ -475,6 +491,9 @@ class MockCubeb {
// The latest cubeb stream. The init of a stream happens in a helper thread
// inside Gecko so a synchronization method is needed.
std::atomic<MockCubebStream*> mCurrentMockStream{nullptr};
MediaEventProducer<MockCubebStream*> mStreamInitEvent;
MediaEventProducer<void> mStreamDestroyEvent;
};
void cubeb_mock_destroy(cubeb* context) {

View File

@ -10,6 +10,7 @@
#include "gtest/gtest.h"
#include "GMPTestMonitor.h"
#include "MediaEngineWebRTCAudio.h"
#include "MockCubeb.h"
TEST(TestAudioTrackGraph, DifferentDeviceIDs)
@ -186,3 +187,122 @@ TEST(TestAudioTrackGraph, ErrorStateCrash)
mon.AwaitFinished();
}
TEST(TestAudioTrackGraph, SourceTrack)
{
MockCubeb* cubeb = new MockCubeb();
CubebUtils::ForceSetCubebContext(cubeb->AsCubebContext());
MediaTrackGraph* primary = MediaTrackGraph::GetInstance(
MediaTrackGraph::AUDIO_THREAD_DRIVER, /*window*/ nullptr,
MediaTrackGraph::REQUEST_DEFAULT_SAMPLE_RATE, nullptr);
RefPtr<SourceMediaTrack> sourceTrack =
primary->CreateSourceTrack(MediaSegment::AUDIO);
RefPtr<ProcessedMediaTrack> outputTrack =
primary->CreateForwardedInputTrack(MediaSegment::AUDIO);
/* How the source track connects to another ProcessedMediaTrack.
* Check in MediaManager how it is connected to AudioStreamTrack. */
RefPtr<MediaInputPort> port = outputTrack->AllocateInputPort(sourceTrack);
outputTrack->AddAudioOutput(reinterpret_cast<void*>(1));
{
// Wait for the output-only stream to be created.
bool done = false;
MediaEventListener onStreamInit = cubeb->StreamInitEvent().Connect(
AbstractThread::GetCurrent(), [&] { done = true; });
SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
[&] { return done; });
onStreamInit.Disconnect();
}
/* Primary graph: Open Audio Input through SourceMediaTrack */
RefPtr<AudioInputProcessing> listener =
new AudioInputProcessing(2, sourceTrack, PRINCIPAL_HANDLE_NONE);
listener->SetPassThrough(true);
RefPtr<AudioInputProcessingPullListener> pullListener =
new AudioInputProcessingPullListener(listener);
sourceTrack->AddListener(pullListener);
class StartStopListener : public ControlMessage {
public:
enum StartStop { Start, Stop };
StartStopListener(AudioInputProcessing* aInputProcessing, StartStop aAction)
: ControlMessage(nullptr),
mInputProcessing(aInputProcessing),
mAction(aAction) {}
void Run() override {
if (mAction == StartStopListener::Start) {
mInputProcessing->Start();
} else if (mAction == StartStopListener::Stop) {
mInputProcessing->Stop();
} else {
MOZ_CRASH("Invalid enum value");
}
}
protected:
RefPtr<AudioInputProcessing> mInputProcessing;
StartStop mAction;
};
sourceTrack->GraphImpl()->AppendMessage(
MakeUnique<StartStopListener>(listener, StartStopListener::Start));
sourceTrack->SetPullingEnabled(true);
// Device id does not matter. Ignore.
sourceTrack->OpenAudioInput((void*)1, listener);
MockCubebStream* stream = nullptr;
{
// Wait for the full-duplex stream to be created.
MediaEventListener onStreamInit = cubeb->StreamInitEvent().Connect(
AbstractThread::GetCurrent(),
[&](MockCubebStream* aStream) { stream = aStream; });
SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
[&] { return !!stream; });
onStreamInit.Disconnect();
}
{
// Wait for a second worth of audio data.
uint32_t totalFrames = 0;
MediaEventListener onFrames = stream->FramesProcessedEvent().Connect(
AbstractThread::GetCurrent(),
[&](uint32_t aFrames) { totalFrames += aFrames; });
stream->VerifyOutput();
SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>([&] {
return totalFrames > static_cast<uint32_t>(primary->GraphRate());
});
onFrames.Disconnect();
}
{
// Wait for the full-duplex stream to be destroyed.
bool done = false;
MediaEventListener onStreamDestroy = cubeb->StreamDestroyEvent().Connect(
AbstractThread::GetCurrent(), [&] { done = true; });
// Clean up on MainThread
outputTrack->RemoveAudioOutput((void*)1);
outputTrack->Destroy();
port->Destroy();
sourceTrack->GraphImpl()->AppendMessage(
MakeUnique<StartStopListener>(listener, StartStopListener::Stop));
sourceTrack->RemoveListener(pullListener);
sourceTrack->SetPullingEnabled(false);
Maybe<CubebUtils::AudioDeviceID> id =
Some(reinterpret_cast<CubebUtils::AudioDeviceID>(1));
sourceTrack->CloseAudioInput(id);
sourceTrack->Destroy();
SpinEventLoopUntil<ProcessFailureBehavior::IgnoreAndContinue>(
[&] { return done; });
onStreamDestroy.Disconnect();
}
}

View File

@ -112,6 +112,7 @@ LOCAL_INCLUDES += [
'/dom/media/mp4',
'/dom/media/platforms',
'/dom/media/platforms/agnostic',
'/dom/media/webrtc',
'/security/certverifier',
]