1 // Copyright (c) 2012 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_video_decode_accelerator.h"
8 #include "base/logging.h"
9 #include "base/metrics/histogram.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/synchronization/waitable_event.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/common/gpu/gpu_channel.h"
15 #include "content/common/gpu/media/accelerated_video_decoder.h"
16 #include "content/common/gpu/media/h264_decoder.h"
17 #include "content/common/gpu/media/vaapi_picture.h"
18 #include "content/common/gpu/media/vp8_decoder.h"
19 #include "media/base/bind_to_current_loop.h"
20 #include "media/video/picture.h"
21 #include "third_party/libva/va/va_dec_vp8.h"
22 #include "ui/gl/gl_bindings.h"
23 #include "ui/gl/gl_image.h"
28 // UMA errors that the VaapiVideoDecodeAccelerator class reports.
29 enum VAVDADecoderFailure
{
31 VAVDA_DECODER_FAILURES_MAX
,
35 static void ReportToUMA(VAVDADecoderFailure failure
) {
36 UMA_HISTOGRAM_ENUMERATION("Media.VAVDA.DecoderFailure", failure
,
37 VAVDA_DECODER_FAILURES_MAX
);
40 #define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
44 NotifyError(error_code); \
49 class VaapiVideoDecodeAccelerator::VaapiDecodeSurface
50 : public base::RefCountedThreadSafe
<VaapiDecodeSurface
> {
52 VaapiDecodeSurface(int32 bitstream_id
,
53 const scoped_refptr
<VASurface
>& va_surface
);
55 int32
bitstream_id() const { return bitstream_id_
; }
56 scoped_refptr
<VASurface
> va_surface() { return va_surface_
; }
59 friend class base::RefCountedThreadSafe
<VaapiDecodeSurface
>;
60 ~VaapiDecodeSurface();
63 scoped_refptr
<VASurface
> va_surface_
;
66 VaapiVideoDecodeAccelerator::VaapiDecodeSurface::VaapiDecodeSurface(
68 const scoped_refptr
<VASurface
>& va_surface
)
69 : bitstream_id_(bitstream_id
), va_surface_(va_surface
) {
72 VaapiVideoDecodeAccelerator::VaapiDecodeSurface::~VaapiDecodeSurface() {
75 class VaapiH264Picture
: public H264Picture
{
77 VaapiH264Picture(const scoped_refptr
<
78 VaapiVideoDecodeAccelerator::VaapiDecodeSurface
>& dec_surface
);
80 VaapiH264Picture
* AsVaapiH264Picture() override
{ return this; }
81 scoped_refptr
<VaapiVideoDecodeAccelerator::VaapiDecodeSurface
> dec_surface() {
86 ~VaapiH264Picture() override
;
88 scoped_refptr
<VaapiVideoDecodeAccelerator::VaapiDecodeSurface
> dec_surface_
;
90 DISALLOW_COPY_AND_ASSIGN(VaapiH264Picture
);
93 VaapiH264Picture::VaapiH264Picture(const scoped_refptr
<
94 VaapiVideoDecodeAccelerator::VaapiDecodeSurface
>& dec_surface
)
95 : dec_surface_(dec_surface
) {
98 VaapiH264Picture::~VaapiH264Picture() {
101 class VaapiVideoDecodeAccelerator::VaapiH264Accelerator
102 : public H264Decoder::H264Accelerator
{
104 VaapiH264Accelerator(VaapiVideoDecodeAccelerator
* vaapi_dec
,
105 VaapiWrapper
* vaapi_wrapper
);
106 ~VaapiH264Accelerator() override
;
108 // H264Decoder::H264Accelerator implementation.
109 scoped_refptr
<H264Picture
> CreateH264Picture() override
;
111 bool SubmitFrameMetadata(const media::H264SPS
* sps
,
112 const media::H264PPS
* pps
,
114 const H264Picture::Vector
& ref_pic_listp0
,
115 const H264Picture::Vector
& ref_pic_listb0
,
116 const H264Picture::Vector
& ref_pic_listb1
,
117 const scoped_refptr
<H264Picture
>& pic
) override
;
119 bool SubmitSlice(const media::H264PPS
* pps
,
120 const media::H264SliceHeader
* slice_hdr
,
121 const H264Picture::Vector
& ref_pic_list0
,
122 const H264Picture::Vector
& ref_pic_list1
,
123 const scoped_refptr
<H264Picture
>& pic
,
125 size_t size
) override
;
127 bool SubmitDecode(const scoped_refptr
<H264Picture
>& pic
) override
;
128 bool OutputPicture(const scoped_refptr
<H264Picture
>& pic
) override
;
130 void Reset() override
;
133 scoped_refptr
<VaapiDecodeSurface
> H264PictureToVaapiDecodeSurface(
134 const scoped_refptr
<H264Picture
>& pic
);
136 void FillVAPicture(VAPictureH264
* va_pic
, scoped_refptr
<H264Picture
> pic
);
137 int FillVARefFramesFromDPB(const H264DPB
& dpb
,
138 VAPictureH264
* va_pics
,
141 VaapiWrapper
* vaapi_wrapper_
;
142 VaapiVideoDecodeAccelerator
* vaapi_dec_
;
144 DISALLOW_COPY_AND_ASSIGN(VaapiH264Accelerator
);
147 class VaapiVP8Picture
: public VP8Picture
{
149 VaapiVP8Picture(const scoped_refptr
<
150 VaapiVideoDecodeAccelerator::VaapiDecodeSurface
>& dec_surface
);
152 VaapiVP8Picture
* AsVaapiVP8Picture() override
{ return this; }
153 scoped_refptr
<VaapiVideoDecodeAccelerator::VaapiDecodeSurface
> dec_surface() {
158 ~VaapiVP8Picture() override
;
160 scoped_refptr
<VaapiVideoDecodeAccelerator::VaapiDecodeSurface
> dec_surface_
;
162 DISALLOW_COPY_AND_ASSIGN(VaapiVP8Picture
);
165 VaapiVP8Picture::VaapiVP8Picture(const scoped_refptr
<
166 VaapiVideoDecodeAccelerator::VaapiDecodeSurface
>& dec_surface
)
167 : dec_surface_(dec_surface
) {
170 VaapiVP8Picture::~VaapiVP8Picture() {
173 class VaapiVideoDecodeAccelerator::VaapiVP8Accelerator
174 : public VP8Decoder::VP8Accelerator
{
176 VaapiVP8Accelerator(VaapiVideoDecodeAccelerator
* vaapi_dec
,
177 VaapiWrapper
* vaapi_wrapper
);
178 ~VaapiVP8Accelerator() override
;
180 // VP8Decoder::VP8Accelerator implementation.
181 scoped_refptr
<VP8Picture
> CreateVP8Picture() override
;
183 bool SubmitDecode(const scoped_refptr
<VP8Picture
>& pic
,
184 const media::Vp8FrameHeader
* frame_hdr
,
185 const scoped_refptr
<VP8Picture
>& last_frame
,
186 const scoped_refptr
<VP8Picture
>& golden_frame
,
187 const scoped_refptr
<VP8Picture
>& alt_frame
) override
;
189 bool OutputPicture(const scoped_refptr
<VP8Picture
>& pic
) override
;
192 scoped_refptr
<VaapiDecodeSurface
> VP8PictureToVaapiDecodeSurface(
193 const scoped_refptr
<VP8Picture
>& pic
);
195 VaapiWrapper
* vaapi_wrapper_
;
196 VaapiVideoDecodeAccelerator
* vaapi_dec_
;
198 DISALLOW_COPY_AND_ASSIGN(VaapiVP8Accelerator
);
201 VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) {
204 VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() {
207 void VaapiVideoDecodeAccelerator::NotifyError(Error error
) {
208 if (message_loop_
!= base::MessageLoop::current()) {
209 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
210 message_loop_
->PostTask(FROM_HERE
, base::Bind(
211 &VaapiVideoDecodeAccelerator::NotifyError
, weak_this_
, error
));
215 // Post Cleanup() as a task so we don't recursively acquire lock_.
216 message_loop_
->PostTask(FROM_HERE
, base::Bind(
217 &VaapiVideoDecodeAccelerator::Cleanup
, weak_this_
));
219 LOG(ERROR
) << "Notifying of error " << error
;
221 client_
->NotifyError(error
);
222 client_ptr_factory_
.reset();
226 VaapiPicture
* VaapiVideoDecodeAccelerator::PictureById(
227 int32 picture_buffer_id
) {
228 Pictures::iterator it
= pictures_
.find(picture_buffer_id
);
229 if (it
== pictures_
.end()) {
230 LOG(ERROR
) << "Picture id " << picture_buffer_id
<< " does not exist";
234 return it
->second
.get();
237 VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
238 const base::Callback
<bool(void)>& make_context_current
,
239 const base::Callback
<void(uint32
, uint32
, scoped_refptr
<gfx::GLImage
>)>&
241 : make_context_current_(make_context_current
),
242 state_(kUninitialized
),
243 input_ready_(&lock_
),
244 surfaces_available_(&lock_
),
245 message_loop_(base::MessageLoop::current()),
246 decoder_thread_("VaapiDecoderThread"),
247 num_frames_at_client_(0),
248 num_stream_bufs_at_decoder_(0),
249 finish_flush_pending_(false),
250 awaiting_va_surfaces_recycle_(false),
251 requested_num_pics_(0),
252 bind_image_(bind_image
),
253 weak_this_factory_(this) {
254 weak_this_
= weak_this_factory_
.GetWeakPtr();
255 va_surface_release_cb_
= media::BindToCurrentLoop(
256 base::Bind(&VaapiVideoDecodeAccelerator::RecycleVASurfaceID
, weak_this_
));
259 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
260 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
263 bool VaapiVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile
,
265 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
267 client_ptr_factory_
.reset(new base::WeakPtrFactory
<Client
>(client
));
268 client_
= client_ptr_factory_
->GetWeakPtr();
270 base::AutoLock
auto_lock(lock_
);
271 DCHECK_EQ(state_
, kUninitialized
);
272 DVLOG(2) << "Initializing VAVDA, profile: " << profile
;
275 if (gfx::GetGLImplementation() != gfx::kGLImplementationDesktopGL
) {
276 DVLOG(1) << "HW video decode acceleration not available without "
280 #elif defined(USE_OZONE)
281 if (gfx::GetGLImplementation() != gfx::kGLImplementationEGLGLES2
) {
282 DVLOG(1) << "HW video decode acceleration not available without "
288 vaapi_wrapper_
= VaapiWrapper::CreateForVideoCodec(
289 VaapiWrapper::kDecode
, profile
, base::Bind(&ReportToUMA
, VAAPI_ERROR
));
291 if (!vaapi_wrapper_
.get()) {
292 DVLOG(1) << "Failed initializing VAAPI for profile " << profile
;
296 if (profile
>= media::H264PROFILE_MIN
&& profile
<= media::H264PROFILE_MAX
) {
297 h264_accelerator_
.reset(
298 new VaapiH264Accelerator(this, vaapi_wrapper_
.get()));
299 decoder_
.reset(new H264Decoder(h264_accelerator_
.get()));
300 } else if (profile
>= media::VP8PROFILE_MIN
&&
301 profile
<= media::VP8PROFILE_MAX
) {
302 vp8_accelerator_
.reset(new VaapiVP8Accelerator(this, vaapi_wrapper_
.get()));
303 decoder_
.reset(new VP8Decoder(vp8_accelerator_
.get()));
305 DLOG(ERROR
) << "Unsupported profile " << profile
;
309 CHECK(decoder_thread_
.Start());
310 decoder_thread_task_runner_
= decoder_thread_
.task_runner();
316 void VaapiVideoDecodeAccelerator::OutputPicture(
317 const scoped_refptr
<VASurface
>& va_surface
,
319 VaapiPicture
* picture
) {
320 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
322 int32 output_id
= picture
->picture_buffer_id();
324 TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface",
325 "input_id", input_id
,
326 "output_id", output_id
);
328 DVLOG(3) << "Outputting VASurface " << va_surface
->id()
329 << " into pixmap bound to picture buffer id " << output_id
;
331 RETURN_AND_NOTIFY_ON_FAILURE(picture
->DownloadFromSurface(va_surface
),
332 "Failed putting surface into pixmap",
335 // Notify the client a picture is ready to be displayed.
336 ++num_frames_at_client_
;
337 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_
);
338 DVLOG(4) << "Notifying output picture id " << output_id
339 << " for input "<< input_id
<< " is ready";
340 // TODO(posciak): Use visible size from decoder here instead
341 // (crbug.com/402760).
343 client_
->PictureReady(media::Picture(output_id
, input_id
,
344 gfx::Rect(picture
->size()),
345 picture
->AllowOverlay()));
348 void VaapiVideoDecodeAccelerator::TryOutputSurface() {
349 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
351 // Handle Destroy() arriving while pictures are queued for output.
355 if (pending_output_cbs_
.empty() || output_buffers_
.empty())
358 OutputCB output_cb
= pending_output_cbs_
.front();
359 pending_output_cbs_
.pop();
361 VaapiPicture
* picture
= PictureById(output_buffers_
.front());
363 output_buffers_
.pop();
365 output_cb
.Run(picture
);
367 if (finish_flush_pending_
&& pending_output_cbs_
.empty())
371 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
372 const media::BitstreamBuffer
& bitstream_buffer
) {
373 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
374 TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id",
375 bitstream_buffer
.id());
377 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer
.id()
378 << " size: " << (int)bitstream_buffer
.size();
380 scoped_ptr
<base::SharedMemory
> shm(
381 new base::SharedMemory(bitstream_buffer
.handle(), true));
382 RETURN_AND_NOTIFY_ON_FAILURE(shm
->Map(bitstream_buffer
.size()),
383 "Failed to map input buffer", UNREADABLE_INPUT
,);
385 base::AutoLock
auto_lock(lock_
);
387 // Set up a new input buffer and queue it for later.
388 linked_ptr
<InputBuffer
> input_buffer(new InputBuffer());
389 input_buffer
->shm
.reset(shm
.release());
390 input_buffer
->id
= bitstream_buffer
.id();
391 input_buffer
->size
= bitstream_buffer
.size();
393 ++num_stream_bufs_at_decoder_
;
394 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
395 num_stream_bufs_at_decoder_
);
397 input_buffers_
.push(input_buffer
);
398 input_ready_
.Signal();
401 bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() {
402 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
403 lock_
.AssertAcquired();
405 if (curr_input_buffer_
.get())
408 // Will only wait if it is expected that in current state new buffers will
409 // be queued from the client via Decode(). The state can change during wait.
410 while (input_buffers_
.empty() && (state_
== kDecoding
|| state_
== kIdle
)) {
414 // We could have got woken up in a different state or never got to sleep
415 // due to current state; check for that.
418 // Here we are only interested in finishing up decoding buffers that are
419 // already queued up. Otherwise will stop decoding.
420 if (input_buffers_
.empty())
425 DCHECK(!input_buffers_
.empty());
427 curr_input_buffer_
= input_buffers_
.front();
428 input_buffers_
.pop();
430 DVLOG(4) << "New current bitstream buffer, id: "
431 << curr_input_buffer_
->id
432 << " size: " << curr_input_buffer_
->size
;
435 static_cast<uint8
*>(curr_input_buffer_
->shm
->memory()),
436 curr_input_buffer_
->size
);
440 // We got woken up due to being destroyed/reset, ignore any already
446 void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() {
447 lock_
.AssertAcquired();
448 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
449 DCHECK(curr_input_buffer_
.get());
451 int32 id
= curr_input_buffer_
->id
;
452 curr_input_buffer_
.reset();
453 DVLOG(4) << "End of input buffer " << id
;
454 message_loop_
->PostTask(FROM_HERE
, base::Bind(
455 &Client::NotifyEndOfBitstreamBuffer
, client_
, id
));
457 --num_stream_bufs_at_decoder_
;
458 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
459 num_stream_bufs_at_decoder_
);
462 // TODO(posciak): refactor the whole class to remove sleeping in wait for
463 // surfaces, and reschedule DecodeTask instead.
464 bool VaapiVideoDecodeAccelerator::WaitForSurfaces_Locked() {
465 lock_
.AssertAcquired();
466 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
468 while (available_va_surfaces_
.empty() &&
469 (state_
== kDecoding
|| state_
== kFlushing
|| state_
== kIdle
)) {
470 surfaces_available_
.Wait();
473 if (state_
!= kDecoding
&& state_
!= kFlushing
&& state_
!= kIdle
)
479 void VaapiVideoDecodeAccelerator::DecodeTask() {
480 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
481 TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask");
482 base::AutoLock
auto_lock(lock_
);
484 if (state_
!= kDecoding
)
488 DVLOG(4) << "Decode task";
490 // Try to decode what stream data is (still) in the decoder until we run out
492 while (GetInputBuffer_Locked()) {
493 DCHECK(curr_input_buffer_
.get());
495 AcceleratedVideoDecoder::DecodeResult res
;
497 // We are OK releasing the lock here, as decoder never calls our methods
498 // directly and we will reacquire the lock before looking at state again.
499 // This is the main decode function of the decoder and while keeping
500 // the lock for its duration would be fine, it would defeat the purpose
501 // of having a separate decoder thread.
502 base::AutoUnlock
auto_unlock(lock_
);
503 res
= decoder_
->Decode();
507 case AcceleratedVideoDecoder::kAllocateNewSurfaces
:
508 DVLOG(1) << "Decoder requesting a new set of surfaces";
509 message_loop_
->PostTask(FROM_HERE
, base::Bind(
510 &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange
, weak_this_
,
511 decoder_
->GetRequiredNumOfPictures(),
512 decoder_
->GetPicSize()));
513 // We'll get rescheduled once ProvidePictureBuffers() finishes.
516 case AcceleratedVideoDecoder::kRanOutOfStreamData
:
517 ReturnCurrInputBuffer_Locked();
520 case AcceleratedVideoDecoder::kRanOutOfSurfaces
:
521 // No more output buffers in the decoder, try getting more or go to
522 // sleep waiting for them.
523 if (!WaitForSurfaces_Locked())
528 case AcceleratedVideoDecoder::kDecodeError
:
529 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
536 void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics
,
538 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
539 DCHECK(!awaiting_va_surfaces_recycle_
);
541 // At this point decoder has stopped running and has already posted onto our
542 // loop any remaining output request callbacks, which executed before we got
543 // here. Some of them might have been pended though, because we might not
544 // have had enough TFPictures to output surfaces to. Initiate a wait cycle,
545 // which will wait for client to return enough PictureBuffers to us, so that
546 // we can finish all pending output callbacks, releasing associated surfaces.
547 DVLOG(1) << "Initiating surface set change";
548 awaiting_va_surfaces_recycle_
= true;
550 requested_num_pics_
= num_pics
;
551 requested_pic_size_
= size
;
553 TryFinishSurfaceSetChange();
556 void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() {
557 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
559 if (!awaiting_va_surfaces_recycle_
)
562 if (!pending_output_cbs_
.empty() ||
563 pictures_
.size() != available_va_surfaces_
.size()) {
565 // 1. Not all pending pending output callbacks have been executed yet.
566 // Wait for the client to return enough pictures and retry later.
567 // 2. The above happened and all surface release callbacks have been posted
568 // as the result, but not all have executed yet. Post ourselves after them
569 // to let them release surfaces.
570 DVLOG(2) << "Awaiting pending output/surface release callbacks to finish";
571 message_loop_
->PostTask(FROM_HERE
, base::Bind(
572 &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange
, weak_this_
));
576 // All surfaces released, destroy them and dismiss all PictureBuffers.
577 awaiting_va_surfaces_recycle_
= false;
578 available_va_surfaces_
.clear();
579 vaapi_wrapper_
->DestroySurfaces();
581 for (Pictures::iterator iter
= pictures_
.begin(); iter
!= pictures_
.end();
583 DVLOG(2) << "Dismissing picture id: " << iter
->first
;
585 client_
->DismissPictureBuffer(iter
->first
);
589 // And ask for a new set as requested.
590 DVLOG(1) << "Requesting " << requested_num_pics_
<< " pictures of size: "
591 << requested_pic_size_
.ToString();
593 message_loop_
->PostTask(
595 base::Bind(&Client::ProvidePictureBuffers
, client_
, requested_num_pics_
,
596 requested_pic_size_
, VaapiPicture::GetGLTextureTarget()));
599 void VaapiVideoDecodeAccelerator::Decode(
600 const media::BitstreamBuffer
& bitstream_buffer
) {
601 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
603 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
604 bitstream_buffer
.id());
606 // We got a new input buffer from the client, map it and queue for later use.
607 MapAndQueueNewInputBuffer(bitstream_buffer
);
609 base::AutoLock
auto_lock(lock_
);
613 decoder_thread_task_runner_
->PostTask(
614 FROM_HERE
, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask
,
615 base::Unretained(this)));
619 // Decoder already running, fallthrough.
621 // When resetting, allow accumulating bitstream buffers, so that
622 // the client can queue after-seek-buffers while we are finishing with
623 // the before-seek one.
627 RETURN_AND_NOTIFY_ON_FAILURE(false,
628 "Decode request from client in invalid state: " << state_
,
634 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
635 VASurfaceID va_surface_id
) {
636 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
637 base::AutoLock
auto_lock(lock_
);
639 available_va_surfaces_
.push_back(va_surface_id
);
640 surfaces_available_
.Signal();
643 void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
644 const std::vector
<media::PictureBuffer
>& buffers
) {
645 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
647 base::AutoLock
auto_lock(lock_
);
648 DCHECK(pictures_
.empty());
650 while (!output_buffers_
.empty())
651 output_buffers_
.pop();
653 RETURN_AND_NOTIFY_ON_FAILURE(
654 buffers
.size() == requested_num_pics_
,
655 "Got an invalid number of picture buffers. (Got " << buffers
.size()
656 << ", requested " << requested_num_pics_
<< ")", INVALID_ARGUMENT
, );
657 DCHECK(requested_pic_size_
== buffers
[0].size());
659 std::vector
<VASurfaceID
> va_surface_ids
;
660 RETURN_AND_NOTIFY_ON_FAILURE(
661 vaapi_wrapper_
->CreateSurfaces(VA_RT_FORMAT_YUV420
, requested_pic_size_
,
662 buffers
.size(), &va_surface_ids
),
663 "Failed creating VA Surfaces", PLATFORM_FAILURE
, );
664 DCHECK_EQ(va_surface_ids
.size(), buffers
.size());
666 for (size_t i
= 0; i
< buffers
.size(); ++i
) {
667 DVLOG(2) << "Assigning picture id: " << buffers
[i
].id()
668 << " to texture id: " << buffers
[i
].texture_id()
669 << " VASurfaceID: " << va_surface_ids
[i
];
671 linked_ptr
<VaapiPicture
> picture(VaapiPicture::CreatePicture(
672 vaapi_wrapper_
.get(), make_context_current_
, buffers
[i
].id(),
673 buffers
[i
].texture_id(), requested_pic_size_
));
675 scoped_refptr
<gfx::GLImage
> image
= picture
->GetImageToBind();
677 bind_image_
.Run(buffers
[i
].internal_texture_id(),
678 VaapiPicture::GetGLTextureTarget(), image
);
681 RETURN_AND_NOTIFY_ON_FAILURE(
682 picture
.get(), "Failed assigning picture buffer to a texture.",
686 pictures_
.insert(std::make_pair(buffers
[i
].id(), picture
)).second
;
689 output_buffers_
.push(buffers
[i
].id());
690 available_va_surfaces_
.push_back(va_surface_ids
[i
]);
691 surfaces_available_
.Signal();
695 decoder_thread_task_runner_
->PostTask(
696 FROM_HERE
, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask
,
697 base::Unretained(this)));
700 void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id
) {
701 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
702 TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id",
705 --num_frames_at_client_
;
706 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_
);
708 output_buffers_
.push(picture_buffer_id
);
712 void VaapiVideoDecodeAccelerator::FlushTask() {
713 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
714 DVLOG(1) << "Flush task";
716 // First flush all the pictures that haven't been outputted, notifying the
717 // client to output them.
718 bool res
= decoder_
->Flush();
719 RETURN_AND_NOTIFY_ON_FAILURE(res
, "Failed flushing the decoder.",
722 // Put the decoder in idle state, ready to resume.
725 message_loop_
->PostTask(FROM_HERE
, base::Bind(
726 &VaapiVideoDecodeAccelerator::FinishFlush
, weak_this_
));
729 void VaapiVideoDecodeAccelerator::Flush() {
730 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
731 DVLOG(1) << "Got flush request";
733 base::AutoLock
auto_lock(lock_
);
735 // Queue a flush task after all existing decoding tasks to clean up.
736 decoder_thread_task_runner_
->PostTask(
737 FROM_HERE
, base::Bind(&VaapiVideoDecodeAccelerator::FlushTask
,
738 base::Unretained(this)));
740 input_ready_
.Signal();
741 surfaces_available_
.Signal();
744 void VaapiVideoDecodeAccelerator::FinishFlush() {
745 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
747 finish_flush_pending_
= false;
749 base::AutoLock
auto_lock(lock_
);
750 if (state_
!= kFlushing
) {
751 DCHECK_EQ(state_
, kDestroying
);
752 return; // We could've gotten destroyed already.
755 // Still waiting for textures from client to finish outputting all pending
756 // frames. Try again later.
757 if (!pending_output_cbs_
.empty()) {
758 finish_flush_pending_
= true;
764 message_loop_
->PostTask(FROM_HERE
, base::Bind(
765 &Client::NotifyFlushDone
, client_
));
767 DVLOG(1) << "Flush finished";
770 void VaapiVideoDecodeAccelerator::ResetTask() {
771 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
772 DVLOG(1) << "ResetTask";
774 // All the decoding tasks from before the reset request from client are done
775 // by now, as this task was scheduled after them and client is expected not
776 // to call Decode() after Reset() and before NotifyResetDone.
779 base::AutoLock
auto_lock(lock_
);
781 // Return current input buffer, if present.
782 if (curr_input_buffer_
.get())
783 ReturnCurrInputBuffer_Locked();
785 // And let client know that we are done with reset.
786 message_loop_
->PostTask(FROM_HERE
, base::Bind(
787 &VaapiVideoDecodeAccelerator::FinishReset
, weak_this_
));
790 void VaapiVideoDecodeAccelerator::Reset() {
791 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
792 DVLOG(1) << "Got reset request";
794 // This will make any new decode tasks exit early.
795 base::AutoLock
auto_lock(lock_
);
797 finish_flush_pending_
= false;
799 // Drop all remaining input buffers, if present.
800 while (!input_buffers_
.empty()) {
801 message_loop_
->PostTask(FROM_HERE
, base::Bind(
802 &Client::NotifyEndOfBitstreamBuffer
, client_
,
803 input_buffers_
.front()->id
));
804 input_buffers_
.pop();
807 decoder_thread_task_runner_
->PostTask(
808 FROM_HERE
, base::Bind(&VaapiVideoDecodeAccelerator::ResetTask
,
809 base::Unretained(this)));
811 input_ready_
.Signal();
812 surfaces_available_
.Signal();
815 void VaapiVideoDecodeAccelerator::FinishReset() {
816 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
817 DVLOG(1) << "FinishReset";
818 base::AutoLock
auto_lock(lock_
);
820 if (state_
!= kResetting
) {
821 DCHECK(state_
== kDestroying
|| state_
== kUninitialized
) << state_
;
822 return; // We could've gotten destroyed already.
825 // Drop pending outputs.
826 while (!pending_output_cbs_
.empty())
827 pending_output_cbs_
.pop();
829 if (awaiting_va_surfaces_recycle_
) {
830 // Decoder requested a new surface set while we were waiting for it to
831 // finish the last DecodeTask, running at the time of Reset().
832 // Let the surface set change finish first before resetting.
833 message_loop_
->PostTask(FROM_HERE
, base::Bind(
834 &VaapiVideoDecodeAccelerator::FinishReset
, weak_this_
));
838 num_stream_bufs_at_decoder_
= 0;
841 message_loop_
->PostTask(FROM_HERE
, base::Bind(
842 &Client::NotifyResetDone
, client_
));
844 // The client might have given us new buffers via Decode() while we were
845 // resetting and might be waiting for our move, and not call Decode() anymore
846 // until we return something. Post a DecodeTask() so that we won't
847 // sleep forever waiting for Decode() in that case. Having two of them
848 // in the pipe is harmless, the additional one will return as soon as it sees
849 // that we are back in kDecoding state.
850 if (!input_buffers_
.empty()) {
852 decoder_thread_task_runner_
->PostTask(
853 FROM_HERE
, base::Bind(&VaapiVideoDecodeAccelerator::DecodeTask
,
854 base::Unretained(this)));
857 DVLOG(1) << "Reset finished";
860 void VaapiVideoDecodeAccelerator::Cleanup() {
861 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
863 base::AutoLock
auto_lock(lock_
);
864 if (state_
== kUninitialized
|| state_
== kDestroying
)
867 DVLOG(1) << "Destroying VAVDA";
868 state_
= kDestroying
;
870 client_ptr_factory_
.reset();
871 weak_this_factory_
.InvalidateWeakPtrs();
873 // Signal all potential waiters on the decoder_thread_, let them early-exit,
874 // as we've just moved to the kDestroying state, and wait for all tasks
876 input_ready_
.Signal();
877 surfaces_available_
.Signal();
879 base::AutoUnlock
auto_unlock(lock_
);
880 decoder_thread_
.Stop();
883 state_
= kUninitialized
;
886 void VaapiVideoDecodeAccelerator::Destroy() {
887 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
892 bool VaapiVideoDecodeAccelerator::CanDecodeOnIOThread() {
896 bool VaapiVideoDecodeAccelerator::DecodeSurface(
897 const scoped_refptr
<VaapiDecodeSurface
>& dec_surface
) {
898 if (!vaapi_wrapper_
->ExecuteAndDestroyPendingBuffers(
899 dec_surface
->va_surface()->id())) {
900 DVLOG(1) << "Failed decoding picture";
907 void VaapiVideoDecodeAccelerator::SurfaceReady(
908 const scoped_refptr
<VaapiDecodeSurface
>& dec_surface
) {
909 if (message_loop_
!= base::MessageLoop::current()) {
910 message_loop_
->PostTask(
911 FROM_HERE
, base::Bind(&VaapiVideoDecodeAccelerator::SurfaceReady
,
912 weak_this_
, dec_surface
));
916 DCHECK(!awaiting_va_surfaces_recycle_
);
919 base::AutoLock
auto_lock(lock_
);
920 // Drop any requests to output if we are resetting or being destroyed.
921 if (state_
== kResetting
|| state_
== kDestroying
)
925 pending_output_cbs_
.push(
926 base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture
, weak_this_
,
927 dec_surface
->va_surface(), dec_surface
->bitstream_id()));
932 scoped_refptr
<VaapiVideoDecodeAccelerator::VaapiDecodeSurface
>
933 VaapiVideoDecodeAccelerator::CreateSurface() {
934 DCHECK(decoder_thread_task_runner_
->BelongsToCurrentThread());
935 base::AutoLock
auto_lock(lock_
);
937 if (available_va_surfaces_
.empty())
940 DCHECK(!awaiting_va_surfaces_recycle_
);
941 scoped_refptr
<VASurface
> va_surface(
942 new VASurface(available_va_surfaces_
.front(), requested_pic_size_
,
943 va_surface_release_cb_
));
944 available_va_surfaces_
.pop_front();
946 scoped_refptr
<VaapiDecodeSurface
> dec_surface
=
947 new VaapiDecodeSurface(curr_input_buffer_
->id
, va_surface
);
952 VaapiVideoDecodeAccelerator::VaapiH264Accelerator::VaapiH264Accelerator(
953 VaapiVideoDecodeAccelerator
* vaapi_dec
,
954 VaapiWrapper
* vaapi_wrapper
)
955 : vaapi_wrapper_(vaapi_wrapper
), vaapi_dec_(vaapi_dec
) {
956 DCHECK(vaapi_wrapper_
);
960 VaapiVideoDecodeAccelerator::VaapiH264Accelerator::~VaapiH264Accelerator() {
963 scoped_refptr
<H264Picture
>
964 VaapiVideoDecodeAccelerator::VaapiH264Accelerator::CreateH264Picture() {
965 scoped_refptr
<VaapiDecodeSurface
> va_surface
= vaapi_dec_
->CreateSurface();
969 return new VaapiH264Picture(va_surface
);
972 // Fill |va_pic| with default/neutral values.
973 static void InitVAPicture(VAPictureH264
* va_pic
) {
974 memset(va_pic
, 0, sizeof(*va_pic
));
975 va_pic
->picture_id
= VA_INVALID_ID
;
976 va_pic
->flags
= VA_PICTURE_H264_INVALID
;
979 bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitFrameMetadata(
980 const media::H264SPS
* sps
,
981 const media::H264PPS
* pps
,
983 const H264Picture::Vector
& ref_pic_listp0
,
984 const H264Picture::Vector
& ref_pic_listb0
,
985 const H264Picture::Vector
& ref_pic_listb1
,
986 const scoped_refptr
<H264Picture
>& pic
) {
987 VAPictureParameterBufferH264 pic_param
;
988 memset(&pic_param
, 0, sizeof(pic_param
));
990 #define FROM_SPS_TO_PP(a) pic_param.a = sps->a;
991 #define FROM_SPS_TO_PP2(a, b) pic_param.b = sps->a;
992 FROM_SPS_TO_PP2(pic_width_in_mbs_minus1
, picture_width_in_mbs_minus1
);
993 // This assumes non-interlaced video
994 FROM_SPS_TO_PP2(pic_height_in_map_units_minus1
, picture_height_in_mbs_minus1
);
995 FROM_SPS_TO_PP(bit_depth_luma_minus8
);
996 FROM_SPS_TO_PP(bit_depth_chroma_minus8
);
997 #undef FROM_SPS_TO_PP
998 #undef FROM_SPS_TO_PP2
1000 #define FROM_SPS_TO_PP_SF(a) pic_param.seq_fields.bits.a = sps->a;
1001 #define FROM_SPS_TO_PP_SF2(a, b) pic_param.seq_fields.bits.b = sps->a;
1002 FROM_SPS_TO_PP_SF(chroma_format_idc
);
1003 FROM_SPS_TO_PP_SF2(separate_colour_plane_flag
,
1004 residual_colour_transform_flag
);
1005 FROM_SPS_TO_PP_SF(gaps_in_frame_num_value_allowed_flag
);
1006 FROM_SPS_TO_PP_SF(frame_mbs_only_flag
);
1007 FROM_SPS_TO_PP_SF(mb_adaptive_frame_field_flag
);
1008 FROM_SPS_TO_PP_SF(direct_8x8_inference_flag
);
1009 pic_param
.seq_fields
.bits
.MinLumaBiPredSize8x8
= (sps
->level_idc
>= 31);
1010 FROM_SPS_TO_PP_SF(log2_max_frame_num_minus4
);
1011 FROM_SPS_TO_PP_SF(pic_order_cnt_type
);
1012 FROM_SPS_TO_PP_SF(log2_max_pic_order_cnt_lsb_minus4
);
1013 FROM_SPS_TO_PP_SF(delta_pic_order_always_zero_flag
);
1014 #undef FROM_SPS_TO_PP_SF
1015 #undef FROM_SPS_TO_PP_SF2
1017 #define FROM_PPS_TO_PP(a) pic_param.a = pps->a;
1018 FROM_PPS_TO_PP(num_slice_groups_minus1
);
1019 pic_param
.slice_group_map_type
= 0;
1020 pic_param
.slice_group_change_rate_minus1
= 0;
1021 FROM_PPS_TO_PP(pic_init_qp_minus26
);
1022 FROM_PPS_TO_PP(pic_init_qs_minus26
);
1023 FROM_PPS_TO_PP(chroma_qp_index_offset
);
1024 FROM_PPS_TO_PP(second_chroma_qp_index_offset
);
1025 #undef FROM_PPS_TO_PP
1027 #define FROM_PPS_TO_PP_PF(a) pic_param.pic_fields.bits.a = pps->a;
1028 #define FROM_PPS_TO_PP_PF2(a, b) pic_param.pic_fields.bits.b = pps->a;
1029 FROM_PPS_TO_PP_PF(entropy_coding_mode_flag
);
1030 FROM_PPS_TO_PP_PF(weighted_pred_flag
);
1031 FROM_PPS_TO_PP_PF(weighted_bipred_idc
);
1032 FROM_PPS_TO_PP_PF(transform_8x8_mode_flag
);
1034 pic_param
.pic_fields
.bits
.field_pic_flag
= 0;
1035 FROM_PPS_TO_PP_PF(constrained_intra_pred_flag
);
1036 FROM_PPS_TO_PP_PF2(bottom_field_pic_order_in_frame_present_flag
,
1037 pic_order_present_flag
);
1038 FROM_PPS_TO_PP_PF(deblocking_filter_control_present_flag
);
1039 FROM_PPS_TO_PP_PF(redundant_pic_cnt_present_flag
);
1040 pic_param
.pic_fields
.bits
.reference_pic_flag
= pic
->ref
;
1041 #undef FROM_PPS_TO_PP_PF
1042 #undef FROM_PPS_TO_PP_PF2
1044 pic_param
.frame_num
= pic
->frame_num
;
1046 InitVAPicture(&pic_param
.CurrPic
);
1047 FillVAPicture(&pic_param
.CurrPic
, pic
);
1049 // Init reference pictures' array.
1050 for (int i
= 0; i
< 16; ++i
)
1051 InitVAPicture(&pic_param
.ReferenceFrames
[i
]);
1053 // And fill it with picture info from DPB.
1054 FillVARefFramesFromDPB(dpb
, pic_param
.ReferenceFrames
,
1055 arraysize(pic_param
.ReferenceFrames
));
1057 pic_param
.num_ref_frames
= sps
->max_num_ref_frames
;
1059 if (!vaapi_wrapper_
->SubmitBuffer(VAPictureParameterBufferType
,
1064 VAIQMatrixBufferH264 iq_matrix_buf
;
1065 memset(&iq_matrix_buf
, 0, sizeof(iq_matrix_buf
));
1067 if (pps
->pic_scaling_matrix_present_flag
) {
1068 for (int i
= 0; i
< 6; ++i
) {
1069 for (int j
= 0; j
< 16; ++j
)
1070 iq_matrix_buf
.ScalingList4x4
[i
][j
] = pps
->scaling_list4x4
[i
][j
];
1073 for (int i
= 0; i
< 2; ++i
) {
1074 for (int j
= 0; j
< 64; ++j
)
1075 iq_matrix_buf
.ScalingList8x8
[i
][j
] = pps
->scaling_list8x8
[i
][j
];
1078 for (int i
= 0; i
< 6; ++i
) {
1079 for (int j
= 0; j
< 16; ++j
)
1080 iq_matrix_buf
.ScalingList4x4
[i
][j
] = sps
->scaling_list4x4
[i
][j
];
1083 for (int i
= 0; i
< 2; ++i
) {
1084 for (int j
= 0; j
< 64; ++j
)
1085 iq_matrix_buf
.ScalingList8x8
[i
][j
] = sps
->scaling_list8x8
[i
][j
];
1089 return vaapi_wrapper_
->SubmitBuffer(VAIQMatrixBufferType
,
1090 sizeof(iq_matrix_buf
),
1094 bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitSlice(
1095 const media::H264PPS
* pps
,
1096 const media::H264SliceHeader
* slice_hdr
,
1097 const H264Picture::Vector
& ref_pic_list0
,
1098 const H264Picture::Vector
& ref_pic_list1
,
1099 const scoped_refptr
<H264Picture
>& pic
,
1100 const uint8_t* data
,
1102 VASliceParameterBufferH264 slice_param
;
1103 memset(&slice_param
, 0, sizeof(slice_param
));
1105 slice_param
.slice_data_size
= slice_hdr
->nalu_size
;
1106 slice_param
.slice_data_offset
= 0;
1107 slice_param
.slice_data_flag
= VA_SLICE_DATA_FLAG_ALL
;
1108 slice_param
.slice_data_bit_offset
= slice_hdr
->header_bit_size
;
1110 #define SHDRToSP(a) slice_param.a = slice_hdr->a;
1111 SHDRToSP(first_mb_in_slice
);
1112 slice_param
.slice_type
= slice_hdr
->slice_type
% 5;
1113 SHDRToSP(direct_spatial_mv_pred_flag
);
1115 // TODO posciak: make sure parser sets those even when override flags
1116 // in slice header is off.
1117 SHDRToSP(num_ref_idx_l0_active_minus1
);
1118 SHDRToSP(num_ref_idx_l1_active_minus1
);
1119 SHDRToSP(cabac_init_idc
);
1120 SHDRToSP(slice_qp_delta
);
1121 SHDRToSP(disable_deblocking_filter_idc
);
1122 SHDRToSP(slice_alpha_c0_offset_div2
);
1123 SHDRToSP(slice_beta_offset_div2
);
1125 if (((slice_hdr
->IsPSlice() || slice_hdr
->IsSPSlice()) &&
1126 pps
->weighted_pred_flag
) ||
1127 (slice_hdr
->IsBSlice() && pps
->weighted_bipred_idc
== 1)) {
1128 SHDRToSP(luma_log2_weight_denom
);
1129 SHDRToSP(chroma_log2_weight_denom
);
1131 SHDRToSP(luma_weight_l0_flag
);
1132 SHDRToSP(luma_weight_l1_flag
);
1134 SHDRToSP(chroma_weight_l0_flag
);
1135 SHDRToSP(chroma_weight_l1_flag
);
1137 for (int i
= 0; i
<= slice_param
.num_ref_idx_l0_active_minus1
; ++i
) {
1138 slice_param
.luma_weight_l0
[i
] =
1139 slice_hdr
->pred_weight_table_l0
.luma_weight
[i
];
1140 slice_param
.luma_offset_l0
[i
] =
1141 slice_hdr
->pred_weight_table_l0
.luma_offset
[i
];
1143 for (int j
= 0; j
< 2; ++j
) {
1144 slice_param
.chroma_weight_l0
[i
][j
] =
1145 slice_hdr
->pred_weight_table_l0
.chroma_weight
[i
][j
];
1146 slice_param
.chroma_offset_l0
[i
][j
] =
1147 slice_hdr
->pred_weight_table_l0
.chroma_offset
[i
][j
];
1151 if (slice_hdr
->IsBSlice()) {
1152 for (int i
= 0; i
<= slice_param
.num_ref_idx_l1_active_minus1
; ++i
) {
1153 slice_param
.luma_weight_l1
[i
] =
1154 slice_hdr
->pred_weight_table_l1
.luma_weight
[i
];
1155 slice_param
.luma_offset_l1
[i
] =
1156 slice_hdr
->pred_weight_table_l1
.luma_offset
[i
];
1158 for (int j
= 0; j
< 2; ++j
) {
1159 slice_param
.chroma_weight_l1
[i
][j
] =
1160 slice_hdr
->pred_weight_table_l1
.chroma_weight
[i
][j
];
1161 slice_param
.chroma_offset_l1
[i
][j
] =
1162 slice_hdr
->pred_weight_table_l1
.chroma_offset
[i
][j
];
1169 arraysize(slice_param
.RefPicList0
) == arraysize(slice_param
.RefPicList1
),
1170 "Invalid RefPicList sizes");
1172 for (size_t i
= 0; i
< arraysize(slice_param
.RefPicList0
); ++i
) {
1173 InitVAPicture(&slice_param
.RefPicList0
[i
]);
1174 InitVAPicture(&slice_param
.RefPicList1
[i
]);
1178 i
< ref_pic_list0
.size() && i
< arraysize(slice_param
.RefPicList0
);
1180 if (ref_pic_list0
[i
])
1181 FillVAPicture(&slice_param
.RefPicList0
[i
], ref_pic_list0
[i
]);
1184 i
< ref_pic_list1
.size() && i
< arraysize(slice_param
.RefPicList1
);
1186 if (ref_pic_list1
[i
])
1187 FillVAPicture(&slice_param
.RefPicList1
[i
], ref_pic_list1
[i
]);
1190 if (!vaapi_wrapper_
->SubmitBuffer(VASliceParameterBufferType
,
1191 sizeof(slice_param
),
1195 // Can't help it, blame libva...
1196 void* non_const_ptr
= const_cast<uint8
*>(data
);
1197 return vaapi_wrapper_
->SubmitBuffer(VASliceDataBufferType
, size
,
1201 bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::SubmitDecode(
1202 const scoped_refptr
<H264Picture
>& pic
) {
1203 DVLOG(4) << "Decoding POC " << pic
->pic_order_cnt
;
1204 scoped_refptr
<VaapiDecodeSurface
> dec_surface
=
1205 H264PictureToVaapiDecodeSurface(pic
);
1207 return vaapi_dec_
->DecodeSurface(dec_surface
);
1210 bool VaapiVideoDecodeAccelerator::VaapiH264Accelerator::OutputPicture(
1211 const scoped_refptr
<H264Picture
>& pic
) {
1212 scoped_refptr
<VaapiDecodeSurface
> dec_surface
=
1213 H264PictureToVaapiDecodeSurface(pic
);
1215 vaapi_dec_
->SurfaceReady(dec_surface
);
1220 void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::Reset() {
1221 vaapi_wrapper_
->DestroyPendingBuffers();
1224 scoped_refptr
<VaapiVideoDecodeAccelerator::VaapiDecodeSurface
>
1225 VaapiVideoDecodeAccelerator::VaapiH264Accelerator::
1226 H264PictureToVaapiDecodeSurface(const scoped_refptr
<H264Picture
>& pic
) {
1227 VaapiH264Picture
* vaapi_pic
= pic
->AsVaapiH264Picture();
1229 return vaapi_pic
->dec_surface();
1232 void VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVAPicture(
1233 VAPictureH264
* va_pic
,
1234 scoped_refptr
<H264Picture
> pic
) {
1235 scoped_refptr
<VaapiDecodeSurface
> dec_surface
=
1236 H264PictureToVaapiDecodeSurface(pic
);
1238 va_pic
->picture_id
= dec_surface
->va_surface()->id();
1239 va_pic
->frame_idx
= pic
->frame_num
;
1242 switch (pic
->field
) {
1243 case H264Picture::FIELD_NONE
:
1245 case H264Picture::FIELD_TOP
:
1246 va_pic
->flags
|= VA_PICTURE_H264_TOP_FIELD
;
1248 case H264Picture::FIELD_BOTTOM
:
1249 va_pic
->flags
|= VA_PICTURE_H264_BOTTOM_FIELD
;
1254 va_pic
->flags
|= pic
->long_term
? VA_PICTURE_H264_LONG_TERM_REFERENCE
1255 : VA_PICTURE_H264_SHORT_TERM_REFERENCE
;
1258 va_pic
->TopFieldOrderCnt
= pic
->top_field_order_cnt
;
1259 va_pic
->BottomFieldOrderCnt
= pic
->bottom_field_order_cnt
;
1262 int VaapiVideoDecodeAccelerator::VaapiH264Accelerator::FillVARefFramesFromDPB(
1264 VAPictureH264
* va_pics
,
1266 H264Picture::Vector::const_reverse_iterator rit
;
1269 // Return reference frames in reverse order of insertion.
1270 // Libva does not document this, but other implementations (e.g. mplayer)
1271 // do it this way as well.
1272 for (rit
= dpb
.rbegin(), i
= 0; rit
!= dpb
.rend() && i
< num_pics
; ++rit
) {
1274 FillVAPicture(&va_pics
[i
++], *rit
);
1280 VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::VaapiVP8Accelerator(
1281 VaapiVideoDecodeAccelerator
* vaapi_dec
,
1282 VaapiWrapper
* vaapi_wrapper
)
1283 : vaapi_wrapper_(vaapi_wrapper
), vaapi_dec_(vaapi_dec
) {
1284 DCHECK(vaapi_wrapper_
);
1288 VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::~VaapiVP8Accelerator() {
1291 scoped_refptr
<VP8Picture
>
1292 VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::CreateVP8Picture() {
1293 scoped_refptr
<VaapiDecodeSurface
> va_surface
= vaapi_dec_
->CreateSurface();
1297 return new VaapiVP8Picture(va_surface
);
1300 #define ARRAY_MEMCPY_CHECKED(to, from) \
1302 static_assert(sizeof(to) == sizeof(from), \
1303 #from " and " #to " arrays must be of same size"); \
1304 memcpy(to, from, sizeof(to)); \
1307 bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::SubmitDecode(
1308 const scoped_refptr
<VP8Picture
>& pic
,
1309 const media::Vp8FrameHeader
* frame_hdr
,
1310 const scoped_refptr
<VP8Picture
>& last_frame
,
1311 const scoped_refptr
<VP8Picture
>& golden_frame
,
1312 const scoped_refptr
<VP8Picture
>& alt_frame
) {
1313 VAIQMatrixBufferVP8 iq_matrix_buf
;
1314 memset(&iq_matrix_buf
, 0, sizeof(VAIQMatrixBufferVP8
));
1316 const media::Vp8SegmentationHeader
& sgmnt_hdr
= frame_hdr
->segmentation_hdr
;
1317 const media::Vp8QuantizationHeader
& quant_hdr
= frame_hdr
->quantization_hdr
;
1319 arraysize(iq_matrix_buf
.quantization_index
) == media::kMaxMBSegments
,
1320 "incorrect quantization matrix size");
1321 for (size_t i
= 0; i
< media::kMaxMBSegments
; ++i
) {
1322 int q
= quant_hdr
.y_ac_qi
;
1324 if (sgmnt_hdr
.segmentation_enabled
) {
1325 if (sgmnt_hdr
.segment_feature_mode
==
1326 media::Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE
)
1327 q
= sgmnt_hdr
.quantizer_update_value
[i
];
1329 q
+= sgmnt_hdr
.quantizer_update_value
[i
];
1332 #define CLAMP_Q(q) std::min(std::max(q, 0), 127)
1333 static_assert(arraysize(iq_matrix_buf
.quantization_index
[i
]) == 6,
1334 "incorrect quantization matrix size");
1335 iq_matrix_buf
.quantization_index
[i
][0] = CLAMP_Q(q
);
1336 iq_matrix_buf
.quantization_index
[i
][1] = CLAMP_Q(q
+ quant_hdr
.y_dc_delta
);
1337 iq_matrix_buf
.quantization_index
[i
][2] = CLAMP_Q(q
+ quant_hdr
.y2_dc_delta
);
1338 iq_matrix_buf
.quantization_index
[i
][3] = CLAMP_Q(q
+ quant_hdr
.y2_ac_delta
);
1339 iq_matrix_buf
.quantization_index
[i
][4] = CLAMP_Q(q
+ quant_hdr
.uv_dc_delta
);
1340 iq_matrix_buf
.quantization_index
[i
][5] = CLAMP_Q(q
+ quant_hdr
.uv_ac_delta
);
1344 if (!vaapi_wrapper_
->SubmitBuffer(VAIQMatrixBufferType
,
1345 sizeof(VAIQMatrixBufferVP8
),
1349 VAProbabilityDataBufferVP8 prob_buf
;
1350 memset(&prob_buf
, 0, sizeof(VAProbabilityDataBufferVP8
));
1352 const media::Vp8EntropyHeader
& entr_hdr
= frame_hdr
->entropy_hdr
;
1353 ARRAY_MEMCPY_CHECKED(prob_buf
.dct_coeff_probs
, entr_hdr
.coeff_probs
);
1355 if (!vaapi_wrapper_
->SubmitBuffer(VAProbabilityBufferType
,
1356 sizeof(VAProbabilityDataBufferVP8
),
1360 VAPictureParameterBufferVP8 pic_param
;
1361 memset(&pic_param
, 0, sizeof(VAPictureParameterBufferVP8
));
1362 pic_param
.frame_width
= frame_hdr
->width
;
1363 pic_param
.frame_height
= frame_hdr
->height
;
1366 scoped_refptr
<VaapiDecodeSurface
> last_frame_surface
=
1367 VP8PictureToVaapiDecodeSurface(last_frame
);
1368 pic_param
.last_ref_frame
= last_frame_surface
->va_surface()->id();
1370 pic_param
.last_ref_frame
= VA_INVALID_SURFACE
;
1374 scoped_refptr
<VaapiDecodeSurface
> golden_frame_surface
=
1375 VP8PictureToVaapiDecodeSurface(golden_frame
);
1376 pic_param
.golden_ref_frame
= golden_frame_surface
->va_surface()->id();
1378 pic_param
.golden_ref_frame
= VA_INVALID_SURFACE
;
1382 scoped_refptr
<VaapiDecodeSurface
> alt_frame_surface
=
1383 VP8PictureToVaapiDecodeSurface(alt_frame
);
1384 pic_param
.alt_ref_frame
= alt_frame_surface
->va_surface()->id();
1386 pic_param
.alt_ref_frame
= VA_INVALID_SURFACE
;
1389 pic_param
.out_of_loop_frame
= VA_INVALID_SURFACE
;
1391 const media::Vp8LoopFilterHeader
& lf_hdr
= frame_hdr
->loopfilter_hdr
;
1393 #define FHDR_TO_PP_PF(a, b) pic_param.pic_fields.bits.a = (b);
1394 FHDR_TO_PP_PF(key_frame
, frame_hdr
->IsKeyframe() ? 0 : 1);
1395 FHDR_TO_PP_PF(version
, frame_hdr
->version
);
1396 FHDR_TO_PP_PF(segmentation_enabled
, sgmnt_hdr
.segmentation_enabled
);
1397 FHDR_TO_PP_PF(update_mb_segmentation_map
,
1398 sgmnt_hdr
.update_mb_segmentation_map
);
1399 FHDR_TO_PP_PF(update_segment_feature_data
,
1400 sgmnt_hdr
.update_segment_feature_data
);
1401 FHDR_TO_PP_PF(filter_type
, lf_hdr
.type
);
1402 FHDR_TO_PP_PF(sharpness_level
, lf_hdr
.sharpness_level
);
1403 FHDR_TO_PP_PF(loop_filter_adj_enable
, lf_hdr
.loop_filter_adj_enable
);
1404 FHDR_TO_PP_PF(mode_ref_lf_delta_update
, lf_hdr
.mode_ref_lf_delta_update
);
1405 FHDR_TO_PP_PF(sign_bias_golden
, frame_hdr
->sign_bias_golden
);
1406 FHDR_TO_PP_PF(sign_bias_alternate
, frame_hdr
->sign_bias_alternate
);
1407 FHDR_TO_PP_PF(mb_no_coeff_skip
, frame_hdr
->mb_no_skip_coeff
);
1408 FHDR_TO_PP_PF(loop_filter_disable
, lf_hdr
.level
== 0);
1409 #undef FHDR_TO_PP_PF
1411 ARRAY_MEMCPY_CHECKED(pic_param
.mb_segment_tree_probs
, sgmnt_hdr
.segment_prob
);
1413 static_assert(arraysize(sgmnt_hdr
.lf_update_value
) ==
1414 arraysize(pic_param
.loop_filter_level
),
1415 "loop filter level arrays mismatch");
1416 for (size_t i
= 0; i
< arraysize(sgmnt_hdr
.lf_update_value
); ++i
) {
1417 int lf_level
= lf_hdr
.level
;
1418 if (sgmnt_hdr
.segmentation_enabled
) {
1419 if (sgmnt_hdr
.segment_feature_mode
==
1420 media::Vp8SegmentationHeader::FEATURE_MODE_ABSOLUTE
)
1421 lf_level
= sgmnt_hdr
.lf_update_value
[i
];
1423 lf_level
+= sgmnt_hdr
.lf_update_value
[i
];
1426 // Clamp to [0..63] range.
1427 lf_level
= std::min(std::max(lf_level
, 0), 63);
1428 pic_param
.loop_filter_level
[i
] = lf_level
;
1431 static_assert(arraysize(lf_hdr
.ref_frame_delta
) ==
1432 arraysize(pic_param
.loop_filter_deltas_ref_frame
) &&
1433 arraysize(lf_hdr
.mb_mode_delta
) ==
1434 arraysize(pic_param
.loop_filter_deltas_mode
) &&
1435 arraysize(lf_hdr
.ref_frame_delta
) ==
1436 arraysize(lf_hdr
.mb_mode_delta
),
1437 "loop filter deltas arrays size mismatch");
1438 for (size_t i
= 0; i
< arraysize(lf_hdr
.ref_frame_delta
); ++i
) {
1439 pic_param
.loop_filter_deltas_ref_frame
[i
] = lf_hdr
.ref_frame_delta
[i
];
1440 pic_param
.loop_filter_deltas_mode
[i
] = lf_hdr
.mb_mode_delta
[i
];
1443 #define FHDR_TO_PP(a) pic_param.a = frame_hdr->a;
1444 FHDR_TO_PP(prob_skip_false
);
1445 FHDR_TO_PP(prob_intra
);
1446 FHDR_TO_PP(prob_last
);
1447 FHDR_TO_PP(prob_gf
);
1450 ARRAY_MEMCPY_CHECKED(pic_param
.y_mode_probs
, entr_hdr
.y_mode_probs
);
1451 ARRAY_MEMCPY_CHECKED(pic_param
.uv_mode_probs
, entr_hdr
.uv_mode_probs
);
1452 ARRAY_MEMCPY_CHECKED(pic_param
.mv_probs
, entr_hdr
.mv_probs
);
1454 pic_param
.bool_coder_ctx
.range
= frame_hdr
->bool_dec_range
;
1455 pic_param
.bool_coder_ctx
.value
= frame_hdr
->bool_dec_value
;
1456 pic_param
.bool_coder_ctx
.count
= frame_hdr
->bool_dec_count
;
1458 if (!vaapi_wrapper_
->SubmitBuffer(VAPictureParameterBufferType
,
1459 sizeof(VAPictureParameterBufferVP8
),
1463 VASliceParameterBufferVP8 slice_param
;
1464 memset(&slice_param
, 0, sizeof(VASliceParameterBufferVP8
));
1465 slice_param
.slice_data_size
= frame_hdr
->frame_size
;
1466 slice_param
.slice_data_offset
= frame_hdr
->first_part_offset
;
1467 slice_param
.slice_data_flag
= VA_SLICE_DATA_FLAG_ALL
;
1468 slice_param
.macroblock_offset
= frame_hdr
->macroblock_bit_offset
;
1469 // Number of DCT partitions plus control partition.
1470 slice_param
.num_of_partitions
= frame_hdr
->num_of_dct_partitions
+ 1;
1472 // Per VAAPI, this size only includes the size of the macroblock data in
1473 // the first partition (in bytes), so we have to subtract the header size.
1474 slice_param
.partition_size
[0] =
1475 frame_hdr
->first_part_size
- ((frame_hdr
->macroblock_bit_offset
+ 7) / 8);
1477 for (size_t i
= 0; i
< frame_hdr
->num_of_dct_partitions
; ++i
)
1478 slice_param
.partition_size
[i
+ 1] = frame_hdr
->dct_partition_sizes
[i
];
1480 if (!vaapi_wrapper_
->SubmitBuffer(VASliceParameterBufferType
,
1481 sizeof(VASliceParameterBufferVP8
),
1485 void* non_const_ptr
= const_cast<uint8
*>(frame_hdr
->data
);
1486 if (!vaapi_wrapper_
->SubmitBuffer(VASliceDataBufferType
,
1487 frame_hdr
->frame_size
,
1491 scoped_refptr
<VaapiDecodeSurface
> dec_surface
=
1492 VP8PictureToVaapiDecodeSurface(pic
);
1494 return vaapi_dec_
->DecodeSurface(dec_surface
);
1497 bool VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::OutputPicture(
1498 const scoped_refptr
<VP8Picture
>& pic
) {
1499 scoped_refptr
<VaapiDecodeSurface
> dec_surface
=
1500 VP8PictureToVaapiDecodeSurface(pic
);
1502 vaapi_dec_
->SurfaceReady(dec_surface
);
1506 scoped_refptr
<VaapiVideoDecodeAccelerator::VaapiDecodeSurface
>
1507 VaapiVideoDecodeAccelerator::VaapiVP8Accelerator::
1508 VP8PictureToVaapiDecodeSurface(const scoped_refptr
<VP8Picture
>& pic
) {
1509 VaapiVP8Picture
* vaapi_pic
= pic
->AsVaapiVP8Picture();
1511 return vaapi_pic
->dec_surface();
1515 media::VideoDecodeAccelerator::SupportedProfiles
1516 VaapiVideoDecodeAccelerator::GetSupportedProfiles() {
1517 return VaapiWrapper::GetSupportedDecodeProfiles();
1520 } // namespace content