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       VaapiWrapper::kDecode,
    304       profile,
    305       x_display_,
    306       base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR));
    307 
    308   if (!vaapi_wrapper_.get()) {
    309     DVLOG(1) << "Failed initializing VAAPI";
    310     return false;
    311   }
    312 
    313   decoder_.reset(
    314       new VaapiH264Decoder(
    315           vaapi_wrapper_.get(),
    316           media::BindToCurrentLoop(base::Bind(
    317               &VaapiVideoDecodeAccelerator::SurfaceReady, weak_this_)),
    318           base::Bind(&ReportToUMA)));
    319 
    320   CHECK(decoder_thread_.Start());
    321   decoder_thread_proxy_ = decoder_thread_.message_loop_proxy();
    322 
    323   state_ = kIdle;
    324   return true;
    325 }
    326 
    327 void VaapiVideoDecodeAccelerator::SurfaceReady(
    328     int32 input_id,
    329     const scoped_refptr<VASurface>& va_surface) {
    330   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    331   DCHECK(!awaiting_va_surfaces_recycle_);
    332 
    333   // Drop any requests to output if we are resetting or being destroyed.
    334   if (state_ == kResetting || state_ == kDestroying)
    335     return;
    336 
    337   pending_output_cbs_.push(
    338       base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture,
    339                  weak_this_, va_surface, input_id));
    340 
    341   TryOutputSurface();
    342 }
    343 
    344 void VaapiVideoDecodeAccelerator::OutputPicture(
    345     const scoped_refptr<VASurface>& va_surface,
    346     int32 input_id,
    347     TFPPicture* tfp_picture) {
    348   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    349 
    350   int32 output_id  = tfp_picture->picture_buffer_id();
    351 
    352   TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface",
    353                "input_id", input_id,
    354                "output_id", output_id);
    355 
    356   DVLOG(3) << "Outputting VASurface " << va_surface->id()
    357            << " into pixmap bound to picture buffer id " << output_id;
    358 
    359   RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(),
    360                                "Failed binding texture to pixmap",
    361                                PLATFORM_FAILURE, );
    362 
    363   RETURN_AND_NOTIFY_ON_FAILURE(
    364       vaapi_wrapper_->PutSurfaceIntoPixmap(va_surface->id(),
    365                                            tfp_picture->x_pixmap(),
    366                                            tfp_picture->size()),
    367       "Failed putting surface into pixmap", PLATFORM_FAILURE, );
    368 
    369   // Notify the client a picture is ready to be displayed.
    370   ++num_frames_at_client_;
    371   TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
    372   DVLOG(4) << "Notifying output picture id " << output_id
    373            << " for input "<< input_id << " is ready";
    374   // TODO(posciak): Use visible size from decoder here instead
    375   // (crbug.com/402760).
    376   if (client_)
    377     client_->PictureReady(
    378         media::Picture(output_id, input_id, gfx::Rect(tfp_picture->size())));
    379 }
    380 
    381 void VaapiVideoDecodeAccelerator::TryOutputSurface() {
    382   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    383 
    384   // Handle Destroy() arriving while pictures are queued for output.
    385   if (!client_)
    386     return;
    387 
    388   if (pending_output_cbs_.empty() || output_buffers_.empty())
    389     return;
    390 
    391   OutputCB output_cb = pending_output_cbs_.front();
    392   pending_output_cbs_.pop();
    393 
    394   TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front());
    395   DCHECK(tfp_picture);
    396   output_buffers_.pop();
    397 
    398   output_cb.Run(tfp_picture);
    399 
    400   if (finish_flush_pending_ && pending_output_cbs_.empty())
    401     FinishFlush();
    402 }
    403 
    404 void VaapiVideoDecodeAccelerator::MapAndQueueNewInputBuffer(
    405     const media::BitstreamBuffer& bitstream_buffer) {
    406   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    407   TRACE_EVENT1("Video Decoder", "MapAndQueueNewInputBuffer", "input_id",
    408       bitstream_buffer.id());
    409 
    410   DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
    411            << " size: " << (int)bitstream_buffer.size();
    412 
    413   scoped_ptr<base::SharedMemory> shm(
    414       new base::SharedMemory(bitstream_buffer.handle(), true));
    415   RETURN_AND_NOTIFY_ON_FAILURE(shm->Map(bitstream_buffer.size()),
    416                               "Failed to map input buffer", UNREADABLE_INPUT,);
    417 
    418   base::AutoLock auto_lock(lock_);
    419 
    420   // Set up a new input buffer and queue it for later.
    421   linked_ptr<InputBuffer> input_buffer(new InputBuffer());
    422   input_buffer->shm.reset(shm.release());
    423   input_buffer->id = bitstream_buffer.id();
    424   input_buffer->size = bitstream_buffer.size();
    425 
    426   ++num_stream_bufs_at_decoder_;
    427   TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
    428                  num_stream_bufs_at_decoder_);
    429 
    430   input_buffers_.push(input_buffer);
    431   input_ready_.Signal();
    432 }
    433 
    434 bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() {
    435   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    436   lock_.AssertAcquired();
    437 
    438   if (curr_input_buffer_.get())
    439     return true;
    440 
    441   // Will only wait if it is expected that in current state new buffers will
    442   // be queued from the client via Decode(). The state can change during wait.
    443   while (input_buffers_.empty() && (state_ == kDecoding || state_ == kIdle)) {
    444     input_ready_.Wait();
    445   }
    446 
    447   // We could have got woken up in a different state or never got to sleep
    448   // due to current state; check for that.
    449   switch (state_) {
    450     case kFlushing:
    451       // Here we are only interested in finishing up decoding buffers that are
    452       // already queued up. Otherwise will stop decoding.
    453       if (input_buffers_.empty())
    454         return false;
    455       // else fallthrough
    456     case kDecoding:
    457     case kIdle:
    458       DCHECK(!input_buffers_.empty());
    459 
    460       curr_input_buffer_ = input_buffers_.front();
    461       input_buffers_.pop();
    462 
    463       DVLOG(4) << "New current bitstream buffer, id: "
    464                << curr_input_buffer_->id
    465                << " size: " << curr_input_buffer_->size;
    466 
    467       decoder_->SetStream(
    468           static_cast<uint8*>(curr_input_buffer_->shm->memory()),
    469           curr_input_buffer_->size, curr_input_buffer_->id);
    470       return true;
    471 
    472     default:
    473       // We got woken up due to being destroyed/reset, ignore any already
    474       // queued inputs.
    475       return false;
    476   }
    477 }
    478 
    479 void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() {
    480   lock_.AssertAcquired();
    481   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    482   DCHECK(curr_input_buffer_.get());
    483 
    484   int32 id = curr_input_buffer_->id;
    485   curr_input_buffer_.reset();
    486   DVLOG(4) << "End of input buffer " << id;
    487   message_loop_->PostTask(FROM_HERE, base::Bind(
    488       &Client::NotifyEndOfBitstreamBuffer, client_, id));
    489 
    490   --num_stream_bufs_at_decoder_;
    491   TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
    492                  num_stream_bufs_at_decoder_);
    493 }
    494 
    495 bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() {
    496   lock_.AssertAcquired();
    497   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    498 
    499   while (available_va_surfaces_.empty() &&
    500          (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) {
    501     surfaces_available_.Wait();
    502   }
    503 
    504   if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle)
    505     return false;
    506 
    507   while (!available_va_surfaces_.empty()) {
    508     scoped_refptr<VASurface> va_surface(
    509         new VASurface(available_va_surfaces_.front(), va_surface_release_cb_));
    510     available_va_surfaces_.pop_front();
    511     decoder_->ReuseSurface(va_surface);
    512   }
    513 
    514   return true;
    515 }
    516 
    517 void VaapiVideoDecodeAccelerator::DecodeTask() {
    518   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    519   TRACE_EVENT0("Video Decoder", "VAVDA::DecodeTask");
    520   base::AutoLock auto_lock(lock_);
    521 
    522   if (state_ != kDecoding)
    523     return;
    524 
    525   // Main decode task.
    526   DVLOG(4) << "Decode task";
    527 
    528   // Try to decode what stream data is (still) in the decoder until we run out
    529   // of it.
    530   while (GetInputBuffer_Locked()) {
    531     DCHECK(curr_input_buffer_.get());
    532 
    533     VaapiH264Decoder::DecResult res;
    534     {
    535       // We are OK releasing the lock here, as decoder never calls our methods
    536       // directly and we will reacquire the lock before looking at state again.
    537       // This is the main decode function of the decoder and while keeping
    538       // the lock for its duration would be fine, it would defeat the purpose
    539       // of having a separate decoder thread.
    540       base::AutoUnlock auto_unlock(lock_);
    541       res = decoder_->Decode();
    542     }
    543 
    544     switch (res) {
    545       case VaapiH264Decoder::kAllocateNewSurfaces:
    546         DVLOG(1) << "Decoder requesting a new set of surfaces";
    547         message_loop_->PostTask(FROM_HERE, base::Bind(
    548             &VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange, weak_this_,
    549                 decoder_->GetRequiredNumOfPictures(),
    550                 decoder_->GetPicSize()));
    551         // We'll get rescheduled once ProvidePictureBuffers() finishes.
    552         return;
    553 
    554       case VaapiH264Decoder::kRanOutOfStreamData:
    555         ReturnCurrInputBuffer_Locked();
    556         break;
    557 
    558       case VaapiH264Decoder::kRanOutOfSurfaces:
    559         // No more output buffers in the decoder, try getting more or go to
    560         // sleep waiting for them.
    561         if (!FeedDecoderWithOutputSurfaces_Locked())
    562           return;
    563 
    564         break;
    565 
    566       case VaapiH264Decoder::kDecodeError:
    567         RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
    568                                      PLATFORM_FAILURE, );
    569         return;
    570     }
    571   }
    572 }
    573 
    574 void VaapiVideoDecodeAccelerator::InitiateSurfaceSetChange(size_t num_pics,
    575                                                            gfx::Size size) {
    576   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    577   DCHECK(!awaiting_va_surfaces_recycle_);
    578 
    579   // At this point decoder has stopped running and has already posted onto our
    580   // loop any remaining output request callbacks, which executed before we got
    581   // here. Some of them might have been pended though, because we might not
    582   // have had enough TFPictures to output surfaces to. Initiate a wait cycle,
    583   // which will wait for client to return enough PictureBuffers to us, so that
    584   // we can finish all pending output callbacks, releasing associated surfaces.
    585   DVLOG(1) << "Initiating surface set change";
    586   awaiting_va_surfaces_recycle_ = true;
    587 
    588   requested_num_pics_ = num_pics;
    589   requested_pic_size_ = size;
    590 
    591   TryFinishSurfaceSetChange();
    592 }
    593 
    594 void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() {
    595   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    596 
    597   if (!awaiting_va_surfaces_recycle_)
    598     return;
    599 
    600   if (!pending_output_cbs_.empty() ||
    601       tfp_pictures_.size() != available_va_surfaces_.size()) {
    602     // Either:
    603     // 1. Not all pending pending output callbacks have been executed yet.
    604     // Wait for the client to return enough pictures and retry later.
    605     // 2. The above happened and all surface release callbacks have been posted
    606     // as the result, but not all have executed yet. Post ourselves after them
    607     // to let them release surfaces.
    608     DVLOG(2) << "Awaiting pending output/surface release callbacks to finish";
    609     message_loop_->PostTask(FROM_HERE, base::Bind(
    610         &VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange, weak_this_));
    611     return;
    612   }
    613 
    614   // All surfaces released, destroy them and dismiss all PictureBuffers.
    615   awaiting_va_surfaces_recycle_ = false;
    616   available_va_surfaces_.clear();
    617   vaapi_wrapper_->DestroySurfaces();
    618 
    619   for (TFPPictures::iterator iter = tfp_pictures_.begin();
    620        iter != tfp_pictures_.end(); ++iter) {
    621     DVLOG(2) << "Dismissing picture id: " << iter->first;
    622     if (client_)
    623       client_->DismissPictureBuffer(iter->first);
    624   }
    625   tfp_pictures_.clear();
    626 
    627   // And ask for a new set as requested.
    628   DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: "
    629            << requested_pic_size_.ToString();
    630 
    631   message_loop_->PostTask(FROM_HERE, base::Bind(
    632       &Client::ProvidePictureBuffers, client_,
    633       requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D));
    634 }
    635 
    636 void VaapiVideoDecodeAccelerator::Decode(
    637     const media::BitstreamBuffer& bitstream_buffer) {
    638   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    639 
    640   TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
    641                bitstream_buffer.id());
    642 
    643   // We got a new input buffer from the client, map it and queue for later use.
    644   MapAndQueueNewInputBuffer(bitstream_buffer);
    645 
    646   base::AutoLock auto_lock(lock_);
    647   switch (state_) {
    648     case kIdle:
    649       state_ = kDecoding;
    650       decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    651           &VaapiVideoDecodeAccelerator::DecodeTask,
    652           base::Unretained(this)));
    653       break;
    654 
    655     case kDecoding:
    656       // Decoder already running, fallthrough.
    657     case kResetting:
    658       // When resetting, allow accumulating bitstream buffers, so that
    659       // the client can queue after-seek-buffers while we are finishing with
    660       // the before-seek one.
    661       break;
    662 
    663     default:
    664       RETURN_AND_NOTIFY_ON_FAILURE(false,
    665           "Decode request from client in invalid state: " << state_,
    666           PLATFORM_FAILURE, );
    667       break;
    668   }
    669 }
    670 
    671 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
    672     VASurfaceID va_surface_id) {
    673   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    674   base::AutoLock auto_lock(lock_);
    675 
    676   available_va_surfaces_.push_back(va_surface_id);
    677   surfaces_available_.Signal();
    678 }
    679 
    680 void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
    681     const std::vector<media::PictureBuffer>& buffers) {
    682   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    683 
    684   base::AutoLock auto_lock(lock_);
    685   DCHECK(tfp_pictures_.empty());
    686 
    687   while (!output_buffers_.empty())
    688     output_buffers_.pop();
    689 
    690   RETURN_AND_NOTIFY_ON_FAILURE(
    691       buffers.size() == requested_num_pics_,
    692       "Got an invalid number of picture buffers. (Got " << buffers.size()
    693       << ", requested " << requested_num_pics_ << ")", INVALID_ARGUMENT, );
    694   DCHECK(requested_pic_size_ == buffers[0].size());
    695 
    696   std::vector<VASurfaceID> va_surface_ids;
    697   RETURN_AND_NOTIFY_ON_FAILURE(
    698       vaapi_wrapper_->CreateSurfaces(requested_pic_size_,
    699                                      buffers.size(),
    700                                      &va_surface_ids),
    701       "Failed creating VA Surfaces", PLATFORM_FAILURE, );
    702   DCHECK_EQ(va_surface_ids.size(), buffers.size());
    703 
    704   for (size_t i = 0; i < buffers.size(); ++i) {
    705     DVLOG(2) << "Assigning picture id: " << buffers[i].id()
    706              << " to texture id: " << buffers[i].texture_id()
    707              << " VASurfaceID: " << va_surface_ids[i];
    708 
    709     linked_ptr<TFPPicture> tfp_picture(
    710         TFPPicture::Create(make_context_current_, fb_config_, x_display_,
    711                            buffers[i].id(), buffers[i].texture_id(),
    712                            requested_pic_size_));
    713 
    714     RETURN_AND_NOTIFY_ON_FAILURE(
    715         tfp_picture.get(), "Failed assigning picture buffer to a texture.",
    716         PLATFORM_FAILURE, );
    717 
    718     bool inserted = tfp_pictures_.insert(std::make_pair(
    719         buffers[i].id(), tfp_picture)).second;
    720     DCHECK(inserted);
    721 
    722     output_buffers_.push(buffers[i].id());
    723     available_va_surfaces_.push_back(va_surface_ids[i]);
    724     surfaces_available_.Signal();
    725   }
    726 
    727   state_ = kDecoding;
    728   decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    729       &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this)));
    730 }
    731 
    732 void VaapiVideoDecodeAccelerator::ReusePictureBuffer(int32 picture_buffer_id) {
    733   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    734   TRACE_EVENT1("Video Decoder", "VAVDA::ReusePictureBuffer", "Picture id",
    735                picture_buffer_id);
    736 
    737   --num_frames_at_client_;
    738   TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
    739 
    740   output_buffers_.push(picture_buffer_id);
    741   TryOutputSurface();
    742 }
    743 
    744 void VaapiVideoDecodeAccelerator::FlushTask() {
    745   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    746   DVLOG(1) << "Flush task";
    747 
    748   // First flush all the pictures that haven't been outputted, notifying the
    749   // client to output them.
    750   bool res = decoder_->Flush();
    751   RETURN_AND_NOTIFY_ON_FAILURE(res, "Failed flushing the decoder.",
    752                                PLATFORM_FAILURE, );
    753 
    754   // Put the decoder in idle state, ready to resume.
    755   decoder_->Reset();
    756 
    757   message_loop_->PostTask(FROM_HERE, base::Bind(
    758       &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_));
    759 }
    760 
    761 void VaapiVideoDecodeAccelerator::Flush() {
    762   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    763   DVLOG(1) << "Got flush request";
    764 
    765   base::AutoLock auto_lock(lock_);
    766   state_ = kFlushing;
    767   // Queue a flush task after all existing decoding tasks to clean up.
    768   decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    769       &VaapiVideoDecodeAccelerator::FlushTask, base::Unretained(this)));
    770 
    771   input_ready_.Signal();
    772   surfaces_available_.Signal();
    773 }
    774 
    775 void VaapiVideoDecodeAccelerator::FinishFlush() {
    776   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    777 
    778   finish_flush_pending_ = false;
    779 
    780   base::AutoLock auto_lock(lock_);
    781   if (state_ != kFlushing) {
    782     DCHECK_EQ(state_, kDestroying);
    783     return;  // We could've gotten destroyed already.
    784   }
    785 
    786   // Still waiting for textures from client to finish outputting all pending
    787   // frames. Try again later.
    788   if (!pending_output_cbs_.empty()) {
    789     finish_flush_pending_ = true;
    790     return;
    791   }
    792 
    793   state_ = kIdle;
    794 
    795   message_loop_->PostTask(FROM_HERE, base::Bind(
    796       &Client::NotifyFlushDone, client_));
    797 
    798   DVLOG(1) << "Flush finished";
    799 }
    800 
    801 void VaapiVideoDecodeAccelerator::ResetTask() {
    802   DCHECK(decoder_thread_proxy_->BelongsToCurrentThread());
    803   DVLOG(1) << "ResetTask";
    804 
    805   // All the decoding tasks from before the reset request from client are done
    806   // by now, as this task was scheduled after them and client is expected not
    807   // to call Decode() after Reset() and before NotifyResetDone.
    808   decoder_->Reset();
    809 
    810   base::AutoLock auto_lock(lock_);
    811 
    812   // Return current input buffer, if present.
    813   if (curr_input_buffer_.get())
    814     ReturnCurrInputBuffer_Locked();
    815 
    816   // And let client know that we are done with reset.
    817   message_loop_->PostTask(FROM_HERE, base::Bind(
    818       &VaapiVideoDecodeAccelerator::FinishReset, weak_this_));
    819 }
    820 
    821 void VaapiVideoDecodeAccelerator::Reset() {
    822   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    823   DVLOG(1) << "Got reset request";
    824 
    825   // This will make any new decode tasks exit early.
    826   base::AutoLock auto_lock(lock_);
    827   state_ = kResetting;
    828   finish_flush_pending_ = false;
    829 
    830   // Drop all remaining input buffers, if present.
    831   while (!input_buffers_.empty()) {
    832     message_loop_->PostTask(FROM_HERE, base::Bind(
    833         &Client::NotifyEndOfBitstreamBuffer, client_,
    834         input_buffers_.front()->id));
    835     input_buffers_.pop();
    836   }
    837 
    838   decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    839       &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this)));
    840 
    841   input_ready_.Signal();
    842   surfaces_available_.Signal();
    843 }
    844 
    845 void VaapiVideoDecodeAccelerator::FinishReset() {
    846   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    847   DVLOG(1) << "FinishReset";
    848   base::AutoLock auto_lock(lock_);
    849 
    850   if (state_ != kResetting) {
    851     DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_;
    852     return;  // We could've gotten destroyed already.
    853   }
    854 
    855   // Drop pending outputs.
    856   while (!pending_output_cbs_.empty())
    857     pending_output_cbs_.pop();
    858 
    859   if (awaiting_va_surfaces_recycle_) {
    860     // Decoder requested a new surface set while we were waiting for it to
    861     // finish the last DecodeTask, running at the time of Reset().
    862     // Let the surface set change finish first before resetting.
    863     message_loop_->PostTask(FROM_HERE, base::Bind(
    864         &VaapiVideoDecodeAccelerator::FinishReset, weak_this_));
    865     return;
    866   }
    867 
    868   num_stream_bufs_at_decoder_ = 0;
    869   state_ = kIdle;
    870 
    871   message_loop_->PostTask(FROM_HERE, base::Bind(
    872       &Client::NotifyResetDone, client_));
    873 
    874   // The client might have given us new buffers via Decode() while we were
    875   // resetting and might be waiting for our move, and not call Decode() anymore
    876   // until we return something. Post a DecodeTask() so that we won't
    877   // sleep forever waiting for Decode() in that case. Having two of them
    878   // in the pipe is harmless, the additional one will return as soon as it sees
    879   // that we are back in kDecoding state.
    880   if (!input_buffers_.empty()) {
    881     state_ = kDecoding;
    882     decoder_thread_proxy_->PostTask(FROM_HERE, base::Bind(
    883       &VaapiVideoDecodeAccelerator::DecodeTask,
    884       base::Unretained(this)));
    885   }
    886 
    887   DVLOG(1) << "Reset finished";
    888 }
    889 
    890 void VaapiVideoDecodeAccelerator::Cleanup() {
    891   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    892 
    893   if (state_ == kUninitialized || state_ == kDestroying)
    894     return;
    895 
    896   DVLOG(1) << "Destroying VAVDA";
    897   base::AutoLock auto_lock(lock_);
    898   state_ = kDestroying;
    899 
    900   client_ptr_factory_.reset();
    901   weak_this_factory_.InvalidateWeakPtrs();
    902 
    903   // Signal all potential waiters on the decoder_thread_, let them early-exit,
    904   // as we've just moved to the kDestroying state, and wait for all tasks
    905   // to finish.
    906   input_ready_.Signal();
    907   surfaces_available_.Signal();
    908   {
    909     base::AutoUnlock auto_unlock(lock_);
    910     decoder_thread_.Stop();
    911   }
    912 
    913   state_ = kUninitialized;
    914 }
    915 
    916 void VaapiVideoDecodeAccelerator::Destroy() {
    917   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    918   Cleanup();
    919   delete this;
    920 }
    921 
    922 bool VaapiVideoDecodeAccelerator::CanDecodeOnIOThread() {
    923   return false;
    924 }
    925 
    926 }  // namespace content
    927