blob: fc2377d8c76e67a3f444a5643321047369fdf8bd [file] [log] [blame]
// Copyright 2019 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 <stdint.h>
#include <va/va.h>
#include <memory>
#include <utility>
#include <vector>
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "base/bind.h"
#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "gpu/config/gpu_finch_features.h"
#include "gpu/config/gpu_info.h"
#include "gpu/ipc/service/image_decode_accelerator_worker.h"
#include "media/gpu/vaapi/vaapi_image_decode_accelerator_worker.h"
#include "media/gpu/vaapi/vaapi_image_decoder.h"
#include "media/gpu/vaapi/vaapi_wrapper.h"
#include "third_party/skia/include/core/SkImageInfo.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/linux/native_pixmap_dmabuf.h"
#include "ui/gfx/native_pixmap_handle.h"
using testing::_;
using testing::AllOf;
using testing::InSequence;
using testing::IsNull;
using testing::NotNull;
using testing::Property;
using testing::Return;
using testing::StrictMock;
namespace media {
namespace {
constexpr gfx::BufferFormat kFormatForDecodes = gfx::BufferFormat::YVU_420;
constexpr gfx::Size kVaSurfaceResolution(128, 256);
constexpr gfx::Size kVisibleSize(120, 250);
constexpr size_t kWebPFileAndVp8ChunkHeaderSizeInBytes = 20u;
// clang-format off
constexpr uint8_t kJpegPFileHeader[] = {0xFF, 0xD8, 0xFF};
constexpr uint8_t kLossyWebPFileHeader[] = {
'R', 'I', 'F', 'F',
0x0c, 0x00, 0x00, 0x00, // == 12 (little endian)
'W', 'E', 'B', 'P',
'V', 'P', '8', ' ',
0x00, 0x00, 0x00, 0x00 // == 0
};
// clang-format on
constexpr base::span<const uint8_t, 3u> kJpegEncodedData = kJpegPFileHeader;
constexpr base::span<const uint8_t, kWebPFileAndVp8ChunkHeaderSizeInBytes>
kLossyWebPEncodedData = kLossyWebPFileHeader;
class MockNativePixmapDmaBuf : public gfx::NativePixmapDmaBuf {
public:
MockNativePixmapDmaBuf(const gfx::Size& size)
: gfx::NativePixmapDmaBuf(size,
kFormatForDecodes,
gfx::NativePixmapHandle()) {}
gfx::NativePixmapHandle ExportHandle() override {
gfx::NativePixmapHandle handle{};
DCHECK_EQ(gfx::BufferFormat::YVU_420, GetBufferFormat());
handle.planes = std::vector<gfx::NativePixmapPlane>(3u);
return handle;
}
protected:
~MockNativePixmapDmaBuf() override = default;
};
class MockVaapiImageDecoder : public VaapiImageDecoder {
public:
MockVaapiImageDecoder(gpu::ImageDecodeAcceleratorType type)
: VaapiImageDecoder(VAProfileNone), type_(type) {}
~MockVaapiImageDecoder() override = default;
gpu::ImageDecodeAcceleratorType GetType() const override { return type_; }
SkYUVColorSpace GetYUVColorSpace() const override {
switch (type_) {
case gpu::ImageDecodeAcceleratorType::kJpeg:
return SkYUVColorSpace::kJPEG_SkYUVColorSpace;
case gpu::ImageDecodeAcceleratorType::kWebP:
return SkYUVColorSpace::kRec601_SkYUVColorSpace;
case gpu::ImageDecodeAcceleratorType::kUnknown:
NOTREACHED();
return SkYUVColorSpace::kIdentity_SkYUVColorSpace;
}
}
gpu::ImageDecodeAcceleratorSupportedProfile GetSupportedProfile()
const override {
return gpu::ImageDecodeAcceleratorSupportedProfile();
}
MOCK_METHOD1(Initialize, bool(const ReportErrorToUMACB&));
MOCK_METHOD1(Decode, VaapiImageDecodeStatus(base::span<const uint8_t>));
MOCK_CONST_METHOD0(GetScopedVASurface, const ScopedVASurface*());
MOCK_METHOD1(
ExportAsNativePixmapDmaBuf,
std::unique_ptr<NativePixmapAndSizeInfo>(VaapiImageDecodeStatus*));
MOCK_METHOD1(AllocateVASurfaceAndSubmitVABuffers,
VaapiImageDecodeStatus(base::span<const uint8_t>));
private:
const gpu::ImageDecodeAcceleratorType type_;
};
} // namespace
class VaapiImageDecodeAcceleratorWorkerTest : public testing::Test {
public:
VaapiImageDecodeAcceleratorWorkerTest() {
feature_list_.InitWithFeatures(
{features::kVaapiJpegImageDecodeAcceleration,
features::kVaapiWebPImageDecodeAcceleration} /* enabled_features */,
{} /* disabled_features */);
VaapiImageDecoderVector decoders;
decoders.push_back(std::make_unique<StrictMock<MockVaapiImageDecoder>>(
gpu::ImageDecodeAcceleratorType::kJpeg));
decoders.push_back(std::make_unique<StrictMock<MockVaapiImageDecoder>>(
gpu::ImageDecodeAcceleratorType::kWebP));
worker_ = base::WrapUnique(
new VaapiImageDecodeAcceleratorWorker(std::move(decoders)));
}
MockVaapiImageDecoder* GetJpegDecoder() const {
auto result =
worker_->decoders_.find(gpu::ImageDecodeAcceleratorType::kJpeg);
return result == worker_->decoders_.end()
? nullptr
: static_cast<MockVaapiImageDecoder*>(result->second.get());
}
MockVaapiImageDecoder* GetWebPDecoder() const {
auto result =
worker_->decoders_.find(gpu::ImageDecodeAcceleratorType::kWebP);
return result == worker_->decoders_.end()
? nullptr
: static_cast<MockVaapiImageDecoder*>(result->second.get());
}
MOCK_METHOD1(
OnDecodeCompleted,
void(std::unique_ptr<gpu::ImageDecodeAcceleratorWorker::DecodeResult>));
protected:
base::test::TaskEnvironment task_environment_;
base::test::ScopedFeatureList feature_list_;
std::unique_ptr<VaapiImageDecodeAcceleratorWorker> worker_;
DISALLOW_COPY_AND_ASSIGN(VaapiImageDecodeAcceleratorWorkerTest);
};
ACTION_P2(ExportAsNativePixmapDmaBufSuccessfully,
va_surface_resolution,
visible_size) {
*arg0 = VaapiImageDecodeStatus::kSuccess;
auto exported_pixmap = std::make_unique<NativePixmapAndSizeInfo>();
exported_pixmap->va_surface_resolution = va_surface_resolution;
exported_pixmap->byte_size = 1u;
exported_pixmap->pixmap =
base::MakeRefCounted<MockNativePixmapDmaBuf>(visible_size);
return exported_pixmap;
}
TEST_F(VaapiImageDecodeAcceleratorWorkerTest, ImageDecodeSucceeds) {
std::vector<uint8_t> jpeg_encoded_data(kJpegEncodedData.begin(),
kJpegEncodedData.end());
std::vector<uint8_t> webp_encoded_data(kLossyWebPEncodedData.begin(),
kLossyWebPEncodedData.end());
{
InSequence sequence;
MockVaapiImageDecoder* jpeg_decoder = GetJpegDecoder();
ASSERT_TRUE(jpeg_decoder);
EXPECT_CALL(
*jpeg_decoder,
Decode(AllOf(Property(&base::span<const uint8_t>::data,
jpeg_encoded_data.data()),
Property(&base::span<const uint8_t>::size,
jpeg_encoded_data.size())) /* encoded_data */))
.WillOnce(Return(VaapiImageDecodeStatus::kSuccess));
EXPECT_CALL(*jpeg_decoder,
ExportAsNativePixmapDmaBuf(NotNull() /* status */))
.WillOnce(ExportAsNativePixmapDmaBufSuccessfully(kVaSurfaceResolution,
kVisibleSize));
EXPECT_CALL(*this, OnDecodeCompleted(NotNull()));
MockVaapiImageDecoder* webp_decoder = GetWebPDecoder();
ASSERT_TRUE(webp_decoder);
EXPECT_CALL(
*webp_decoder,
Decode(AllOf(Property(&base::span<const uint8_t>::data,
webp_encoded_data.data()),
Property(&base::span<const uint8_t>::size,
webp_encoded_data.size())) /* encoded_data */))
.WillOnce(Return(VaapiImageDecodeStatus::kSuccess));
EXPECT_CALL(*webp_decoder,
ExportAsNativePixmapDmaBuf(NotNull() /* status */))
.WillOnce(ExportAsNativePixmapDmaBufSuccessfully(kVaSurfaceResolution,
kVisibleSize));
EXPECT_CALL(*this, OnDecodeCompleted(NotNull()));
}
worker_->Decode(
std::move(jpeg_encoded_data), kVisibleSize,
base::BindOnce(&VaapiImageDecodeAcceleratorWorkerTest::OnDecodeCompleted,
base::Unretained(this)));
worker_->Decode(
std::move(webp_encoded_data), kVisibleSize,
base::BindOnce(&VaapiImageDecodeAcceleratorWorkerTest::OnDecodeCompleted,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
TEST_F(VaapiImageDecodeAcceleratorWorkerTest, ImageDecodeFails) {
std::vector<uint8_t> jpeg_encoded_data(kJpegEncodedData.begin(),
kJpegEncodedData.end());
std::vector<uint8_t> webp_encoded_data(kLossyWebPEncodedData.begin(),
kLossyWebPEncodedData.end());
{
InSequence sequence;
MockVaapiImageDecoder* jpeg_decoder = GetJpegDecoder();
ASSERT_TRUE(jpeg_decoder);
EXPECT_CALL(
*jpeg_decoder,
Decode(AllOf(Property(&base::span<const uint8_t>::data,
jpeg_encoded_data.data()),
Property(&base::span<const uint8_t>::size,
jpeg_encoded_data.size())) /* encoded_data */))
.WillOnce(Return(VaapiImageDecodeStatus::kExecuteDecodeFailed));
EXPECT_CALL(*this, OnDecodeCompleted(IsNull()));
MockVaapiImageDecoder* webp_decoder = GetWebPDecoder();
ASSERT_TRUE(webp_decoder);
EXPECT_CALL(
*webp_decoder,
Decode(AllOf(Property(&base::span<const uint8_t>::data,
webp_encoded_data.data()),
Property(&base::span<const uint8_t>::size,
webp_encoded_data.size())) /* encoded_data */))
.WillOnce(Return(VaapiImageDecodeStatus::kExecuteDecodeFailed));
EXPECT_CALL(*this, OnDecodeCompleted(IsNull()));
}
worker_->Decode(
std::move(jpeg_encoded_data), kVisibleSize,
base::BindOnce(&VaapiImageDecodeAcceleratorWorkerTest::OnDecodeCompleted,
base::Unretained(this)));
worker_->Decode(
std::move(webp_encoded_data), kVisibleSize,
base::BindOnce(&VaapiImageDecodeAcceleratorWorkerTest::OnDecodeCompleted,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
TEST_F(VaapiImageDecodeAcceleratorWorkerTest, UnknownImageDecodeFails) {
std::vector<uint8_t> encoded_data = {1u, 2u, 3u};
EXPECT_CALL(*this, OnDecodeCompleted(IsNull()));
worker_->Decode(
std::move(encoded_data), kVisibleSize,
base::BindOnce(&VaapiImageDecodeAcceleratorWorkerTest::OnDecodeCompleted,
base::Unretained(this)));
task_environment_.RunUntilIdle();
}
} // namespace media