blob: ab0a4a7b6694c78b5607129706b8ac926f9969e5 [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 <stddef.h>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/memory/ptr_util.h"
#include "base/run_loop.h"
#include "base/test/task_environment.h"
#include "gpu/config/gpu_driver_bug_workarounds.h"
#include "gpu/config/gpu_preferences.h"
#include "media/mojo/mojom/video_encode_accelerator.mojom.h"
#include "media/mojo/services/mojo_video_encode_accelerator_service.h"
#include "media/video/fake_video_encode_accelerator.h"
#include "media/video/video_encode_accelerator.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace gpu {
struct GpuPreferences;
} // namespace gpu
using ::testing::_;
namespace media {
static const gfx::Size kInputVisibleSize(64, 48);
std::unique_ptr<VideoEncodeAccelerator> CreateAndInitializeFakeVEA(
bool will_initialization_succeed,
const VideoEncodeAccelerator::Config& config,
VideoEncodeAccelerator::Client* client,
const gpu::GpuPreferences& gpu_preferences,
const gpu::GpuDriverBugWorkarounds& gpu_workarounds) {
// Use FakeVEA as scoped_ptr to guarantee proper destruction via Destroy().
auto vea = std::make_unique<FakeVideoEncodeAccelerator>(
base::ThreadTaskRunnerHandle::Get());
vea->SetWillInitializationSucceed(will_initialization_succeed);
const bool result = vea->Initialize(config, client);
// Mimic the behaviour of GpuVideoEncodeAcceleratorFactory::CreateVEA().
return result ? base::WrapUnique<VideoEncodeAccelerator>(vea.release())
: nullptr;
}
class MockMojoVideoEncodeAcceleratorClient
: public mojom::VideoEncodeAcceleratorClient {
public:
MockMojoVideoEncodeAcceleratorClient() = default;
MOCK_METHOD3(RequireBitstreamBuffers,
void(uint32_t, const gfx::Size&, uint32_t));
MOCK_METHOD2(BitstreamBufferReady,
void(int32_t, const media::BitstreamBufferMetadata&));
MOCK_METHOD1(NotifyError, void(VideoEncodeAccelerator::Error));
MOCK_METHOD1(NotifyEncoderInfoChange, void(const VideoEncoderInfo& info));
private:
DISALLOW_COPY_AND_ASSIGN(MockMojoVideoEncodeAcceleratorClient);
};
// Test harness for a MojoVideoEncodeAcceleratorService; the tests manipulate it
// via its MojoVideoEncodeAcceleratorService interface while observing a
// "remote" mojo::VideoEncodeAcceleratorClient (that we keep inside a Mojo
// binding). The class under test uses a FakeVideoEncodeAccelerator as
// implementation.
class MojoVideoEncodeAcceleratorServiceTest : public ::testing::Test {
public:
MojoVideoEncodeAcceleratorServiceTest() = default;
void TearDown() override {
// The destruction of a mojo::SelfOwnedReceiver closes the bound message
// pipe but does not destroy the implementation object: needs to happen
// manually, otherwise we leak it. This only applies if BindAndInitialize()
// has been called.
if (mojo_vea_receiver_)
mojo_vea_receiver_->Close();
}
// Creates the class under test, configuring the underlying FakeVEA to succeed
// upon initialization (by default) or not.
void CreateMojoVideoEncodeAccelerator(
bool will_fake_vea_initialization_succeed = true) {
mojo_vea_service_ = std::make_unique<MojoVideoEncodeAcceleratorService>(
base::BindRepeating(&CreateAndInitializeFakeVEA,
will_fake_vea_initialization_succeed),
gpu::GpuPreferences(), gpu::GpuDriverBugWorkarounds());
}
void BindAndInitialize() {
// Create an Mojo VEA Client remote and bind it to our Mock.
mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> mojo_vea_client;
mojo_vea_receiver_ = mojo::MakeSelfOwnedReceiver(
std::make_unique<MockMojoVideoEncodeAcceleratorClient>(),
mojo_vea_client.InitWithNewPipeAndPassReceiver());
EXPECT_CALL(*mock_mojo_vea_client(),
RequireBitstreamBuffers(_, kInputVisibleSize, _));
constexpr media::Bitrate kInitialBitrate =
media::Bitrate::ConstantBitrate(100000u);
const media::VideoEncodeAccelerator::Config config(
PIXEL_FORMAT_I420, kInputVisibleSize, H264PROFILE_MIN, kInitialBitrate);
mojo_vea_service()->Initialize(
config, std::move(mojo_vea_client),
base::BindOnce([](bool success) { ASSERT_TRUE(success); }));
base::RunLoop().RunUntilIdle();
}
MojoVideoEncodeAcceleratorService* mojo_vea_service() {
return mojo_vea_service_.get();
}
MockMojoVideoEncodeAcceleratorClient* mock_mojo_vea_client() const {
return static_cast<media::MockMojoVideoEncodeAcceleratorClient*>(
mojo_vea_receiver_->impl());
}
FakeVideoEncodeAccelerator* fake_vea() const {
return static_cast<FakeVideoEncodeAccelerator*>(
mojo_vea_service_->encoder_.get());
}
private:
base::test::SingleThreadTaskEnvironment task_environment_;
mojo::SelfOwnedReceiverRef<mojom::VideoEncodeAcceleratorClient>
mojo_vea_receiver_;
// The class under test.
std::unique_ptr<MojoVideoEncodeAcceleratorService> mojo_vea_service_;
DISALLOW_COPY_AND_ASSIGN(MojoVideoEncodeAcceleratorServiceTest);
};
// This test verifies the BindAndInitialize() communication prologue in
// isolation.
TEST_F(MojoVideoEncodeAcceleratorServiceTest,
InitializeAndRequireBistreamBuffers) {
CreateMojoVideoEncodeAccelerator();
BindAndInitialize();
}
// This test verifies the BindAndInitialize() communication prologue followed by
// a sharing of a single bitstream buffer and the Encode() of one frame.
TEST_F(MojoVideoEncodeAcceleratorServiceTest, EncodeOneFrame) {
CreateMojoVideoEncodeAccelerator();
BindAndInitialize();
const int32_t kBitstreamBufferId = 17;
{
const uint64_t kShMemSize = fake_vea()->minimum_output_buffer_size();
auto handle = mojo::SharedBufferHandle::Create(kShMemSize);
mojo_vea_service()->UseOutputBitstreamBuffer(kBitstreamBufferId,
std::move(handle));
base::RunLoop().RunUntilIdle();
}
{
const auto video_frame = VideoFrame::CreateBlackFrame(kInputVisibleSize);
EXPECT_CALL(*mock_mojo_vea_client(),
BitstreamBufferReady(kBitstreamBufferId, _));
mojo_vea_service()->Encode(video_frame, true /* is_keyframe */,
base::DoNothing());
base::RunLoop().RunUntilIdle();
}
}
// Tests that a RequestEncodingParametersChange() ripples through correctly.
TEST_F(MojoVideoEncodeAcceleratorServiceTest, EncodingParametersChange) {
CreateMojoVideoEncodeAccelerator();
BindAndInitialize();
const uint32_t kNewBitrate = 123123u;
const uint32_t kNewFramerate = 321321u;
VideoBitrateAllocation bitrate_allocation;
bitrate_allocation.SetBitrate(0, 0, kNewBitrate);
mojo_vea_service()->RequestEncodingParametersChangeWithLayers(
bitrate_allocation, kNewFramerate);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_vea());
VideoBitrateAllocation expected_allocation;
expected_allocation.SetBitrate(0, 0, kNewBitrate);
EXPECT_EQ(expected_allocation,
fake_vea()->stored_bitrate_allocations().back());
}
// Tests that a RequestEncodingParametersChange() ripples through correctly.
TEST_F(MojoVideoEncodeAcceleratorServiceTest,
EncodingParametersWithBitrateAllocation) {
CreateMojoVideoEncodeAccelerator();
BindAndInitialize();
const uint32_t kNewFramerate = 321321u;
const size_t kMaxNumBitrates = VideoBitrateAllocation::kMaxSpatialLayers *
VideoBitrateAllocation::kMaxTemporalLayers;
// Verify translation of VideoBitrateAllocation into vector of bitrates for
// everything from empty array up to max number of layers.
VideoBitrateAllocation bitrate_allocation;
for (size_t i = 0; i <= kMaxNumBitrates; ++i) {
if (i > 0) {
int layer_bitrate = i * 1000;
const size_t si = (i - 1) / VideoBitrateAllocation::kMaxTemporalLayers;
const size_t ti = (i - 1) % VideoBitrateAllocation::kMaxTemporalLayers;
bitrate_allocation.SetBitrate(si, ti, layer_bitrate);
}
mojo_vea_service()->RequestEncodingParametersChangeWithLayers(
bitrate_allocation, kNewFramerate);
base::RunLoop().RunUntilIdle();
ASSERT_TRUE(fake_vea());
EXPECT_EQ(bitrate_allocation,
fake_vea()->stored_bitrate_allocations().back());
}
}
// This test verifies that MojoVEA::Initialize() fails with an invalid |client|.
TEST_F(MojoVideoEncodeAcceleratorServiceTest,
InitializeWithInvalidClientFails) {
CreateMojoVideoEncodeAccelerator();
mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient>
invalid_mojo_vea_client;
constexpr media::Bitrate kInitialBitrate =
media::Bitrate::ConstantBitrate(100000u);
const media::VideoEncodeAccelerator::Config config(
PIXEL_FORMAT_I420, kInputVisibleSize, H264PROFILE_MIN, kInitialBitrate);
mojo_vea_service()->Initialize(
config, std::move(invalid_mojo_vea_client),
base::BindOnce([](bool success) { ASSERT_FALSE(success); }));
base::RunLoop().RunUntilIdle();
}
// This test verifies that when FakeVEA is configured to fail upon start,
// MojoVEA::Initialize() causes a NotifyError().
TEST_F(MojoVideoEncodeAcceleratorServiceTest, InitializeFailure) {
CreateMojoVideoEncodeAccelerator(
false /* will_fake_vea_initialization_succeed */);
mojo::PendingRemote<mojom::VideoEncodeAcceleratorClient> mojo_vea_client;
auto mojo_vea_receiver = mojo::MakeSelfOwnedReceiver(
std::make_unique<MockMojoVideoEncodeAcceleratorClient>(),
mojo_vea_client.InitWithNewPipeAndPassReceiver());
constexpr media::Bitrate kInitialBitrate =
media::Bitrate::ConstantBitrate(100000u);
const media::VideoEncodeAccelerator::Config config(
PIXEL_FORMAT_I420, kInputVisibleSize, H264PROFILE_MIN, kInitialBitrate);
mojo_vea_service()->Initialize(
config, std::move(mojo_vea_client),
base::BindOnce([](bool success) { ASSERT_FALSE(success); }));
base::RunLoop().RunUntilIdle();
mojo_vea_receiver->Close();
}
// This test verifies that UseOutputBitstreamBuffer() with a wrong ShMem size
// causes NotifyError().
TEST_F(MojoVideoEncodeAcceleratorServiceTest,
UseOutputBitstreamBufferWithWrongSizeFails) {
CreateMojoVideoEncodeAccelerator();
BindAndInitialize();
const int32_t kBitstreamBufferId = 17;
const uint64_t wrong_size = fake_vea()->minimum_output_buffer_size() / 2;
auto handle = mojo::SharedBufferHandle::Create(wrong_size);
EXPECT_CALL(*mock_mojo_vea_client(),
NotifyError(VideoEncodeAccelerator::kInvalidArgumentError));
mojo_vea_service()->UseOutputBitstreamBuffer(kBitstreamBufferId,
std::move(handle));
base::RunLoop().RunUntilIdle();
}
// This test verifies that Encode() with wrong coded size causes NotifyError().
TEST_F(MojoVideoEncodeAcceleratorServiceTest, EncodeWithWrongSizeFails) {
CreateMojoVideoEncodeAccelerator();
BindAndInitialize();
// We should send a UseOutputBitstreamBuffer() first but in unit tests we can
// skip that prologue.
const gfx::Size wrong_size(kInputVisibleSize.width() / 2,
kInputVisibleSize.height() / 2);
const auto video_frame = VideoFrame::CreateBlackFrame(wrong_size);
EXPECT_CALL(*mock_mojo_vea_client(),
NotifyError(VideoEncodeAccelerator::kInvalidArgumentError));
mojo_vea_service()->Encode(video_frame, true /* is_keyframe */,
base::DoNothing());
base::RunLoop().RunUntilIdle();
}
// This test verifies that an any mojom::VEA method call (e.g. Encode(),
// UseOutputBitstreamBuffer() etc) before MojoVEA::Initialize() is ignored (we
// can't expect NotifyError()s since there's no mojo client registered).
TEST_F(MojoVideoEncodeAcceleratorServiceTest, CallsBeforeInitializeAreIgnored) {
CreateMojoVideoEncodeAccelerator();
{
const auto video_frame = VideoFrame::CreateBlackFrame(kInputVisibleSize);
mojo_vea_service()->Encode(video_frame, true /* is_keyframe */,
base::DoNothing());
base::RunLoop().RunUntilIdle();
}
{
const int32_t kBitstreamBufferId = 17;
const uint64_t kShMemSize = 10;
auto handle = mojo::SharedBufferHandle::Create(kShMemSize);
mojo_vea_service()->UseOutputBitstreamBuffer(kBitstreamBufferId,
std::move(handle));
base::RunLoop().RunUntilIdle();
}
{
const uint32_t kNewBitrate = 123123u;
const uint32_t kNewFramerate = 321321u;
media::VideoBitrateAllocation bitrate_allocation;
bitrate_allocation.SetBitrate(0, 0, kNewBitrate);
mojo_vea_service()->RequestEncodingParametersChangeWithLayers(
bitrate_allocation, kNewFramerate);
base::RunLoop().RunUntilIdle();
}
}
// This test verifies that IsFlushSupported/Flush on FakeVEA.
TEST_F(MojoVideoEncodeAcceleratorServiceTest, IsFlushSupportedAndFlush) {
CreateMojoVideoEncodeAccelerator();
BindAndInitialize();
ASSERT_TRUE(fake_vea());
// media::VideoEncodeAccelerator::IsFlushSupported and Flush are return
// false as default, so here expect false for both IsFlushSupported and
// Flush.
auto flush_support =
base::BindOnce([](bool status) { EXPECT_EQ(status, false); });
mojo_vea_service()->IsFlushSupported(std::move(flush_support));
base::RunLoop().RunUntilIdle();
auto flush_callback =
base::BindOnce([](bool status) { EXPECT_EQ(status, false); });
mojo_vea_service()->IsFlushSupported(std::move(flush_callback));
}
} // namespace media