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.
5 #include "content/common/gpu/media/vaapi_jpeg_decode_accelerator.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "base/trace_event/trace_event.h"
12 #include "content/common/gpu/gpu_channel.h"
13 #include "content/common/gpu/media/vaapi_picture.h"
14 #include "media/base/video_frame.h"
15 #include "media/filters/jpeg_parser.h"
16 #include "third_party/libyuv/include/libyuv.h"
21 // UMA errors that the VaapiJpegDecodeAccelerator class reports.
22 enum VAJDADecoderFailure
{
24 VAJDA_DECODER_FAILURES_MAX
,
27 static void ReportToUMA(VAJDADecoderFailure failure
) {
28 UMA_HISTOGRAM_ENUMERATION("Media.VAJDA.DecoderFailure", failure
,
29 VAJDA_DECODER_FAILURES_MAX
);
32 static unsigned int VaSurfaceFormatForJpeg(
33 const media::JpegFrameHeader
& frame_header
) {
34 // The range of sampling factor is [1, 4]. Pack them into integer to make the
35 // matching code simpler. For example, 0x211 means the sampling factor are 2,
36 // 1, 1 for 3 components.
37 unsigned int h
= 0, v
= 0;
38 for (int i
= 0; i
< frame_header
.num_components
; i
++) {
39 DCHECK_LE(frame_header
.components
[i
].horizontal_sampling_factor
, 4);
40 DCHECK_LE(frame_header
.components
[i
].vertical_sampling_factor
, 4);
41 h
= h
<< 4 | frame_header
.components
[i
].horizontal_sampling_factor
;
42 v
= v
<< 4 | frame_header
.components
[i
].vertical_sampling_factor
;
45 switch (frame_header
.num_components
) {
47 return VA_RT_FORMAT_YUV400
;
49 case 3: // Y Cb Cr color image
50 // See https://en.wikipedia.org/wiki/Chroma_subsampling for the
51 // definition of these numbers.
52 if (h
== 0x211 && v
== 0x211)
53 return VA_RT_FORMAT_YUV420
;
55 if (h
== 0x211 && v
== 0x111)
56 return VA_RT_FORMAT_YUV422
;
58 if (h
== 0x111 && v
== 0x111)
59 return VA_RT_FORMAT_YUV444
;
61 if (h
== 0x411 && v
== 0x111)
62 return VA_RT_FORMAT_YUV411
;
64 DVLOG(1) << "Unsupported sampling factor: num_components="
65 << frame_header
.num_components
<< ", h=" << std::hex
<< h
73 VaapiJpegDecodeAccelerator::DecodeRequest::DecodeRequest(
74 const media::BitstreamBuffer
& bitstream_buffer
,
75 scoped_ptr
<base::SharedMemory
> shm
,
76 const scoped_refptr
<media::VideoFrame
>& video_frame
)
77 : bitstream_buffer(bitstream_buffer
),
79 video_frame(video_frame
) {
82 VaapiJpegDecodeAccelerator::DecodeRequest::~DecodeRequest() {
85 void VaapiJpegDecodeAccelerator::NotifyError(int32_t bitstream_buffer_id
,
87 DCHECK(task_runner_
->BelongsToCurrentThread());
88 DLOG(ERROR
) << "Notifying of error " << error
;
90 client_
->NotifyError(bitstream_buffer_id
, error
);
93 void VaapiJpegDecodeAccelerator::NotifyErrorFromDecoderThread(
94 int32_t bitstream_buffer_id
,
96 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
97 task_runner_
->PostTask(FROM_HERE
,
98 base::Bind(&VaapiJpegDecodeAccelerator::NotifyError
,
99 weak_this_
, bitstream_buffer_id
, error
));
102 void VaapiJpegDecodeAccelerator::VideoFrameReady(int32_t bitstream_buffer_id
) {
103 DCHECK(task_runner_
->BelongsToCurrentThread());
104 client_
->VideoFrameReady(bitstream_buffer_id
);
107 VaapiJpegDecodeAccelerator::VaapiJpegDecodeAccelerator(
108 const scoped_refptr
<base::SingleThreadTaskRunner
>& io_task_runner
)
109 : task_runner_(base::ThreadTaskRunnerHandle::Get()),
110 io_task_runner_(io_task_runner
),
111 decoder_thread_("VaapiJpegDecoderThread"),
112 va_surface_id_(VA_INVALID_SURFACE
),
113 weak_this_factory_(this) {
114 weak_this_
= weak_this_factory_
.GetWeakPtr();
117 VaapiJpegDecodeAccelerator::~VaapiJpegDecodeAccelerator() {
118 DCHECK(task_runner_
->BelongsToCurrentThread());
119 DVLOG(1) << "Destroying VaapiJpegDecodeAccelerator";
121 weak_this_factory_
.InvalidateWeakPtrs();
122 decoder_thread_
.Stop();
125 bool VaapiJpegDecodeAccelerator::Initialize(Client
* client
) {
126 DCHECK(task_runner_
->BelongsToCurrentThread());
131 VaapiWrapper::Create(VaapiWrapper::kDecode
, VAProfileJPEGBaseline
,
132 base::Bind(&ReportToUMA
, VAAPI_ERROR
));
134 if (!vaapi_wrapper_
.get()) {
135 DLOG(ERROR
) << "Failed initializing VAAPI";
139 if (!decoder_thread_
.Start()) {
140 DLOG(ERROR
) << "Failed to start decoding thread.";
143 decoder_task_runner_
= decoder_thread_
.task_runner();
148 bool VaapiJpegDecodeAccelerator::OutputPicture(
149 VASurfaceID va_surface_id
,
150 int32_t input_buffer_id
,
151 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
152 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
154 TRACE_EVENT1("jpeg", "VaapiJpegDecodeAccelerator::OutputPicture",
155 "input_buffer_id", input_buffer_id
);
157 DVLOG(3) << "Outputting VASurface " << va_surface_id
158 << " into video_frame associated with input buffer id "
162 VAImageFormat format
;
163 const uint32_t kI420Fourcc
= VA_FOURCC('I', '4', '2', '0');
164 memset(&image
, 0, sizeof(image
));
165 memset(&format
, 0, sizeof(format
));
166 format
.fourcc
= kI420Fourcc
;
167 format
.byte_order
= VA_LSB_FIRST
;
168 format
.bits_per_pixel
= 12; // 12 for I420
170 uint8_t* mem
= nullptr;
171 gfx::Size coded_size
= video_frame
->coded_size();
172 if (!vaapi_wrapper_
->GetVaImage(va_surface_id
, &format
, coded_size
, &image
,
173 reinterpret_cast<void**>(&mem
))) {
174 DLOG(ERROR
) << "Cannot get VAImage";
178 // Copy image content from VAImage to VideoFrame.
179 // The component order of VAImage I420 are Y, U, and V.
180 DCHECK_EQ(image
.num_planes
, 3u);
181 DCHECK_GE(image
.width
, coded_size
.width());
182 DCHECK_GE(image
.height
, coded_size
.height());
183 const uint8_t* src_y
= mem
+ image
.offsets
[0];
184 const uint8_t* src_u
= mem
+ image
.offsets
[1];
185 const uint8_t* src_v
= mem
+ image
.offsets
[2];
186 size_t src_y_stride
= image
.pitches
[0];
187 size_t src_u_stride
= image
.pitches
[1];
188 size_t src_v_stride
= image
.pitches
[2];
189 uint8_t* dst_y
= video_frame
->data(media::VideoFrame::kYPlane
);
190 uint8_t* dst_u
= video_frame
->data(media::VideoFrame::kUPlane
);
191 uint8_t* dst_v
= video_frame
->data(media::VideoFrame::kVPlane
);
192 size_t dst_y_stride
= video_frame
->stride(media::VideoFrame::kYPlane
);
193 size_t dst_u_stride
= video_frame
->stride(media::VideoFrame::kUPlane
);
194 size_t dst_v_stride
= video_frame
->stride(media::VideoFrame::kVPlane
);
196 if (libyuv::I420Copy(src_y
, src_y_stride
, // Y
197 src_u
, src_u_stride
, // U
198 src_v
, src_v_stride
, // V
199 dst_y
, dst_y_stride
, // Y
200 dst_u
, dst_u_stride
, // U
201 dst_v
, dst_v_stride
, // V
202 coded_size
.width(), coded_size
.height())) {
203 DLOG(ERROR
) << "I420Copy failed";
207 vaapi_wrapper_
->ReturnVaImage(&image
);
209 task_runner_
->PostTask(
210 FROM_HERE
, base::Bind(&VaapiJpegDecodeAccelerator::VideoFrameReady
,
211 weak_this_
, input_buffer_id
));
216 void VaapiJpegDecodeAccelerator::DecodeTask(
217 const scoped_ptr
<DecodeRequest
>& request
) {
218 DVLOG(3) << __func__
;
219 DCHECK(decoder_task_runner_
->BelongsToCurrentThread());
220 TRACE_EVENT0("jpeg", "DecodeTask");
222 media::JpegParseResult parse_result
;
223 if (!media::ParseJpegPicture(
224 reinterpret_cast<const uint8_t*>(request
->shm
->memory()),
225 request
->bitstream_buffer
.size(), &parse_result
)) {
226 DLOG(ERROR
) << "ParseJpegPicture failed";
227 NotifyErrorFromDecoderThread(request
->bitstream_buffer
.id(),
232 unsigned int new_va_rt_format
=
233 VaSurfaceFormatForJpeg(parse_result
.frame_header
);
234 if (!new_va_rt_format
) {
235 DLOG(ERROR
) << "Unsupported subsampling";
236 NotifyErrorFromDecoderThread(request
->bitstream_buffer
.id(),
241 // Reuse VASurface if size doesn't change.
242 gfx::Size
new_coded_size(parse_result
.frame_header
.coded_width
,
243 parse_result
.frame_header
.coded_height
);
244 if (new_coded_size
!= coded_size_
|| va_surface_id_
== VA_INVALID_SURFACE
||
245 new_va_rt_format
!= va_rt_format_
) {
246 vaapi_wrapper_
->DestroySurfaces();
247 va_surface_id_
= VA_INVALID_SURFACE
;
248 va_rt_format_
= new_va_rt_format
;
250 std::vector
<VASurfaceID
> va_surfaces
;
251 if (!vaapi_wrapper_
->CreateSurfaces(va_rt_format_
, new_coded_size
, 1,
253 LOG(ERROR
) << "Create VA surface failed";
254 NotifyErrorFromDecoderThread(request
->bitstream_buffer
.id(),
258 va_surface_id_
= va_surfaces
[0];
259 coded_size_
= new_coded_size
;
262 if (!VaapiJpegDecoder::Decode(vaapi_wrapper_
.get(), parse_result
,
264 LOG(ERROR
) << "Decode JPEG failed";
265 NotifyErrorFromDecoderThread(request
->bitstream_buffer
.id(),
270 if (!OutputPicture(va_surface_id_
, request
->bitstream_buffer
.id(),
271 request
->video_frame
)) {
272 LOG(ERROR
) << "Output picture failed";
273 NotifyErrorFromDecoderThread(request
->bitstream_buffer
.id(),
279 void VaapiJpegDecodeAccelerator::Decode(
280 const media::BitstreamBuffer
& bitstream_buffer
,
281 const scoped_refptr
<media::VideoFrame
>& video_frame
) {
282 DVLOG(3) << __func__
;
283 DCHECK(io_task_runner_
->BelongsToCurrentThread());
284 TRACE_EVENT1("jpeg", "Decode", "input_id", bitstream_buffer
.id());
286 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer
.id()
287 << " size: " << bitstream_buffer
.size();
288 scoped_ptr
<base::SharedMemory
> shm(
289 new base::SharedMemory(bitstream_buffer
.handle(), true));
291 if (!shm
->Map(bitstream_buffer
.size())) {
292 LOG(ERROR
) << "Failed to map input buffer";
293 NotifyErrorFromDecoderThread(bitstream_buffer
.id(), UNREADABLE_INPUT
);
297 scoped_ptr
<DecodeRequest
> request(
298 new DecodeRequest(bitstream_buffer
, shm
.Pass(), video_frame
));
300 decoder_task_runner_
->PostTask(
301 FROM_HERE
, base::Bind(&VaapiJpegDecodeAccelerator::DecodeTask
,
302 base::Unretained(this), base::Passed(&request
)));
305 bool VaapiJpegDecodeAccelerator::IsSupported() {
306 return VaapiWrapper::IsJpegDecodeSupported();
309 } // namespace content