Home | History | Annotate | Download | only in filters
      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 "media/filters/gpu_video_decoder.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/callback_helpers.h"
     11 #include "base/cpu.h"
     12 #include "base/message_loop/message_loop.h"
     13 #include "base/stl_util.h"
     14 #include "base/task_runner_util.h"
     15 #include "media/base/bind_to_loop.h"
     16 #include "media/base/decoder_buffer.h"
     17 #include "media/base/pipeline.h"
     18 #include "media/base/pipeline_status.h"
     19 #include "media/base/video_decoder_config.h"
     20 #include "media/filters/gpu_video_decoder_factories.h"
     21 
     22 namespace media {
     23 
     24 // Maximum number of concurrent VDA::Decode() operations GVD will maintain.
     25 // Higher values allow better pipelining in the GPU, but also require more
     26 // resources.
     27 enum { kMaxInFlightDecodes = 4 };
     28 
     29 // Size of shared-memory segments we allocate.  Since we reuse them we let them
     30 // be on the beefy side.
     31 static const size_t kSharedMemorySegmentBytes = 100 << 10;
     32 
     33 GpuVideoDecoder::SHMBuffer::SHMBuffer(base::SharedMemory* m, size_t s)
     34     : shm(m), size(s) {
     35 }
     36 
     37 GpuVideoDecoder::SHMBuffer::~SHMBuffer() {}
     38 
     39 GpuVideoDecoder::BufferPair::BufferPair(
     40     SHMBuffer* s, const scoped_refptr<DecoderBuffer>& b)
     41     : shm_buffer(s), buffer(b) {
     42 }
     43 
     44 GpuVideoDecoder::BufferPair::~BufferPair() {}
     45 
     46 GpuVideoDecoder::BufferData::BufferData(
     47     int32 bbid, base::TimeDelta ts, const gfx::Rect& vr, const gfx::Size& ns)
     48     : bitstream_buffer_id(bbid), timestamp(ts), visible_rect(vr),
     49       natural_size(ns) {
     50 }
     51 
     52 GpuVideoDecoder::BufferData::~BufferData() {}
     53 
     54 GpuVideoDecoder::GpuVideoDecoder(
     55     const scoped_refptr<GpuVideoDecoderFactories>& factories)
     56     : needs_bitstream_conversion_(false),
     57       gvd_loop_proxy_(factories->GetMessageLoop()),
     58       weak_factory_(this),
     59       factories_(factories),
     60       state_(kNormal),
     61       decoder_texture_target_(0),
     62       next_picture_buffer_id_(0),
     63       next_bitstream_buffer_id_(0),
     64       available_pictures_(0) {
     65   DCHECK(factories_.get());
     66 }
     67 
     68 void GpuVideoDecoder::Reset(const base::Closure& closure)  {
     69   DVLOG(3) << "Reset()";
     70   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
     71 
     72   if (state_ == kDrainingDecoder && !factories_->IsAborted()) {
     73     gvd_loop_proxy_->PostTask(FROM_HERE, base::Bind(
     74         &GpuVideoDecoder::Reset, weak_this_, closure));
     75     // NOTE: if we're deferring Reset() until a Flush() completes, return
     76     // queued pictures to the VDA so they can be used to finish that Flush().
     77     if (pending_decode_cb_.is_null())
     78       ready_video_frames_.clear();
     79     return;
     80   }
     81 
     82   // Throw away any already-decoded, not-yet-delivered frames.
     83   ready_video_frames_.clear();
     84 
     85   if (!vda_) {
     86     gvd_loop_proxy_->PostTask(FROM_HERE, closure);
     87     return;
     88   }
     89 
     90   if (!pending_decode_cb_.is_null())
     91     EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame());
     92 
     93   DCHECK(pending_reset_cb_.is_null());
     94   pending_reset_cb_ = BindToCurrentLoop(closure);
     95 
     96   vda_->Reset();
     97 }
     98 
     99 void GpuVideoDecoder::Stop(const base::Closure& closure) {
    100   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    101   if (vda_)
    102     DestroyVDA();
    103   if (!pending_decode_cb_.is_null())
    104     EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame());
    105   if (!pending_reset_cb_.is_null())
    106     base::ResetAndReturn(&pending_reset_cb_).Run();
    107   BindToCurrentLoop(closure).Run();
    108 }
    109 
    110 static bool IsCodedSizeSupported(const gfx::Size& coded_size) {
    111   // Only non-Windows, Ivy Bridge+ platforms can support more than 1920x1080.
    112   // We test against 1088 to account for 16x16 macroblocks.
    113   if (coded_size.width() <= 1920 && coded_size.height() <= 1088)
    114     return true;
    115 
    116   base::CPU cpu;
    117   bool hw_large_video_support =
    118       (cpu.vendor_name() == "GenuineIntel") && cpu.model() >= 58;
    119   bool os_large_video_support = true;
    120 #if defined(OS_WIN)
    121   os_large_video_support = false;
    122 #endif
    123   return os_large_video_support && hw_large_video_support;
    124 }
    125 
    126 void GpuVideoDecoder::Initialize(const VideoDecoderConfig& config,
    127                                  const PipelineStatusCB& orig_status_cb) {
    128   DVLOG(3) << "Initialize()";
    129   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    130   DCHECK(config.IsValidConfig());
    131   DCHECK(!config.is_encrypted());
    132 
    133   weak_this_ = weak_factory_.GetWeakPtr();
    134 
    135   PipelineStatusCB status_cb = CreateUMAReportingPipelineCB(
    136       "Media.GpuVideoDecoderInitializeStatus",
    137       BindToCurrentLoop(orig_status_cb));
    138 
    139   bool previously_initialized = config_.IsValidConfig();
    140 #if !defined(OS_CHROMEOS)
    141   if (previously_initialized) {
    142     // TODO(xhwang): Make GpuVideoDecoder reinitializable.
    143     // See http://crbug.com/233608
    144     DVLOG(1) << "GpuVideoDecoder reinitialization not supported.";
    145     status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
    146     return;
    147   }
    148 #endif
    149   DVLOG(1) << "(Re)initializing GVD with config: "
    150            << config.AsHumanReadableString();
    151 
    152   // TODO(posciak): destroy and create a new VDA on codec/profile change
    153   // (http://crbug.com/260224).
    154   if (previously_initialized && (config_.profile() != config.profile())) {
    155     DVLOG(1) << "Codec or profile changed, cannot reinitialize.";
    156     status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
    157     return;
    158   }
    159 
    160   if (!IsCodedSizeSupported(config.coded_size())) {
    161     status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
    162     return;
    163   }
    164 
    165   config_ = config;
    166   needs_bitstream_conversion_ = (config.codec() == kCodecH264);
    167 
    168   if (previously_initialized) {
    169     // Reinitialization with a different config (but same codec and profile).
    170     // VDA should handle it by detecting this in-stream by itself,
    171     // no need to notify it.
    172     status_cb.Run(PIPELINE_OK);
    173     return;
    174   }
    175 
    176   vda_.reset(factories_->CreateVideoDecodeAccelerator(config.profile(), this));
    177   if (!vda_) {
    178     status_cb.Run(DECODER_ERROR_NOT_SUPPORTED);
    179     return;
    180   }
    181 
    182   DVLOG(3) << "GpuVideoDecoder::Initialize() succeeded.";
    183   status_cb.Run(PIPELINE_OK);
    184 }
    185 
    186 void GpuVideoDecoder::DestroyTextures() {
    187   std::map<int32, PictureBuffer>::iterator it;
    188 
    189   for (it = assigned_picture_buffers_.begin();
    190        it != assigned_picture_buffers_.end(); ++it) {
    191     factories_->DeleteTexture(it->second.texture_id());
    192   }
    193   assigned_picture_buffers_.clear();
    194 
    195   for (it = dismissed_picture_buffers_.begin();
    196        it != dismissed_picture_buffers_.end(); ++it) {
    197     factories_->DeleteTexture(it->second.texture_id());
    198   }
    199   dismissed_picture_buffers_.clear();
    200 }
    201 
    202 void GpuVideoDecoder::DestroyVDA() {
    203   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    204 
    205   if (vda_)
    206     vda_.release()->Destroy();
    207 
    208   DestroyTextures();
    209 }
    210 
    211 void GpuVideoDecoder::Decode(const scoped_refptr<DecoderBuffer>& buffer,
    212                              const DecodeCB& decode_cb) {
    213   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    214   DCHECK(pending_reset_cb_.is_null());
    215   DCHECK(pending_decode_cb_.is_null());
    216 
    217   pending_decode_cb_ = BindToCurrentLoop(decode_cb);
    218 
    219   if (state_ == kError || !vda_) {
    220     base::ResetAndReturn(&pending_decode_cb_).Run(kDecodeError, NULL);
    221     return;
    222   }
    223 
    224   switch (state_) {
    225     case kDecoderDrained:
    226       if (!ready_video_frames_.empty()) {
    227         EnqueueFrameAndTriggerFrameDelivery(NULL);
    228         return;
    229       }
    230       state_ = kNormal;
    231       // Fall-through.
    232     case kNormal:
    233       break;
    234     case kDrainingDecoder:
    235       DCHECK(buffer->end_of_stream());
    236       // Do nothing.  Will be satisfied either by a PictureReady or
    237       // NotifyFlushDone below.
    238       return;
    239     case kError:
    240       NOTREACHED();
    241       return;
    242   }
    243 
    244   if (buffer->end_of_stream()) {
    245     if (state_ == kNormal) {
    246       state_ = kDrainingDecoder;
    247       vda_->Flush();
    248     }
    249     return;
    250   }
    251 
    252   size_t size = buffer->data_size();
    253   SHMBuffer* shm_buffer = GetSHM(size);
    254   if (!shm_buffer) {
    255     base::ResetAndReturn(&pending_decode_cb_).Run(kDecodeError, NULL);
    256     return;
    257   }
    258 
    259   memcpy(shm_buffer->shm->memory(), buffer->data(), size);
    260   BitstreamBuffer bitstream_buffer(
    261       next_bitstream_buffer_id_, shm_buffer->shm->handle(), size);
    262   // Mask against 30 bits, to avoid (undefined) wraparound on signed integer.
    263   next_bitstream_buffer_id_ = (next_bitstream_buffer_id_ + 1) & 0x3FFFFFFF;
    264   bool inserted = bitstream_buffers_in_decoder_.insert(std::make_pair(
    265       bitstream_buffer.id(), BufferPair(shm_buffer, buffer))).second;
    266   DCHECK(inserted);
    267   RecordBufferData(bitstream_buffer, *buffer.get());
    268 
    269   vda_->Decode(bitstream_buffer);
    270 
    271   if (!ready_video_frames_.empty()) {
    272     EnqueueFrameAndTriggerFrameDelivery(NULL);
    273     return;
    274   }
    275 
    276   if (CanMoreDecodeWorkBeDone())
    277     base::ResetAndReturn(&pending_decode_cb_).Run(kNotEnoughData, NULL);
    278 }
    279 
    280 bool GpuVideoDecoder::CanMoreDecodeWorkBeDone() {
    281   return bitstream_buffers_in_decoder_.size() < kMaxInFlightDecodes;
    282 }
    283 
    284 void GpuVideoDecoder::RecordBufferData(const BitstreamBuffer& bitstream_buffer,
    285                                        const DecoderBuffer& buffer) {
    286   input_buffer_data_.push_front(BufferData(bitstream_buffer.id(),
    287                                            buffer.timestamp(),
    288                                            config_.visible_rect(),
    289                                            config_.natural_size()));
    290   // Why this value?  Because why not.  avformat.h:MAX_REORDER_DELAY is 16, but
    291   // that's too small for some pathological B-frame test videos.  The cost of
    292   // using too-high a value is low (192 bits per extra slot).
    293   static const size_t kMaxInputBufferDataSize = 128;
    294   // Pop from the back of the list, because that's the oldest and least likely
    295   // to be useful in the future data.
    296   if (input_buffer_data_.size() > kMaxInputBufferDataSize)
    297     input_buffer_data_.pop_back();
    298 }
    299 
    300 void GpuVideoDecoder::GetBufferData(int32 id, base::TimeDelta* timestamp,
    301                                     gfx::Rect* visible_rect,
    302                                     gfx::Size* natural_size) {
    303   for (std::list<BufferData>::const_iterator it =
    304            input_buffer_data_.begin(); it != input_buffer_data_.end();
    305        ++it) {
    306     if (it->bitstream_buffer_id != id)
    307       continue;
    308     *timestamp = it->timestamp;
    309     *visible_rect = it->visible_rect;
    310     *natural_size = it->natural_size;
    311     return;
    312   }
    313   NOTREACHED() << "Missing bitstreambuffer id: " << id;
    314 }
    315 
    316 bool GpuVideoDecoder::HasAlpha() const {
    317   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    318   return true;
    319 }
    320 
    321 bool GpuVideoDecoder::NeedsBitstreamConversion() const {
    322   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    323   return needs_bitstream_conversion_;
    324 }
    325 
    326 bool GpuVideoDecoder::CanReadWithoutStalling() const {
    327   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    328   return available_pictures_ > 0 || !ready_video_frames_.empty();
    329 }
    330 
    331 void GpuVideoDecoder::NotifyInitializeDone() {
    332   NOTREACHED() << "GpuVideoDecodeAcceleratorHost::Initialize is synchronous!";
    333 }
    334 
    335 void GpuVideoDecoder::ProvidePictureBuffers(uint32 count,
    336                                             const gfx::Size& size,
    337                                             uint32 texture_target) {
    338   DVLOG(3) << "ProvidePictureBuffers(" << count << ", "
    339            << size.width() << "x" << size.height() << ")";
    340   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    341 
    342   std::vector<uint32> texture_ids;
    343   std::vector<gpu::Mailbox> texture_mailboxes;
    344   decoder_texture_target_ = texture_target;
    345   // Discards the sync point returned here since PictureReady will imply that
    346   // the produce has already happened, and the texture is ready for use.
    347   if (!factories_->CreateTextures(count,
    348                                   size,
    349                                   &texture_ids,
    350                                   &texture_mailboxes,
    351                                   decoder_texture_target_)) {
    352     NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
    353     return;
    354   }
    355   DCHECK_EQ(count, texture_ids.size());
    356   DCHECK_EQ(count, texture_mailboxes.size());
    357 
    358   if (!vda_)
    359     return;
    360 
    361   std::vector<PictureBuffer> picture_buffers;
    362   for (size_t i = 0; i < texture_ids.size(); ++i) {
    363     picture_buffers.push_back(PictureBuffer(
    364         next_picture_buffer_id_++, size, texture_ids[i], texture_mailboxes[i]));
    365     bool inserted = assigned_picture_buffers_.insert(std::make_pair(
    366         picture_buffers.back().id(), picture_buffers.back())).second;
    367     DCHECK(inserted);
    368   }
    369 
    370   available_pictures_ += count;
    371 
    372   vda_->AssignPictureBuffers(picture_buffers);
    373 }
    374 
    375 void GpuVideoDecoder::DismissPictureBuffer(int32 id) {
    376   DVLOG(3) << "DismissPictureBuffer(" << id << ")";
    377   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    378 
    379   std::map<int32, PictureBuffer>::iterator it =
    380       assigned_picture_buffers_.find(id);
    381   if (it == assigned_picture_buffers_.end()) {
    382     NOTREACHED() << "Missing picture buffer: " << id;
    383     return;
    384   }
    385 
    386   PictureBuffer buffer_to_dismiss = it->second;
    387   assigned_picture_buffers_.erase(it);
    388 
    389   std::set<int32>::iterator at_display_it =
    390       picture_buffers_at_display_.find(id);
    391 
    392   if (at_display_it == picture_buffers_at_display_.end()) {
    393     // We can delete the texture immediately as it's not being displayed.
    394     factories_->DeleteTexture(buffer_to_dismiss.texture_id());
    395     CHECK_GT(available_pictures_, 0);
    396     --available_pictures_;
    397   } else {
    398     // Texture in display. Postpone deletion until after it's returned to us.
    399     bool inserted = dismissed_picture_buffers_.insert(std::make_pair(
    400         id, buffer_to_dismiss)).second;
    401     DCHECK(inserted);
    402   }
    403 }
    404 
    405 void GpuVideoDecoder::PictureReady(const media::Picture& picture) {
    406   DVLOG(3) << "PictureReady()";
    407   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    408 
    409   std::map<int32, PictureBuffer>::iterator it =
    410       assigned_picture_buffers_.find(picture.picture_buffer_id());
    411   if (it == assigned_picture_buffers_.end()) {
    412     NOTREACHED() << "Missing picture buffer: " << picture.picture_buffer_id();
    413     NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
    414     return;
    415   }
    416   const PictureBuffer& pb = it->second;
    417 
    418   // Update frame's timestamp.
    419   base::TimeDelta timestamp;
    420   gfx::Rect visible_rect;
    421   gfx::Size natural_size;
    422   GetBufferData(picture.bitstream_buffer_id(), &timestamp, &visible_rect,
    423                 &natural_size);
    424   DCHECK(decoder_texture_target_);
    425 
    426   scoped_refptr<VideoFrame> frame(VideoFrame::WrapNativeTexture(
    427       new VideoFrame::MailboxHolder(
    428           pb.texture_mailbox(),
    429           0,  // sync_point
    430           BindToCurrentLoop(base::Bind(&GpuVideoDecoder::ReusePictureBuffer,
    431                                        weak_this_,
    432                                        picture.picture_buffer_id()))),
    433       decoder_texture_target_,
    434       pb.size(),
    435       visible_rect,
    436       natural_size,
    437       timestamp,
    438       base::Bind(&GpuVideoDecoderFactories::ReadPixels,
    439                  factories_,
    440                  pb.texture_id(),
    441                  decoder_texture_target_,
    442                  gfx::Size(visible_rect.width(), visible_rect.height())),
    443       base::Closure()));
    444   CHECK_GT(available_pictures_, 0);
    445   --available_pictures_;
    446   bool inserted =
    447       picture_buffers_at_display_.insert(picture.picture_buffer_id()).second;
    448   DCHECK(inserted);
    449 
    450   EnqueueFrameAndTriggerFrameDelivery(frame);
    451 }
    452 
    453 void GpuVideoDecoder::EnqueueFrameAndTriggerFrameDelivery(
    454     const scoped_refptr<VideoFrame>& frame) {
    455   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    456 
    457   // During a pending vda->Reset(), we don't accumulate frames.  Drop it on the
    458   // floor and return.
    459   if (!pending_reset_cb_.is_null())
    460     return;
    461 
    462   if (frame.get())
    463     ready_video_frames_.push_back(frame);
    464   else
    465     DCHECK(!ready_video_frames_.empty());
    466 
    467   if (pending_decode_cb_.is_null())
    468     return;
    469 
    470   base::ResetAndReturn(&pending_decode_cb_)
    471       .Run(kOk, ready_video_frames_.front());
    472   ready_video_frames_.pop_front();
    473 }
    474 
    475 void GpuVideoDecoder::ReusePictureBuffer(int64 picture_buffer_id,
    476                                          uint32 sync_point) {
    477   DVLOG(3) << "ReusePictureBuffer(" << picture_buffer_id << ")";
    478   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    479 
    480   if (!vda_)
    481     return;
    482 
    483   CHECK(!picture_buffers_at_display_.empty());
    484 
    485   size_t num_erased = picture_buffers_at_display_.erase(picture_buffer_id);
    486   DCHECK(num_erased);
    487 
    488   std::map<int32, PictureBuffer>::iterator it =
    489       assigned_picture_buffers_.find(picture_buffer_id);
    490 
    491   if (it == assigned_picture_buffers_.end()) {
    492     // This picture was dismissed while in display, so we postponed deletion.
    493     it = dismissed_picture_buffers_.find(picture_buffer_id);
    494     DCHECK(it != dismissed_picture_buffers_.end());
    495     factories_->DeleteTexture(it->second.texture_id());
    496     dismissed_picture_buffers_.erase(it);
    497     return;
    498   }
    499 
    500   factories_->WaitSyncPoint(sync_point);
    501   ++available_pictures_;
    502 
    503   vda_->ReusePictureBuffer(picture_buffer_id);
    504 }
    505 
    506 GpuVideoDecoder::SHMBuffer* GpuVideoDecoder::GetSHM(size_t min_size) {
    507   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    508   if (available_shm_segments_.empty() ||
    509       available_shm_segments_.back()->size < min_size) {
    510     size_t size_to_allocate = std::max(min_size, kSharedMemorySegmentBytes);
    511     base::SharedMemory* shm = factories_->CreateSharedMemory(size_to_allocate);
    512     // CreateSharedMemory() can return NULL during Shutdown.
    513     if (!shm)
    514       return NULL;
    515     return new SHMBuffer(shm, size_to_allocate);
    516   }
    517   SHMBuffer* ret = available_shm_segments_.back();
    518   available_shm_segments_.pop_back();
    519   return ret;
    520 }
    521 
    522 void GpuVideoDecoder::PutSHM(SHMBuffer* shm_buffer) {
    523   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    524   available_shm_segments_.push_back(shm_buffer);
    525 }
    526 
    527 void GpuVideoDecoder::NotifyEndOfBitstreamBuffer(int32 id) {
    528   DVLOG(3) << "NotifyEndOfBitstreamBuffer(" << id << ")";
    529   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    530 
    531   std::map<int32, BufferPair>::iterator it =
    532       bitstream_buffers_in_decoder_.find(id);
    533   if (it == bitstream_buffers_in_decoder_.end()) {
    534     NotifyError(VideoDecodeAccelerator::PLATFORM_FAILURE);
    535     NOTREACHED() << "Missing bitstream buffer: " << id;
    536     return;
    537   }
    538 
    539   PutSHM(it->second.shm_buffer);
    540   bitstream_buffers_in_decoder_.erase(it);
    541 
    542   if (pending_reset_cb_.is_null() && state_ != kDrainingDecoder &&
    543       CanMoreDecodeWorkBeDone() && !pending_decode_cb_.is_null()) {
    544     base::ResetAndReturn(&pending_decode_cb_).Run(kNotEnoughData, NULL);
    545   }
    546 }
    547 
    548 GpuVideoDecoder::~GpuVideoDecoder() {
    549   DCHECK(!vda_.get());  // Stop should have been already called.
    550   DCHECK(pending_decode_cb_.is_null());
    551   for (size_t i = 0; i < available_shm_segments_.size(); ++i) {
    552     available_shm_segments_[i]->shm->Close();
    553     delete available_shm_segments_[i];
    554   }
    555   available_shm_segments_.clear();
    556   for (std::map<int32, BufferPair>::iterator it =
    557            bitstream_buffers_in_decoder_.begin();
    558        it != bitstream_buffers_in_decoder_.end(); ++it) {
    559     it->second.shm_buffer->shm->Close();
    560   }
    561   bitstream_buffers_in_decoder_.clear();
    562 
    563   DestroyTextures();
    564 }
    565 
    566 void GpuVideoDecoder::NotifyFlushDone() {
    567   DVLOG(3) << "NotifyFlushDone()";
    568   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    569   DCHECK_EQ(state_, kDrainingDecoder);
    570   state_ = kDecoderDrained;
    571   EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame());
    572 }
    573 
    574 void GpuVideoDecoder::NotifyResetDone() {
    575   DVLOG(3) << "NotifyResetDone()";
    576   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    577   DCHECK(ready_video_frames_.empty());
    578 
    579   // This needs to happen after the Reset() on vda_ is done to ensure pictures
    580   // delivered during the reset can find their time data.
    581   input_buffer_data_.clear();
    582 
    583   if (!pending_reset_cb_.is_null())
    584     base::ResetAndReturn(&pending_reset_cb_).Run();
    585 
    586   if (!pending_decode_cb_.is_null())
    587     EnqueueFrameAndTriggerFrameDelivery(VideoFrame::CreateEmptyFrame());
    588 }
    589 
    590 void GpuVideoDecoder::NotifyError(media::VideoDecodeAccelerator::Error error) {
    591   DCHECK(gvd_loop_proxy_->BelongsToCurrentThread());
    592   if (!vda_)
    593     return;
    594 
    595   DLOG(ERROR) << "VDA Error: " << error;
    596   DestroyVDA();
    597 
    598   state_ = kError;
    599 
    600   if (!pending_decode_cb_.is_null()) {
    601     base::ResetAndReturn(&pending_decode_cb_).Run(kDecodeError, NULL);
    602     return;
    603   }
    604 }
    605 
    606 }  // namespace media
    607