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 "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"
     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_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
     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_.InvalidateWeakPtrs();
     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 {
     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   uint32 texture_id() {
     88     return texture_id_;
     89   }
     90 
     91   gfx::Size size() {
     92     return size_;
     93   }
     94 
     95   int x_pixmap() {
     96     return x_pixmap_;
     97   }
     98 
     99   // Bind texture to pixmap. Needs to be called every frame.
    100   bool Bind();
    101 
    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);
    108 
    109   bool Initialize(const GLXFBConfig& fb_config);
    110 
    111   base::Callback<bool(void)> make_context_current_;
    112 
    113   Display* x_display_;
    114 
    115   // Output id for the client.
    116   int32 picture_buffer_id_;
    117   uint32 texture_id_;
    118 
    119   gfx::Size size_;
    120 
    121   // Pixmaps bound to this texture.
    122   Pixmap x_pixmap_;
    123   GLXPixmap glx_pixmap_;
    124 
    125   DISALLOW_COPY_AND_ASSIGN(TFPPicture);
    126 };
    127 
    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());
    142 };
    143 
    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) {
    152 
    153   linked_ptr<TFPPicture> tfp_picture(
    154       new TFPPicture(make_context_current, x_display, picture_buffer_id,
    155                      texture_id, size));
    156 
    157   if (!tfp_picture->Initialize(fb_config))
    158     tfp_picture.reset();
    159 
    160   return tfp_picture;
    161 }
    162 
    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());
    168 
    169   if (!make_context_current_.Run())
    170     return false;
    171 
    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;
    181   }
    182 
    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,
    187   };
    188 
    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;
    194   }
    195 
    196   return true;
    197 }
    198 
    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());
    203 
    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_);
    208   }
    209 
    210   if (x_pixmap_)
    211     XFreePixmap(x_display_, x_pixmap_);
    212   XSync(x_display_, False);  // Needed to work around buggy vdpau-driver.
    213 }
    214 
    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());
    221 
    222   if (!make_context_current_.Run())
    223     return false;
    224 
    225   gfx::ScopedTextureBinder texture_binder(GL_TEXTURE_2D, texture_id_);
    226   glXBindTexImageEXT(x_display_, glx_pixmap_, GLX_FRONT_LEFT_EXT, NULL);
    227 
    228   return true;
    229 }
    230 
    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;
    237   }
    238 
    239   return it->second.get();
    240 }
    241 
    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);
    263 }
    264 
    265 VaapiVideoDecodeAccelerator::~VaapiVideoDecodeAccelerator() {
    266   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    267 }
    268 
    269 class ScopedPtrXFree {
    270  public:
    271   void operator()(void* x) const {
    272     ::XFree(x);
    273   }
    274 };
    275 
    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,
    283   };
    284 
    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;
    293 
    294   fb_config_ = glx_fb_configs.get()[0];
    295   return true;
    296 }
    297 
    298 bool VaapiVideoDecodeAccelerator::Initialize(
    299     media::VideoCodecProfile profile) {
    300   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    301 
    302   base::AutoLock auto_lock(lock_);
    303   DCHECK_EQ(state_, kUninitialized);
    304   DVLOG(2) << "Initializing VAVDA, profile: " << profile;
    305 
    306   if (!make_context_current_.Run())
    307     return false;
    308 
    309   if (!InitializeFBConfig()) {
    310     DVLOG(1) << "Could not get a usable FBConfig";
    311     return false;
    312   }
    313 
    314   vaapi_wrapper_ = VaapiWrapper::Create(
    315       profile, x_display_,
    316       base::Bind(&ReportToUMA, content::VaapiH264Decoder::VAAPI_ERROR));
    317 
    318   if (!vaapi_wrapper_.get()) {
    319     DVLOG(1) << "Failed initializing VAAPI";
    320     return false;
    321   }
    322 
    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)));
    329 
    330   CHECK(decoder_thread_.Start());
    331 
    332   state_ = kIdle;
    333 
    334   message_loop_->PostTask(FROM_HERE, base::Bind(
    335       &Client::NotifyInitializeDone, client_));
    336   return true;
    337 }
    338 
    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_);
    344 
    345   // Drop any requests to output if we are resetting or being destroyed.
    346   if (state_ == kResetting || state_ == kDestroying)
    347     return;
    348 
    349   pending_output_cbs_.push(
    350       base::Bind(&VaapiVideoDecodeAccelerator::OutputPicture,
    351                  weak_this_, va_surface, input_id));
    352 
    353   TryOutputSurface();
    354 }
    355 
    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());
    361 
    362   int32 output_id  = tfp_picture->picture_buffer_id();
    363 
    364   TRACE_EVENT2("Video Decoder", "VAVDA::OutputSurface",
    365                "input_id", input_id,
    366                "output_id", output_id);
    367 
    368   DVLOG(3) << "Outputting VASurface " << va_surface->id()
    369            << " into pixmap bound to picture buffer id " << output_id;
    370 
    371   RETURN_AND_NOTIFY_ON_FAILURE(tfp_picture->Bind(),
    372                                "Failed binding texture to pixmap",
    373                                PLATFORM_FAILURE, );
    374 
    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, );
    380 
    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));
    387 }
    388 
    389 void VaapiVideoDecodeAccelerator::TryOutputSurface() {
    390   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    391 
    392   // Handle Destroy() arriving while pictures are queued for output.
    393   if (!client_)
    394     return;
    395 
    396   if (pending_output_cbs_.empty() || output_buffers_.empty())
    397     return;
    398 
    399   OutputCB output_cb = pending_output_cbs_.front();
    400   pending_output_cbs_.pop();
    401 
    402   TFPPicture* tfp_picture = TFPPictureById(output_buffers_.front());
    403   DCHECK(tfp_picture);
    404   output_buffers_.pop();
    405 
    406   output_cb.Run(tfp_picture);
    407 
    408   if (finish_flush_pending_ && pending_output_cbs_.empty())
    409     FinishFlush();
    410 }
    411 
    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());
    417 
    418   DVLOG(4) << "Mapping new input buffer id: " << bitstream_buffer.id()
    419            << " size: " << (int)bitstream_buffer.size();
    420 
    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,);
    425 
    426   base::AutoLock auto_lock(lock_);
    427 
    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();
    433 
    434   ++num_stream_bufs_at_decoder_;
    435   TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
    436                  num_stream_bufs_at_decoder_);
    437 
    438   input_buffers_.push(input_buffer);
    439   input_ready_.Signal();
    440 }
    441 
    442 bool VaapiVideoDecodeAccelerator::GetInputBuffer_Locked() {
    443   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
    444   lock_.AssertAcquired();
    445 
    446   if (curr_input_buffer_.get())
    447     return true;
    448 
    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();
    453   }
    454 
    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());
    467 
    468       curr_input_buffer_ = input_buffers_.front();
    469       input_buffers_.pop();
    470 
    471       DVLOG(4) << "New current bitstream buffer, id: "
    472                << curr_input_buffer_->id
    473                << " size: " << curr_input_buffer_->size;
    474 
    475       decoder_->SetStream(
    476           static_cast<uint8*>(curr_input_buffer_->shm->memory()),
    477           curr_input_buffer_->size, curr_input_buffer_->id);
    478       return true;
    479 
    480     default:
    481       // We got woken up due to being destroyed/reset, ignore any already
    482       // queued inputs.
    483       return false;
    484   }
    485 }
    486 
    487 void VaapiVideoDecodeAccelerator::ReturnCurrInputBuffer_Locked() {
    488   lock_.AssertAcquired();
    489   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
    490   DCHECK(curr_input_buffer_.get());
    491 
    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));
    497 
    498   --num_stream_bufs_at_decoder_;
    499   TRACE_COUNTER1("Video Decoder", "Stream buffers at decoder",
    500                  num_stream_bufs_at_decoder_);
    501 }
    502 
    503 bool VaapiVideoDecodeAccelerator::FeedDecoderWithOutputSurfaces_Locked() {
    504   lock_.AssertAcquired();
    505   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
    506 
    507   while (available_va_surfaces_.empty() &&
    508          (state_ == kDecoding || state_ == kFlushing || state_ == kIdle)) {
    509     surfaces_available_.Wait();
    510   }
    511 
    512   if (state_ != kDecoding && state_ != kFlushing && state_ != kIdle)
    513     return false;
    514 
    515   VASurface::ReleaseCB va_surface_release_cb =
    516       media::BindToLoop(message_loop_->message_loop_proxy(), base::Bind(
    517           &VaapiVideoDecodeAccelerator::RecycleVASurfaceID, weak_this_));
    518 
    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);
    524   }
    525 
    526   return true;
    527 }
    528 
    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_);
    533 
    534   if (state_ != kDecoding)
    535     return;
    536 
    537   // Main decode task.
    538   DVLOG(4) << "Decode task";
    539 
    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());
    544 
    545     VaapiH264Decoder::DecResult res;
    546     {
    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();
    554     }
    555 
    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;
    565 
    566       case VaapiH264Decoder::kRanOutOfStreamData:
    567         ReturnCurrInputBuffer_Locked();
    568         break;
    569 
    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;
    575 
    576         break;
    577 
    578       case VaapiH264Decoder::kDecodeError:
    579         RETURN_AND_NOTIFY_ON_FAILURE(false, "Error decoding stream",
    580                                      PLATFORM_FAILURE, );
    581         return;
    582     }
    583   }
    584 }
    585 
    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_);
    590 
    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;
    599 
    600   requested_num_pics_ = num_pics;
    601   requested_pic_size_ = size;
    602 
    603   TryFinishSurfaceSetChange();
    604 }
    605 
    606 void VaapiVideoDecodeAccelerator::TryFinishSurfaceSetChange() {
    607   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    608 
    609   if (!awaiting_va_surfaces_recycle_)
    610     return;
    611 
    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;
    624   }
    625 
    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();
    630 
    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);
    635   }
    636   tfp_pictures_.clear();
    637 
    638   // And ask for a new set as requested.
    639   DVLOG(1) << "Requesting " << requested_num_pics_ << " pictures of size: "
    640            << requested_pic_size_.ToString();
    641 
    642   message_loop_->PostTask(FROM_HERE, base::Bind(
    643       &Client::ProvidePictureBuffers, client_,
    644       requested_num_pics_, requested_pic_size_, GL_TEXTURE_2D));
    645 }
    646 
    647 void VaapiVideoDecodeAccelerator::Decode(
    648     const media::BitstreamBuffer& bitstream_buffer) {
    649   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    650 
    651   TRACE_EVENT1("Video Decoder", "VAVDA::Decode", "Buffer id",
    652                bitstream_buffer.id());
    653 
    654   // We got a new input buffer from the client, map it and queue for later use.
    655   MapAndQueueNewInputBuffer(bitstream_buffer);
    656 
    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;
    665 
    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;
    673 
    674     default:
    675       RETURN_AND_NOTIFY_ON_FAILURE(false,
    676           "Decode request from client in invalid state: " << state_,
    677           PLATFORM_FAILURE, );
    678       break;
    679   }
    680 }
    681 
    682 void VaapiVideoDecodeAccelerator::RecycleVASurfaceID(
    683     VASurfaceID va_surface_id) {
    684   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    685   base::AutoLock auto_lock(lock_);
    686 
    687   available_va_surfaces_.push_back(va_surface_id);
    688   surfaces_available_.Signal();
    689 }
    690 
    691 void VaapiVideoDecodeAccelerator::AssignPictureBuffers(
    692     const std::vector<media::PictureBuffer>& buffers) {
    693   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    694 
    695   base::AutoLock auto_lock(lock_);
    696   DCHECK(tfp_pictures_.empty());
    697 
    698   while (!output_buffers_.empty())
    699     output_buffers_.pop();
    700 
    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());
    706 
    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());
    714 
    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];
    719 
    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_));
    724 
    725     RETURN_AND_NOTIFY_ON_FAILURE(
    726         tfp_picture.get(), "Failed assigning picture buffer to a texture.",
    727         PLATFORM_FAILURE, );
    728 
    729     bool inserted = tfp_pictures_.insert(std::make_pair(
    730         buffers[i].id(), tfp_picture)).second;
    731     DCHECK(inserted);
    732 
    733     output_buffers_.push(buffers[i].id());
    734     available_va_surfaces_.push_back(va_surface_ids[i]);
    735     surfaces_available_.Signal();
    736   }
    737 
    738   state_ = kDecoding;
    739   decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
    740       &VaapiVideoDecodeAccelerator::DecodeTask, base::Unretained(this)));
    741 }
    742 
    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);
    747 
    748   --num_frames_at_client_;
    749   TRACE_COUNTER1("Video Decoder", "Textures at client", num_frames_at_client_);
    750 
    751   output_buffers_.push(picture_buffer_id);
    752   TryOutputSurface();
    753 }
    754 
    755 void VaapiVideoDecodeAccelerator::FlushTask() {
    756   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
    757   DVLOG(1) << "Flush task";
    758 
    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, );
    764 
    765   // Put the decoder in idle state, ready to resume.
    766   decoder_->Reset();
    767 
    768   message_loop_->PostTask(FROM_HERE, base::Bind(
    769       &VaapiVideoDecodeAccelerator::FinishFlush, weak_this_));
    770 }
    771 
    772 void VaapiVideoDecodeAccelerator::Flush() {
    773   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    774   DVLOG(1) << "Got flush request";
    775 
    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)));
    781 
    782   input_ready_.Signal();
    783   surfaces_available_.Signal();
    784 }
    785 
    786 void VaapiVideoDecodeAccelerator::FinishFlush() {
    787   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    788 
    789   finish_flush_pending_ = false;
    790 
    791   base::AutoLock auto_lock(lock_);
    792   if (state_ != kFlushing) {
    793     DCHECK_EQ(state_, kDestroying);
    794     return;  // We could've gotten destroyed already.
    795   }
    796 
    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;
    802   }
    803 
    804   state_ = kIdle;
    805 
    806   message_loop_->PostTask(FROM_HERE, base::Bind(
    807       &Client::NotifyFlushDone, client_));
    808 
    809   DVLOG(1) << "Flush finished";
    810 }
    811 
    812 void VaapiVideoDecodeAccelerator::ResetTask() {
    813   DCHECK_EQ(decoder_thread_.message_loop(), base::MessageLoop::current());
    814   DVLOG(1) << "ResetTask";
    815 
    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();
    820 
    821   base::AutoLock auto_lock(lock_);
    822 
    823   // Return current input buffer, if present.
    824   if (curr_input_buffer_.get())
    825     ReturnCurrInputBuffer_Locked();
    826 
    827   // And let client know that we are done with reset.
    828   message_loop_->PostTask(FROM_HERE, base::Bind(
    829       &VaapiVideoDecodeAccelerator::FinishReset, weak_this_));
    830 }
    831 
    832 void VaapiVideoDecodeAccelerator::Reset() {
    833   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    834   DVLOG(1) << "Got reset request";
    835 
    836   // This will make any new decode tasks exit early.
    837   base::AutoLock auto_lock(lock_);
    838   state_ = kResetting;
    839   finish_flush_pending_ = false;
    840 
    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();
    847   }
    848 
    849   decoder_thread_.message_loop()->PostTask(FROM_HERE, base::Bind(
    850       &VaapiVideoDecodeAccelerator::ResetTask, base::Unretained(this)));
    851 
    852   input_ready_.Signal();
    853   surfaces_available_.Signal();
    854 }
    855 
    856 void VaapiVideoDecodeAccelerator::FinishReset() {
    857   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    858   DVLOG(1) << "FinishReset";
    859   base::AutoLock auto_lock(lock_);
    860 
    861   if (state_ != kResetting) {
    862     DCHECK(state_ == kDestroying || state_ == kUninitialized) << state_;
    863     return;  // We could've gotten destroyed already.
    864   }
    865 
    866   // Drop pending outputs.
    867   while (!pending_output_cbs_.empty())
    868     pending_output_cbs_.pop();
    869 
    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;
    877   }
    878 
    879   num_stream_bufs_at_decoder_ = 0;
    880   state_ = kIdle;
    881 
    882   message_loop_->PostTask(FROM_HERE, base::Bind(
    883       &Client::NotifyResetDone, client_));
    884 
    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)));
    896   }
    897 
    898   DVLOG(1) << "Reset finished";
    899 }
    900 
    901 void VaapiVideoDecodeAccelerator::Cleanup() {
    902   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    903 
    904   if (state_ == kUninitialized || state_ == kDestroying)
    905     return;
    906 
    907   DVLOG(1) << "Destroying VAVDA";
    908   base::AutoLock auto_lock(lock_);
    909   state_ = kDestroying;
    910 
    911   client_ptr_factory_.InvalidateWeakPtrs();
    912 
    913   {
    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();
    923   }
    924 
    925   state_ = kUninitialized;
    926 }
    927 
    928 void VaapiVideoDecodeAccelerator::Destroy() {
    929   DCHECK_EQ(message_loop_, base::MessageLoop::current());
    930   Cleanup();
    931   delete this;
    932 }
    933 
    934 }  // namespace content
    935