Home | History | Annotate | Download | only in media
      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.
      4 
      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 "base/threading/non_thread_safe.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_current_loop.h"
     16 #include "media/video/picture.h"
     17 #include "ui/gl/gl_bindings.h"
     18 #include "ui/gl/scoped_binders.h"
     19 
     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);
     26 }
     27 
     28 namespace content {
     29 
     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)
     38 
     39 VaapiVideoDecodeAccelerator::InputBuffer::InputBuffer() : id(0), size(0) {
     40 }
     41 
     42 VaapiVideoDecodeAccelerator::InputBuffer::~InputBuffer() {
     43 }
     44 
     45 void VaapiVideoDecodeAccelerator::NotifyError(Error error) {
     46   if (message_loop_ != base::MessageLoop::current()) {
     47     DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
     48     message_loop_->PostTask(FROM_HERE, base::Bind(
     49         &VaapiVideoDecodeAccelerator::NotifyError, weak_this_, error));
     50     return;
     51   }
     52 
     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_));
     56 
     57   DVLOG(1) << "Notifying of error " << error;
     58   if (client_) {
     59     client_->NotifyError(error);
     60     client_ptr_factory_.reset();
     61   }
     62 }
     63 
     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).
     68 //
     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 : public base::NonThreadSafe {
     72  public:
     73   ~TFPPicture();
     74 
     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);
     82 
     83   int32 picture_buffer_id() {
     84     return picture_buffer_id_;
     85   }
     86 
     87   gfx::Size size() {
     88     return size_;
     89   }
     90 
     91   int x_pixmap() {
     92     return x_pixmap_;
     93   }
     94 
     95   // Bind texture to pixmap. Needs to be called every frame.
     96   bool Bind();
     97 
     98  private:
     99   TFPPicture(const base::Callback<bool(void)>& make_context_current,
    100              Display* x_display,
    101              int32 picture_buffer_id,
    102              uint32 texture_id,
    103              gfx::Size size);
    104 
    105   bool Initialize(const GLXFBConfig& fb_config);
    106 
    107   base::Callback<bool(void)> make_context_current_;
    108 
    109   Display* x_display_;
    110 
    111   // Output id for the client.
    112   int32 picture_buffer_id_;
    113   uint32 texture_id_;
    114 
    115   gfx::Size size_;
    116 
    117   // Pixmaps bound to this texture.
    118   Pixmap x_pixmap_;
    119   GLXPixmap glx_pixmap_;
    120 
    121   DISALLOW_COPY_AND_ASSIGN(TFPPicture);
    122 };
    123 
    124 VaapiVideoDecodeAccelerator::TFPPicture::TFPPicture(
    125     const base::Callback<bool(void)>& make_context_current,
    126     Display* x_display,
    127     int32 picture_buffer_id,
    128     uint32 texture_id,
    129     gfx::Size size)
    130     : make_context_current_(make_context_current),
    131       x_display_(x_display),
    132       picture_buffer_id_(picture_buffer_id),
    133       texture_id_(texture_id),
    134       size_(size),
    135       x_pixmap_(0),
    136       glx_pixmap_(0) {
    137   DCHECK(!make_context_current_.is_null());
    138 };
    139 
    140 linked_ptr<VaapiVideoDecodeAccelerator::TFPPicture>
    141 VaapiVideoDecodeAccelerator::TFPPicture::Create(
    142     const base::Callback<bool(void)>& make_context_current,
    143     const GLXFBConfig& fb_config,
    144     Display* x_display,
    145     int32 picture_buffer_id,
    146     uint32 texture_id,
    147     gfx::Size size) {
    148 
    149   linked_ptr<TFPPicture> tfp_picture(
    150       new TFPPicture(make_context_current, x_display, picture_buffer_id,
    151                      texture_id, size));
    152 
    153   if (!tfp_picture->Initialize(fb_config))
    154     tfp_picture.reset();
    155 
    156   return tfp_picture;
    157 }
    158 
    159 bool VaapiVideoDecodeAccelerator::TFPPicture::Initialize(
    160     const GLXFBConfig& fb_config) {
    161   DCHECK(CalledOnValidThread());
    162   if (!make_context_current_.Run())
    163     return false;
    164 
    165   XWindowAttributes win_attr;
    166   int screen = DefaultScreen(x_display_);
    167   XGetWindowAttributes(x_display_, RootWindow(x_display_, screen), &win_attr);
    168   //TODO(posciak): pass the depth required by libva, not the RootWindow's depth
    169   x_pixmap_ = XCreatePixmap(x_display_, RootWindow(x_display_, screen),
    170                             size_.width(), size_.height(), win_attr.depth);
    171   if (!x_pixmap_) {
    172     DVLOG(1) << "Failed creating an X Pixmap for TFP";
    173     return false;
    174   }
    175 
    176   static const int pixmap_attr[] = {
    177     GLX_TEXTURE_TARGET_EXT, GLX_TEXTURE_2D_EXT,
    178     GLX_TEXTURE_FORMAT_EXT, GLX_TEXTURE_FORMAT_RGB_EXT,
    179     GL_NONE,
    180   };
    181 
    182   glx_pixmap_ = glXCreatePixmap(x_display_, fb_config, x_pixmap_, pixmap_attr);
    183   if (!glx_pixmap_) {
    184     // x_pixmap_ will be freed in the destructor.
    185     DVLOG(1) << "Failed creating a GLX Pixmap for TFP";
    186     return false;
    187   }
    188 
    189   return true;
    190 }
    191 
    192 VaapiVideoDecodeAccelerator::TFPPicture::~TFPPicture() {
    193   DCHECK(CalledOnValidThread());
    194   // Unbind surface from texture and deallocate resources.
    195   if (glx_pixmap_ && make_context_current_.Run()) {
    196     glXReleaseTexImageEXT(x_display_, glx_pixmap_, GLX_FRONT_LEFT_EXT);
    197     glXDestroyPixmap(x_display_, glx_pixmap_);
    198   }
    199 
    200   if (x_pixmap_)
    201     XFreePixmap(x_display_, x_pixmap_);
    202   XSync(x_display_, False);  // Needed to work around buggy vdpau-driver.
    203 }
    204 
    205 bool VaapiVideoDecodeAccelerator::TFPPicture::Bind() {
    206   DCHECK(CalledOnValidThread());
    207   DCHECK(x_pixmap_);
    208   DCHECK(glx_pixmap_);
    209   if (!make_context_current_.Run())
    210     return false;
    211 
    212   gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_);
    213   glXBindTexImageEXT(x_display_, glx_pixmap_, GLX_FRONT_LEFT_EXT, NULL);
    214 
    215   return true;
    216 }
    217 
    218 VaapiVideoDecodeAccelerator::TFPPicture*
    219     VaapiVideoDecodeAccelerator::TFPPictureById(int32 picture_buffer_id) {
    220   TFPPictures::iterator it = tfp_pictures_.find(picture_buffer_id);
    221   if (it == tfp_pictures_.end()) {
    222     DVLOG(1) << "Picture id " << picture_buffer_id << " does not exist";
    223     return NULL;
    224   }
    225 
    226   return it->second.get();
    227 }
    228 
    229 VaapiVideoDecodeAccelerator::VaapiVideoDecodeAccelerator(
    230     Display* x_display,
    231     const base::Callback<bool(void)>& make_context_current)
    232     : x_display_(x_display),
    233       make_context_current_(make_context_current),
    234       state_(kUninitialized),
    235       input_ready_(&lock_),
    236       surfaces_available_(&lock_),
    237       message_loop_(base::MessageLoop::current()),
    238       decoder_thread_("VaapiDecoderThread"),
    239       num_frames_at_client_(0),
    240       num_stream_bufs_at_decoder_(0),
    241       finish_flush_pending_(false),
    242       awaiting_va_surfaces_recycle_(false),
    243       requested_num_pics_(0),
    244       weak_this_factory_(this) {
    245   weak_this_ = weak_this_factory_.GetWeakPtr();
    246   va_surface_release_cb_ = media::BindToCurrentLoop(
    247       base::Bind(&VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_));
    248 }
    249 
    250 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
    251   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    252 }
    253 
    254 class XFreeDeleter {
    255  public:
    256   void operator()(void* x) const {
    257     ::XFree(x);
    258   }
    259 };
    260 
    261 bool VaapiVideoDecodeAccelerator::InitializeFBConfig() {
    262   const int fbconfig_attr[] = {
    263     GLX_DRAWABLE_TYPE, GLX_PIXMAP_BIT,
    264     GLX_BIND_TO_TEXTURE_TARGETS_EXT, GLX_TEXTURE_2D_BIT_EXT,
    265     GLX_BIND_TO_TEXTURE_RGB_EXT, GL_TRUE,
    266     GLX_Y_INVERTED_EXT, GL_TRUE,
    267     GL_NONE,
    268   };
    269 
    270   int num_fbconfigs;
    271   scoped_ptr<GLXFBConfig, XFreeDeleter> glx_fb_configs(
    272       glXChooseFBConfig(x_display_, DefaultScreen(x_display_), fbconfig_attr,
    273                         &num_fbconfigs));
    274   if (!glx_fb_configs)
    275     return false;
    276   if (!num_fbconfigs)
    277     return false;
    278 
    279   fb_config_ = glx_fb_configs.get()[0];
    280   return true;
    281 }
    282 
    283 bool VaapiVideoDecodeAccelerator::Initialize(media::VideoCodecProfile profile,
    284                                              Client* client) {
    285   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    286 
    287   client_ptr_factory_.reset(new base::WeakPtrFactory<Client>(client));
    288   client_ = client_ptr_factory_->GetWeakPtr();
    289 
    290   base::AutoLock auto_lock(lock_);
    291   DCHECK_EQ(state_, kUninitialized);
    292   DVLOG(2) << "Initializing VAVDA, profile: " << profile;
    293 
    294   if (!make_context_current_.Run())
    295     return false;
    296 
    297   if (!InitializeFBConfig()) {
    298     DVLOG(1) << "Could not get a usable FBConfig";
    299     return false;
    300   }
    301 
    302   vaapi_wrapper_ = VaapiWrapper::Create(
    303       profile, x_display_,
    304       base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR));
    305 
    306   if (!vaapi_wrapper_.get()) {
    307     DVLOG(1) << "Failed initializing VAAPI";
    308     return false;
    309   }
    310 
    311   decoder_.reset(
    312       new VaapiH264Decoder(
    313           vaapi_wrapper_.get(),
    314           media::BindToCurrentLoop(base::Bind(
    315               &VaapiVideoDecodeAccelerator::SurfaceReady, weak_this_)),
    316           base::Bind(&ReportToUMA)));
    317 
    318   CHECK(decoder_thread_.Start());
    319   decoder_thread_proxy_ = decoder_thread_.message_loop_proxy();
    320 
    321   state_ = kIdle;
    322   return true;
    323 }
    324 
    325 void VaapiVideoDecodeAccelerator::SurfaceReady(
    326     int32 input_id,
    327     const scoped_refptr<VASurface>& va_surface) {
    328   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    329   DCHECK(!awaiting_va_surfaces_recycle_);
    330 
    331   // Drop any requests to output if we are resetting or being destroyed.
    332   if (state_ == kResetting || state_ == kDestroying)
    333     return;
    334 
    335   pending_output_cbs_.push(
    336       base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture,
    337                  weak_this_, va_surface, input_id));
    338 
    339   TryOutputSurface();
    340 }
    341 
    342 void VaapiVideoDecodeAccelerator::OutputPicture(
    343     const scoped_refptr<VASurface>& va_surface,
    344     int32 input_id,
    345     TFPPicture* tfp_picture) {
    346   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    347 
    348   int32 output_id  = tfp_picture->picture_buffer_id();
    349 
    350   TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface",
    351                "input_id", input_id,
    352                "output_id", output_id);
    353 
    354   DVLOG(3) << "Outputting VASurface " << va_surface->id()
    355            << " into pixmap bound to picture buffer id " << output_id;
    356 
    357   RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(),
    358                                "Failed binding texture to pixmap",
    359                                PLATFORM_FAILURE, );
    360 
    361   RETURN_AND_NOTIFY_ON_FAILURE(
    362       vaapi_wrapper_->PutSurfaceIntoPixmap(va_surface->id(),
    363                                            tfp_picture->x_pixmap(),
    364                                            tfp_picture->size()),
    365       "Failed putting surface into pixmap", PLATFORM_FAILURE, );
    366 
    367   // Notify the client a picture is ready to be displayed.
    368   ++num_frames_at_client_;
    369   TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
    370   DVLOG(4) << "Notifying output picture id " << output_id
    371            << " for input "<< input_id << " is ready";
    372   if (client_)
    373     client_->PictureReady(media::Picture(output_id, input_id));
    374 }
    375 
    376 void VaapiVideoDecodeAccelerator::TryOutputSurface() {
    377   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    378 
    379   // Handle Destroy() arriving while pictures are queued for output.
    380   if (!client_)
    381     return;
    382 
    383   if (pending_output_cbs_.empty() || output_buffers_.empty())
    384     return;
    385 
    386   OutputCB output_cb = pending_output_cbs_.front();
    387   pending_output_cbs_.pop();
    388 
    389   TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front());
    390   DCHECK(tfp_picture);
    391   output_buffers_.pop();
    392 
    393   output_cb.Run(tfp_picture);
    394 
    395   if (finish_flush_pending_ && pending_output_cbs_.empty())
    396     FinishFlush();
    397 }
    398 
    399 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
    400     const media::BitstreamBuffer& bitstream_buffer) {
    401   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    402   TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id",
    403       bitstream_buffer.id());
    404 
    405   DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
    406            << " size: " << (int)bitstream_buffer.size();
    407 
    408   scoped_ptr<base::SharedMemory> shm(
    409       new base::SharedMemory(bitstream_buffer.handle(), true));
    410   RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()),
    411                               "Failed to map input buffer", UNREADABLE_INPUT,);
    412 
    413   base::AutoLock auto_lock(lock_);
    414 
    415   // Set up a new input buffer and queue it for later.
    416   linked_ptr<InputBuffer> input_buffer(new InputBuffer());
    417   input_buffer->shm.reset(shm.release());
    418   input_buffer->id = bitstream_buffer.id();
    419   input_buffer->size = bitstream_buffer.size();
    420 
    421   ++num_stream_bufs_at_decoder_;
    422   TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
    423                  num_stream_bufs_at_decoder_);
    424 
    425   input_buffers_.push(input_buffer);
    426   input_ready_.Signal();
    427 }
    428 
    429 bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() {
    430   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    431   lock_.AssertAcquired();
    432 
    433   if (curr_input_buffer_.get())
    434     return true;
    435 
    436   // Will only wait if it is expected that in current state new buffers will
    437   // be queued from the client via Decode(). The state can change during wait.
    438   while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) {
    439     input_ready_.Wait();
    440   }
    441 
    442   // We could have got woken up in a different state or never got to sleep
    443   // due to current state; check for that.
    444   switch (state_) {
    445     case kFlushing:
    446       // Here we are only interested in finishing up decoding buffers that are
    447       // already queued up. Otherwise will stop decoding.
    448       if (input_buffers_.empty())
    449         return false;
    450       // else fallthrough
    451     case kDecoding:
    452     case kIdle:
    453       DCHECK(!input_buffers_.empty());
    454 
    455       curr_input_buffer_ = input_buffers_.front();
    456       input_buffers_.pop();
    457 
    458       DVLOG(4) << "New current bitstream buffer, id: "
    459                << curr_input_buffer_->id
    460                << " size: " << curr_input_buffer_->size;
    461 
    462       decoder_->SetStream(
    463           static_cast<uint8*>(curr_input_buffer_->shm->memory()),
    464           curr_input_buffer_->size, curr_input_buffer_->id);
    465       return true;
    466 
    467     default:
    468       // We got woken up due to being destroyed/reset, ignore any already
    469       // queued inputs.
    470       return false;
    471   }
    472 }
    473 
    474 void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() {
    475   lock_.AssertAcquired();
    476   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    477   DCHECK(curr_input_buffer_.get());
    478 
    479   int32 id = curr_input_buffer_->id;
    480   curr_input_buffer_.reset();
    481   DVLOG(4) << "End of input buffer " << id;
    482   message_loop_->PostTask(FROM_HERE, base::Bind(
    483       &Client::NotifyEndOfBitstreamBuffer, client_, id));
    484 
    485   --num_stream_bufs_at_decoder_;
    486   TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
    487                  num_stream_bufs_at_decoder_);
    488 }
    489 
    490 bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() {
    491   lock_.AssertAcquired();
    492   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    493 
    494   while (available_va_surfaces_.empty() &&
    495          (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) {
    496     surfaces_available_.Wait();
    497   }
    498 
    499   if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle)
    500     return false;
    501 
    502   while (!available_va_surfaces_.empty()) {
    503     scoped_refptr<VASurface> va_surface(
    504         new VASurface(available_va_surfaces_.front(), va_surface_release_cb_));
    505     available_va_surfaces_.pop_front();
    506     decoder_->ReuseSurface(va_surface);
    507   }
    508 
    509   return true;
    510 }
    511 
    512 void VaapiVideoDecodeAccelerator::DecodeTask() {
    513   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    514   TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask");
    515   base::AutoLock auto_lock(lock_);
    516 
    517   if (state_ != kDecoding)
    518     return;
    519 
    520   // Main decode task.
    521   DVLOG(4) << "Decode task";
    522 
    523   // Try to decode what stream data is (still) in the decoder until we run out
    524   // of it.
    525   while (GetInputBuffer_Locked()) {
    526     DCHECK(curr_input_buffer_.get());
    527 
    528     VaapiH264Decoder::DecResult res;
    529     {
    530       // We are OK releasing the lock here, as decoder never calls our methods
    531       // directly and we will reacquire the lock before looking at state again.
    532       // This is the main decode function of the decoder and while keeping
    533       // the lock for its duration would be fine, it would defeat the purpose
    534       // of having a separate decoder thread.
    535       base::AutoUnlock auto_unlock(lock_);
    536       res = decoder_->Decode();
    537     }
    538 
    539     switch (res) {
    540       case VaapiH264Decoder::kAllocateNewSurfaces:
    541         DVLOG(1) << "Decoder requesting a new set of surfaces";
    542         message_loop_->PostTask(FROM_HERE, base::Bind(
    543             &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, weak_this_,
    544                 decoder_->GetRequiredNumOfPictures(),
    545                 decoder_->GetPicSize()));
    546         // We'll get rescheduled once ProvidePictureBuffers() finishes.
    547         return;
    548 
    549       case VaapiH264Decoder::kRanOutOfStreamData:
    550         ReturnCurrInputBuffer_Locked();
    551         break;
    552 
    553       case VaapiH264Decoder::kRanOutOfSurfaces:
    554         // No more output buffers in the decoder, try getting more or go to
    555         // sleep waiting for them.
    556         if (!FeedDecoderWithOutputSurfaces_Locked())
    557           return;
    558 
    559         break;
    560 
    561       case VaapiH264Decoder::kDecodeError:
    562         RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
    563                                      PLATFORM_FAILURE, );
    564         return;
    565     }
    566   }
    567 }
    568 
    569 void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics,
    570                                                            gfx::Size size) {
    571   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    572   DCHECK(!awaiting_va_surfaces_recycle_);
    573 
    574   // At this point decoder has stopped running and has already posted onto our
    575   // loop any remaining output request callbacks, which executed before we got
    576   // here. Some of them might have been pended though, because we might not
    577   // have had enough TFPictures to output surfaces to. Initiate a wait cycle,
    578   // which will wait for client to return enough PictureBuffers to us, so that
    579   // we can finish all pending output callbacks, releasing associated surfaces.
    580   DVLOG(1) << "Initiating surface set change";
    581   awaiting_va_surfaces_recycle_ = true;
    582 
    583   requested_num_pics_ = num_pics;
    584   requested_pic_size_ = size;
    585 
    586   TryFinishSurfaceSetChange();
    587 }
    588 
    589 void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() {
    590   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    591 
    592   if (!awaiting_va_surfaces_recycle_)
    593     return;
    594 
    595   if (!pending_output_cbs_.empty() ||
    596       tfp_pictures_.size() != available_va_surfaces_.size()) {
    597     // Either:
    598     // 1. Not all pending pending output callbacks have been executed yet.
    599     // Wait for the client to return enough pictures and retry later.
    600     // 2. The above happened and all surface release callbacks have been posted
    601     // as the result, but not all have executed yet. Post ourselves after them
    602     // to let them release surfaces.
    603     DVLOG(2) << "Awaiting pending output/surface release callbacks to finish";
    604     message_loop_->PostTask(FROM_HERE, base::Bind(
    605         &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, weak_this_));
    606     return;
    607   }
    608 
    609   // All surfaces released, destroy them and dismiss all PictureBuffers.
    610   awaiting_va_surfaces_recycle_ = false;
    611   available_va_surfaces_.clear();
    612   vaapi_wrapper_->DestroySurfaces();
    613 
    614   for (TFPPictures::iterator iter = tfp_pictures_.begin();
    615        iter != tfp_pictures_.end(); ++iter) {
    616     DVLOG(2) << "Dismissing picture id: " << iter->first;
    617     if (client_)
    618       client_->DismissPictureBuffer(iter->first);
    619   }
    620   tfp_pictures_.clear();
    621 
    622   // And ask for a new set as requested.
    623   DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: "
    624            << requested_pic_size_.ToString();
    625 
    626   message_loop_->PostTask(FROM_HERE, base::Bind(
    627       &Client::ProvidePictureBuffers, client_,
    628       requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D));
    629 }
    630 
    631 void VaapiVideoDecodeAccelerator::Decode(
    632     const media::BitstreamBuffer& bitstream_buffer) {
    633   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    634 
    635   TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
    636                bitstream_buffer.id());
    637 
    638   // We got a new input buffer from the client, map it and queue for later use.
    639   MapAndQueueNewInputBuffer(bitstream_buffer);
    640 
    641   base::AutoLock auto_lock(lock_);
    642   switch (state_) {
    643     case kIdle:
    644       state_ = kDecoding;
    645       decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    646           &VaapiVideoDecodeAccelerator::DecodeTask,
    647           base::Unretained(this)));
    648       break;
    649 
    650     case kDecoding:
    651       // Decoder already running, fallthrough.
    652     case kResetting:
    653       // When resetting, allow accumulating bitstream buffers, so that
    654       // the client can queue after-seek-buffers while we are finishing with
    655       // the before-seek one.
    656       break;
    657 
    658     default:
    659       RETURN_AND_NOTIFY_ON_FAILURE(false,
    660           "Decode request from client in invalid state: " << state_,
    661           PLATFORM_FAILURE, );
    662       break;
    663   }
    664 }
    665 
    666 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
    667     VASurfaceID va_surface_id) {
    668   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    669   base::AutoLock auto_lock(lock_);
    670 
    671   available_va_surfaces_.push_back(va_surface_id);
    672   surfaces_available_.Signal();
    673 }
    674 
    675 void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
    676     const std::vector<media::PictureBuffer>& buffers) {
    677   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    678 
    679   base::AutoLock auto_lock(lock_);
    680   DCHECK(tfp_pictures_.empty());
    681 
    682   while (!output_buffers_.empty())
    683     output_buffers_.pop();
    684 
    685   RETURN_AND_NOTIFY_ON_FAILURE(
    686       buffers.size() == requested_num_pics_,
    687       "Got an invalid number of picture buffers. (Got " << buffers.size()
    688       << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, );
    689   DCHECK(requested_pic_size_ == buffers[0].size());
    690 
    691   std::vector<VASurfaceID> va_surface_ids;
    692   RETURN_AND_NOTIFY_ON_FAILURE(
    693       vaapi_wrapper_->CreateSurfaces(requested_pic_size_,
    694                                      buffers.size(),
    695                                      &va_surface_ids),
    696       "Failed creating VA Surfaces", PLATFORM_FAILURE, );
    697   DCHECK_EQ(va_surface_ids.size(), buffers.size());
    698 
    699   for (size_t i = 0; i < buffers.size(); ++i) {
    700     DVLOG(2) << "Assigning picture id: " << buffers[i].id()
    701              << " to texture id: " << buffers[i].texture_id()
    702              << " VASurfaceID: " << va_surface_ids[i];
    703 
    704     linked_ptr<TFPPicture> tfp_picture(
    705         TFPPicture::Create(make_context_current_, fb_config_, x_display_,
    706                            buffers[i].id(), buffers[i].texture_id(),
    707                            requested_pic_size_));
    708 
    709     RETURN_AND_NOTIFY_ON_FAILURE(
    710         tfp_picture.get(), "Failed assigning picture buffer to a texture.",
    711         PLATFORM_FAILURE, );
    712 
    713     bool inserted = tfp_pictures_.insert(std::make_pair(
    714         buffers[i].id(), tfp_picture)).second;
    715     DCHECK(inserted);
    716 
    717     output_buffers_.push(buffers[i].id());
    718     available_va_surfaces_.push_back(va_surface_ids[i]);
    719     surfaces_available_.Signal();
    720   }
    721 
    722   state_ = kDecoding;
    723   decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    724       &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this)));
    725 }
    726 
    727 void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
    728   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    729   TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id",
    730                picture_buffer_id);
    731 
    732   --num_frames_at_client_;
    733   TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
    734 
    735   output_buffers_.push(picture_buffer_id);
    736   TryOutputSurface();
    737 }
    738 
    739 void VaapiVideoDecodeAccelerator::FlushTask() {
    740   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    741   DVLOG(1) << "Flush task";
    742 
    743   // First flush all the pictures that haven't been outputted, notifying the
    744   // client to output them.
    745   bool res = decoder_->Flush();
    746   RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.",
    747                                PLATFORM_FAILURE, );
    748 
    749   // Put the decoder in idle state, ready to resume.
    750   decoder_->Reset();
    751 
    752   message_loop_->PostTask(FROM_HERE, base::Bind(
    753       &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_));
    754 }
    755 
    756 void VaapiVideoDecodeAccelerator::Flush() {
    757   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    758   DVLOG(1) << "Got flush request";
    759 
    760   base::AutoLock auto_lock(lock_);
    761   state_ = kFlushing;
    762   // Queue a flush task after all existing decoding tasks to clean up.
    763   decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    764       &VaapiVideoDecodeAccelerator::FlushTask, base::Unretained(this)));
    765 
    766   input_ready_.Signal();
    767   surfaces_available_.Signal();
    768 }
    769 
    770 void VaapiVideoDecodeAccelerator::FinishFlush() {
    771   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    772 
    773   finish_flush_pending_ = false;
    774 
    775   base::AutoLock auto_lock(lock_);
    776   if (state_ != kFlushing) {
    777     DCHECK_EQ(state_, kDestroying);
    778     return;  // We could've gotten destroyed already.
    779   }
    780 
    781   // Still waiting for textures from client to finish outputting all pending
    782   // frames. Try again later.
    783   if (!pending_output_cbs_.empty()) {
    784     finish_flush_pending_ = true;
    785     return;
    786   }
    787 
    788   state_ = kIdle;
    789 
    790   message_loop_->PostTask(FROM_HERE, base::Bind(
    791       &Client::NotifyFlushDone, client_));
    792 
    793   DVLOG(1) << "Flush finished";
    794 }
    795 
    796 void VaapiVideoDecodeAccelerator::ResetTask() {
    797   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    798   DVLOG(1) << "ResetTask";
    799 
    800   // All the decoding tasks from before the reset request from client are done
    801   // by now, as this task was scheduled after them and client is expected not
    802   // to call Decode() after Reset() and before NotifyResetDone.
    803   decoder_->Reset();
    804 
    805   base::AutoLock auto_lock(lock_);
    806 
    807   // Return current input buffer, if present.
    808   if (curr_input_buffer_.get())
    809     ReturnCurrInputBuffer_Locked();
    810 
    811   // And let client know that we are done with reset.
    812   message_loop_->PostTask(FROM_HERE, base::Bind(
    813       &VaapiVideoDecodeAccelerator::FinishReset, weak_this_));
    814 }
    815 
    816 void VaapiVideoDecodeAccelerator::Reset() {
    817   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    818   DVLOG(1) << "Got reset request";
    819 
    820   // This will make any new decode tasks exit early.
    821   base::AutoLock auto_lock(lock_);
    822   state_ = kResetting;
    823   finish_flush_pending_ = false;
    824 
    825   // Drop all remaining input buffers, if present.
    826   while (!input_buffers_.empty()) {
    827     message_loop_->PostTask(FROM_HERE, base::Bind(
    828         &Client::NotifyEndOfBitstreamBuffer, client_,
    829         input_buffers_.front()->id));
    830     input_buffers_.pop();
    831   }
    832 
    833   decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    834       &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this)));
    835 
    836   input_ready_.Signal();
    837   surfaces_available_.Signal();
    838 }
    839 
    840 void VaapiVideoDecodeAccelerator::FinishReset() {
    841   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    842   DVLOG(1) << "FinishReset";
    843   base::AutoLock auto_lock(lock_);
    844 
    845   if (state_ != kResetting) {
    846     DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_;
    847     return;  // We could've gotten destroyed already.
    848   }
    849 
    850   // Drop pending outputs.
    851   while (!pending_output_cbs_.empty())
    852     pending_output_cbs_.pop();
    853 
    854   if (awaiting_va_surfaces_recycle_) {
    855     // Decoder requested a new surface set while we were waiting for it to
    856     // finish the last DecodeTask, running at the time of Reset().
    857     // Let the surface set change finish first before resetting.
    858     message_loop_->PostTask(FROM_HERE, base::Bind(
    859         &VaapiVideoDecodeAccelerator::FinishReset, weak_this_));
    860     return;
    861   }
    862 
    863   num_stream_bufs_at_decoder_ = 0;
    864   state_ = kIdle;
    865 
    866   message_loop_->PostTask(FROM_HERE, base::Bind(
    867       &Client::NotifyResetDone, client_));
    868 
    869   // The client might have given us new buffers via Decode() while we were
    870   // resetting and might be waiting for our move, and not call Decode() anymore
    871   // until we return something. Post a DecodeTask() so that we won't
    872   // sleep forever waiting for Decode() in that case. Having two of them
    873   // in the pipe is harmless, the additional one will return as soon as it sees
    874   // that we are back in kDecoding state.
    875   if (!input_buffers_.empty()) {
    876     state_ = kDecoding;
    877     decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    878       &VaapiVideoDecodeAccelerator::DecodeTask,
    879       base::Unretained(this)));
    880   }
    881 
    882   DVLOG(1) << "Reset finished";
    883 }
    884 
    885 void VaapiVideoDecodeAccelerator::Cleanup() {
    886   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    887 
    888   if (state_ == kUninitialized || state_ == kDestroying)
    889     return;
    890 
    891   DVLOG(1) << "Destroying VAVDA";
    892   base::AutoLock auto_lock(lock_);
    893   state_ = kDestroying;
    894 
    895   client_ptr_factory_.reset();
    896   weak_this_factory_.InvalidateWeakPtrs();
    897 
    898   // Signal all potential waiters on the decoder_thread_, let them early-exit,
    899   // as we've just moved to the kDestroying state, and wait for all tasks
    900   // to finish.
    901   input_ready_.Signal();
    902   surfaces_available_.Signal();
    903   {
    904     base::AutoUnlock auto_unlock(lock_);
    905     decoder_thread_.Stop();
    906   }
    907 
    908   state_ = kUninitialized;
    909 }
    910 
    911 void VaapiVideoDecodeAccelerator::Destroy() {
    912   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    913   Cleanup();
    914   delete this;
    915 }
    916 
    917 bool VaapiVideoDecodeAccelerator::CanDecodeOnIOThread() {
    918   return false;
    919 }
    920 
    921 }  // namespace content
    922