blob: 4ca2d9bab9c481fab097b298d6de00d71b7a6427 [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 "media/gpu/vaapi_video_decode_accelerator.h"
#include "base/bind.h"
#include "base/memory/ptr_util.h"
#include "base/test/scoped_task_environment.h"
#include "media/gpu/accelerated_video_decoder.h"
#include "media/gpu/vaapi/vaapi_picture.h"
#include "media/gpu/vaapi_wrapper.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::Return;
using ::testing::TestWithParam;
using ::testing::ValuesIn;
using ::testing::WithArgs;
namespace media {
namespace {
ACTION_P(RunClosure, closure) {
closure.Run();
}
constexpr VideoCodecProfile kCodecProfiles[] = {H264PROFILE_MIN, VP8PROFILE_MIN,
VP9PROFILE_MIN};
constexpr int kBitstreamId = 123;
constexpr size_t kInputSize = 256;
} // namespace
class MockAcceleratedVideoDecoder : public AcceleratedVideoDecoder {
public:
MockAcceleratedVideoDecoder() = default;
~MockAcceleratedVideoDecoder() override = default;
MOCK_METHOD2(SetStream, void(const uint8_t* ptr, size_t size));
MOCK_METHOD0(Flush, bool());
MOCK_METHOD0(Reset, void());
MOCK_METHOD0(Decode, DecodeResult());
MOCK_CONST_METHOD0(GetPicSize, gfx::Size());
MOCK_CONST_METHOD0(GetRequiredNumOfPictures, size_t());
};
class MockVaapiWrapper : public VaapiWrapper {
public:
MockVaapiWrapper() = default;
MOCK_METHOD4(
CreateSurfaces,
bool(unsigned int, const gfx::Size&, size_t, std::vector<VASurfaceID>*));
MOCK_METHOD0(DestroySurfaces, void());
private:
~MockVaapiWrapper() override = default;
};
class MockVaapiPicture : public VaapiPicture {
public:
MockVaapiPicture(const scoped_refptr<VaapiWrapper>& vaapi_wrapper,
const MakeGLContextCurrentCallback& make_context_current_cb,
const BindGLImageCallback& bind_image_cb,
int32_t picture_buffer_id,
const gfx::Size& size,
uint32_t texture_id,
uint32_t client_texture_id)
: VaapiPicture(vaapi_wrapper,
make_context_current_cb,
bind_image_cb,
picture_buffer_id,
size,
texture_id,
client_texture_id) {}
~MockVaapiPicture() override = default;
// VaapiPicture implementation.
bool Allocate(gfx::BufferFormat format) override { return true; }
bool ImportGpuMemoryBufferHandle(
gfx::BufferFormat format,
const gfx::GpuMemoryBufferHandle& gpu_memory_buffer_handle) override {
return true;
}
bool DownloadFromSurface(
const scoped_refptr<VASurface>& va_surface) override {
return true;
}
bool AllowOverlay() const override { return false; }
};
class VaapiVideoDecodeAcceleratorTest : public TestWithParam<VideoCodecProfile>,
public VideoDecodeAccelerator::Client {
public:
VaapiVideoDecodeAcceleratorTest()
: vda_(base::Bind([] { return true; }),
base::Bind([](uint32_t client_texture_id,
uint32_t texture_target,
const scoped_refptr<gl::GLImage>& image,
bool can_bind_to_sampler) { return true; })),
decoder_thread_("VaapiVideoDecodeAcceleratorTestThread"),
mock_decoder_(new MockAcceleratedVideoDecoder),
mock_vaapi_wrapper_(new MockVaapiWrapper()),
weak_ptr_factory_(this) {
decoder_thread_.Start();
// Don't want to go through a vda_->Initialize() because it binds too many
// items of the environment. Instead, just start the decoder thread.
vda_.decoder_thread_task_runner_ = decoder_thread_.task_runner();
// Plug in our |mock_decoder_| and ourselves as the |client_|.
vda_.decoder_.reset(mock_decoder_);
vda_.client_ = weak_ptr_factory_.GetWeakPtr();
vda_.vaapi_wrapper_ = mock_vaapi_wrapper_;
vda_.create_vaapi_picture_callback_ =
base::Bind(&VaapiVideoDecodeAcceleratorTest::CreateVaapiPicture,
base::Unretained(this));
vda_.state_ = VaapiVideoDecodeAccelerator::kIdle;
}
~VaapiVideoDecodeAcceleratorTest() {}
void SetUp() override {
in_shm_.reset(new base::SharedMemory);
ASSERT_TRUE(in_shm_->CreateAndMapAnonymous(kInputSize));
}
void SetVdaStateToUnitialized() {
vda_.state_ = VaapiVideoDecodeAccelerator::kUninitialized;
}
void QueueInputBuffer(const BitstreamBuffer& bitstream_buffer) {
vda_.QueueInputBuffer(bitstream_buffer);
}
void AssignPictureBuffers(const std::vector<PictureBuffer>& picture_buffers) {
vda_.AssignPictureBuffers(picture_buffers);
}
// Reset epilogue, needed to get |vda_| worker thread out of its Wait().
void ResetSequence() {
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*mock_decoder_, Reset());
EXPECT_CALL(*this, NotifyResetDone()).WillOnce(RunClosure(quit_closure));
vda_.Reset();
run_loop.Run();
}
// TODO(mcasas): Use a Mock VaapiPicture factory, https://crbug.com/784507.
MOCK_METHOD2(MockCreateVaapiPicture, void(VaapiWrapper*, const gfx::Size&));
std::unique_ptr<VaapiPicture> CreateVaapiPicture(
const scoped_refptr<VaapiWrapper>& vaapi_wrapper,
const MakeGLContextCurrentCallback& make_context_current_cb,
const BindGLImageCallback& bind_image_cb,
int32_t picture_buffer_id,
const gfx::Size& size,
uint32_t texture_id,
uint32_t client_texture_id) {
MockCreateVaapiPicture(vaapi_wrapper.get(), size);
return base::MakeUnique<MockVaapiPicture>(
vaapi_wrapper, make_context_current_cb, bind_image_cb,
picture_buffer_id, size, texture_id, client_texture_id);
}
// VideoDecodeAccelerator::Client methods.
MOCK_METHOD1(NotifyInitializationComplete, void(bool));
MOCK_METHOD5(
ProvidePictureBuffers,
void(uint32_t, VideoPixelFormat, uint32_t, const gfx::Size&, uint32_t));
MOCK_METHOD1(DismissPictureBuffer, void(int32_t));
MOCK_METHOD1(PictureReady, void(const Picture&));
MOCK_METHOD1(NotifyEndOfBitstreamBuffer, void(int32_t));
MOCK_METHOD0(NotifyFlushDone, void());
MOCK_METHOD0(NotifyResetDone, void());
MOCK_METHOD1(NotifyError, void(VideoDecodeAccelerator::Error));
base::test::ScopedTaskEnvironment scoped_task_environment_;
// The class under test and a worker thread for it.
VaapiVideoDecodeAccelerator vda_;
base::Thread decoder_thread_;
// Ownership passed to |vda_|, but we retain a pointer to it for MOCK checks.
MockAcceleratedVideoDecoder* mock_decoder_;
scoped_refptr<MockVaapiWrapper> mock_vaapi_wrapper_;
std::unique_ptr<base::SharedMemory> in_shm_;
private:
base::WeakPtrFactory<VaapiVideoDecodeAcceleratorTest> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(VaapiVideoDecodeAcceleratorTest);
};
// This test checks that QueueInputBuffer() fails when state is kUnitialized.
TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndError) {
SetVdaStateToUnitialized();
base::SharedMemoryHandle handle;
handle = base::SharedMemory::DuplicateHandle(in_shm_->handle());
BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize);
EXPECT_CALL(*this,
NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE));
QueueInputBuffer(bitstream_buffer);
}
// Verifies that Decode() returning kDecodeError ends up pinging NotifyError().
TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeError) {
base::SharedMemoryHandle handle;
handle = base::SharedMemory::DuplicateHandle(in_shm_->handle());
BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize);
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize));
EXPECT_CALL(*mock_decoder_, Decode())
.WillOnce(Return(AcceleratedVideoDecoder::kDecodeError));
EXPECT_CALL(*this, NotifyError(VaapiVideoDecodeAccelerator::PLATFORM_FAILURE))
.WillOnce(RunClosure(quit_closure));
QueueInputBuffer(bitstream_buffer);
run_loop.Run();
}
// Tests usual startup sequence: a BitstreamBuffer is enqueued for decode,
// |vda_| asks for PictureBuffers, that we provide, and then the same Decode()
// is tried again.
TEST_P(VaapiVideoDecodeAcceleratorTest,
QueueInputBufferAndAssignPictureBuffersAndDecode) {
// Try and QueueInputBuffer(), |vda_| will ping us to ProvidePictureBuffers().
const uint32_t kNumPictures = 2;
const gfx::Size kPictureSize(64, 48);
{
base::SharedMemoryHandle handle;
handle = base::SharedMemory::DuplicateHandle(in_shm_->handle());
BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize);
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize));
EXPECT_CALL(*mock_decoder_, Decode())
.WillOnce(Return(AcceleratedVideoDecoder::kAllocateNewSurfaces));
EXPECT_CALL(*mock_decoder_, GetRequiredNumOfPictures())
.WillOnce(Return(kNumPictures));
EXPECT_CALL(*mock_decoder_, GetPicSize()).WillOnce(Return(kPictureSize));
EXPECT_CALL(*mock_vaapi_wrapper_, DestroySurfaces());
EXPECT_CALL(*this,
ProvidePictureBuffers(kNumPictures, _, 1, kPictureSize, _))
.WillOnce(RunClosure(quit_closure));
QueueInputBuffer(bitstream_buffer);
run_loop.Run();
}
// AssignPictureBuffers() accordingly and expect another go at Decode().
{
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
const std::vector<PictureBuffer> kPictureBuffers(
{{2, kPictureSize}, {3, kPictureSize}});
EXPECT_EQ(kPictureBuffers.size(), kNumPictures);
EXPECT_CALL(*mock_vaapi_wrapper_,
CreateSurfaces(_, kPictureSize, kNumPictures, _))
.WillOnce(DoAll(
WithArgs<3>(Invoke([](std::vector<VASurfaceID>* va_surface_ids) {
va_surface_ids->resize(kNumPictures);
})),
Return(true)));
EXPECT_CALL(*this,
MockCreateVaapiPicture(mock_vaapi_wrapper_.get(), kPictureSize))
.Times(2);
EXPECT_CALL(*mock_decoder_, Decode())
.WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData));
EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId))
.WillOnce(RunClosure(quit_closure));
AssignPictureBuffers(kPictureBuffers);
run_loop.Run();
}
ResetSequence();
}
// Verifies that Decode() replying kRanOutOfStreamData (to signal it's finished)
// rolls to a NotifyEndOfBitstreamBuffer().
TEST_P(VaapiVideoDecodeAcceleratorTest, QueueInputBufferAndDecodeFinished) {
base::SharedMemoryHandle handle;
handle = base::SharedMemory::DuplicateHandle(in_shm_->handle());
BitstreamBuffer bitstream_buffer(kBitstreamId, handle, kInputSize);
{
base::RunLoop run_loop;
base::Closure quit_closure = run_loop.QuitClosure();
EXPECT_CALL(*mock_decoder_, SetStream(_, kInputSize));
EXPECT_CALL(*mock_decoder_, Decode())
.WillOnce(Return(AcceleratedVideoDecoder::kRanOutOfStreamData));
EXPECT_CALL(*this, NotifyEndOfBitstreamBuffer(kBitstreamId))
.WillOnce(RunClosure(quit_closure));
QueueInputBuffer(bitstream_buffer);
run_loop.Run();
}
ResetSequence();
}
INSTANTIATE_TEST_CASE_P(/* No prefix. */,
VaapiVideoDecodeAcceleratorTest,
ValuesIn(kCodecProfiles));
} // namespace media