mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-19 16:25:38 +00:00
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:
parent
332eacb86a
commit
0d36f710e8
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -112,6 +112,7 @@ LOCAL_INCLUDES += [
|
||||
'/dom/media/mp4',
|
||||
'/dom/media/platforms',
|
||||
'/dom/media/platforms/agnostic',
|
||||
'/dom/media/webrtc',
|
||||
'/security/certverifier',
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user