123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711 |
- // Copyright 2015 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 <algorithm>
- #include <memory>
- #include <string>
- #include <vector>
- // This has to be included first.
- // See http://code.google.com/p/googletest/issues/detail?id=371
- #include "testing/gtest/include/gtest/gtest.h"
- #include "base/containers/span.h"
- #include "base/files/file_path.h"
- #include "base/files/file_util.h"
- #include "base/logging.h"
- #include "base/memory/scoped_refptr.h"
- #include "base/numerics/safe_conversions.h"
- #include "base/strings/string_util.h"
- #include "media/base/video_types.h"
- #include "media/gpu/test/local_gpu_memory_buffer_manager.h"
- #include "media/gpu/vaapi/test_utils.h"
- #include "media/gpu/vaapi/va_surface.h"
- #include "media/gpu/vaapi/vaapi_image_decoder.h"
- #include "media/gpu/vaapi/vaapi_image_decoder_test_common.h"
- #include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
- #include "media/gpu/vaapi/vaapi_utils.h"
- #include "media/gpu/vaapi/vaapi_wrapper.h"
- #include "media/parsers/jpeg_parser.h"
- #include "third_party/libyuv/include/libyuv.h"
- #include "third_party/skia/include/core/SkColor.h"
- #include "third_party/skia/include/core/SkImageInfo.h"
- #include "third_party/skia/include/core/SkPixmap.h"
- #include "third_party/skia/include/encode/SkJpegEncoder.h"
- #include "ui/gfx/buffer_format_util.h"
- #include "ui/gfx/buffer_types.h"
- #include "ui/gfx/codec/jpeg_codec.h"
- #include "ui/gfx/geometry/rect.h"
- #include "ui/gfx/geometry/size.h"
- #include "ui/gfx/gpu_memory_buffer.h"
- #include "ui/gfx/linux/native_pixmap_dmabuf.h"
- #include "ui/gfx/native_pixmap_handle.h"
- namespace media {
- namespace {
- using DecodedImagePtr = std::unique_ptr<vaapi_test_utils::DecodedImage>;
- constexpr const char* kYuv422Filename = "pixel-1280x720.jpg";
- constexpr const char* kYuv420Filename = "pixel-1280x720-yuv420.jpg";
- constexpr const char* kYuv444Filename = "pixel-1280x720-yuv444.jpg";
- constexpr const char* kOddHeightImageFilename = "pixel-40x23-yuv420.jpg";
- constexpr const char* kOddWidthImageFilename = "pixel-41x22-yuv420.jpg";
- constexpr const char* kOddDimensionsImageFilename = "pixel-41x23-yuv420.jpg";
- const vaapi_test_utils::TestParam kVAImageTestCases[] = {
- {"YUV422", kYuv422Filename},
- {"YUV420", kYuv420Filename},
- {"YUV444", kYuv444Filename},
- };
- const vaapi_test_utils::TestParam kDmaBufTestCases[] = {
- {"YUV420", kYuv420Filename},
- {"OddHeightImage40x23", kOddHeightImageFilename},
- {"OddWidthImage41x22", kOddWidthImageFilename},
- {"OddDimensionsImage41x23", kOddDimensionsImageFilename},
- };
- constexpr double kMinSsim = 0.995;
- // This file is not supported by the VAAPI, so we don't define expectations on
- // the decode result.
- constexpr const char* kUnsupportedFilename = "pixel-1280x720-grayscale.jpg";
- // The size of the minimum coded unit for a YUV 4:2:0 image (both the width and
- // the height of the MCU are the same for 4:2:0).
- constexpr int k420MCUSize = 16;
- // The largest maximum supported surface size we expect a driver to report for
- // JPEG decoding.
- constexpr gfx::Size kLargestSupportedSize(16 * 1024, 16 * 1024);
- // Decodes the given |encoded_image| using libyuv and returns the result as a
- // DecodedImage object. The decoded planes will be stored in |dest_*|.
- // Note however, that this function does not takes ownership of |dest_*| or
- // manage their memory.
- DecodedImagePtr GetSwDecode(base::span<const uint8_t> encoded_image,
- std::vector<uint8_t>* dest_y,
- std::vector<uint8_t>* dest_u,
- std::vector<uint8_t>* dest_v) {
- DCHECK(dest_y && dest_u && dest_v);
- JpegParseResult parse_result;
- const bool result = ParseJpegPicture(encoded_image.data(),
- encoded_image.size(), &parse_result);
- if (!result)
- return nullptr;
- const gfx::Size jpeg_size(
- base::strict_cast<int>(parse_result.frame_header.visible_width),
- base::strict_cast<int>(parse_result.frame_header.visible_height));
- if (jpeg_size.IsEmpty())
- return nullptr;
- const gfx::Size half_jpeg_size((jpeg_size.width() + 1) / 2,
- (jpeg_size.height() + 1) / 2);
- dest_y->resize(jpeg_size.GetArea());
- dest_u->resize(half_jpeg_size.GetArea());
- dest_v->resize(half_jpeg_size.GetArea());
- if (libyuv::ConvertToI420(
- encoded_image.data(), encoded_image.size(), dest_y->data(),
- jpeg_size.width(), dest_u->data(), half_jpeg_size.width(),
- dest_v->data(), half_jpeg_size.width(), 0, 0, jpeg_size.width(),
- jpeg_size.height(), jpeg_size.width(), jpeg_size.height(),
- libyuv::kRotate0, libyuv::FOURCC_MJPG) != 0) {
- return nullptr;
- }
- auto sw_decoded_jpeg = std::make_unique<vaapi_test_utils::DecodedImage>();
- sw_decoded_jpeg->fourcc = VA_FOURCC_I420;
- sw_decoded_jpeg->number_of_planes = 3u;
- sw_decoded_jpeg->size = jpeg_size;
- sw_decoded_jpeg->planes[0].data = dest_y->data();
- sw_decoded_jpeg->planes[0].stride = jpeg_size.width();
- sw_decoded_jpeg->planes[1].data = dest_u->data();
- sw_decoded_jpeg->planes[1].stride = half_jpeg_size.width();
- sw_decoded_jpeg->planes[2].data = dest_v->data();
- sw_decoded_jpeg->planes[2].stride = half_jpeg_size.width();
- return sw_decoded_jpeg;
- }
- // Generates a checkerboard pattern as a JPEG image of a specified |size| and
- // |subsampling| format. Returns an empty vector on failure.
- std::vector<unsigned char> GenerateJpegImage(
- const gfx::Size& size,
- SkJpegEncoder::Downsample subsampling = SkJpegEncoder::Downsample::k420) {
- DCHECK(!size.IsEmpty());
- // First build a raw RGBA image of the given size with a checkerboard pattern.
- const SkImageInfo image_info = SkImageInfo::Make(
- size.width(), size.height(), SkColorType::kRGBA_8888_SkColorType,
- SkAlphaType::kOpaque_SkAlphaType);
- const size_t byte_size = image_info.computeMinByteSize();
- if (byte_size == SIZE_MAX)
- return {};
- const size_t stride = image_info.minRowBytes();
- DCHECK_EQ(4, SkColorTypeBytesPerPixel(image_info.colorType()));
- DCHECK_EQ(4 * size.width(), base::checked_cast<int>(stride));
- constexpr gfx::Size kCheckerRectSize(3, 5);
- std::vector<uint8_t> rgba_data(byte_size);
- uint8_t* data = rgba_data.data();
- for (int y = 0; y < size.height(); y++) {
- const bool y_bit = (((y / kCheckerRectSize.height()) & 0x1) == 0);
- for (int x = 0; x < base::checked_cast<int>(stride); x += 4) {
- const bool x_bit = (((x / kCheckerRectSize.width()) & 0x1) == 0);
- const SkColor color = (x_bit != y_bit) ? SK_ColorBLUE : SK_ColorMAGENTA;
- data[x + 0] = SkColorGetR(color);
- data[x + 1] = SkColorGetG(color);
- data[x + 2] = SkColorGetB(color);
- data[x + 3] = SkColorGetA(color);
- }
- data += stride;
- }
- // Now, encode it as a JPEG.
- //
- // TODO(andrescj): if this generates a large enough image (in terms of byte
- // size), it will be decoded incorrectly in AMD Stoney Ridge (see
- // b/127874877). When that's resolved, change the quality here to 100 so that
- // the generated JPEG is large.
- std::vector<unsigned char> jpeg_data;
- if (gfx::JPEGCodec::Encode(
- SkPixmap(image_info, rgba_data.data(), stride) /* input */,
- 95 /* quality */, subsampling /* downsample */,
- &jpeg_data /* output */)) {
- return jpeg_data;
- }
- return {};
- }
- // Rounds |n| to the greatest multiple of |m| that is less than or equal to |n|.
- int RoundDownToMultiple(int n, int m) {
- DCHECK_GE(n, 0);
- DCHECK_GT(m, 0);
- return (n / m) * m;
- }
- // Rounds |n| to the smallest multiple of |m| that is greater than or equal to
- // |n|.
- int RoundUpToMultiple(int n, int m) {
- DCHECK_GE(n, 0);
- DCHECK_GT(m, 0);
- if (n % m == 0)
- return n;
- base::CheckedNumeric<int> safe_n(n);
- safe_n += m;
- return RoundDownToMultiple(safe_n.ValueOrDie(), m);
- }
- // Given a minimum supported surface dimension (width or height) value
- // |min_surface_supported|, this function returns a non-zero coded dimension of
- // a 4:2:0 JPEG image that would not be supported because the dimension is right
- // below the supported value. For example, if |min_surface_supported| is 19,
- // this function should return 16 because for a 4:2:0 image, both coded
- // dimensions should be multiples of 16. If an unsupported dimension was found
- // (i.e., |min_surface_supported| > 16), this function returns true, false
- // otherwise.
- bool GetMinUnsupportedDimension(int min_surface_supported,
- int* min_unsupported) {
- if (min_surface_supported <= k420MCUSize)
- return false;
- *min_unsupported =
- RoundDownToMultiple(min_surface_supported - 1, k420MCUSize);
- return true;
- }
- // Given a minimum supported surface dimension (width or height) value
- // |min_surface_supported|, this function returns a non-zero coded dimension of
- // a 4:2:0 JPEG image that would be supported because the dimension is at least
- // the minimum. For example, if |min_surface_supported| is 35, this function
- // should return 48 because for a 4:2:0 image, both coded dimensions should be
- // multiples of 16.
- int GetMinSupportedDimension(int min_surface_supported) {
- LOG_ASSERT(min_surface_supported > 0);
- return RoundUpToMultiple(min_surface_supported, k420MCUSize);
- }
- // Given a maximum supported surface dimension (width or height) value
- // |max_surface_supported|, this function returns the coded dimension of a 4:2:0
- // JPEG image that would be supported because the dimension is at most the
- // maximum. For example, if |max_surface_supported| is 65, this function
- // should return 64 because for a 4:2:0 image, both coded dimensions should be
- // multiples of 16.
- int GetMaxSupportedDimension(int max_surface_supported) {
- return RoundDownToMultiple(max_surface_supported, k420MCUSize);
- }
- } // namespace
- class VaapiJpegDecoderTest : public VaapiImageDecoderTestCommon {
- protected:
- VaapiJpegDecoderTest()
- : VaapiImageDecoderTestCommon(std::make_unique<VaapiJpegDecoder>()) {}
- std::unique_ptr<ScopedVAImage> Decode(
- base::span<const uint8_t> encoded_image,
- uint32_t preferred_fourcc,
- VaapiImageDecodeStatus* status = nullptr);
- std::unique_ptr<ScopedVAImage> Decode(
- base::span<const uint8_t> encoded_image,
- VaapiImageDecodeStatus* status = nullptr);
- };
- std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
- base::span<const uint8_t> encoded_image,
- uint32_t preferred_fourcc,
- VaapiImageDecodeStatus* status) {
- const VaapiImageDecodeStatus decode_status = Decoder()->Decode(encoded_image);
- EXPECT_EQ(!!Decoder()->GetScopedVASurface(),
- decode_status == VaapiImageDecodeStatus::kSuccess);
- // Still try to get image when decode fails.
- VaapiImageDecodeStatus image_status;
- std::unique_ptr<ScopedVAImage> scoped_image;
- scoped_image = static_cast<VaapiJpegDecoder*>(Decoder())->GetImage(
- preferred_fourcc, &image_status);
- EXPECT_EQ(!!scoped_image, image_status == VaapiImageDecodeStatus::kSuccess);
- // Return the first fail status.
- if (status) {
- *status = decode_status != VaapiImageDecodeStatus::kSuccess ? decode_status
- : image_status;
- }
- return scoped_image;
- }
- std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
- base::span<const uint8_t> encoded_image,
- VaapiImageDecodeStatus* status) {
- return Decode(encoded_image, VA_FOURCC_I420, status);
- }
- // The intention of this test is to ensure that the workarounds added in
- // VaapiWrapper::GetJpegDecodeSuitableImageFourCC() don't result in an
- // unsupported image format.
- TEST_F(VaapiJpegDecoderTest, MinimalImageFormatSupport) {
- // All drivers should support at least I420.
- VAImageFormat i420_format{};
- i420_format.fourcc = VA_FOURCC_I420;
- ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(i420_format));
- // Additionally, the mesa VAAPI driver should support YV12, NV12 and YUYV.
- ASSERT_NE(VAImplementation::kInvalid, VaapiWrapper::GetImplementationType());
- if (VaapiWrapper::GetImplementationType() == VAImplementation::kMesaGallium) {
- VAImageFormat yv12_format{};
- yv12_format.fourcc = VA_FOURCC_YV12;
- ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(yv12_format));
- VAImageFormat nv12_format{};
- nv12_format.fourcc = VA_FOURCC_NV12;
- ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(nv12_format));
- VAImageFormat yuyv_format{};
- yuyv_format.fourcc = VA_FOURCC('Y', 'U', 'Y', 'V');
- ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(yuyv_format));
- }
- }
- TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) {
- base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
- std::string jpeg_data;
- ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
- << "failed to read input data from " << input_file.value();
- const auto encoded_image = base::as_bytes(base::make_span(jpeg_data));
- // Skip the image if the VAAPI driver doesn't claim to support its chroma
- // subsampling format. However, we expect at least 4:2:0 and 4:2:2 support.
- const VaapiWrapper::InternalFormats supported_internal_formats =
- VaapiWrapper::GetDecodeSupportedInternalFormats(VAProfileJPEGBaseline);
- ASSERT_TRUE(supported_internal_formats.yuv420);
- ASSERT_TRUE(supported_internal_formats.yuv422);
- JpegParseResult parse_result;
- ASSERT_TRUE(ParseJpegPicture(encoded_image.data(), encoded_image.size(),
- &parse_result));
- const unsigned int rt_format =
- VaSurfaceFormatForJpeg(parse_result.frame_header);
- ASSERT_NE(kInvalidVaRtFormat, rt_format);
- if (!VaapiWrapper::IsDecodingSupportedForInternalFormat(VAProfileJPEGBaseline,
- rt_format)) {
- GTEST_SKIP();
- }
- // Note that this test together with
- // VaapiJpegDecoderTest.MinimalImageFormatSupport gives us two guarantees:
- //
- // 1) Every combination of supported internal format (returned by
- // GetJpegDecodeSupportedInternalFormats()) and supported image format
- // works with vaGetImage() (for JPEG decoding).
- //
- // 2) The FOURCC returned by VaapiWrapper::GetJpegDecodeSuitableImageFourCC()
- // corresponds to a supported image format.
- //
- // Note that we expect VA_FOURCC_I420 and VA_FOURCC_NV12 support in all
- // drivers.
- const std::vector<VAImageFormat>& supported_image_formats =
- VaapiWrapper::GetSupportedImageFormatsForTesting();
- EXPECT_GE(supported_image_formats.size(), 2u);
- VAImageFormat i420_format{};
- i420_format.fourcc = VA_FOURCC_I420;
- EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(i420_format));
- VAImageFormat nv12_format{};
- nv12_format.fourcc = VA_FOURCC_NV12;
- EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(nv12_format));
- // Decode the image using libyuv. Using |temp_*| for resource management.
- std::vector<uint8_t> temp_y;
- std::vector<uint8_t> temp_u;
- std::vector<uint8_t> temp_v;
- DecodedImagePtr sw_decoded_jpeg =
- GetSwDecode(encoded_image, &temp_y, &temp_u, &temp_v);
- ASSERT_TRUE(sw_decoded_jpeg);
- // Now run the comparison between the sw and hw image decodes for the
- // supported formats.
- for (const auto& image_format : supported_image_formats) {
- std::unique_ptr<ScopedVAImage> scoped_image =
- Decode(encoded_image, image_format.fourcc);
- ASSERT_TRUE(scoped_image);
- ASSERT_TRUE(Decoder()->GetScopedVASurface());
- EXPECT_TRUE(Decoder()->GetScopedVASurface()->IsValid());
- EXPECT_EQ(Decoder()->GetScopedVASurface()->size().width(),
- base::strict_cast<int>(parse_result.frame_header.visible_width));
- EXPECT_EQ(Decoder()->GetScopedVASurface()->size().height(),
- base::strict_cast<int>(parse_result.frame_header.visible_height));
- EXPECT_EQ(rt_format, Decoder()->GetScopedVASurface()->format());
- const uint32_t actual_fourcc = scoped_image->image()->format.fourcc;
- // TODO(andrescj): CompareImages() only supports I420, NV12, YUY2, and YUYV.
- // Make it support all the image formats we expect and call it
- // unconditionally.
- if (actual_fourcc == VA_FOURCC_I420 || actual_fourcc == VA_FOURCC_NV12 ||
- actual_fourcc == VA_FOURCC_YUY2 ||
- actual_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
- ASSERT_TRUE(vaapi_test_utils::CompareImages(
- *sw_decoded_jpeg,
- vaapi_test_utils::ScopedVAImageToDecodedImage(scoped_image.get()),
- kMinSsim));
- }
- DVLOG(1) << "Got a " << FourccToString(scoped_image->image()->format.fourcc)
- << " VAImage (preferred " << FourccToString(image_format.fourcc)
- << ")";
- }
- }
- // Make sure that JPEGs whose size is in the supported size range are decoded
- // successfully.
- //
- // TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
- // TODO(andrescj): consider recreating the decoder for every size so that no
- // state is retained.
- TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) {
- gfx::Size min_supported_size;
- gfx::Size max_supported_size;
- ASSERT_TRUE(VaapiWrapper::GetSupportedResolutions(
- VAProfileJPEGBaseline, VaapiWrapper::CodecMode::kDecode,
- min_supported_size, max_supported_size));
- // Ensure the maximum supported size is reasonable.
- ASSERT_GE(max_supported_size.width(), min_supported_size.width());
- ASSERT_GE(max_supported_size.height(), min_supported_size.height());
- ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
- ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
- // The actual image min/max coded size depends on the subsampling format. For
- // example, for 4:2:0, the coded dimensions must be multiples of 16. So, if
- // the minimum surface size is, e.g., 18x18, the minimum image coded size is
- // 32x32. Get those actual min/max coded sizes now.
- const int min_width = GetMinSupportedDimension(min_supported_size.width());
- const int min_height = GetMinSupportedDimension(min_supported_size.height());
- const int max_width = GetMaxSupportedDimension(max_supported_size.width());
- const int max_height = GetMaxSupportedDimension(max_supported_size.height());
- ASSERT_GT(max_width, 0);
- ASSERT_GT(max_height, 0);
- const std::vector<gfx::Size> test_sizes = {{min_width, min_height},
- {min_width, max_height},
- {max_width, min_height},
- {max_width, max_height}};
- for (const auto& test_size : test_sizes) {
- const std::vector<unsigned char> jpeg_data = GenerateJpegImage(test_size);
- auto jpeg_data_span = base::as_bytes(base::make_span(jpeg_data));
- ASSERT_FALSE(jpeg_data.empty());
- std::unique_ptr<ScopedVAImage> scoped_image = Decode(jpeg_data_span);
- ASSERT_TRUE(scoped_image)
- << "Decode unexpectedly failed for size = " << test_size.ToString();
- ASSERT_TRUE(Decoder()->GetScopedVASurface());
- EXPECT_TRUE(Decoder()->GetScopedVASurface()->IsValid());
- // Decode the image using libyuv. Using |temp_*| for resource management.
- std::vector<uint8_t> temp_y;
- std::vector<uint8_t> temp_u;
- std::vector<uint8_t> temp_v;
- DecodedImagePtr sw_decoded_jpeg =
- GetSwDecode(jpeg_data_span, &temp_y, &temp_u, &temp_v);
- ASSERT_TRUE(sw_decoded_jpeg);
- EXPECT_TRUE(vaapi_test_utils::CompareImages(
- *sw_decoded_jpeg,
- vaapi_test_utils::ScopedVAImageToDecodedImage(scoped_image.get()),
- kMinSsim))
- << "The SSIM check unexpectedly failed for size = "
- << test_size.ToString();
- }
- }
- class VaapiJpegDecoderWithDmaBufsTest : public VaapiJpegDecoderTest {
- public:
- VaapiJpegDecoderWithDmaBufsTest() = default;
- ~VaapiJpegDecoderWithDmaBufsTest() override = default;
- };
- // TODO(andrescj): test other JPEG formats besides YUV 4:2:0.
- TEST_P(VaapiJpegDecoderWithDmaBufsTest, DecodeSucceeds) {
- ASSERT_NE(VAImplementation::kInvalid, VaapiWrapper::GetImplementationType());
- if (VaapiWrapper::GetImplementationType() == VAImplementation::kMesaGallium) {
- // TODO(crbug.com/974438): until we support surfaces with multiple buffer
- // objects, the AMD driver fails this test.
- GTEST_SKIP();
- }
- base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
- std::string jpeg_data;
- ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
- << "failed to read input data from " << input_file.value();
- const auto encoded_image = base::as_bytes(base::make_span(jpeg_data));
- // Decode into a VAAPI-allocated surface.
- const VaapiImageDecodeStatus decode_status = Decoder()->Decode(encoded_image);
- EXPECT_EQ(VaapiImageDecodeStatus::kSuccess, decode_status);
- ASSERT_TRUE(Decoder()->GetScopedVASurface());
- const gfx::Size va_surface_visible_size =
- Decoder()->GetScopedVASurface()->size();
- // The size stored in the ScopedVASurface should be the visible size of the
- // JPEG.
- JpegParseResult parse_result;
- ASSERT_TRUE(ParseJpegPicture(encoded_image.data(), encoded_image.size(),
- &parse_result));
- EXPECT_EQ(gfx::Size(parse_result.frame_header.visible_width,
- parse_result.frame_header.visible_height),
- va_surface_visible_size);
- // Export the surface.
- VaapiImageDecodeStatus export_status = VaapiImageDecodeStatus::kInvalidState;
- std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
- Decoder()->ExportAsNativePixmapDmaBuf(&export_status);
- EXPECT_EQ(VaapiImageDecodeStatus::kSuccess, export_status);
- ASSERT_TRUE(exported_pixmap);
- ASSERT_TRUE(exported_pixmap->pixmap);
- EXPECT_FALSE(Decoder()->GetScopedVASurface());
- // For JPEG decoding, the size of the surface we request is the coded size of
- // the JPEG. Make sure the surface contains that coded area.
- EXPECT_TRUE(gfx::Rect(exported_pixmap->va_surface_resolution)
- .Contains(gfx::Rect(parse_result.frame_header.coded_width,
- parse_result.frame_header.coded_height)));
- // Make sure the visible area is contained by the surface.
- EXPECT_EQ(va_surface_visible_size, exported_pixmap->pixmap->GetBufferSize());
- EXPECT_FALSE(exported_pixmap->va_surface_resolution.IsEmpty());
- EXPECT_FALSE(exported_pixmap->pixmap->GetBufferSize().IsEmpty());
- ASSERT_TRUE(
- gfx::Rect(exported_pixmap->va_surface_resolution)
- .Contains(gfx::Rect(exported_pixmap->pixmap->GetBufferSize())));
- // TODO(andrescj): we could get a better lower bound based on the dimensions
- // and the format.
- ASSERT_GT(exported_pixmap->byte_size, 0u);
- // After exporting the surface, we should not be able to obtain a VAImage with
- // the decoded data.
- VAImageFormat i420_format{};
- i420_format.fourcc = VA_FOURCC_I420;
- EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(i420_format));
- VaapiImageDecodeStatus image_status = VaapiImageDecodeStatus::kSuccess;
- EXPECT_FALSE(static_cast<VaapiJpegDecoder*>(Decoder())->GetImage(
- i420_format.fourcc, &image_status));
- EXPECT_EQ(VaapiImageDecodeStatus::kInvalidState, image_status);
- // Workaround: in order to import and map the pixmap using minigbm when the
- // format is gfx::BufferFormat::YVU_420, we need to reorder the planes so that
- // the offsets are in increasing order as assumed in https://bit.ly/2NLubNN.
- // Otherwise, we get a validation error. In essence, we're making minigbm
- // think that it is mapping a YVU_420, but it's actually mapping a YUV_420.
- //
- // TODO(andrescj): revisit this once crrev.com/c/1573718 lands.
- gfx::NativePixmapHandle handle = exported_pixmap->pixmap->ExportHandle();
- ASSERT_EQ(gfx::NumberOfPlanesForLinearBufferFormat(
- exported_pixmap->pixmap->GetBufferFormat()),
- handle.planes.size());
- if (exported_pixmap->pixmap->GetBufferFormat() == gfx::BufferFormat::YVU_420)
- std::swap(handle.planes[1], handle.planes[2]);
- LocalGpuMemoryBufferManager gpu_memory_buffer_manager;
- std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
- gpu_memory_buffer_manager.ImportDmaBuf(
- handle, exported_pixmap->pixmap->GetBufferSize(),
- exported_pixmap->pixmap->GetBufferFormat());
- ASSERT_TRUE(gpu_memory_buffer);
- ASSERT_TRUE(gpu_memory_buffer->Map());
- vaapi_test_utils::DecodedImage decoded_image{};
- const gfx::BufferFormat format = gpu_memory_buffer->GetFormat();
- if (format == gfx::BufferFormat::YVU_420) {
- decoded_image.fourcc = VA_FOURCC_I420;
- decoded_image.number_of_planes = 3u;
- } else if (format == gfx::BufferFormat::YUV_420_BIPLANAR) {
- decoded_image.fourcc = VA_FOURCC_NV12;
- decoded_image.number_of_planes = 2u;
- } else {
- ASSERT_TRUE(false) << "Unsupported format "
- << gfx::BufferFormatToString(format);
- }
- decoded_image.size = gpu_memory_buffer->GetSize();
- for (size_t plane = 0u;
- plane < base::strict_cast<size_t>(decoded_image.number_of_planes);
- plane++) {
- decoded_image.planes[plane].data =
- static_cast<uint8_t*>(gpu_memory_buffer->memory(plane));
- decoded_image.planes[plane].stride = gpu_memory_buffer->stride(plane);
- }
- // Decode the image using libyuv. Using |temp_*| for resource management.
- std::vector<uint8_t> temp_y;
- std::vector<uint8_t> temp_u;
- std::vector<uint8_t> temp_v;
- DecodedImagePtr sw_decoded_jpeg =
- GetSwDecode(encoded_image, &temp_y, &temp_u, &temp_v);
- ASSERT_TRUE(sw_decoded_jpeg);
- EXPECT_TRUE(vaapi_test_utils::CompareImages(*sw_decoded_jpeg, decoded_image,
- kMinSsim));
- gpu_memory_buffer->Unmap();
- }
- // Make sure that JPEGs whose size is below the supported size range are
- // rejected.
- //
- // TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
- TEST_F(VaapiJpegDecoderTest, DecodeFailsForBelowMinSize) {
- gfx::Size min_supported_size;
- gfx::Size max_supported_size;
- ASSERT_TRUE(VaapiWrapper::GetSupportedResolutions(
- VAProfileJPEGBaseline, VaapiWrapper::CodecMode::kDecode,
- min_supported_size, max_supported_size));
- // Ensure the maximum supported size is reasonable.
- ASSERT_GE(max_supported_size.width(), min_supported_size.width());
- ASSERT_GE(max_supported_size.height(), min_supported_size.height());
- ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
- ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
- // Get good (supported) minimum dimensions.
- const int good_width = GetMinSupportedDimension(min_supported_size.width());
- ASSERT_LE(good_width, max_supported_size.width());
- const int good_height = GetMinSupportedDimension(min_supported_size.height());
- ASSERT_LE(good_height, max_supported_size.height());
- // Get bad (unsupported) dimensions.
- int bad_width;
- const bool got_bad_width =
- GetMinUnsupportedDimension(min_supported_size.width(), &bad_width);
- int bad_height;
- const bool got_bad_height =
- GetMinUnsupportedDimension(min_supported_size.height(), &bad_height);
- // Now build and test the good/bad combinations that we expect will fail.
- std::vector<gfx::Size> test_sizes;
- if (got_bad_width)
- test_sizes.push_back({bad_width, good_height});
- if (got_bad_height)
- test_sizes.push_back({good_width, bad_height});
- if (got_bad_width && got_bad_height)
- test_sizes.push_back({bad_width, bad_height});
- for (const auto& test_size : test_sizes) {
- const std::vector<unsigned char> jpeg_data = GenerateJpegImage(test_size);
- ASSERT_FALSE(jpeg_data.empty());
- VaapiImageDecodeStatus status = VaapiImageDecodeStatus::kSuccess;
- ASSERT_FALSE(Decode(base::as_bytes(base::make_span(jpeg_data)), &status))
- << "Decode unexpectedly succeeded for size = " << test_size.ToString();
- EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
- EXPECT_FALSE(Decoder()->GetScopedVASurface());
- }
- }
- // Make sure that JPEGs whose size is above the supported size range are
- // rejected.
- //
- // TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
- TEST_F(VaapiJpegDecoderTest, DecodeFailsForAboveMaxSize) {
- gfx::Size min_supported_size;
- gfx::Size max_supported_size;
- ASSERT_TRUE(VaapiWrapper::GetSupportedResolutions(
- VAProfileJPEGBaseline, VaapiWrapper::CodecMode::kDecode,
- min_supported_size, max_supported_size));
- // Ensure the maximum supported size is reasonable.
- ASSERT_GE(max_supported_size.width(), min_supported_size.width());
- ASSERT_GE(max_supported_size.height(), min_supported_size.height());
- ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
- ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
- // Get good (supported) maximum dimensions.
- const int good_width = GetMaxSupportedDimension(max_supported_size.width());
- ASSERT_GE(good_width, min_supported_size.width());
- ASSERT_GT(good_width, 0);
- const int good_height = GetMaxSupportedDimension(max_supported_size.height());
- ASSERT_GE(good_height, min_supported_size.height());
- ASSERT_GT(good_height, 0);
- // Get bad (unsupported) dimensions.
- const int bad_width =
- RoundUpToMultiple(max_supported_size.width() + 1, k420MCUSize);
- const int bad_height =
- RoundUpToMultiple(max_supported_size.height() + 1, k420MCUSize);
- // Now build and test the good/bad combinations that we expect will fail.
- const std::vector<gfx::Size> test_sizes = {{bad_width, good_height},
- {good_width, bad_height},
- {bad_width, bad_height}};
- for (const auto& test_size : test_sizes) {
- const std::vector<unsigned char> jpeg_data = GenerateJpegImage(test_size);
- ASSERT_FALSE(jpeg_data.empty());
- VaapiImageDecodeStatus status = VaapiImageDecodeStatus::kSuccess;
- ASSERT_FALSE(Decode(base::as_bytes(base::make_span(jpeg_data)), &status))
- << "Decode unexpectedly succeeded for size = " << test_size.ToString();
- EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
- EXPECT_FALSE(Decoder()->GetScopedVASurface());
- }
- }
- TEST_F(VaapiJpegDecoderTest, DecodeFails) {
- // A grayscale image (4:0:0) should be rejected.
- base::FilePath input_file = FindTestDataFilePath(kUnsupportedFilename);
- std::string jpeg_data;
- ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
- << "failed to read input data from " << input_file.value();
- VaapiImageDecodeStatus status = VaapiImageDecodeStatus::kSuccess;
- ASSERT_FALSE(Decode(base::as_bytes(base::make_span(jpeg_data)), &status));
- EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedSubsampling, status);
- EXPECT_FALSE(Decoder()->GetScopedVASurface());
- }
- INSTANTIATE_TEST_SUITE_P(All,
- VaapiJpegDecoderTest,
- testing::ValuesIn(kVAImageTestCases),
- vaapi_test_utils::TestParamToString);
- INSTANTIATE_TEST_SUITE_P(All,
- VaapiJpegDecoderWithDmaBufsTest,
- testing::ValuesIn(kDmaBufTestCases),
- vaapi_test_utils::TestParamToString);
- } // namespace media
|