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