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.
6 #include "base/debug/trace_event.h"
7 #include "base/logging.h"
8 #include "base/metrics/histogram.h"
9 #include "base/stl_util.h"
10 #include "base/strings/string_util.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "content/child/child_thread.h"
13 #include "content/common/gpu/gpu_channel.h"
14 #include "content/common/gpu/media/vaapi_video_decode_accelerator.h"
15 #include "media/base/bind_to_loop.h"
16 #include "media/video/picture.h"
17 #include "ui/gl/gl_bindings.h"
18 #include "ui/gl/scoped_binders.h"
20 static void ReportToUMA(
21 content::VaapiH264Decoder::VAVDAH264DecoderFailure failure
) {
22 UMA_HISTOGRAM_ENUMERATION(
23 "Media.VAVDAH264.DecoderFailure",
25 content::VaapiH264Decoder::VAVDA_H264_DECODER_FAILURES_MAX
);
30 #define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
34 NotifyError(error_code); \
39 VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) {
42 VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() {
45 void VaapiVideoDecodeAccelerator::NotifyError(Error error
) {
46 if (message_loop_
!= base::MessageLoop::current()) {
47 DCHECK_EQ(decoder_thread_
.message_loop(), base::MessageLoop::current());
48 message_loop_
->PostTask(FROM_HERE
, base::Bind(
49 &VaapiVideoDecodeAccelerator::NotifyError
, weak_this_
, error
));
53 // Post Cleanup() as a task so we don't recursively acquire lock_.
54 message_loop_
->PostTask(FROM_HERE
, base::Bind(
55 &VaapiVideoDecodeAccelerator::Cleanup
, weak_this_
));
57 DVLOG(1) << "Notifying of error " << error
;
59 client_
->NotifyError(error
);
60 client_ptr_factory_
.InvalidateWeakPtrs();
64 // TFPPicture allocates X Pixmaps and binds them to textures passed
65 // in PictureBuffers from clients to them. TFPPictures are created as
66 // a consequence of receiving a set of PictureBuffers from clients and released
67 // at the end of decode (or when a new set of PictureBuffers is required).
69 // TFPPictures are used for output, contents of VASurfaces passed from decoder
70 // are put into the associated pixmap memory and sent to client.
71 class VaapiVideoDecodeAccelerator::TFPPicture
{
75 static linked_ptr
<TFPPicture
> Create(
76 const base::Callback
<bool(void)>& make_context_current
,
77 const GLXFBConfig
& fb_config
,
79 int32 picture_buffer_id
,
83 int32
picture_buffer_id() {
84 return picture_buffer_id_
;
99 // Bind texture to pixmap. Needs to be called every frame.
103 TFPPicture(const base::Callback
<bool(void)>& make_context_current
,
105 int32 picture_buffer_id
,
109 bool Initialize(const GLXFBConfig
& fb_config
);
111 base::Callback
<bool(void)> make_context_current_
;
115 // Output id for the client.
116 int32 picture_buffer_id_
;
121 // Pixmaps bound to this texture.
123 GLXPixmap glx_pixmap_
;
125 DISALLOW_COPY_AND_ASSIGN(TFPPicture
);
128 VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture(
129 const base::Callback
<bool(void)>& make_context_current
,
131 int32 picture_buffer_id
,
134 : make_context_current_(make_context_current
),
135 x_display_(x_display
),
136 picture_buffer_id_(picture_buffer_id
),
137 texture_id_(texture_id
),
141 DCHECK(!make_context_current_
.is_null());
144 linked_ptr
<VaapiVideoDecodeAccelerator::TFPPicture
>
145 VaapiVideoDecodeAccelerator::TFPPicture::Create(
146 const base::Callback
<bool(void)>& make_context_current
,
147 const GLXFBConfig
& fb_config
,
149 int32 picture_buffer_id
,
153 linked_ptr
<TFPPicture
> tfp_picture(
154 new TFPPicture(make_context_current
, x_display
, picture_buffer_id
,
157 if (!tfp_picture
->Initialize(fb_config
))
163 bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize(
164 const GLXFBConfig
& fb_config
) {
165 // Check for NULL prevents unittests from crashing on nonexistent ChildThread.
166 DCHECK(ChildThread::current() == NULL
||
167 ChildThread::current()->message_loop() == base::MessageLoop::current());
169 if (!make_context_current_
.Run())
172 XWindowAttributes win_attr
;
173 int screen
= DefaultScreen(x_display_
);
174 XGetWindowAttributes(x_display_
, RootWindow(x_display_
, screen
), &win_attr
);
175 //TODO(posciak): pass the depth required by libva, not the RootWindow's depth
176 x_pixmap_
= XCreatePixmap(x_display_
, RootWindow(x_display_
, screen
),
177 size_
.width(), size_
.height(), win_attr
.depth
);
179 DVLOG(1) << "Failed creating an X Pixmap for TFP";
183 static const int pixmap_attr
[] = {
184 GLX_TEXTURE_TARGET_EXT
, GLX_TEXTURE_2D_EXT
,
185 GLX_TEXTURE_FORMAT_EXT
, GLX_TEXTURE_FORMAT_RGB_EXT
,
189 glx_pixmap_
= glXCreatePixmap(x_display_
, fb_config
, x_pixmap_
, pixmap_attr
);
191 // x_pixmap_ will be freed in the destructor.
192 DVLOG(1) << "Failed creating a GLX Pixmap for TFP";
199 VaapiVideoDecodeAccelerator::TFPPicture::~TFPPicture() {
200 // Check for NULL prevents unittests from crashing on nonexistent ChildThread.
201 DCHECK(ChildThread::current() == NULL
||
202 ChildThread::current()->message_loop() == base::MessageLoop::current());
204 // Unbind surface from texture and deallocate resources.
205 if (glx_pixmap_
&& make_context_current_
.Run()) {
206 glXReleaseTexImageEXT(x_display_
, glx_pixmap_
, GLX_FRONT_LEFT_EXT
);
207 glXDestroyPixmap(x_display_
, glx_pixmap_
);
211 XFreePixmap(x_display_
, x_pixmap_
);
212 XSync(x_display_
, False
); // Needed to work around buggy vdpau-driver.
215 bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() {
218 // Check for NULL prevents unittests from crashing on nonexistent ChildThread.
219 DCHECK(ChildThread::current() == NULL
||
220 ChildThread::current()->message_loop() == base::MessageLoop::current());
222 if (!make_context_current_
.Run())
225 gfx::ScopedTextureBinder
texture_binder(GL_TEXTURE_2D
, texture_id_
);
226 glXBindTexImageEXT(x_display_
, glx_pixmap_
, GLX_FRONT_LEFT_EXT
, NULL
);
231 VaapiVideoDecodeAccelerator::TFPPicture
*
232 VaapiVideoDecodeAccelerator::TFPPictureById(int32 picture_buffer_id
) {
233 TFPPictures::iterator it
= tfp_pictures_
.find(picture_buffer_id
);
234 if (it
== tfp_pictures_
.end()) {
235 DVLOG(1) << "Picture id " << picture_buffer_id
<< " does not exist";
239 return it
->second
.get();
242 VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
243 Display
* x_display
, GLXContext glx_context
,
245 const base::Callback
<bool(void)>& make_context_current
)
246 : x_display_(x_display
),
247 glx_context_(glx_context
),
248 make_context_current_(make_context_current
),
249 state_(kUninitialized
),
250 input_ready_(&lock_
),
251 surfaces_available_(&lock_
),
252 message_loop_(base::MessageLoop::current()),
253 weak_this_(base::AsWeakPtr(this)),
254 client_ptr_factory_(client
),
255 client_(client_ptr_factory_
.GetWeakPtr()),
256 decoder_thread_("VaapiDecoderThread"),
257 num_frames_at_client_(0),
258 num_stream_bufs_at_decoder_(0),
259 finish_flush_pending_(false),
260 awaiting_va_surfaces_recycle_(false),
261 requested_num_pics_(0) {
265 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
266 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
269 class ScopedPtrXFree
{
271 void operator()(void* x
) const {
276 bool VaapiVideoDecodeAccelerator::InitializeFBConfig() {
277 const int fbconfig_attr
[] = {
278 GLX_DRAWABLE_TYPE
, GLX_PIXMAP_BIT
,
279 GLX_BIND_TO_TEXTURE_TARGETS_EXT
, GLX_TEXTURE_2D_BIT_EXT
,
280 GLX_BIND_TO_TEXTURE_RGB_EXT
, GL_TRUE
,
281 GLX_Y_INVERTED_EXT
, GL_TRUE
,
286 scoped_ptr_malloc
<GLXFBConfig
, ScopedPtrXFree
> glx_fb_configs(
287 glXChooseFBConfig(x_display_
, DefaultScreen(x_display_
), fbconfig_attr
,
294 fb_config_
= glx_fb_configs
.get()[0];
298 bool VaapiVideoDecodeAccelerator::Initialize(
299 media::VideoCodecProfile profile
) {
300 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
302 base::AutoLock
auto_lock(lock_
);
303 DCHECK_EQ(state_
, kUninitialized
);
304 DVLOG(2) << "Initializing VAVDA, profile: " << profile
;
306 if (!make_context_current_
.Run())
309 if (!InitializeFBConfig()) {
310 DVLOG(1) << "Could not get a usable FBConfig";
314 vaapi_wrapper_
= VaapiWrapper::Create(
316 base::Bind(&ReportToUMA
, content::VaapiH264Decoder::VAAPI_ERROR
));
318 if (!vaapi_wrapper_
.get()) {
319 DVLOG(1) << "Failed initializing VAAPI";
324 new VaapiH264Decoder(
325 vaapi_wrapper_
.get(),
326 media::BindToLoop(message_loop_
->message_loop_proxy(), base::Bind(
327 &VaapiVideoDecodeAccelerator::SurfaceReady
, weak_this_
)),
328 base::Bind(&ReportToUMA
)));
330 CHECK(decoder_thread_
.Start());
334 message_loop_
->PostTask(FROM_HERE
, base::Bind(
335 &Client::NotifyInitializeDone
, client_
));
339 void VaapiVideoDecodeAccelerator::SurfaceReady(
341 const scoped_refptr
<VASurface
>& va_surface
) {
342 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
343 DCHECK(!awaiting_va_surfaces_recycle_
);
345 // Drop any requests to output if we are resetting or being destroyed.
346 if (state_
== kResetting
|| state_
== kDestroying
)
349 pending_output_cbs_
.push(
350 base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture
,
351 weak_this_
, va_surface
, input_id
));
356 void VaapiVideoDecodeAccelerator::OutputPicture(
357 const scoped_refptr
<VASurface
>& va_surface
,
359 TFPPicture
* tfp_picture
) {
360 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
362 int32 output_id
= tfp_picture
->picture_buffer_id();
364 TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface",
365 "input_id", input_id
,
366 "output_id", output_id
);
368 DVLOG(3) << "Outputting VASurface " << va_surface
->id()
369 << " into pixmap bound to picture buffer id " << output_id
;
371 RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture
->Bind(),
372 "Failed binding texture to pixmap",
375 RETURN_AND_NOTIFY_ON_FAILURE(
376 vaapi_wrapper_
->PutSurfaceIntoPixmap(va_surface
->id(),
377 tfp_picture
->x_pixmap(),
378 tfp_picture
->size()),
379 "Failed putting surface into pixmap", PLATFORM_FAILURE
, );
381 // Notify the client a picture is ready to be displayed.
382 ++num_frames_at_client_
;
383 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_
);
384 DVLOG(4) << "Notifying output picture id " << output_id
385 << " for input "<< input_id
<< " is ready";
386 client_
->PictureReady(media::Picture(output_id
, input_id
));
389 void VaapiVideoDecodeAccelerator::TryOutputSurface() {
390 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
392 // Handle Destroy() arriving while pictures are queued for output.
396 if (pending_output_cbs_
.empty() || output_buffers_
.empty())
399 OutputCB output_cb
= pending_output_cbs_
.front();
400 pending_output_cbs_
.pop();
402 TFPPicture
* tfp_picture
= TFPPictureById(output_buffers_
.front());
404 output_buffers_
.pop();
406 output_cb
.Run(tfp_picture
);
408 if (finish_flush_pending_
&& pending_output_cbs_
.empty())
412 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
413 const media::BitstreamBuffer
& bitstream_buffer
) {
414 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
415 TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id",
416 bitstream_buffer
.id());
418 DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer
.id()
419 << " size: " << (int)bitstream_buffer
.size();
421 scoped_ptr
<base::SharedMemory
> shm(
422 new base::SharedMemory(bitstream_buffer
.handle(), true));
423 RETURN_AND_NOTIFY_ON_FAILURE(shm
->Map(bitstream_buffer
.size()),
424 "Failed to map input buffer", UNREADABLE_INPUT
,);
426 base::AutoLock
auto_lock(lock_
);
428 // Set up a new input buffer and queue it for later.
429 linked_ptr
<InputBuffer
> input_buffer(new InputBuffer());
430 input_buffer
->shm
.reset(shm
.release());
431 input_buffer
->id
= bitstream_buffer
.id();
432 input_buffer
->size
= bitstream_buffer
.size();
434 ++num_stream_bufs_at_decoder_
;
435 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
436 num_stream_bufs_at_decoder_
);
438 input_buffers_
.push(input_buffer
);
439 input_ready_
.Signal();
442 bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() {
443 DCHECK_EQ(decoder_thread_
.message_loop(), base::MessageLoop::current());
444 lock_
.AssertAcquired();
446 if (curr_input_buffer_
.get())
449 // Will only wait if it is expected that in current state new buffers will
450 // be queued from the client via Decode(). The state can change during wait.
451 while (input_buffers_
.empty() && (state_
== kDecoding
|| state_
== kIdle
)) {
455 // We could have got woken up in a different state or never got to sleep
456 // due to current state; check for that.
459 // Here we are only interested in finishing up decoding buffers that are
460 // already queued up. Otherwise will stop decoding.
461 if (input_buffers_
.empty())
466 DCHECK(!input_buffers_
.empty());
468 curr_input_buffer_
= input_buffers_
.front();
469 input_buffers_
.pop();
471 DVLOG(4) << "New current bitstream buffer, id: "
472 << curr_input_buffer_
->id
473 << " size: " << curr_input_buffer_
->size
;
476 static_cast<uint8
*>(curr_input_buffer_
->shm
->memory()),
477 curr_input_buffer_
->size
, curr_input_buffer_
->id
);
481 // We got woken up due to being destroyed/reset, ignore any already
487 void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() {
488 lock_
.AssertAcquired();
489 DCHECK_EQ(decoder_thread_
.message_loop(), base::MessageLoop::current());
490 DCHECK(curr_input_buffer_
.get());
492 int32 id
= curr_input_buffer_
->id
;
493 curr_input_buffer_
.reset();
494 DVLOG(4) << "End of input buffer " << id
;
495 message_loop_
->PostTask(FROM_HERE
, base::Bind(
496 &Client::NotifyEndOfBitstreamBuffer
, client_
, id
));
498 --num_stream_bufs_at_decoder_
;
499 TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
500 num_stream_bufs_at_decoder_
);
503 bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() {
504 lock_
.AssertAcquired();
505 DCHECK_EQ(decoder_thread_
.message_loop(), base::MessageLoop::current());
507 while (available_va_surfaces_
.empty() &&
508 (state_
== kDecoding
|| state_
== kFlushing
|| state_
== kIdle
)) {
509 surfaces_available_
.Wait();
512 if (state_
!= kDecoding
&& state_
!= kFlushing
&& state_
!= kIdle
)
515 VASurface::ReleaseCB va_surface_release_cb
=
516 media::BindToLoop(message_loop_
->message_loop_proxy(), base::Bind(
517 &VaapiVideoDecodeAccelerator::RecycleVASurfaceID
, weak_this_
));
519 while (!available_va_surfaces_
.empty()) {
520 scoped_refptr
<VASurface
> va_surface(
521 new VASurface(available_va_surfaces_
.front(), va_surface_release_cb
));
522 available_va_surfaces_
.pop_front();
523 decoder_
->ReuseSurface(va_surface
);
529 void VaapiVideoDecodeAccelerator::DecodeTask() {
530 DCHECK_EQ(decoder_thread_
.message_loop(), base::MessageLoop::current());
531 TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask");
532 base::AutoLock
auto_lock(lock_
);
534 if (state_
!= kDecoding
)
538 DVLOG(4) << "Decode task";
540 // Try to decode what stream data is (still) in the decoder until we run out
542 while (GetInputBuffer_Locked()) {
543 DCHECK(curr_input_buffer_
.get());
545 VaapiH264Decoder::DecResult res
;
547 // We are OK releasing the lock here, as decoder never calls our methods
548 // directly and we will reacquire the lock before looking at state again.
549 // This is the main decode function of the decoder and while keeping
550 // the lock for its duration would be fine, it would defeat the purpose
551 // of having a separate decoder thread.
552 base::AutoUnlock
auto_unlock(lock_
);
553 res
= decoder_
->Decode();
557 case VaapiH264Decoder::kAllocateNewSurfaces
:
558 DVLOG(1) << "Decoder requesting a new set of surfaces";
559 message_loop_
->PostTask(FROM_HERE
, base::Bind(
560 &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange
, weak_this_
,
561 decoder_
->GetRequiredNumOfPictures(),
562 decoder_
->GetPicSize()));
563 // We'll get rescheduled once ProvidePictureBuffers() finishes.
566 case VaapiH264Decoder::kRanOutOfStreamData
:
567 ReturnCurrInputBuffer_Locked();
570 case VaapiH264Decoder::kRanOutOfSurfaces
:
571 // No more output buffers in the decoder, try getting more or go to
572 // sleep waiting for them.
573 if (!FeedDecoderWithOutputSurfaces_Locked())
578 case VaapiH264Decoder::kDecodeError
:
579 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
586 void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics
,
588 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
589 DCHECK(!awaiting_va_surfaces_recycle_
);
591 // At this point decoder has stopped running and has already posted onto our
592 // loop any remaining output request callbacks, which executed before we got
593 // here. Some of them might have been pended though, because we might not
594 // have had enough TFPictures to output surfaces to. Initiate a wait cycle,
595 // which will wait for client to return enough PictureBuffers to us, so that
596 // we can finish all pending output callbacks, releasing associated surfaces.
597 DVLOG(1) << "Initiating surface set change";
598 awaiting_va_surfaces_recycle_
= true;
600 requested_num_pics_
= num_pics
;
601 requested_pic_size_
= size
;
603 TryFinishSurfaceSetChange();
606 void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() {
607 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
609 if (!awaiting_va_surfaces_recycle_
)
612 if (!pending_output_cbs_
.empty() ||
613 tfp_pictures_
.size() != available_va_surfaces_
.size()) {
615 // 1. Not all pending pending output callbacks have been executed yet.
616 // Wait for the client to return enough pictures and retry later.
617 // 2. The above happened and all surface release callbacks have been posted
618 // as the result, but not all have executed yet. Post ourselves after them
619 // to let them release surfaces.
620 DVLOG(2) << "Awaiting pending output/surface release callbacks to finish";
621 message_loop_
->PostTask(FROM_HERE
, base::Bind(
622 &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange
, weak_this_
));
626 // All surfaces released, destroy them and dismiss all PictureBuffers.
627 awaiting_va_surfaces_recycle_
= false;
628 available_va_surfaces_
.clear();
629 vaapi_wrapper_
->DestroySurfaces();
631 for (TFPPictures::iterator iter
= tfp_pictures_
.begin();
632 iter
!= tfp_pictures_
.end(); ++iter
) {
633 DVLOG(2) << "Dismissing picture id: " << iter
->first
;
634 client_
->DismissPictureBuffer(iter
->first
);
636 tfp_pictures_
.clear();
638 // And ask for a new set as requested.
639 DVLOG(1) << "Requesting " << requested_num_pics_
<< " pictures of size: "
640 << requested_pic_size_
.ToString();
642 message_loop_
->PostTask(FROM_HERE
, base::Bind(
643 &Client::ProvidePictureBuffers
, client_
,
644 requested_num_pics_
, requested_pic_size_
, GL_TEXTURE_2D
));
647 void VaapiVideoDecodeAccelerator::Decode(
648 const media::BitstreamBuffer
& bitstream_buffer
) {
649 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
651 TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
652 bitstream_buffer
.id());
654 // We got a new input buffer from the client, map it and queue for later use.
655 MapAndQueueNewInputBuffer(bitstream_buffer
);
657 base::AutoLock
auto_lock(lock_
);
661 decoder_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
662 &VaapiVideoDecodeAccelerator::DecodeTask
,
663 base::Unretained(this)));
667 // Decoder already running, fallthrough.
669 // When resetting, allow accumulating bitstream buffers, so that
670 // the client can queue after-seek-buffers while we are finishing with
671 // the before-seek one.
675 RETURN_AND_NOTIFY_ON_FAILURE(false,
676 "Decode request from client in invalid state: " << state_
,
682 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
683 VASurfaceID va_surface_id
) {
684 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
685 base::AutoLock
auto_lock(lock_
);
687 available_va_surfaces_
.push_back(va_surface_id
);
688 surfaces_available_
.Signal();
691 void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
692 const std::vector
<media::PictureBuffer
>& buffers
) {
693 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
695 base::AutoLock
auto_lock(lock_
);
696 DCHECK(tfp_pictures_
.empty());
698 while (!output_buffers_
.empty())
699 output_buffers_
.pop();
701 RETURN_AND_NOTIFY_ON_FAILURE(
702 buffers
.size() == requested_num_pics_
,
703 "Got an invalid number of picture buffers. (Got " << buffers
.size()
704 << ", requested " << requested_num_pics_
<< ")", INVALID_ARGUMENT
, );
705 DCHECK(requested_pic_size_
== buffers
[0].size());
707 std::vector
<VASurfaceID
> va_surface_ids
;
708 RETURN_AND_NOTIFY_ON_FAILURE(
709 vaapi_wrapper_
->CreateSurfaces(requested_pic_size_
,
712 "Failed creating VA Surfaces", PLATFORM_FAILURE
, );
713 DCHECK_EQ(va_surface_ids
.size(), buffers
.size());
715 for (size_t i
= 0; i
< buffers
.size(); ++i
) {
716 DVLOG(2) << "Assigning picture id: " << buffers
[i
].id()
717 << " to texture id: " << buffers
[i
].texture_id()
718 << " VASurfaceID: " << va_surface_ids
[i
];
720 linked_ptr
<TFPPicture
> tfp_picture(
721 TFPPicture::Create(make_context_current_
, fb_config_
, x_display_
,
722 buffers
[i
].id(), buffers
[i
].texture_id(),
723 requested_pic_size_
));
725 RETURN_AND_NOTIFY_ON_FAILURE(
726 tfp_picture
.get(), "Failed assigning picture buffer to a texture.",
729 bool inserted
= tfp_pictures_
.insert(std::make_pair(
730 buffers
[i
].id(), tfp_picture
)).second
;
733 output_buffers_
.push(buffers
[i
].id());
734 available_va_surfaces_
.push_back(va_surface_ids
[i
]);
735 surfaces_available_
.Signal();
739 decoder_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
740 &VaapiVideoDecodeAccelerator::DecodeTask
, base::Unretained(this)));
743 void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id
) {
744 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
745 TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id",
748 --num_frames_at_client_
;
749 TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_
);
751 output_buffers_
.push(picture_buffer_id
);
755 void VaapiVideoDecodeAccelerator::FlushTask() {
756 DCHECK_EQ(decoder_thread_
.message_loop(), base::MessageLoop::current());
757 DVLOG(1) << "Flush task";
759 // First flush all the pictures that haven't been outputted, notifying the
760 // client to output them.
761 bool res
= decoder_
->Flush();
762 RETURN_AND_NOTIFY_ON_FAILURE(res
, "Failed flushing the decoder.",
765 // Put the decoder in idle state, ready to resume.
768 message_loop_
->PostTask(FROM_HERE
, base::Bind(
769 &VaapiVideoDecodeAccelerator::FinishFlush
, weak_this_
));
772 void VaapiVideoDecodeAccelerator::Flush() {
773 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
774 DVLOG(1) << "Got flush request";
776 base::AutoLock
auto_lock(lock_
);
778 // Queue a flush task after all existing decoding tasks to clean up.
779 decoder_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
780 &VaapiVideoDecodeAccelerator::FlushTask
, base::Unretained(this)));
782 input_ready_
.Signal();
783 surfaces_available_
.Signal();
786 void VaapiVideoDecodeAccelerator::FinishFlush() {
787 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
789 finish_flush_pending_
= false;
791 base::AutoLock
auto_lock(lock_
);
792 if (state_
!= kFlushing
) {
793 DCHECK_EQ(state_
, kDestroying
);
794 return; // We could've gotten destroyed already.
797 // Still waiting for textures from client to finish outputting all pending
798 // frames. Try again later.
799 if (!pending_output_cbs_
.empty()) {
800 finish_flush_pending_
= true;
806 message_loop_
->PostTask(FROM_HERE
, base::Bind(
807 &Client::NotifyFlushDone
, client_
));
809 DVLOG(1) << "Flush finished";
812 void VaapiVideoDecodeAccelerator::ResetTask() {
813 DCHECK_EQ(decoder_thread_
.message_loop(), base::MessageLoop::current());
814 DVLOG(1) << "ResetTask";
816 // All the decoding tasks from before the reset request from client are done
817 // by now, as this task was scheduled after them and client is expected not
818 // to call Decode() after Reset() and before NotifyResetDone.
821 base::AutoLock
auto_lock(lock_
);
823 // Return current input buffer, if present.
824 if (curr_input_buffer_
.get())
825 ReturnCurrInputBuffer_Locked();
827 // And let client know that we are done with reset.
828 message_loop_
->PostTask(FROM_HERE
, base::Bind(
829 &VaapiVideoDecodeAccelerator::FinishReset
, weak_this_
));
832 void VaapiVideoDecodeAccelerator::Reset() {
833 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
834 DVLOG(1) << "Got reset request";
836 // This will make any new decode tasks exit early.
837 base::AutoLock
auto_lock(lock_
);
839 finish_flush_pending_
= false;
841 // Drop all remaining input buffers, if present.
842 while (!input_buffers_
.empty()) {
843 message_loop_
->PostTask(FROM_HERE
, base::Bind(
844 &Client::NotifyEndOfBitstreamBuffer
, client_
,
845 input_buffers_
.front()->id
));
846 input_buffers_
.pop();
849 decoder_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
850 &VaapiVideoDecodeAccelerator::ResetTask
, base::Unretained(this)));
852 input_ready_
.Signal();
853 surfaces_available_
.Signal();
856 void VaapiVideoDecodeAccelerator::FinishReset() {
857 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
858 DVLOG(1) << "FinishReset";
859 base::AutoLock
auto_lock(lock_
);
861 if (state_
!= kResetting
) {
862 DCHECK(state_
== kDestroying
|| state_
== kUninitialized
) << state_
;
863 return; // We could've gotten destroyed already.
866 // Drop pending outputs.
867 while (!pending_output_cbs_
.empty())
868 pending_output_cbs_
.pop();
870 if (awaiting_va_surfaces_recycle_
) {
871 // Decoder requested a new surface set while we were waiting for it to
872 // finish the last DecodeTask, running at the time of Reset().
873 // Let the surface set change finish first before resetting.
874 message_loop_
->PostTask(FROM_HERE
, base::Bind(
875 &VaapiVideoDecodeAccelerator::FinishReset
, weak_this_
));
879 num_stream_bufs_at_decoder_
= 0;
882 message_loop_
->PostTask(FROM_HERE
, base::Bind(
883 &Client::NotifyResetDone
, client_
));
885 // The client might have given us new buffers via Decode() while we were
886 // resetting and might be waiting for our move, and not call Decode() anymore
887 // until we return something. Post a DecodeTask() so that we won't
888 // sleep forever waiting for Decode() in that case. Having two of them
889 // in the pipe is harmless, the additional one will return as soon as it sees
890 // that we are back in kDecoding state.
891 if (!input_buffers_
.empty()) {
893 decoder_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
894 &VaapiVideoDecodeAccelerator::DecodeTask
,
895 base::Unretained(this)));
898 DVLOG(1) << "Reset finished";
901 void VaapiVideoDecodeAccelerator::Cleanup() {
902 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
904 if (state_
== kUninitialized
|| state_
== kDestroying
)
907 DVLOG(1) << "Destroying VAVDA";
908 base::AutoLock
auto_lock(lock_
);
909 state_
= kDestroying
;
911 client_ptr_factory_
.InvalidateWeakPtrs();
914 base::AutoUnlock
auto_unlock(lock_
);
915 // Post a dummy task to the decoder_thread_ to ensure it is drained.
916 base::WaitableEvent
waiter(false, false);
917 decoder_thread_
.message_loop()->PostTask(FROM_HERE
, base::Bind(
918 &base::WaitableEvent::Signal
, base::Unretained(&waiter
)));
919 input_ready_
.Signal();
920 surfaces_available_
.Signal();
922 decoder_thread_
.Stop();
925 state_
= kUninitialized
;
928 void VaapiVideoDecodeAccelerator::Destroy() {
929 DCHECK_EQ(message_loop_
, base::MessageLoop::current());
934 } // namespace content