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