vaapi_jpeg_decoder_unittest.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711
  1. // Copyright 2015 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include <stddef.h>
  5. #include <stdint.h>
  6. #include <va/va.h>
  7. #include <algorithm>
  8. #include <memory>
  9. #include <string>
  10. #include <vector>
  11. // This has to be included first.
  12. // See http://code.google.com/p/googletest/issues/detail?id=371
  13. #include "testing/gtest/include/gtest/gtest.h"
  14. #include "base/containers/span.h"
  15. #include "base/files/file_path.h"
  16. #include "base/files/file_util.h"
  17. #include "base/logging.h"
  18. #include "base/memory/scoped_refptr.h"
  19. #include "base/numerics/safe_conversions.h"
  20. #include "base/strings/string_util.h"
  21. #include "media/base/video_types.h"
  22. #include "media/gpu/test/local_gpu_memory_buffer_manager.h"
  23. #include "media/gpu/vaapi/test_utils.h"
  24. #include "media/gpu/vaapi/va_surface.h"
  25. #include "media/gpu/vaapi/vaapi_image_decoder.h"
  26. #include "media/gpu/vaapi/vaapi_image_decoder_test_common.h"
  27. #include "media/gpu/vaapi/vaapi_jpeg_decoder.h"
  28. #include "media/gpu/vaapi/vaapi_utils.h"
  29. #include "media/gpu/vaapi/vaapi_wrapper.h"
  30. #include "media/parsers/jpeg_parser.h"
  31. #include "third_party/libyuv/include/libyuv.h"
  32. #include "third_party/skia/include/core/SkColor.h"
  33. #include "third_party/skia/include/core/SkImageInfo.h"
  34. #include "third_party/skia/include/core/SkPixmap.h"
  35. #include "third_party/skia/include/encode/SkJpegEncoder.h"
  36. #include "ui/gfx/buffer_format_util.h"
  37. #include "ui/gfx/buffer_types.h"
  38. #include "ui/gfx/codec/jpeg_codec.h"
  39. #include "ui/gfx/geometry/rect.h"
  40. #include "ui/gfx/geometry/size.h"
  41. #include "ui/gfx/gpu_memory_buffer.h"
  42. #include "ui/gfx/linux/native_pixmap_dmabuf.h"
  43. #include "ui/gfx/native_pixmap_handle.h"
  44. namespace media {
  45. namespace {
  46. using DecodedImagePtr = std::unique_ptr<vaapi_test_utils::DecodedImage>;
  47. constexpr const char* kYuv422Filename = "pixel-1280x720.jpg";
  48. constexpr const char* kYuv420Filename = "pixel-1280x720-yuv420.jpg";
  49. constexpr const char* kYuv444Filename = "pixel-1280x720-yuv444.jpg";
  50. constexpr const char* kOddHeightImageFilename = "pixel-40x23-yuv420.jpg";
  51. constexpr const char* kOddWidthImageFilename = "pixel-41x22-yuv420.jpg";
  52. constexpr const char* kOddDimensionsImageFilename = "pixel-41x23-yuv420.jpg";
  53. const vaapi_test_utils::TestParam kVAImageTestCases[] = {
  54. {"YUV422", kYuv422Filename},
  55. {"YUV420", kYuv420Filename},
  56. {"YUV444", kYuv444Filename},
  57. };
  58. const vaapi_test_utils::TestParam kDmaBufTestCases[] = {
  59. {"YUV420", kYuv420Filename},
  60. {"OddHeightImage40x23", kOddHeightImageFilename},
  61. {"OddWidthImage41x22", kOddWidthImageFilename},
  62. {"OddDimensionsImage41x23", kOddDimensionsImageFilename},
  63. };
  64. constexpr double kMinSsim = 0.995;
  65. // This file is not supported by the VAAPI, so we don't define expectations on
  66. // the decode result.
  67. constexpr const char* kUnsupportedFilename = "pixel-1280x720-grayscale.jpg";
  68. // The size of the minimum coded unit for a YUV 4:2:0 image (both the width and
  69. // the height of the MCU are the same for 4:2:0).
  70. constexpr int k420MCUSize = 16;
  71. // The largest maximum supported surface size we expect a driver to report for
  72. // JPEG decoding.
  73. constexpr gfx::Size kLargestSupportedSize(16 * 1024, 16 * 1024);
  74. // Decodes the given |encoded_image| using libyuv and returns the result as a
  75. // DecodedImage object. The decoded planes will be stored in |dest_*|.
  76. // Note however, that this function does not takes ownership of |dest_*| or
  77. // manage their memory.
  78. DecodedImagePtr GetSwDecode(base::span<const uint8_t> encoded_image,
  79. std::vector<uint8_t>* dest_y,
  80. std::vector<uint8_t>* dest_u,
  81. std::vector<uint8_t>* dest_v) {
  82. DCHECK(dest_y && dest_u && dest_v);
  83. JpegParseResult parse_result;
  84. const bool result = ParseJpegPicture(encoded_image.data(),
  85. encoded_image.size(), &parse_result);
  86. if (!result)
  87. return nullptr;
  88. const gfx::Size jpeg_size(
  89. base::strict_cast<int>(parse_result.frame_header.visible_width),
  90. base::strict_cast<int>(parse_result.frame_header.visible_height));
  91. if (jpeg_size.IsEmpty())
  92. return nullptr;
  93. const gfx::Size half_jpeg_size((jpeg_size.width() + 1) / 2,
  94. (jpeg_size.height() + 1) / 2);
  95. dest_y->resize(jpeg_size.GetArea());
  96. dest_u->resize(half_jpeg_size.GetArea());
  97. dest_v->resize(half_jpeg_size.GetArea());
  98. if (libyuv::ConvertToI420(
  99. encoded_image.data(), encoded_image.size(), dest_y->data(),
  100. jpeg_size.width(), dest_u->data(), half_jpeg_size.width(),
  101. dest_v->data(), half_jpeg_size.width(), 0, 0, jpeg_size.width(),
  102. jpeg_size.height(), jpeg_size.width(), jpeg_size.height(),
  103. libyuv::kRotate0, libyuv::FOURCC_MJPG) != 0) {
  104. return nullptr;
  105. }
  106. auto sw_decoded_jpeg = std::make_unique<vaapi_test_utils::DecodedImage>();
  107. sw_decoded_jpeg->fourcc = VA_FOURCC_I420;
  108. sw_decoded_jpeg->number_of_planes = 3u;
  109. sw_decoded_jpeg->size = jpeg_size;
  110. sw_decoded_jpeg->planes[0].data = dest_y->data();
  111. sw_decoded_jpeg->planes[0].stride = jpeg_size.width();
  112. sw_decoded_jpeg->planes[1].data = dest_u->data();
  113. sw_decoded_jpeg->planes[1].stride = half_jpeg_size.width();
  114. sw_decoded_jpeg->planes[2].data = dest_v->data();
  115. sw_decoded_jpeg->planes[2].stride = half_jpeg_size.width();
  116. return sw_decoded_jpeg;
  117. }
  118. // Generates a checkerboard pattern as a JPEG image of a specified |size| and
  119. // |subsampling| format. Returns an empty vector on failure.
  120. std::vector<unsigned char> GenerateJpegImage(
  121. const gfx::Size& size,
  122. SkJpegEncoder::Downsample subsampling = SkJpegEncoder::Downsample::k420) {
  123. DCHECK(!size.IsEmpty());
  124. // First build a raw RGBA image of the given size with a checkerboard pattern.
  125. const SkImageInfo image_info = SkImageInfo::Make(
  126. size.width(), size.height(), SkColorType::kRGBA_8888_SkColorType,
  127. SkAlphaType::kOpaque_SkAlphaType);
  128. const size_t byte_size = image_info.computeMinByteSize();
  129. if (byte_size == SIZE_MAX)
  130. return {};
  131. const size_t stride = image_info.minRowBytes();
  132. DCHECK_EQ(4, SkColorTypeBytesPerPixel(image_info.colorType()));
  133. DCHECK_EQ(4 * size.width(), base::checked_cast<int>(stride));
  134. constexpr gfx::Size kCheckerRectSize(3, 5);
  135. std::vector<uint8_t> rgba_data(byte_size);
  136. uint8_t* data = rgba_data.data();
  137. for (int y = 0; y < size.height(); y++) {
  138. const bool y_bit = (((y / kCheckerRectSize.height()) & 0x1) == 0);
  139. for (int x = 0; x < base::checked_cast<int>(stride); x += 4) {
  140. const bool x_bit = (((x / kCheckerRectSize.width()) & 0x1) == 0);
  141. const SkColor color = (x_bit != y_bit) ? SK_ColorBLUE : SK_ColorMAGENTA;
  142. data[x + 0] = SkColorGetR(color);
  143. data[x + 1] = SkColorGetG(color);
  144. data[x + 2] = SkColorGetB(color);
  145. data[x + 3] = SkColorGetA(color);
  146. }
  147. data += stride;
  148. }
  149. // Now, encode it as a JPEG.
  150. //
  151. // TODO(andrescj): if this generates a large enough image (in terms of byte
  152. // size), it will be decoded incorrectly in AMD Stoney Ridge (see
  153. // b/127874877). When that's resolved, change the quality here to 100 so that
  154. // the generated JPEG is large.
  155. std::vector<unsigned char> jpeg_data;
  156. if (gfx::JPEGCodec::Encode(
  157. SkPixmap(image_info, rgba_data.data(), stride) /* input */,
  158. 95 /* quality */, subsampling /* downsample */,
  159. &jpeg_data /* output */)) {
  160. return jpeg_data;
  161. }
  162. return {};
  163. }
  164. // Rounds |n| to the greatest multiple of |m| that is less than or equal to |n|.
  165. int RoundDownToMultiple(int n, int m) {
  166. DCHECK_GE(n, 0);
  167. DCHECK_GT(m, 0);
  168. return (n / m) * m;
  169. }
  170. // Rounds |n| to the smallest multiple of |m| that is greater than or equal to
  171. // |n|.
  172. int RoundUpToMultiple(int n, int m) {
  173. DCHECK_GE(n, 0);
  174. DCHECK_GT(m, 0);
  175. if (n % m == 0)
  176. return n;
  177. base::CheckedNumeric<int> safe_n(n);
  178. safe_n += m;
  179. return RoundDownToMultiple(safe_n.ValueOrDie(), m);
  180. }
  181. // Given a minimum supported surface dimension (width or height) value
  182. // |min_surface_supported|, this function returns a non-zero coded dimension of
  183. // a 4:2:0 JPEG image that would not be supported because the dimension is right
  184. // below the supported value. For example, if |min_surface_supported| is 19,
  185. // this function should return 16 because for a 4:2:0 image, both coded
  186. // dimensions should be multiples of 16. If an unsupported dimension was found
  187. // (i.e., |min_surface_supported| > 16), this function returns true, false
  188. // otherwise.
  189. bool GetMinUnsupportedDimension(int min_surface_supported,
  190. int* min_unsupported) {
  191. if (min_surface_supported <= k420MCUSize)
  192. return false;
  193. *min_unsupported =
  194. RoundDownToMultiple(min_surface_supported - 1, k420MCUSize);
  195. return true;
  196. }
  197. // Given a minimum supported surface dimension (width or height) value
  198. // |min_surface_supported|, this function returns a non-zero coded dimension of
  199. // a 4:2:0 JPEG image that would be supported because the dimension is at least
  200. // the minimum. For example, if |min_surface_supported| is 35, this function
  201. // should return 48 because for a 4:2:0 image, both coded dimensions should be
  202. // multiples of 16.
  203. int GetMinSupportedDimension(int min_surface_supported) {
  204. LOG_ASSERT(min_surface_supported > 0);
  205. return RoundUpToMultiple(min_surface_supported, k420MCUSize);
  206. }
  207. // Given a maximum supported surface dimension (width or height) value
  208. // |max_surface_supported|, this function returns the coded dimension of a 4:2:0
  209. // JPEG image that would be supported because the dimension is at most the
  210. // maximum. For example, if |max_surface_supported| is 65, this function
  211. // should return 64 because for a 4:2:0 image, both coded dimensions should be
  212. // multiples of 16.
  213. int GetMaxSupportedDimension(int max_surface_supported) {
  214. return RoundDownToMultiple(max_surface_supported, k420MCUSize);
  215. }
  216. } // namespace
  217. class VaapiJpegDecoderTest : public VaapiImageDecoderTestCommon {
  218. protected:
  219. VaapiJpegDecoderTest()
  220. : VaapiImageDecoderTestCommon(std::make_unique<VaapiJpegDecoder>()) {}
  221. std::unique_ptr<ScopedVAImage> Decode(
  222. base::span<const uint8_t> encoded_image,
  223. uint32_t preferred_fourcc,
  224. VaapiImageDecodeStatus* status = nullptr);
  225. std::unique_ptr<ScopedVAImage> Decode(
  226. base::span<const uint8_t> encoded_image,
  227. VaapiImageDecodeStatus* status = nullptr);
  228. };
  229. std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
  230. base::span<const uint8_t> encoded_image,
  231. uint32_t preferred_fourcc,
  232. VaapiImageDecodeStatus* status) {
  233. const VaapiImageDecodeStatus decode_status = Decoder()->Decode(encoded_image);
  234. EXPECT_EQ(!!Decoder()->GetScopedVASurface(),
  235. decode_status == VaapiImageDecodeStatus::kSuccess);
  236. // Still try to get image when decode fails.
  237. VaapiImageDecodeStatus image_status;
  238. std::unique_ptr<ScopedVAImage> scoped_image;
  239. scoped_image = static_cast<VaapiJpegDecoder*>(Decoder())->GetImage(
  240. preferred_fourcc, &image_status);
  241. EXPECT_EQ(!!scoped_image, image_status == VaapiImageDecodeStatus::kSuccess);
  242. // Return the first fail status.
  243. if (status) {
  244. *status = decode_status != VaapiImageDecodeStatus::kSuccess ? decode_status
  245. : image_status;
  246. }
  247. return scoped_image;
  248. }
  249. std::unique_ptr<ScopedVAImage> VaapiJpegDecoderTest::Decode(
  250. base::span<const uint8_t> encoded_image,
  251. VaapiImageDecodeStatus* status) {
  252. return Decode(encoded_image, VA_FOURCC_I420, status);
  253. }
  254. // The intention of this test is to ensure that the workarounds added in
  255. // VaapiWrapper::GetJpegDecodeSuitableImageFourCC() don't result in an
  256. // unsupported image format.
  257. TEST_F(VaapiJpegDecoderTest, MinimalImageFormatSupport) {
  258. // All drivers should support at least I420.
  259. VAImageFormat i420_format{};
  260. i420_format.fourcc = VA_FOURCC_I420;
  261. ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(i420_format));
  262. // Additionally, the mesa VAAPI driver should support YV12, NV12 and YUYV.
  263. ASSERT_NE(VAImplementation::kInvalid, VaapiWrapper::GetImplementationType());
  264. if (VaapiWrapper::GetImplementationType() == VAImplementation::kMesaGallium) {
  265. VAImageFormat yv12_format{};
  266. yv12_format.fourcc = VA_FOURCC_YV12;
  267. ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(yv12_format));
  268. VAImageFormat nv12_format{};
  269. nv12_format.fourcc = VA_FOURCC_NV12;
  270. ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(nv12_format));
  271. VAImageFormat yuyv_format{};
  272. yuyv_format.fourcc = VA_FOURCC('Y', 'U', 'Y', 'V');
  273. ASSERT_TRUE(VaapiWrapper::IsImageFormatSupported(yuyv_format));
  274. }
  275. }
  276. TEST_P(VaapiJpegDecoderTest, DecodeSucceeds) {
  277. base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
  278. std::string jpeg_data;
  279. ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
  280. << "failed to read input data from " << input_file.value();
  281. const auto encoded_image = base::as_bytes(base::make_span(jpeg_data));
  282. // Skip the image if the VAAPI driver doesn't claim to support its chroma
  283. // subsampling format. However, we expect at least 4:2:0 and 4:2:2 support.
  284. const VaapiWrapper::InternalFormats supported_internal_formats =
  285. VaapiWrapper::GetDecodeSupportedInternalFormats(VAProfileJPEGBaseline);
  286. ASSERT_TRUE(supported_internal_formats.yuv420);
  287. ASSERT_TRUE(supported_internal_formats.yuv422);
  288. JpegParseResult parse_result;
  289. ASSERT_TRUE(ParseJpegPicture(encoded_image.data(), encoded_image.size(),
  290. &parse_result));
  291. const unsigned int rt_format =
  292. VaSurfaceFormatForJpeg(parse_result.frame_header);
  293. ASSERT_NE(kInvalidVaRtFormat, rt_format);
  294. if (!VaapiWrapper::IsDecodingSupportedForInternalFormat(VAProfileJPEGBaseline,
  295. rt_format)) {
  296. GTEST_SKIP();
  297. }
  298. // Note that this test together with
  299. // VaapiJpegDecoderTest.MinimalImageFormatSupport gives us two guarantees:
  300. //
  301. // 1) Every combination of supported internal format (returned by
  302. // GetJpegDecodeSupportedInternalFormats()) and supported image format
  303. // works with vaGetImage() (for JPEG decoding).
  304. //
  305. // 2) The FOURCC returned by VaapiWrapper::GetJpegDecodeSuitableImageFourCC()
  306. // corresponds to a supported image format.
  307. //
  308. // Note that we expect VA_FOURCC_I420 and VA_FOURCC_NV12 support in all
  309. // drivers.
  310. const std::vector<VAImageFormat>& supported_image_formats =
  311. VaapiWrapper::GetSupportedImageFormatsForTesting();
  312. EXPECT_GE(supported_image_formats.size(), 2u);
  313. VAImageFormat i420_format{};
  314. i420_format.fourcc = VA_FOURCC_I420;
  315. EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(i420_format));
  316. VAImageFormat nv12_format{};
  317. nv12_format.fourcc = VA_FOURCC_NV12;
  318. EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(nv12_format));
  319. // Decode the image using libyuv. Using |temp_*| for resource management.
  320. std::vector<uint8_t> temp_y;
  321. std::vector<uint8_t> temp_u;
  322. std::vector<uint8_t> temp_v;
  323. DecodedImagePtr sw_decoded_jpeg =
  324. GetSwDecode(encoded_image, &temp_y, &temp_u, &temp_v);
  325. ASSERT_TRUE(sw_decoded_jpeg);
  326. // Now run the comparison between the sw and hw image decodes for the
  327. // supported formats.
  328. for (const auto& image_format : supported_image_formats) {
  329. std::unique_ptr<ScopedVAImage> scoped_image =
  330. Decode(encoded_image, image_format.fourcc);
  331. ASSERT_TRUE(scoped_image);
  332. ASSERT_TRUE(Decoder()->GetScopedVASurface());
  333. EXPECT_TRUE(Decoder()->GetScopedVASurface()->IsValid());
  334. EXPECT_EQ(Decoder()->GetScopedVASurface()->size().width(),
  335. base::strict_cast<int>(parse_result.frame_header.visible_width));
  336. EXPECT_EQ(Decoder()->GetScopedVASurface()->size().height(),
  337. base::strict_cast<int>(parse_result.frame_header.visible_height));
  338. EXPECT_EQ(rt_format, Decoder()->GetScopedVASurface()->format());
  339. const uint32_t actual_fourcc = scoped_image->image()->format.fourcc;
  340. // TODO(andrescj): CompareImages() only supports I420, NV12, YUY2, and YUYV.
  341. // Make it support all the image formats we expect and call it
  342. // unconditionally.
  343. if (actual_fourcc == VA_FOURCC_I420 || actual_fourcc == VA_FOURCC_NV12 ||
  344. actual_fourcc == VA_FOURCC_YUY2 ||
  345. actual_fourcc == VA_FOURCC('Y', 'U', 'Y', 'V')) {
  346. ASSERT_TRUE(vaapi_test_utils::CompareImages(
  347. *sw_decoded_jpeg,
  348. vaapi_test_utils::ScopedVAImageToDecodedImage(scoped_image.get()),
  349. kMinSsim));
  350. }
  351. DVLOG(1) << "Got a " << FourccToString(scoped_image->image()->format.fourcc)
  352. << " VAImage (preferred " << FourccToString(image_format.fourcc)
  353. << ")";
  354. }
  355. }
  356. // Make sure that JPEGs whose size is in the supported size range are decoded
  357. // successfully.
  358. //
  359. // TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
  360. // TODO(andrescj): consider recreating the decoder for every size so that no
  361. // state is retained.
  362. TEST_F(VaapiJpegDecoderTest, DecodeSucceedsForSupportedSizes) {
  363. gfx::Size min_supported_size;
  364. gfx::Size max_supported_size;
  365. ASSERT_TRUE(VaapiWrapper::GetSupportedResolutions(
  366. VAProfileJPEGBaseline, VaapiWrapper::CodecMode::kDecode,
  367. min_supported_size, max_supported_size));
  368. // Ensure the maximum supported size is reasonable.
  369. ASSERT_GE(max_supported_size.width(), min_supported_size.width());
  370. ASSERT_GE(max_supported_size.height(), min_supported_size.height());
  371. ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
  372. ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
  373. // The actual image min/max coded size depends on the subsampling format. For
  374. // example, for 4:2:0, the coded dimensions must be multiples of 16. So, if
  375. // the minimum surface size is, e.g., 18x18, the minimum image coded size is
  376. // 32x32. Get those actual min/max coded sizes now.
  377. const int min_width = GetMinSupportedDimension(min_supported_size.width());
  378. const int min_height = GetMinSupportedDimension(min_supported_size.height());
  379. const int max_width = GetMaxSupportedDimension(max_supported_size.width());
  380. const int max_height = GetMaxSupportedDimension(max_supported_size.height());
  381. ASSERT_GT(max_width, 0);
  382. ASSERT_GT(max_height, 0);
  383. const std::vector<gfx::Size> test_sizes = {{min_width, min_height},
  384. {min_width, max_height},
  385. {max_width, min_height},
  386. {max_width, max_height}};
  387. for (const auto& test_size : test_sizes) {
  388. const std::vector<unsigned char> jpeg_data = GenerateJpegImage(test_size);
  389. auto jpeg_data_span = base::as_bytes(base::make_span(jpeg_data));
  390. ASSERT_FALSE(jpeg_data.empty());
  391. std::unique_ptr<ScopedVAImage> scoped_image = Decode(jpeg_data_span);
  392. ASSERT_TRUE(scoped_image)
  393. << "Decode unexpectedly failed for size = " << test_size.ToString();
  394. ASSERT_TRUE(Decoder()->GetScopedVASurface());
  395. EXPECT_TRUE(Decoder()->GetScopedVASurface()->IsValid());
  396. // Decode the image using libyuv. Using |temp_*| for resource management.
  397. std::vector<uint8_t> temp_y;
  398. std::vector<uint8_t> temp_u;
  399. std::vector<uint8_t> temp_v;
  400. DecodedImagePtr sw_decoded_jpeg =
  401. GetSwDecode(jpeg_data_span, &temp_y, &temp_u, &temp_v);
  402. ASSERT_TRUE(sw_decoded_jpeg);
  403. EXPECT_TRUE(vaapi_test_utils::CompareImages(
  404. *sw_decoded_jpeg,
  405. vaapi_test_utils::ScopedVAImageToDecodedImage(scoped_image.get()),
  406. kMinSsim))
  407. << "The SSIM check unexpectedly failed for size = "
  408. << test_size.ToString();
  409. }
  410. }
  411. class VaapiJpegDecoderWithDmaBufsTest : public VaapiJpegDecoderTest {
  412. public:
  413. VaapiJpegDecoderWithDmaBufsTest() = default;
  414. ~VaapiJpegDecoderWithDmaBufsTest() override = default;
  415. };
  416. // TODO(andrescj): test other JPEG formats besides YUV 4:2:0.
  417. TEST_P(VaapiJpegDecoderWithDmaBufsTest, DecodeSucceeds) {
  418. ASSERT_NE(VAImplementation::kInvalid, VaapiWrapper::GetImplementationType());
  419. if (VaapiWrapper::GetImplementationType() == VAImplementation::kMesaGallium) {
  420. // TODO(crbug.com/974438): until we support surfaces with multiple buffer
  421. // objects, the AMD driver fails this test.
  422. GTEST_SKIP();
  423. }
  424. base::FilePath input_file = FindTestDataFilePath(GetParam().filename);
  425. std::string jpeg_data;
  426. ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
  427. << "failed to read input data from " << input_file.value();
  428. const auto encoded_image = base::as_bytes(base::make_span(jpeg_data));
  429. // Decode into a VAAPI-allocated surface.
  430. const VaapiImageDecodeStatus decode_status = Decoder()->Decode(encoded_image);
  431. EXPECT_EQ(VaapiImageDecodeStatus::kSuccess, decode_status);
  432. ASSERT_TRUE(Decoder()->GetScopedVASurface());
  433. const gfx::Size va_surface_visible_size =
  434. Decoder()->GetScopedVASurface()->size();
  435. // The size stored in the ScopedVASurface should be the visible size of the
  436. // JPEG.
  437. JpegParseResult parse_result;
  438. ASSERT_TRUE(ParseJpegPicture(encoded_image.data(), encoded_image.size(),
  439. &parse_result));
  440. EXPECT_EQ(gfx::Size(parse_result.frame_header.visible_width,
  441. parse_result.frame_header.visible_height),
  442. va_surface_visible_size);
  443. // Export the surface.
  444. VaapiImageDecodeStatus export_status = VaapiImageDecodeStatus::kInvalidState;
  445. std::unique_ptr<NativePixmapAndSizeInfo> exported_pixmap =
  446. Decoder()->ExportAsNativePixmapDmaBuf(&export_status);
  447. EXPECT_EQ(VaapiImageDecodeStatus::kSuccess, export_status);
  448. ASSERT_TRUE(exported_pixmap);
  449. ASSERT_TRUE(exported_pixmap->pixmap);
  450. EXPECT_FALSE(Decoder()->GetScopedVASurface());
  451. // For JPEG decoding, the size of the surface we request is the coded size of
  452. // the JPEG. Make sure the surface contains that coded area.
  453. EXPECT_TRUE(gfx::Rect(exported_pixmap->va_surface_resolution)
  454. .Contains(gfx::Rect(parse_result.frame_header.coded_width,
  455. parse_result.frame_header.coded_height)));
  456. // Make sure the visible area is contained by the surface.
  457. EXPECT_EQ(va_surface_visible_size, exported_pixmap->pixmap->GetBufferSize());
  458. EXPECT_FALSE(exported_pixmap->va_surface_resolution.IsEmpty());
  459. EXPECT_FALSE(exported_pixmap->pixmap->GetBufferSize().IsEmpty());
  460. ASSERT_TRUE(
  461. gfx::Rect(exported_pixmap->va_surface_resolution)
  462. .Contains(gfx::Rect(exported_pixmap->pixmap->GetBufferSize())));
  463. // TODO(andrescj): we could get a better lower bound based on the dimensions
  464. // and the format.
  465. ASSERT_GT(exported_pixmap->byte_size, 0u);
  466. // After exporting the surface, we should not be able to obtain a VAImage with
  467. // the decoded data.
  468. VAImageFormat i420_format{};
  469. i420_format.fourcc = VA_FOURCC_I420;
  470. EXPECT_TRUE(VaapiWrapper::IsImageFormatSupported(i420_format));
  471. VaapiImageDecodeStatus image_status = VaapiImageDecodeStatus::kSuccess;
  472. EXPECT_FALSE(static_cast<VaapiJpegDecoder*>(Decoder())->GetImage(
  473. i420_format.fourcc, &image_status));
  474. EXPECT_EQ(VaapiImageDecodeStatus::kInvalidState, image_status);
  475. // Workaround: in order to import and map the pixmap using minigbm when the
  476. // format is gfx::BufferFormat::YVU_420, we need to reorder the planes so that
  477. // the offsets are in increasing order as assumed in https://bit.ly/2NLubNN.
  478. // Otherwise, we get a validation error. In essence, we're making minigbm
  479. // think that it is mapping a YVU_420, but it's actually mapping a YUV_420.
  480. //
  481. // TODO(andrescj): revisit this once crrev.com/c/1573718 lands.
  482. gfx::NativePixmapHandle handle = exported_pixmap->pixmap->ExportHandle();
  483. ASSERT_EQ(gfx::NumberOfPlanesForLinearBufferFormat(
  484. exported_pixmap->pixmap->GetBufferFormat()),
  485. handle.planes.size());
  486. if (exported_pixmap->pixmap->GetBufferFormat() == gfx::BufferFormat::YVU_420)
  487. std::swap(handle.planes[1], handle.planes[2]);
  488. LocalGpuMemoryBufferManager gpu_memory_buffer_manager;
  489. std::unique_ptr<gfx::GpuMemoryBuffer> gpu_memory_buffer =
  490. gpu_memory_buffer_manager.ImportDmaBuf(
  491. handle, exported_pixmap->pixmap->GetBufferSize(),
  492. exported_pixmap->pixmap->GetBufferFormat());
  493. ASSERT_TRUE(gpu_memory_buffer);
  494. ASSERT_TRUE(gpu_memory_buffer->Map());
  495. vaapi_test_utils::DecodedImage decoded_image{};
  496. const gfx::BufferFormat format = gpu_memory_buffer->GetFormat();
  497. if (format == gfx::BufferFormat::YVU_420) {
  498. decoded_image.fourcc = VA_FOURCC_I420;
  499. decoded_image.number_of_planes = 3u;
  500. } else if (format == gfx::BufferFormat::YUV_420_BIPLANAR) {
  501. decoded_image.fourcc = VA_FOURCC_NV12;
  502. decoded_image.number_of_planes = 2u;
  503. } else {
  504. ASSERT_TRUE(false) << "Unsupported format "
  505. << gfx::BufferFormatToString(format);
  506. }
  507. decoded_image.size = gpu_memory_buffer->GetSize();
  508. for (size_t plane = 0u;
  509. plane < base::strict_cast<size_t>(decoded_image.number_of_planes);
  510. plane++) {
  511. decoded_image.planes[plane].data =
  512. static_cast<uint8_t*>(gpu_memory_buffer->memory(plane));
  513. decoded_image.planes[plane].stride = gpu_memory_buffer->stride(plane);
  514. }
  515. // Decode the image using libyuv. Using |temp_*| for resource management.
  516. std::vector<uint8_t> temp_y;
  517. std::vector<uint8_t> temp_u;
  518. std::vector<uint8_t> temp_v;
  519. DecodedImagePtr sw_decoded_jpeg =
  520. GetSwDecode(encoded_image, &temp_y, &temp_u, &temp_v);
  521. ASSERT_TRUE(sw_decoded_jpeg);
  522. EXPECT_TRUE(vaapi_test_utils::CompareImages(*sw_decoded_jpeg, decoded_image,
  523. kMinSsim));
  524. gpu_memory_buffer->Unmap();
  525. }
  526. // Make sure that JPEGs whose size is below the supported size range are
  527. // rejected.
  528. //
  529. // TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
  530. TEST_F(VaapiJpegDecoderTest, DecodeFailsForBelowMinSize) {
  531. gfx::Size min_supported_size;
  532. gfx::Size max_supported_size;
  533. ASSERT_TRUE(VaapiWrapper::GetSupportedResolutions(
  534. VAProfileJPEGBaseline, VaapiWrapper::CodecMode::kDecode,
  535. min_supported_size, max_supported_size));
  536. // Ensure the maximum supported size is reasonable.
  537. ASSERT_GE(max_supported_size.width(), min_supported_size.width());
  538. ASSERT_GE(max_supported_size.height(), min_supported_size.height());
  539. ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
  540. ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
  541. // Get good (supported) minimum dimensions.
  542. const int good_width = GetMinSupportedDimension(min_supported_size.width());
  543. ASSERT_LE(good_width, max_supported_size.width());
  544. const int good_height = GetMinSupportedDimension(min_supported_size.height());
  545. ASSERT_LE(good_height, max_supported_size.height());
  546. // Get bad (unsupported) dimensions.
  547. int bad_width;
  548. const bool got_bad_width =
  549. GetMinUnsupportedDimension(min_supported_size.width(), &bad_width);
  550. int bad_height;
  551. const bool got_bad_height =
  552. GetMinUnsupportedDimension(min_supported_size.height(), &bad_height);
  553. // Now build and test the good/bad combinations that we expect will fail.
  554. std::vector<gfx::Size> test_sizes;
  555. if (got_bad_width)
  556. test_sizes.push_back({bad_width, good_height});
  557. if (got_bad_height)
  558. test_sizes.push_back({good_width, bad_height});
  559. if (got_bad_width && got_bad_height)
  560. test_sizes.push_back({bad_width, bad_height});
  561. for (const auto& test_size : test_sizes) {
  562. const std::vector<unsigned char> jpeg_data = GenerateJpegImage(test_size);
  563. ASSERT_FALSE(jpeg_data.empty());
  564. VaapiImageDecodeStatus status = VaapiImageDecodeStatus::kSuccess;
  565. ASSERT_FALSE(Decode(base::as_bytes(base::make_span(jpeg_data)), &status))
  566. << "Decode unexpectedly succeeded for size = " << test_size.ToString();
  567. EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
  568. EXPECT_FALSE(Decoder()->GetScopedVASurface());
  569. }
  570. }
  571. // Make sure that JPEGs whose size is above the supported size range are
  572. // rejected.
  573. //
  574. // TODO(andrescj): for now, this assumes 4:2:0. Handle other formats.
  575. TEST_F(VaapiJpegDecoderTest, DecodeFailsForAboveMaxSize) {
  576. gfx::Size min_supported_size;
  577. gfx::Size max_supported_size;
  578. ASSERT_TRUE(VaapiWrapper::GetSupportedResolutions(
  579. VAProfileJPEGBaseline, VaapiWrapper::CodecMode::kDecode,
  580. min_supported_size, max_supported_size));
  581. // Ensure the maximum supported size is reasonable.
  582. ASSERT_GE(max_supported_size.width(), min_supported_size.width());
  583. ASSERT_GE(max_supported_size.height(), min_supported_size.height());
  584. ASSERT_LE(max_supported_size.width(), kLargestSupportedSize.width());
  585. ASSERT_LE(max_supported_size.height(), kLargestSupportedSize.height());
  586. // Get good (supported) maximum dimensions.
  587. const int good_width = GetMaxSupportedDimension(max_supported_size.width());
  588. ASSERT_GE(good_width, min_supported_size.width());
  589. ASSERT_GT(good_width, 0);
  590. const int good_height = GetMaxSupportedDimension(max_supported_size.height());
  591. ASSERT_GE(good_height, min_supported_size.height());
  592. ASSERT_GT(good_height, 0);
  593. // Get bad (unsupported) dimensions.
  594. const int bad_width =
  595. RoundUpToMultiple(max_supported_size.width() + 1, k420MCUSize);
  596. const int bad_height =
  597. RoundUpToMultiple(max_supported_size.height() + 1, k420MCUSize);
  598. // Now build and test the good/bad combinations that we expect will fail.
  599. const std::vector<gfx::Size> test_sizes = {{bad_width, good_height},
  600. {good_width, bad_height},
  601. {bad_width, bad_height}};
  602. for (const auto& test_size : test_sizes) {
  603. const std::vector<unsigned char> jpeg_data = GenerateJpegImage(test_size);
  604. ASSERT_FALSE(jpeg_data.empty());
  605. VaapiImageDecodeStatus status = VaapiImageDecodeStatus::kSuccess;
  606. ASSERT_FALSE(Decode(base::as_bytes(base::make_span(jpeg_data)), &status))
  607. << "Decode unexpectedly succeeded for size = " << test_size.ToString();
  608. EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedImage, status);
  609. EXPECT_FALSE(Decoder()->GetScopedVASurface());
  610. }
  611. }
  612. TEST_F(VaapiJpegDecoderTest, DecodeFails) {
  613. // A grayscale image (4:0:0) should be rejected.
  614. base::FilePath input_file = FindTestDataFilePath(kUnsupportedFilename);
  615. std::string jpeg_data;
  616. ASSERT_TRUE(base::ReadFileToString(input_file, &jpeg_data))
  617. << "failed to read input data from " << input_file.value();
  618. VaapiImageDecodeStatus status = VaapiImageDecodeStatus::kSuccess;
  619. ASSERT_FALSE(Decode(base::as_bytes(base::make_span(jpeg_data)), &status));
  620. EXPECT_EQ(VaapiImageDecodeStatus::kUnsupportedSubsampling, status);
  621. EXPECT_FALSE(Decoder()->GetScopedVASurface());
  622. }
  623. INSTANTIATE_TEST_SUITE_P(All,
  624. VaapiJpegDecoderTest,
  625. testing::ValuesIn(kVAImageTestCases),
  626. vaapi_test_utils::TestParamToString);
  627. INSTANTIATE_TEST_SUITE_P(All,
  628. VaapiJpegDecoderWithDmaBufsTest,
  629. testing::ValuesIn(kDmaBufTestCases),
  630. vaapi_test_utils::TestParamToString);
  631. } // namespace media