Respond with QuotaExceededError when IndexedDB has no disk space on open.
[chromium-blink-merge.git] / content / common / gpu / media / vaapi_video_decode_accelerator.cc
blobcd18574a8d8142d65f6fb5485444a60218d62a25
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 "base/bind.h"
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",
24 failure,
25 content::VaapiH264Decoder::VAVDA_H264_DECODER_FAILURES_MAX);
28 namespace content {
30 #define RETURN_AND_NOTIFY_ON_FAILURE(result, log, error_code, ret) \
31 do { \
32 if (!(result)) { \
33 DVLOG(1) << log; \
34 NotifyError(error_code); \
35 return ret; \
36 } \
37 } while (0)
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));
50 return;
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;
58 if (client_) {
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 {
72 public:
73 ~TFPPicture();
75 static linked_ptr<TFPPicture> Create(
76 const base::Callback<bool(void)>& make_context_current,
77 const GLXFBConfig& fb_config,
78 Display* x_display,
79 int32 picture_buffer_id,
80 uint32 texture_id,
81 gfx::Size size);
83 int32 picture_buffer_id() {
84 return picture_buffer_id_;
87 uint32 texture_id() {
88 return texture_id_;
91 gfx::Size size() {
92 return size_;
95 int x_pixmap() {
96 return x_pixmap_;
99 // Bind texture to pixmap. Needs to be called every frame.
100 bool Bind();
102 private:
103 TFPPicture(const base::Callback<bool(void)>& make_context_current,
104 Display* x_display,
105 int32 picture_buffer_id,
106 uint32 texture_id,
107 gfx::Size size);
109 bool Initialize(const GLXFBConfig& fb_config);
111 base::Callback<bool(void)> make_context_current_;
113 Display* x_display_;
115 // Output id for the client.
116 int32 picture_buffer_id_;
117 uint32 texture_id_;
119 gfx::Size size_;
121 // Pixmaps bound to this texture.
122 Pixmap x_pixmap_;
123 GLXPixmap glx_pixmap_;
125 DISALLOW_COPY_AND_ASSIGN(TFPPicture);
128 VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture(
129 const base::Callback<bool(void)>& make_context_current,
130 Display* x_display,
131 int32 picture_buffer_id,
132 uint32 texture_id,
133 gfx::Size size)
134 : make_context_current_(make_context_current),
135 x_display_(x_display),
136 picture_buffer_id_(picture_buffer_id),
137 texture_id_(texture_id),
138 size_(size),
139 x_pixmap_(0),
140 glx_pixmap_(0) {
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,
148 Display* x_display,
149 int32 picture_buffer_id,
150 uint32 texture_id,
151 gfx::Size size) {
153 linked_ptr<TFPPicture> tfp_picture(
154 new TFPPicture(make_context_current, x_display, picture_buffer_id,
155 texture_id, size));
157 if (!tfp_picture->Initialize(fb_config))
158 tfp_picture.reset();
160 return tfp_picture;
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())
170 return false;
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);
178 if (!x_pixmap_) {
179 DVLOG(1) << "Failed creating an X Pixmap for TFP";
180 return false;
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,
186 GL_NONE,
189 glx_pixmap_ = glXCreatePixmap(x_display_, fb_config, x_pixmap_, pixmap_attr);
190 if (!glx_pixmap_) {
191 // x_pixmap_ will be freed in the destructor.
192 DVLOG(1) << "Failed creating a GLX Pixmap for TFP";
193 return false;
196 return true;
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_);
210 if (x_pixmap_)
211 XFreePixmap(x_display_, x_pixmap_);
212 XSync(x_display_, False); // Needed to work around buggy vdpau-driver.
215 bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() {
216 DCHECK(x_pixmap_);
217 DCHECK(glx_pixmap_);
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())
223 return false;
225 gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_);
226 glXBindTexImageEXT(x_display_, glx_pixmap_, GLX_FRONT_LEFT_EXT, NULL);
228 return true;
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";
236 return NULL;
239 return it->second.get();
242 VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
243 Display* x_display, GLXContext glx_context,
244 Client* client,
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) {
262 DCHECK(client);
265 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
266 DCHECK_EQ(message_loop_, base::MessageLoop::current());
269 class ScopedPtrXFree {
270 public:
271 void operator()(void* x) const {
272 ::XFree(x);
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,
282 GL_NONE,
285 int num_fbconfigs;
286 scoped_ptr_malloc<GLXFBConfig, ScopedPtrXFree> glx_fb_configs(
287 glXChooseFBConfig(x_display_, DefaultScreen(x_display_), fbconfig_attr,
288 &num_fbconfigs));
289 if (!glx_fb_configs)
290 return false;
291 if (!num_fbconfigs)
292 return false;
294 fb_config_ = glx_fb_configs.get()[0];
295 return true;
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())
307 return false;
309 if (!InitializeFBConfig()) {
310 DVLOG(1) << "Could not get a usable FBConfig";
311 return false;
314 vaapi_wrapper_ = VaapiWrapper::Create(
315 profile, x_display_,
316 base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR));
318 if (!vaapi_wrapper_.get()) {
319 DVLOG(1) << "Failed initializing VAAPI";
320 return false;
323 decoder_.reset(
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());
332 state_ = kIdle;
334 message_loop_->PostTask(FROM_HERE, base::Bind(
335 &Client::NotifyInitializeDone, client_));
336 return true;
339 void VaapiVideoDecodeAccelerator::SurfaceReady(
340 int32 input_id,
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)
347 return;
349 pending_output_cbs_.push(
350 base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture,
351 weak_this_, va_surface, input_id));
353 TryOutputSurface();
356 void VaapiVideoDecodeAccelerator::OutputPicture(
357 const scoped_refptr<VASurface>& va_surface,
358 int32 input_id,
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",
373 PLATFORM_FAILURE, );
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.
393 if (!client_)
394 return;
396 if (pending_output_cbs_.empty() || output_buffers_.empty())
397 return;
399 OutputCB output_cb = pending_output_cbs_.front();
400 pending_output_cbs_.pop();
402 TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front());
403 DCHECK(tfp_picture);
404 output_buffers_.pop();
406 output_cb.Run(tfp_picture);
408 if (finish_flush_pending_ && pending_output_cbs_.empty())
409 FinishFlush();
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())
447 return true;
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)) {
452 input_ready_.Wait();
455 // We could have got woken up in a different state or never got to sleep
456 // due to current state; check for that.
457 switch (state_) {
458 case kFlushing:
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())
462 return false;
463 // else fallthrough
464 case kDecoding:
465 case kIdle:
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;
475 decoder_->SetStream(
476 static_cast<uint8*>(curr_input_buffer_->shm->memory()),
477 curr_input_buffer_->size, curr_input_buffer_->id);
478 return true;
480 default:
481 // We got woken up due to being destroyed/reset, ignore any already
482 // queued inputs.
483 return false;
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)
513 return false;
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);
526 return true;
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)
535 return;
537 // Main decode task.
538 DVLOG(4) << "Decode task";
540 // Try to decode what stream data is (still) in the decoder until we run out
541 // of it.
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();
556 switch (res) {
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.
564 return;
566 case VaapiH264Decoder::kRanOutOfStreamData:
567 ReturnCurrInputBuffer_Locked();
568 break;
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())
574 return;
576 break;
578 case VaapiH264Decoder::kDecodeError:
579 RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
580 PLATFORM_FAILURE, );
581 return;
586 void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics,
587 gfx::Size size) {
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_)
610 return;
612 if (!pending_output_cbs_.empty() ||
613 tfp_pictures_.size() != available_va_surfaces_.size()) {
614 // Either:
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_));
623 return;
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_);
658 switch (state_) {
659 case kIdle:
660 state_ = kDecoding;
661 decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
662 &VaapiVideoDecodeAccelerator::DecodeTask,
663 base::Unretained(this)));
664 break;
666 case kDecoding:
667 // Decoder already running, fallthrough.
668 case kResetting:
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.
672 break;
674 default:
675 RETURN_AND_NOTIFY_ON_FAILURE(false,
676 "Decode request from client in invalid state: " << state_,
677 PLATFORM_FAILURE, );
678 break;
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_,
710 buffers.size(),
711 &va_surface_ids),
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.",
727 PLATFORM_FAILURE, );
729 bool inserted = tfp_pictures_.insert(std::make_pair(
730 buffers[i].id(), tfp_picture)).second;
731 DCHECK(inserted);
733 output_buffers_.push(buffers[i].id());
734 available_va_surfaces_.push_back(va_surface_ids[i]);
735 surfaces_available_.Signal();
738 state_ = kDecoding;
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",
746 picture_buffer_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);
752 TryOutputSurface();
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.",
763 PLATFORM_FAILURE, );
765 // Put the decoder in idle state, ready to resume.
766 decoder_->Reset();
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_);
777 state_ = kFlushing;
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;
801 return;
804 state_ = kIdle;
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.
819 decoder_->Reset();
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_);
838 state_ = kResetting;
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_));
876 return;
879 num_stream_bufs_at_decoder_ = 0;
880 state_ = kIdle;
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()) {
892 state_ = kDecoding;
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)
905 return;
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();
921 waiter.Wait();
922 decoder_thread_.Stop();
925 state_ = kUninitialized;
928 void VaapiVideoDecodeAccelerator::Destroy() {
929 DCHECK_EQ(message_loop_, base::MessageLoop::current());
930 Cleanup();
931 delete this;
934 } // namespace content