blob: 6ea235c1e90391d9d195896be7a905d9ea3c9b3c [file] [log] [blame]
// Copyright 2014 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 <vector>
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "chromecast/media/cma/backend/audio_decoder_default.h"
#include "chromecast/media/cma/backend/media_pipeline_backend_default.h"
#include "chromecast/media/cma/backend/video_decoder_default.h"
#include "chromecast/media/cma/pipeline/av_pipeline_client.h"
#include "chromecast/media/cma/pipeline/media_pipeline_impl.h"
#include "chromecast/media/cma/pipeline/video_pipeline_client.h"
#include "chromecast/media/cma/test/frame_generator_for_test.h"
#include "chromecast/media/cma/test/mock_frame_provider.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/media_util.h"
#include "media/base/video_decoder_config.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// Total number of frames generated by CodedFrameProvider.
// The first frame has config, while the last one is EOS.
const int kNumFrames = 100;
const int kFrameSize = 512;
const int kFrameDurationUs = 40 * 1000;
const int kLastFrameTimestamp = (kNumFrames - 2) * kFrameDurationUs;
} // namespace
namespace chromecast {
namespace media {
using AudioVideoTuple = ::testing::tuple<bool, bool>;
class AudioVideoPipelineImplTest
: public ::testing::TestWithParam<AudioVideoTuple> {
public:
AudioVideoPipelineImplTest() : pipeline_backend_(nullptr) {
eos_[STREAM_AUDIO] = eos_[STREAM_VIDEO] = false;
}
void Start(const base::Closure& eos_cb) {
eos_cb_ = eos_cb;
eos_[STREAM_AUDIO] = !media_pipeline_.HasAudio();
eos_[STREAM_VIDEO] = !media_pipeline_.HasVideo();
base::TimeDelta start_time = base::TimeDelta::FromMilliseconds(0);
media_pipeline_.StartPlayingFrom(start_time);
}
void Flush(const base::Closure& flush_cb) { media_pipeline_.Flush(flush_cb); }
void Stop() {
media_pipeline_.Stop();
base::MessageLoop::current()->QuitWhenIdle();
}
MediaPipelineBackendDefault* pipeline_backend() { return pipeline_backend_; }
protected:
void SetUp() override {
std::unique_ptr<MediaPipelineBackendDefault> backend =
base::WrapUnique(new MediaPipelineBackendDefault());
pipeline_backend_ = backend.get();
media_pipeline_.Initialize(kLoadTypeURL, std::move(backend));
if (::testing::get<0>(GetParam())) {
::media::AudioDecoderConfig audio_config(
::media::kCodecMP3, ::media::kSampleFormatS16,
::media::CHANNEL_LAYOUT_STEREO, 44100, ::media::EmptyExtraData(),
::media::Unencrypted());
AvPipelineClient client;
client.eos_cb = base::Bind(&AudioVideoPipelineImplTest::OnEos,
base::Unretained(this), STREAM_AUDIO);
::media::PipelineStatus status = media_pipeline_.InitializeAudio(
audio_config, client, CreateFrameProvider());
ASSERT_EQ(::media::PIPELINE_OK, status);
}
if (::testing::get<1>(GetParam())) {
std::vector<::media::VideoDecoderConfig> video_configs;
video_configs.push_back(::media::VideoDecoderConfig(
::media::kCodecH264, ::media::H264PROFILE_MAIN,
::media::PIXEL_FORMAT_I420, ::media::COLOR_SPACE_UNSPECIFIED,
gfx::Size(640, 480), gfx::Rect(0, 0, 640, 480), gfx::Size(640, 480),
::media::EmptyExtraData(), ::media::Unencrypted()));
VideoPipelineClient client;
client.av_pipeline_client.eos_cb =
base::Bind(&AudioVideoPipelineImplTest::OnEos, base::Unretained(this),
STREAM_VIDEO);
::media::PipelineStatus status = media_pipeline_.InitializeVideo(
video_configs, client, CreateFrameProvider());
ASSERT_EQ(::media::PIPELINE_OK, status);
}
}
base::MessageLoop message_loop_;
private:
enum Stream { STREAM_AUDIO, STREAM_VIDEO };
DISALLOW_COPY_AND_ASSIGN(AudioVideoPipelineImplTest);
std::unique_ptr<CodedFrameProvider> CreateFrameProvider() {
std::vector<FrameGeneratorForTest::FrameSpec> frame_specs;
frame_specs.resize(kNumFrames);
for (size_t k = 0; k < frame_specs.size() - 1; k++) {
frame_specs[k].has_config = (k == 0);
frame_specs[k].timestamp =
base::TimeDelta::FromMicroseconds(kFrameDurationUs) * k;
frame_specs[k].size = kFrameSize;
frame_specs[k].has_decrypt_config = false;
}
frame_specs[frame_specs.size() - 1].is_eos = true;
std::unique_ptr<FrameGeneratorForTest> frame_generator(
new FrameGeneratorForTest(frame_specs));
bool provider_delayed_pattern[] = {false, true};
std::unique_ptr<MockFrameProvider> frame_provider(new MockFrameProvider());
frame_provider->Configure(
std::vector<bool>(
provider_delayed_pattern,
provider_delayed_pattern + arraysize(provider_delayed_pattern)),
std::move(frame_generator));
frame_provider->SetDelayFlush(true);
return std::move(frame_provider);
}
void OnEos(Stream stream) {
eos_[stream] = true;
if (eos_[STREAM_AUDIO] && eos_[STREAM_VIDEO] && !eos_cb_.is_null())
eos_cb_.Run();
}
bool eos_[2];
base::Closure eos_cb_;
MediaPipelineImpl media_pipeline_;
MediaPipelineBackendDefault* pipeline_backend_;
};
static void VerifyPlay(AudioVideoPipelineImplTest* pipeline_test) {
// The backend must still be running.
MediaPipelineBackendDefault* backend = pipeline_test->pipeline_backend();
EXPECT_TRUE(backend->running());
// The decoders must have received a few frames.
const AudioDecoderDefault* audio_decoder = backend->audio_decoder();
const VideoDecoderDefault* video_decoder = backend->video_decoder();
ASSERT_TRUE(audio_decoder || video_decoder);
if (audio_decoder)
EXPECT_EQ(kLastFrameTimestamp, audio_decoder->last_push_pts());
if (video_decoder)
EXPECT_EQ(kLastFrameTimestamp, video_decoder->last_push_pts());
pipeline_test->Stop();
}
TEST_P(AudioVideoPipelineImplTest, Play) {
base::Closure verify_task = base::Bind(&VerifyPlay, base::Unretained(this));
message_loop_.PostTask(FROM_HERE,
base::Bind(&AudioVideoPipelineImplTest::Start,
base::Unretained(this), verify_task));
message_loop_.Run();
}
static void VerifyFlush(AudioVideoPipelineImplTest* pipeline_test) {
// The backend must have been stopped.
MediaPipelineBackendDefault* backend = pipeline_test->pipeline_backend();
EXPECT_FALSE(backend->running());
// The decoders must not have received any frame.
const AudioDecoderDefault* audio_decoder = backend->audio_decoder();
const VideoDecoderDefault* video_decoder = backend->video_decoder();
ASSERT_TRUE(audio_decoder || video_decoder);
if (audio_decoder)
EXPECT_LT(audio_decoder->last_push_pts(), 0);
if (video_decoder)
EXPECT_LT(video_decoder->last_push_pts(), 0);
pipeline_test->Stop();
}
static void VerifyNotReached() {
EXPECT_TRUE(false);
}
TEST_P(AudioVideoPipelineImplTest, Flush) {
base::Closure verify_task = base::Bind(&VerifyFlush, base::Unretained(this));
message_loop_.PostTask(
FROM_HERE,
base::Bind(&AudioVideoPipelineImplTest::Start, base::Unretained(this),
base::Bind(&VerifyNotReached)));
message_loop_.PostTask(FROM_HERE,
base::Bind(&AudioVideoPipelineImplTest::Flush,
base::Unretained(this), verify_task));
message_loop_.Run();
}
TEST_P(AudioVideoPipelineImplTest, FullCycle) {
base::Closure stop_task =
base::Bind(&AudioVideoPipelineImplTest::Stop, base::Unretained(this));
base::Closure eos_cb = base::Bind(&AudioVideoPipelineImplTest::Flush,
base::Unretained(this), stop_task);
message_loop_.PostTask(FROM_HERE,
base::Bind(&AudioVideoPipelineImplTest::Start,
base::Unretained(this), eos_cb));
message_loop_.Run();
};
// Test all three types of pipeline: audio-only, video-only, audio-video.
INSTANTIATE_TEST_CASE_P(
MediaPipelineImplTests,
AudioVideoPipelineImplTest,
::testing::Values(AudioVideoTuple(true, false), // Audio only.
AudioVideoTuple(false, true), // Video only.
AudioVideoTuple(true, true))); // Audio and Video.
} // namespace media
} // namespace chromecast