blob: daefd12b009354a376991f8114e2515c56a4d9ee [file] [log] [blame]
// Copyright 2017 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 <stdint.h>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback_forward.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/memory/scoped_refptr.h"
#include "base/test/mock_callback.h"
#include "base/test/scoped_task_environment.h"
#include "base/time/time.h"
#include "gpu/command_buffer/common/mailbox_holder.h"
#include "media/base/decode_status.h"
#include "media/base/decoder_buffer.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/mock_media_log.h"
#include "media/base/test_helpers.h"
#include "media/base/video_decoder.h"
#include "media/base/video_frame.h"
#include "media/mojo/clients/mojo_video_decoder.h"
#include "media/mojo/services/mojo_cdm_service_context.h"
#include "media/mojo/services/mojo_media_client.h"
#include "media/mojo/services/mojo_video_decoder_service.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::Invoke;
using ::testing::Mock;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::StrictMock;
namespace media {
namespace {
// A mock VideoDecoder with helpful default functionality.
// TODO(sandersd): Determine how best to merge this with MockVideoDecoder
// declared in mock_filters.h.
class MockVideoDecoder : public VideoDecoder {
public:
MockVideoDecoder() {
// Treat const getters like a NiceMock.
EXPECT_CALL(*this, GetDisplayName())
.WillRepeatedly(Return("MockVideoDecoder"));
EXPECT_CALL(*this, NeedsBitstreamConversion())
.WillRepeatedly(Return(false));
EXPECT_CALL(*this, CanReadWithoutStalling()).WillRepeatedly(Return(true));
EXPECT_CALL(*this, GetMaxDecodeRequests()).WillRepeatedly(Return(1));
// For regular methods, only configure a default action.
ON_CALL(*this, DoInitialize(_)).WillByDefault(RunCallback<0>(true));
ON_CALL(*this, Decode(_, _))
.WillByDefault(Invoke(this, &MockVideoDecoder::DoDecode));
ON_CALL(*this, Reset(_)).WillByDefault(RunCallback<0>());
}
// Re-declare as public.
~MockVideoDecoder() override {}
// media::VideoDecoder implementation
MOCK_CONST_METHOD0(GetDisplayName, std::string());
// Initialize() records values before delegating to the mock method.
void Initialize(const VideoDecoderConfig& config,
bool low_delay,
CdmContext* cdm_context,
const InitCB& init_cb,
const OutputCB& output_cb) override {
config_ = config;
output_cb_ = output_cb;
DoInitialize(init_cb);
}
MOCK_METHOD2(Decode,
void(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB&));
MOCK_METHOD1(Reset, void(const base::Closure&));
MOCK_CONST_METHOD0(NeedsBitstreamConversion, bool());
MOCK_CONST_METHOD0(CanReadWithoutStalling, bool());
MOCK_CONST_METHOD0(GetMaxDecodeRequests, int());
// Mock helpers.
MOCK_METHOD1(DoInitialize, void(const InitCB&));
// Returns an output frame immediately.
// TODO(sandersd): Extend to support tests of MojoVideoFrame frames.
void DoDecode(const scoped_refptr<DecoderBuffer>& buffer,
const DecodeCB& decode_cb) {
if (!buffer->end_of_stream()) {
gpu::MailboxHolder mailbox_holders[VideoFrame::kMaxPlanes];
mailbox_holders[0].mailbox.name[0] = 1;
output_cb_.Run(VideoFrame::WrapNativeTextures(
PIXEL_FORMAT_ARGB, mailbox_holders,
// TODO(sandersd): Hook release callbacks.
VideoFrame::ReleaseMailboxCB(), config_.coded_size(),
config_.visible_rect(), config_.natural_size(), buffer->timestamp()));
}
// |decode_cb| must not be called from the same stack.
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::Bind(decode_cb, DecodeStatus::OK));
}
private:
// Destructing a std::unique_ptr<VideoDecoder>(this) is a no-op.
// TODO(sandersd): After this, any method call is an error. Implement checks
// for that.
void Destroy() override { DVLOG(1) << __func__ << "(): Ignored"; }
VideoDecoderConfig config_;
OutputCB output_cb_;
DISALLOW_COPY_AND_ASSIGN(MockVideoDecoder);
};
// Proxies CreateVideoDecoder() to a callback.
class FakeMojoMediaClient : public MojoMediaClient {
public:
using CreateVideoDecoderCB =
base::Callback<std::unique_ptr<VideoDecoder>(MediaLog*)>;
FakeMojoMediaClient(CreateVideoDecoderCB create_video_decoder_cb)
: create_video_decoder_cb_(std::move(create_video_decoder_cb)) {}
std::unique_ptr<VideoDecoder> CreateVideoDecoder(
scoped_refptr<base::SingleThreadTaskRunner> task_runner,
MediaLog* media_log,
mojom::CommandBufferIdPtr command_buffer_id,
OutputWithReleaseMailboxCB output_cb,
RequestOverlayInfoCB request_overlay_info_cb) override {
return create_video_decoder_cb_.Run(media_log);
}
private:
CreateVideoDecoderCB create_video_decoder_cb_;
DISALLOW_COPY_AND_ASSIGN(FakeMojoMediaClient);
};
} // namespace
class MojoVideoDecoderIntegrationTest : public ::testing::Test {
public:
MojoVideoDecoderIntegrationTest()
: mojo_media_client_(
base::Bind(&MojoVideoDecoderIntegrationTest::CreateVideoDecoder,
base::Unretained(this))) {
mojom::VideoDecoderPtr remote_video_decoder;
binding_ = mojo::MakeStrongBinding(
std::make_unique<MojoVideoDecoderService>(&mojo_media_client_,
&mojo_cdm_service_context_),
mojo::MakeRequest(&remote_video_decoder));
client_ = std::make_unique<MojoVideoDecoder>(
base::ThreadTaskRunnerHandle::Get(), nullptr, &client_media_log_,
std::move(remote_video_decoder), RequestOverlayInfoCB());
}
void TearDown() override {
if (client_) {
client_.reset();
RunUntilIdle();
}
}
protected:
void RunUntilIdle() { scoped_task_environment_.RunUntilIdle(); }
bool Initialize() {
bool result = false;
EXPECT_CALL(*decoder_, DoInitialize(_));
StrictMock<base::MockCallback<VideoDecoder::InitCB>> init_cb;
EXPECT_CALL(init_cb, Run(_)).WillOnce(SaveArg<0>(&result));
client_->Initialize(TestVideoConfig::Normal(), false, nullptr,
init_cb.Get(), output_cb_.Get());
RunUntilIdle();
return result;
}
DecodeStatus Decode(const scoped_refptr<DecoderBuffer>& buffer) {
DecodeStatus result = DecodeStatus::DECODE_ERROR;
EXPECT_CALL(*decoder_, Decode(_, _));
StrictMock<base::MockCallback<VideoDecoder::DecodeCB>> decode_cb;
EXPECT_CALL(decode_cb, Run(_)).WillOnce(SaveArg<0>(&result));
client_->Decode(buffer, decode_cb.Get());
RunUntilIdle();
return result;
}
scoped_refptr<DecoderBuffer> CreateKeyframe(int64_t timestamp_ms) {
std::vector<uint8_t> data = {1, 2, 3, 4};
scoped_refptr<DecoderBuffer> buffer =
DecoderBuffer::CopyFrom(data.data(), data.size());
buffer->set_timestamp(base::TimeDelta::FromMilliseconds(timestamp_ms));
buffer->set_duration(base::TimeDelta::FromMilliseconds(10));
buffer->set_is_key_frame(true);
return buffer;
}
// Callback that |client_| will deliver VideoFrames to.
StrictMock<base::MockCallback<VideoDecoder::OutputCB>> output_cb_;
// MojoVideoDecoder (client) under test.
std::unique_ptr<MojoVideoDecoder> client_;
// MediaLog that |client_| will deliver log events to.
StrictMock<MockMediaLog> client_media_log_;
// VideoDecoder (impl used by service) under test.
std::unique_ptr<MockVideoDecoder> decoder_ =
std::make_unique<StrictMock<MockVideoDecoder>>();
// MediaLog that the service has provided to |decoder_|. This should be
// proxied to |client_media_log_|.
MediaLog* decoder_media_log_ = nullptr;
private:
// Passes |decoder_| to the service.
std::unique_ptr<VideoDecoder> CreateVideoDecoder(MediaLog* media_log) {
DCHECK(!decoder_media_log_);
decoder_media_log_ = media_log;
// Since MockVideoDecoder::Destroy() is a no-op, this doesn't actually
// transfer ownership.
return std::unique_ptr<VideoDecoder>(decoder_.get());
}
base::test::ScopedTaskEnvironment scoped_task_environment_;
MojoCdmServiceContext mojo_cdm_service_context_;
// Provides |decoder_| to the service.
FakeMojoMediaClient mojo_media_client_;
// Owns the service, bound to |client_|.
mojo::StrongBindingPtr<mojom::VideoDecoder> binding_;
DISALLOW_COPY_AND_ASSIGN(MojoVideoDecoderIntegrationTest);
};
TEST_F(MojoVideoDecoderIntegrationTest, CreateAndDestroy) {}
TEST_F(MojoVideoDecoderIntegrationTest, Initialize) {
ASSERT_TRUE(Initialize());
EXPECT_EQ(client_->GetDisplayName(), "MojoVideoDecoder");
EXPECT_EQ(client_->NeedsBitstreamConversion(), false);
EXPECT_EQ(client_->CanReadWithoutStalling(), true);
EXPECT_EQ(client_->GetMaxDecodeRequests(), 1);
}
TEST_F(MojoVideoDecoderIntegrationTest, Decode) {
ASSERT_TRUE(Initialize());
EXPECT_CALL(output_cb_, Run(_));
ASSERT_EQ(Decode(CreateKeyframe(0)), DecodeStatus::OK);
Mock::VerifyAndClearExpectations(&output_cb_);
ASSERT_EQ(Decode(DecoderBuffer::CreateEOSBuffer()), DecodeStatus::OK);
}
} // namespace media