| // 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 <string> |
| |
| // 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 "media/gpu/test/local_gpu_memory_buffer_manager.h" |
| #include "media/gpu/vaapi/test_utils.h" |
| #include "media/gpu/vaapi/vaapi_image_decoder.h" |
| #include "media/gpu/vaapi/vaapi_image_decoder_test_common.h" |
| #include "media/gpu/vaapi/vaapi_utils.h" |
| #include "media/gpu/vaapi/vaapi_webp_decoder.h" |
| #include "media/gpu/vaapi/vaapi_wrapper.h" |
| #include "media/parsers/vp8_parser.h" |
| #include "media/parsers/webp_parser.h" |
| #include "third_party/libwebp/src/webp/decode.h" |
| #include "ui/gfx/buffer_format_util.h" |
| #include "ui/gfx/buffer_types.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 { |
| |
| constexpr const char* kSmallImageFilename = |
| "RGB_noise_large_pixels_115x115.webp"; |
| constexpr const char* kMediumImageFilename = |
| "RGB_noise_large_pixels_2015x2015.webp"; |
| constexpr const char* kLargeImageFilename = |
| "RGB_noise_large_pixels_4000x4000.webp"; |
| |
| constexpr const char* kLowFreqImageFilename = "solid_green_2015x2015.webp"; |
| constexpr const char* kMedFreqImageFilename = |
| "BlackAndWhite_criss-cross_pattern_2015x2015.webp"; |
| constexpr const char* kHighFreqImageFilename = "RGB_noise_2015x2015.webp"; |
| |
| const vaapi_test_utils::TestParam kTestCases[] = { |
| {"SmallImage_115x115", kSmallImageFilename}, |
| {"MediumImage_2015x2015", kMediumImageFilename}, |
| {"LargeImage_4000x4000", kLargeImageFilename}, |
| {"LowFreqImage", kLowFreqImageFilename}, |
| {"MedFreqImage", kMedFreqImageFilename}, |
| {"HighFreqImage", kHighFreqImageFilename}, |
| }; |
| |
| // Any number above 99.5% should do. We are being very aggressive here. |
| constexpr double kMinSsim = 0.999; |
| |
| // WebpDecode*() returns memory that has to be released in a specific way. |
| struct WebpDecodeDeleter { |
| void operator()(void* ptr) { WebPFree(ptr); } |
| }; |
| |
| } // namespace |
| |
| class VaapiWebPDecoderTest : public VaapiImageDecoderTestCommon { |
| protected: |
| VaapiWebPDecoderTest() |
| : VaapiImageDecoderTestCommon(std::make_unique<VaapiWebPDecoder>()) {} |
| |
| void SetUp() override { |
| if (!VaapiWrapper::IsDecodeSupported(VAProfileVP8Version0_3)) { |
| DLOG(INFO) << "VP8 decoding is not supported by the VA-API."; |
| GTEST_SKIP(); |
| } |
| |
| VaapiImageDecoderTestCommon::SetUp(); |
| } |
| |
| std::unique_ptr<NativePixmapAndSizeInfo> Decode( |
| base::span<const uint8_t> encoded_image, |
| VaapiImageDecodeStatus* status = nullptr) { |
| const VaapiImageDecodeStatus decode_status = |
| Decoder()->Decode(encoded_image); |
| EXPECT_EQ(!!Decoder()->GetScopedVASurface(), |
| decode_status == VaapiImageDecodeStatus::kSuccess); |
| |
| // Still try to export the surface when decode fails. |
| VaapiImageDecodeStatus export_status; |
| std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap = |
| Decoder()->ExportAsNativePixmapDmaBuf(&export_status); |
| EXPECT_EQ(!!exported_pixmap, |
| export_status == VaapiImageDecodeStatus::kSuccess); |
| |
| // Return the first fail status. |
| if (status) { |
| *status = decode_status != VaapiImageDecodeStatus::kSuccess |
| ? decode_status |
| : export_status; |
| } |
| return exported_pixmap; |
| } |
| }; |
| |
| TEST_P(VaapiWebPDecoderTest, DecodeAndExportAsNativePixmapDmaBuf) { |
| base::FilePath input_file = FindTestDataFilePath(GetParam().filename); |
| std::string webp_data; |
| ASSERT_TRUE(base::ReadFileToString(input_file, &webp_data)) |
| << "failed to read input data from " << input_file.value(); |
| const auto encoded_image = base::as_bytes(base::make_span(webp_data)); |
| |
| // Decode the image using the VA-API and wrap the decoded image in a |
| // DecodedImage object. |
| ASSERT_TRUE(VaapiWrapper::IsDecodingSupportedForInternalFormat( |
| VAProfileVP8Version0_3, VA_RT_FORMAT_YUV420)); |
| |
| VaapiImageDecodeStatus status; |
| std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap = |
| Decode(encoded_image, &status); |
| ASSERT_EQ(VaapiImageDecodeStatus::kSuccess, status); |
| EXPECT_FALSE(Decoder()->GetScopedVASurface()); |
| ASSERT_TRUE(exported_pixmap); |
| ASSERT_TRUE(exported_pixmap->pixmap); |
| ASSERT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, |
| exported_pixmap->pixmap->GetBufferFormat()); |
| |
| // Make sure the visible area is contained by the surface. |
| 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); |
| |
| gfx::NativePixmapHandle handle = exported_pixmap->pixmap->ExportHandle(); |
| ASSERT_EQ(gfx::NumberOfPlanesForLinearBufferFormat( |
| exported_pixmap->pixmap->GetBufferFormat()), |
| handle.planes.size()); |
| |
| 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()); |
| ASSERT_EQ(gfx::BufferFormat::YUV_420_BIPLANAR, |
| gpu_memory_buffer->GetFormat()); |
| |
| vaapi_test_utils::DecodedImage hw_decoded_webp{}; |
| hw_decoded_webp.fourcc = VA_FOURCC_NV12; |
| hw_decoded_webp.number_of_planes = 2u; |
| hw_decoded_webp.size = gpu_memory_buffer->GetSize(); |
| for (size_t plane = 0u; |
| plane < base::strict_cast<size_t>(hw_decoded_webp.number_of_planes); |
| plane++) { |
| hw_decoded_webp.planes[plane].data = |
| static_cast<uint8_t*>(gpu_memory_buffer->memory(plane)); |
| hw_decoded_webp.planes[plane].stride = gpu_memory_buffer->stride(plane); |
| } |
| |
| // Decode the image using libwebp and wrap the decoded image in a |
| // DecodedImage object. |
| std::unique_ptr<Vp8FrameHeader> parse_result = ParseWebPImage(encoded_image); |
| ASSERT_TRUE(parse_result); |
| |
| int reference_width; |
| int reference_height; |
| int y_stride; |
| int uv_stride; |
| uint8_t* libwebp_u_plane = nullptr; |
| uint8_t* libwebp_v_plane = nullptr; |
| std::unique_ptr<uint8_t, WebpDecodeDeleter> libwebp_y_plane( |
| WebPDecodeYUV(encoded_image.data(), encoded_image.size(), |
| &reference_width, &reference_height, &libwebp_u_plane, |
| &libwebp_v_plane, &y_stride, &uv_stride)); |
| ASSERT_TRUE(libwebp_y_plane && libwebp_u_plane && libwebp_v_plane); |
| ASSERT_EQ(reference_width, base::strict_cast<int>(parse_result->width)); |
| ASSERT_EQ(reference_height, base::strict_cast<int>(parse_result->height)); |
| |
| // Wrap the software decoded image in a DecodedImage object. |
| vaapi_test_utils::DecodedImage sw_decoded_webp{}; |
| sw_decoded_webp.fourcc = VA_FOURCC_I420; |
| sw_decoded_webp.number_of_planes = 3u; |
| sw_decoded_webp.size = gfx::Size(reference_width, reference_height); |
| sw_decoded_webp.planes[0].data = libwebp_y_plane.get(); |
| sw_decoded_webp.planes[0].stride = y_stride; |
| sw_decoded_webp.planes[1].data = libwebp_u_plane; |
| sw_decoded_webp.planes[1].stride = uv_stride; |
| sw_decoded_webp.planes[2].data = libwebp_v_plane; |
| sw_decoded_webp.planes[2].stride = uv_stride; |
| |
| EXPECT_TRUE(vaapi_test_utils::CompareImages(sw_decoded_webp, hw_decoded_webp, |
| kMinSsim)); |
| gpu_memory_buffer->Unmap(); |
| } |
| |
| // TODO(crbug.com/986073): expand test coverage. See |
| // vaapi_jpeg_decoder_unittest.cc as reference: |
| // cs.chromium.org/chromium/src/media/gpu/vaapi/vaapi_jpeg_decoder_unittest.cc |
| INSTANTIATE_TEST_SUITE_P(All, |
| VaapiWebPDecoderTest, |
| testing::ValuesIn(kTestCases), |
| vaapi_test_utils::TestParamToString); |
| |
| } // namespace media |