1 // Copyright 2020 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
5 #include <va/va.h>
6
7 #include "base/files/file_util.h"
8 #include "media/base/video_types.h"
9 #include "media/gpu/vaapi/test/macros.h"
10 #include "media/gpu/vaapi/test/shared_va_surface.h"
11 #include "third_party/libyuv/include/libyuv.h"
12 #include "ui/gfx/codec/png_codec.h"
13
14 namespace media {
15 namespace vaapi_test {
16
17 namespace {
18
19 // Derives the VAImage metadata and image data from |surface_id| in |display|,
20 // returning true on success.
DeriveImage(VADisplay display,VASurfaceID surface_id,VAImage * image,uint8_t ** image_data)21 bool DeriveImage(VADisplay display,
22 VASurfaceID surface_id,
23 VAImage* image,
24 uint8_t** image_data) {
25 VAStatus res = vaDeriveImage(display, surface_id, image);
26 VLOG_IF(2, (res != VA_STATUS_SUCCESS))
27 << "vaDeriveImage failed, VA error: " << vaErrorStr(res);
28
29 if (image->format.fourcc != VA_FOURCC_NV12) {
30 VLOG(2) << "Test decoder binary does not support derived surface format "
31 << "with fourcc " << media::FourccToString(image->format.fourcc);
32 res = vaDestroyImage(display, image->image_id);
33 VA_LOG_ASSERT(res, "vaDestroyImage");
34 return false;
35 }
36
37 res = vaMapBuffer(display, image->buf, reinterpret_cast<void**>(image_data));
38 VA_LOG_ASSERT(res, "vaMapBuffer");
39 return true;
40 }
41
42 // Maps the image data from |surface_id| in |display| with given |size| by
43 // attempting to derive into |image| and |image_data|, or creating an NV12
44 // VAImage to use with vaGetImage as fallback and setting |image| and
45 // |image_data| accordingly.
GetSurfaceImage(VADisplay display,VASurfaceID surface_id,const gfx::Size size,VAImage * image,uint8_t ** image_data)46 void GetSurfaceImage(VADisplay display,
47 VASurfaceID surface_id,
48 const gfx::Size size,
49 VAImage* image,
50 uint8_t** image_data) {
51 // First attempt to derive the image from the surface.
52 if (DeriveImage(display, surface_id, image, image_data))
53 return;
54
55 // Fall back to getting the image as NV12.
56 VAImageFormat format{
57 .fourcc = VA_FOURCC_NV12,
58 .byte_order = VA_LSB_FIRST,
59 .bits_per_pixel = 12,
60 };
61 VAStatus res =
62 vaCreateImage(display, &format, size.width(), size.height(), image);
63 VA_LOG_ASSERT(res, "vaCreateImage");
64
65 res = vaGetImage(display, surface_id, 0, 0, size.width(), size.height(),
66 image->image_id);
67 VA_LOG_ASSERT(res, "vaGetImage");
68
69 res = vaMapBuffer(display, image->buf, reinterpret_cast<void**>(image_data));
70 VA_LOG_ASSERT(res, "vaMapBuffer");
71 }
72
73 } // namespace
74
SharedVASurface(const VaapiDevice & va,VASurfaceID id,const gfx::Size & size,unsigned int format)75 SharedVASurface::SharedVASurface(const VaapiDevice& va,
76 VASurfaceID id,
77 const gfx::Size& size,
78 unsigned int format)
79 : va_(va), id_(id), size_(size), internal_va_format_(format) {}
80
81 // static
Create(const VaapiDevice & va,const gfx::Size & size,VASurfaceAttrib attrib)82 scoped_refptr<SharedVASurface> SharedVASurface::Create(const VaapiDevice& va,
83 const gfx::Size& size,
84 VASurfaceAttrib attrib) {
85 VASurfaceID surface_id;
86 VAStatus res =
87 vaCreateSurfaces(va.display(), va.internal_va_format(),
88 base::checked_cast<unsigned int>(size.width()),
89 base::checked_cast<unsigned int>(size.height()),
90 &surface_id, 1u, &attrib, 1u);
91 VA_LOG_ASSERT(res, "vaCreateSurfaces");
92 VLOG(1) << "created surface: " << surface_id;
93 return base::WrapRefCounted(
94 new SharedVASurface(va, surface_id, size, va.internal_va_format()));
95 }
96
~SharedVASurface()97 SharedVASurface::~SharedVASurface() {
98 VAStatus res =
99 vaDestroySurfaces(va_.display(), const_cast<VASurfaceID*>(&id_), 1u);
100 VA_LOG_ASSERT(res, "vaDestroySurfaces");
101 VLOG(1) << "destroyed surface " << id_;
102 }
103
SaveAsPNG(const std::string & path)104 void SharedVASurface::SaveAsPNG(const std::string& path) {
105 VAImage image;
106 uint8_t* image_data;
107
108 GetSurfaceImage(va_.display(), id_, size_, &image, &image_data);
109
110 // Convert the NV12 image data to ARGB and write to |path|.
111 const size_t argb_stride = image.width * 4;
112 auto argb_data = std::make_unique<uint8_t[]>(argb_stride * image.height);
113 const int convert_res = libyuv::NV12ToARGB(
114 (uint8_t*)(image_data + image.offsets[0]), image.pitches[0],
115 (uint8_t*)(image_data + image.offsets[1]), image.pitches[1],
116 argb_data.get(), argb_stride, image.width, image.height);
117 DCHECK(convert_res == 0);
118
119 std::vector<unsigned char> image_buffer;
120 const bool result = gfx::PNGCodec::Encode(
121 argb_data.get(), gfx::PNGCodec::FORMAT_BGRA, size_, argb_stride,
122 true /* discard_transparency */, std::vector<gfx::PNGCodec::Comment>(),
123 &image_buffer);
124 DCHECK(result);
125
126 LOG_ASSERT(base::WriteFile(base::FilePath(path), image_buffer));
127
128 // Clean up VA handles.
129 VAStatus res = vaUnmapBuffer(va_.display(), image.buf);
130 VA_LOG_ASSERT(res, "vaUnmapBuffer");
131
132 res = vaDestroyImage(va_.display(), image.image_id);
133 VA_LOG_ASSERT(res, "vaDestroyImage");
134 }
135
136 } // namespace vaapi_test
137 } // namespace media
138