blob: 9e6475c2042f92611748cba73bfc71d80de2c241 [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/run_loop.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/task_environment.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "media/base/media_util.h"
#include "media/base/mock_filters.h"
#include "media/filters/decoder_stream.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::base::test::RunCallback;
using ::base::test::RunOnceCallback;
using testing::_;
using testing::AnyNumber;
using testing::DoAll;
using testing::Invoke;
using testing::Return;
using testing::SaveArg;
namespace media {
MATCHER(IsRegularDecoderBuffer, "") {
return !arg->end_of_stream();
}
MATCHER(IsEOSDecoderBuffer, "") {
return arg->end_of_stream();
}
static void OnAudioDecoderStreamInitialized(base::OnceClosure closure,
bool success) {
ASSERT_TRUE(success);
std::move(closure).Run();
}
class AudioDecoderStreamTest : public testing::Test {
public:
AudioDecoderStreamTest()
: audio_decoder_stream_(
std::make_unique<AudioDecoderStream::StreamTraits>(
&media_log_,
CHANNEL_LAYOUT_STEREO),
task_environment_.GetMainThreadTaskRunner(),
base::BindRepeating(&AudioDecoderStreamTest::CreateMockAudioDecoder,
base::Unretained(this)),
&media_log_) {
// Any valid config will do.
demuxer_stream_.set_audio_decoder_config(
{kCodecAAC, kSampleFormatS16, CHANNEL_LAYOUT_STEREO, 44100, {}, {}});
EXPECT_CALL(demuxer_stream_, SupportsConfigChanges())
.WillRepeatedly(Return(true));
base::RunLoop run_loop;
audio_decoder_stream_.Initialize(
&demuxer_stream_,
base::BindOnce(&OnAudioDecoderStreamInitialized,
run_loop.QuitClosure()),
nullptr, base::DoNothing(), base::DoNothing());
run_loop.Run();
}
MockDemuxerStream* demuxer_stream() { return &demuxer_stream_; }
MockAudioDecoder* decoder() { return decoder_; }
void ReadAudioBuffer(base::OnceClosure closure) {
audio_decoder_stream_.Read(
base::BindOnce(&AudioDecoderStreamTest::OnAudioBufferReadDone,
base::Unretained(this), std::move(closure)));
}
void ProduceDecoderOutput(scoped_refptr<DecoderBuffer> buffer,
AudioDecoder::DecodeCB decode_cb) {
// Make sure successive AudioBuffers have increasing timestamps.
last_timestamp_ += base::TimeDelta::FromMilliseconds(27);
const auto& config = demuxer_stream_.audio_decoder_config();
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
decoder_output_cb_,
AudioBuffer::CreateEmptyBuffer(
config.channel_layout(), config.channels(),
config.samples_per_second(), 1221, last_timestamp_)));
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(decode_cb), DecodeStatus::OK));
}
void RunUntilIdle() { task_environment_.RunUntilIdle(); }
private:
std::vector<std::unique_ptr<AudioDecoder>> CreateMockAudioDecoder() {
auto decoder = std::make_unique<MockAudioDecoder>();
EXPECT_CALL(*decoder, Initialize_(_, _, _, _, _))
.Times(AnyNumber())
.WillRepeatedly(DoAll(SaveArg<3>(&decoder_output_cb_),
RunOnceCallback<2>(OkStatus())));
decoder_ = decoder.get();
std::vector<std::unique_ptr<AudioDecoder>> result;
result.push_back(std::move(decoder));
return result;
}
void OnAudioBufferReadDone(base::OnceClosure closure,
AudioDecoderStream::ReadResult result) {
std::move(closure).Run();
}
base::test::TaskEnvironment task_environment_;
NullMediaLog media_log_;
testing::NiceMock<MockDemuxerStream> demuxer_stream_{DemuxerStream::AUDIO};
AudioDecoderStream audio_decoder_stream_;
MockAudioDecoder* decoder_ = nullptr;
AudioDecoder::OutputCB decoder_output_cb_;
base::TimeDelta last_timestamp_;
DISALLOW_COPY_AND_ASSIGN(AudioDecoderStreamTest);
};
TEST_F(AudioDecoderStreamTest, FlushOnConfigChange) {
MockAudioDecoder* first_decoder = decoder();
ASSERT_NE(first_decoder, nullptr);
// Make a regular DemuxerStream::Read().
EXPECT_CALL(*demuxer_stream(), OnRead(_))
.WillOnce(RunOnceCallback<0>(DemuxerStream::kOk, new DecoderBuffer(12)));
EXPECT_CALL(*decoder(), Decode(IsRegularDecoderBuffer(), _))
.WillOnce(Invoke(this, &AudioDecoderStreamTest::ProduceDecoderOutput));
base::RunLoop run_loop0;
ReadAudioBuffer(run_loop0.QuitClosure());
run_loop0.Run();
// Make a config-change DemuxerStream::Read().
// Expect the decoder to be flushed. Upon flushing, the decoder releases
// internally buffered output.
EXPECT_CALL(*demuxer_stream(), OnRead(_))
.WillOnce(RunOnceCallback<0>(DemuxerStream::kConfigChanged, nullptr));
EXPECT_CALL(*decoder(), Decode(IsEOSDecoderBuffer(), _))
.WillOnce(Invoke(this, &AudioDecoderStreamTest::ProduceDecoderOutput));
base::RunLoop run_loop1;
ReadAudioBuffer(run_loop1.QuitClosure());
run_loop1.Run();
// Expect the decoder to be re-initialized when AudioDecoderStream finishes
// processing the last decode.
EXPECT_CALL(*decoder(), Initialize_(_, _, _, _, _));
RunUntilIdle();
}
} // namespace media