• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "content/renderer/pepper/video_decoder_shim.h"
6 
7 #include <GLES2/gl2.h>
8 #include <GLES2/gl2ext.h>
9 #include <GLES2/gl2extchromium.h>
10 
11 #include "base/bind.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/single_thread_task_runner.h"
14 #include "content/public/renderer/render_thread.h"
15 #include "content/renderer/pepper/pepper_video_decoder_host.h"
16 #include "content/renderer/render_thread_impl.h"
17 #include "gpu/command_buffer/client/gles2_implementation.h"
18 #include "media/base/decoder_buffer.h"
19 #include "media/base/limits.h"
20 #include "media/base/video_decoder.h"
21 #include "media/filters/ffmpeg_video_decoder.h"
22 #include "media/filters/vpx_video_decoder.h"
23 #include "media/video/picture.h"
24 #include "media/video/video_decode_accelerator.h"
25 #include "ppapi/c/pp_errors.h"
26 #include "third_party/libyuv/include/libyuv.h"
27 #include "webkit/common/gpu/context_provider_web_context.h"
28 
29 namespace content {
30 
31 struct VideoDecoderShim::PendingDecode {
32   PendingDecode(uint32_t decode_id,
33                 const scoped_refptr<media::DecoderBuffer>& buffer);
34   ~PendingDecode();
35 
36   const uint32_t decode_id;
37   const scoped_refptr<media::DecoderBuffer> buffer;
38 };
39 
PendingDecode(uint32_t decode_id,const scoped_refptr<media::DecoderBuffer> & buffer)40 VideoDecoderShim::PendingDecode::PendingDecode(
41     uint32_t decode_id,
42     const scoped_refptr<media::DecoderBuffer>& buffer)
43     : decode_id(decode_id), buffer(buffer) {
44 }
45 
~PendingDecode()46 VideoDecoderShim::PendingDecode::~PendingDecode() {
47 }
48 
49 struct VideoDecoderShim::PendingFrame {
50   explicit PendingFrame(uint32_t decode_id);
51   PendingFrame(uint32_t decode_id,
52                const gfx::Size& coded_size,
53                const gfx::Rect& visible_rect);
54   ~PendingFrame();
55 
56   const uint32_t decode_id;
57   const gfx::Size coded_size;
58   const gfx::Rect visible_rect;
59   std::vector<uint8_t> argb_pixels;
60 
61  private:
62   // This could be expensive to copy, so guard against that.
63   DISALLOW_COPY_AND_ASSIGN(PendingFrame);
64 };
65 
PendingFrame(uint32_t decode_id)66 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id)
67     : decode_id(decode_id) {
68 }
69 
PendingFrame(uint32_t decode_id,const gfx::Size & coded_size,const gfx::Rect & visible_rect)70 VideoDecoderShim::PendingFrame::PendingFrame(uint32_t decode_id,
71                                              const gfx::Size& coded_size,
72                                              const gfx::Rect& visible_rect)
73     : decode_id(decode_id),
74       coded_size(coded_size),
75       visible_rect(visible_rect),
76       argb_pixels(coded_size.width() * coded_size.height() * 4) {
77 }
78 
~PendingFrame()79 VideoDecoderShim::PendingFrame::~PendingFrame() {
80 }
81 
82 // DecoderImpl runs the underlying VideoDecoder on the media thread, receiving
83 // calls from the VideoDecodeShim on the main thread and sending results back.
84 // This class is constructed on the main thread, but used and destructed on the
85 // media thread.
86 class VideoDecoderShim::DecoderImpl {
87  public:
88   explicit DecoderImpl(const base::WeakPtr<VideoDecoderShim>& proxy);
89   ~DecoderImpl();
90 
91   void Initialize(media::VideoDecoderConfig config);
92   void Decode(uint32_t decode_id, scoped_refptr<media::DecoderBuffer> buffer);
93   void Reset();
94   void Stop();
95 
96  private:
97   void OnPipelineStatus(media::PipelineStatus status);
98   void DoDecode();
99   void OnDecodeComplete(uint32_t decode_id, media::VideoDecoder::Status status);
100   void OnOutputComplete(const scoped_refptr<media::VideoFrame>& frame);
101   void OnResetComplete();
102 
103   // WeakPtr is bound to main_message_loop_. Use only in shim callbacks.
104   base::WeakPtr<VideoDecoderShim> shim_;
105   scoped_ptr<media::VideoDecoder> decoder_;
106   scoped_refptr<base::MessageLoopProxy> main_message_loop_;
107   // Queue of decodes waiting for the decoder.
108   typedef std::queue<PendingDecode> PendingDecodeQueue;
109   PendingDecodeQueue pending_decodes_;
110   int max_decodes_at_decoder_;
111   int num_decodes_at_decoder_;
112   // VideoDecoder returns pictures without information about the decode buffer
113   // that generated it. Save the decode_id from the last decode that completed,
114   // which is close for most decoders, which only decode one buffer at a time.
115   uint32_t decode_id_;
116 };
117 
DecoderImpl(const base::WeakPtr<VideoDecoderShim> & proxy)118 VideoDecoderShim::DecoderImpl::DecoderImpl(
119     const base::WeakPtr<VideoDecoderShim>& proxy)
120     : shim_(proxy),
121       main_message_loop_(base::MessageLoopProxy::current()),
122       max_decodes_at_decoder_(0),
123       num_decodes_at_decoder_(0),
124       decode_id_(0) {
125 }
126 
~DecoderImpl()127 VideoDecoderShim::DecoderImpl::~DecoderImpl() {
128   DCHECK(pending_decodes_.empty());
129 }
130 
Initialize(media::VideoDecoderConfig config)131 void VideoDecoderShim::DecoderImpl::Initialize(
132     media::VideoDecoderConfig config) {
133   DCHECK(!decoder_);
134 #if !defined(MEDIA_DISABLE_LIBVPX)
135   if (config.codec() == media::kCodecVP9) {
136     decoder_.reset(
137         new media::VpxVideoDecoder(base::MessageLoopProxy::current()));
138   } else
139 #endif
140   {
141     scoped_ptr<media::FFmpegVideoDecoder> ffmpeg_video_decoder(
142         new media::FFmpegVideoDecoder(base::MessageLoopProxy::current()));
143     ffmpeg_video_decoder->set_decode_nalus(true);
144     decoder_ = ffmpeg_video_decoder.Pass();
145   }
146   max_decodes_at_decoder_ = decoder_->GetMaxDecodeRequests();
147   // We can use base::Unretained() safely in decoder callbacks because
148   // |decoder_| is owned by DecoderImpl. During Stop(), the |decoder_| will be
149   // destroyed and all outstanding callbacks will be fired.
150   decoder_->Initialize(
151       config,
152       true /* low_delay */,
153       base::Bind(&VideoDecoderShim::DecoderImpl::OnPipelineStatus,
154                  base::Unretained(this)),
155       base::Bind(&VideoDecoderShim::DecoderImpl::OnOutputComplete,
156                  base::Unretained(this)));
157 }
158 
Decode(uint32_t decode_id,scoped_refptr<media::DecoderBuffer> buffer)159 void VideoDecoderShim::DecoderImpl::Decode(
160     uint32_t decode_id,
161     scoped_refptr<media::DecoderBuffer> buffer) {
162   DCHECK(decoder_);
163   pending_decodes_.push(PendingDecode(decode_id, buffer));
164   DoDecode();
165 }
166 
Reset()167 void VideoDecoderShim::DecoderImpl::Reset() {
168   DCHECK(decoder_);
169   // Abort all pending decodes.
170   while (!pending_decodes_.empty()) {
171     const PendingDecode& decode = pending_decodes_.front();
172     scoped_ptr<PendingFrame> pending_frame(new PendingFrame(decode.decode_id));
173     main_message_loop_->PostTask(FROM_HERE,
174                                  base::Bind(&VideoDecoderShim::OnDecodeComplete,
175                                             shim_,
176                                             media::VideoDecoder::kAborted,
177                                             decode.decode_id));
178     pending_decodes_.pop();
179   }
180   decoder_->Reset(base::Bind(&VideoDecoderShim::DecoderImpl::OnResetComplete,
181                              base::Unretained(this)));
182 }
183 
Stop()184 void VideoDecoderShim::DecoderImpl::Stop() {
185   DCHECK(decoder_);
186   // Clear pending decodes now. We don't want OnDecodeComplete to call DoDecode
187   // again.
188   while (!pending_decodes_.empty())
189     pending_decodes_.pop();
190   decoder_.reset();
191   // This instance is deleted once we exit this scope.
192 }
193 
OnPipelineStatus(media::PipelineStatus status)194 void VideoDecoderShim::DecoderImpl::OnPipelineStatus(
195     media::PipelineStatus status) {
196   int32_t result;
197   switch (status) {
198     case media::PIPELINE_OK:
199       result = PP_OK;
200       break;
201     case media::DECODER_ERROR_NOT_SUPPORTED:
202       result = PP_ERROR_NOTSUPPORTED;
203       break;
204     default:
205       result = PP_ERROR_FAILED;
206       break;
207   }
208 
209   // Calculate how many textures the shim should create.
210   uint32_t shim_texture_pool_size =
211       max_decodes_at_decoder_ + media::limits::kMaxVideoFrames;
212   main_message_loop_->PostTask(
213       FROM_HERE,
214       base::Bind(&VideoDecoderShim::OnInitializeComplete,
215                  shim_,
216                  result,
217                  shim_texture_pool_size));
218 }
219 
DoDecode()220 void VideoDecoderShim::DecoderImpl::DoDecode() {
221   while (!pending_decodes_.empty() &&
222          num_decodes_at_decoder_ < max_decodes_at_decoder_) {
223     num_decodes_at_decoder_++;
224     const PendingDecode& decode = pending_decodes_.front();
225     decoder_->Decode(
226         decode.buffer,
227         base::Bind(&VideoDecoderShim::DecoderImpl::OnDecodeComplete,
228                    base::Unretained(this),
229                    decode.decode_id));
230     pending_decodes_.pop();
231   }
232 }
233 
OnDecodeComplete(uint32_t decode_id,media::VideoDecoder::Status status)234 void VideoDecoderShim::DecoderImpl::OnDecodeComplete(
235     uint32_t decode_id,
236     media::VideoDecoder::Status status) {
237   num_decodes_at_decoder_--;
238   decode_id_ = decode_id;
239 
240   int32_t result;
241   switch (status) {
242     case media::VideoDecoder::kOk:
243     case media::VideoDecoder::kAborted:
244       result = PP_OK;
245       break;
246     case media::VideoDecoder::kDecodeError:
247       result = PP_ERROR_RESOURCE_FAILED;
248       break;
249     default:
250       NOTREACHED();
251       result = PP_ERROR_FAILED;
252       break;
253   }
254 
255   main_message_loop_->PostTask(
256       FROM_HERE,
257       base::Bind(
258           &VideoDecoderShim::OnDecodeComplete, shim_, result, decode_id));
259 
260   DoDecode();
261 }
262 
OnOutputComplete(const scoped_refptr<media::VideoFrame> & frame)263 void VideoDecoderShim::DecoderImpl::OnOutputComplete(
264     const scoped_refptr<media::VideoFrame>& frame) {
265   scoped_ptr<PendingFrame> pending_frame;
266   if (!frame->end_of_stream()) {
267     pending_frame.reset(new PendingFrame(
268         decode_id_, frame->coded_size(), frame->visible_rect()));
269     // Convert the VideoFrame pixels to ABGR to match VideoDecodeAccelerator.
270     libyuv::I420ToABGR(frame->data(media::VideoFrame::kYPlane),
271                        frame->stride(media::VideoFrame::kYPlane),
272                        frame->data(media::VideoFrame::kUPlane),
273                        frame->stride(media::VideoFrame::kUPlane),
274                        frame->data(media::VideoFrame::kVPlane),
275                        frame->stride(media::VideoFrame::kVPlane),
276                        &pending_frame->argb_pixels.front(),
277                        frame->coded_size().width() * 4,
278                        frame->coded_size().width(),
279                        frame->coded_size().height());
280   } else {
281     pending_frame.reset(new PendingFrame(decode_id_));
282   }
283 
284   main_message_loop_->PostTask(FROM_HERE,
285                                base::Bind(&VideoDecoderShim::OnOutputComplete,
286                                           shim_,
287                                           base::Passed(&pending_frame)));
288 }
289 
OnResetComplete()290 void VideoDecoderShim::DecoderImpl::OnResetComplete() {
291   main_message_loop_->PostTask(
292       FROM_HERE, base::Bind(&VideoDecoderShim::OnResetComplete, shim_));
293 }
294 
VideoDecoderShim(PepperVideoDecoderHost * host)295 VideoDecoderShim::VideoDecoderShim(PepperVideoDecoderHost* host)
296     : state_(UNINITIALIZED),
297       host_(host),
298       media_task_runner_(
299           RenderThreadImpl::current()->GetMediaThreadTaskRunner()),
300       context_provider_(
301           RenderThreadImpl::current()->SharedMainThreadContextProvider()),
302       texture_pool_size_(0),
303       num_pending_decodes_(0),
304       weak_ptr_factory_(this) {
305   DCHECK(host_);
306   DCHECK(media_task_runner_.get());
307   DCHECK(context_provider_.get());
308   decoder_impl_.reset(new DecoderImpl(weak_ptr_factory_.GetWeakPtr()));
309 }
310 
~VideoDecoderShim()311 VideoDecoderShim::~VideoDecoderShim() {
312   DCHECK(RenderThreadImpl::current());
313   // Delete any remaining textures.
314   TextureIdMap::iterator it = texture_id_map_.begin();
315   for (; it != texture_id_map_.end(); ++it)
316     DeleteTexture(it->second);
317   texture_id_map_.clear();
318 
319   FlushCommandBuffer();
320 
321   weak_ptr_factory_.InvalidateWeakPtrs();
322   // No more callbacks from the delegate will be received now.
323 
324   // The callback now holds the only reference to the DecoderImpl, which will be
325   // deleted when Stop completes.
326   media_task_runner_->PostTask(
327       FROM_HERE,
328       base::Bind(&VideoDecoderShim::DecoderImpl::Stop,
329                  base::Owned(decoder_impl_.release())));
330 }
331 
Initialize(media::VideoCodecProfile profile,media::VideoDecodeAccelerator::Client * client)332 bool VideoDecoderShim::Initialize(
333     media::VideoCodecProfile profile,
334     media::VideoDecodeAccelerator::Client* client) {
335   DCHECK_EQ(client, host_);
336   DCHECK(RenderThreadImpl::current());
337   DCHECK_EQ(state_, UNINITIALIZED);
338   media::VideoCodec codec = media::kUnknownVideoCodec;
339   if (profile <= media::H264PROFILE_MAX)
340     codec = media::kCodecH264;
341   else if (profile <= media::VP8PROFILE_MAX)
342     codec = media::kCodecVP8;
343   else if (profile <= media::VP9PROFILE_MAX)
344     codec = media::kCodecVP9;
345   DCHECK_NE(codec, media::kUnknownVideoCodec);
346 
347   media::VideoDecoderConfig config(
348       codec,
349       profile,
350       media::VideoFrame::YV12,
351       gfx::Size(32, 24),  // Small sizes that won't fail.
352       gfx::Rect(32, 24),
353       gfx::Size(32, 24),
354       NULL /* extra_data */,  // TODO(bbudge) Verify this isn't needed.
355       0 /* extra_data_size */,
356       false /* decryption */);
357 
358   media_task_runner_->PostTask(
359       FROM_HERE,
360       base::Bind(&VideoDecoderShim::DecoderImpl::Initialize,
361                  base::Unretained(decoder_impl_.get()),
362                  config));
363   // Return success, even though we are asynchronous, to mimic
364   // media::VideoDecodeAccelerator.
365   return true;
366 }
367 
Decode(const media::BitstreamBuffer & bitstream_buffer)368 void VideoDecoderShim::Decode(const media::BitstreamBuffer& bitstream_buffer) {
369   DCHECK(RenderThreadImpl::current());
370   DCHECK_EQ(state_, DECODING);
371 
372   // We need the address of the shared memory, so we can copy the buffer.
373   const uint8_t* buffer = host_->DecodeIdToAddress(bitstream_buffer.id());
374   DCHECK(buffer);
375 
376   media_task_runner_->PostTask(
377       FROM_HERE,
378       base::Bind(
379           &VideoDecoderShim::DecoderImpl::Decode,
380           base::Unretained(decoder_impl_.get()),
381           bitstream_buffer.id(),
382           media::DecoderBuffer::CopyFrom(buffer, bitstream_buffer.size())));
383   num_pending_decodes_++;
384 }
385 
AssignPictureBuffers(const std::vector<media::PictureBuffer> & buffers)386 void VideoDecoderShim::AssignPictureBuffers(
387     const std::vector<media::PictureBuffer>& buffers) {
388   DCHECK(RenderThreadImpl::current());
389   DCHECK_EQ(state_, DECODING);
390   if (buffers.empty()) {
391     NOTREACHED();
392     return;
393   }
394   DCHECK_EQ(buffers.size(), pending_texture_mailboxes_.size());
395   GLuint num_textures = base::checked_cast<GLuint>(buffers.size());
396   std::vector<uint32_t> local_texture_ids(num_textures);
397   gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
398   for (uint32_t i = 0; i < num_textures; i++) {
399     local_texture_ids[i] = gles2->CreateAndConsumeTextureCHROMIUM(
400         GL_TEXTURE_2D, pending_texture_mailboxes_[i].name);
401     // Map the plugin texture id to the local texture id.
402     uint32_t plugin_texture_id = buffers[i].texture_id();
403     texture_id_map_[plugin_texture_id] = local_texture_ids[i];
404     available_textures_.insert(plugin_texture_id);
405   }
406   pending_texture_mailboxes_.clear();
407   SendPictures();
408 }
409 
ReusePictureBuffer(int32 picture_buffer_id)410 void VideoDecoderShim::ReusePictureBuffer(int32 picture_buffer_id) {
411   DCHECK(RenderThreadImpl::current());
412   uint32_t texture_id = static_cast<uint32_t>(picture_buffer_id);
413   if (textures_to_dismiss_.find(texture_id) != textures_to_dismiss_.end()) {
414     DismissTexture(texture_id);
415   } else if (texture_id_map_.find(texture_id) != texture_id_map_.end()) {
416     available_textures_.insert(texture_id);
417     SendPictures();
418   } else {
419     NOTREACHED();
420   }
421 }
422 
Flush()423 void VideoDecoderShim::Flush() {
424   DCHECK(RenderThreadImpl::current());
425   DCHECK_EQ(state_, DECODING);
426   state_ = FLUSHING;
427 }
428 
Reset()429 void VideoDecoderShim::Reset() {
430   DCHECK(RenderThreadImpl::current());
431   DCHECK_EQ(state_, DECODING);
432   state_ = RESETTING;
433   media_task_runner_->PostTask(
434       FROM_HERE,
435       base::Bind(&VideoDecoderShim::DecoderImpl::Reset,
436                  base::Unretained(decoder_impl_.get())));
437 }
438 
Destroy()439 void VideoDecoderShim::Destroy() {
440   // This will be called, but our destructor does the actual work.
441 }
442 
OnInitializeComplete(int32_t result,uint32_t texture_pool_size)443 void VideoDecoderShim::OnInitializeComplete(int32_t result,
444                                             uint32_t texture_pool_size) {
445   DCHECK(RenderThreadImpl::current());
446   DCHECK(host_);
447 
448   if (result == PP_OK) {
449     state_ = DECODING;
450     texture_pool_size_ = texture_pool_size;
451   }
452 
453   host_->OnInitializeComplete(result);
454 }
455 
OnDecodeComplete(int32_t result,uint32_t decode_id)456 void VideoDecoderShim::OnDecodeComplete(int32_t result, uint32_t decode_id) {
457   DCHECK(RenderThreadImpl::current());
458   DCHECK(host_);
459 
460   if (result == PP_ERROR_RESOURCE_FAILED) {
461     host_->NotifyError(media::VideoDecodeAccelerator::PLATFORM_FAILURE);
462     return;
463   }
464 
465   num_pending_decodes_--;
466   completed_decodes_.push(decode_id);
467 
468   // If frames are being queued because we're out of textures, don't notify
469   // the host that decode has completed. This exerts "back pressure" to keep
470   // the host from sending buffers that will cause pending_frames_ to grow.
471   if (pending_frames_.empty())
472     NotifyCompletedDecodes();
473 }
474 
OnOutputComplete(scoped_ptr<PendingFrame> frame)475 void VideoDecoderShim::OnOutputComplete(scoped_ptr<PendingFrame> frame) {
476   DCHECK(RenderThreadImpl::current());
477   DCHECK(host_);
478 
479   if (!frame->argb_pixels.empty()) {
480     if (texture_size_ != frame->coded_size) {
481       // If the size has changed, all current textures must be dismissed. Add
482       // all textures to |textures_to_dismiss_| and dismiss any that aren't in
483       // use by the plugin. We will dismiss the rest as they are recycled.
484       for (TextureIdMap::const_iterator it = texture_id_map_.begin();
485            it != texture_id_map_.end();
486            ++it) {
487         textures_to_dismiss_.insert(it->second);
488       }
489       for (TextureIdSet::const_iterator it = available_textures_.begin();
490            it != available_textures_.end();
491            ++it) {
492         DismissTexture(*it);
493       }
494       available_textures_.clear();
495       FlushCommandBuffer();
496 
497       DCHECK(pending_texture_mailboxes_.empty());
498       for (uint32_t i = 0; i < texture_pool_size_; i++)
499         pending_texture_mailboxes_.push_back(gpu::Mailbox::Generate());
500 
501       host_->RequestTextures(texture_pool_size_,
502                              frame->coded_size,
503                              GL_TEXTURE_2D,
504                              pending_texture_mailboxes_);
505       texture_size_ = frame->coded_size;
506     }
507 
508     pending_frames_.push(linked_ptr<PendingFrame>(frame.release()));
509     SendPictures();
510   }
511 }
512 
SendPictures()513 void VideoDecoderShim::SendPictures() {
514   DCHECK(RenderThreadImpl::current());
515   DCHECK(host_);
516   while (!pending_frames_.empty() && !available_textures_.empty()) {
517     const linked_ptr<PendingFrame>& frame = pending_frames_.front();
518 
519     TextureIdSet::iterator it = available_textures_.begin();
520     uint32_t texture_id = *it;
521     available_textures_.erase(it);
522 
523     uint32_t local_texture_id = texture_id_map_[texture_id];
524     gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
525     gles2->ActiveTexture(GL_TEXTURE0);
526     gles2->BindTexture(GL_TEXTURE_2D, local_texture_id);
527     gles2->TexImage2D(GL_TEXTURE_2D,
528                       0,
529                       GL_RGBA,
530                       texture_size_.width(),
531                       texture_size_.height(),
532                       0,
533                       GL_RGBA,
534                       GL_UNSIGNED_BYTE,
535                       &frame->argb_pixels.front());
536 
537     host_->PictureReady(
538         media::Picture(texture_id, frame->decode_id, frame->visible_rect));
539     pending_frames_.pop();
540   }
541 
542   FlushCommandBuffer();
543 
544   if (pending_frames_.empty()) {
545     // If frames aren't backing up, notify the host of any completed decodes so
546     // it can send more buffers.
547     NotifyCompletedDecodes();
548 
549     if (state_ == FLUSHING && !num_pending_decodes_) {
550       state_ = DECODING;
551       host_->NotifyFlushDone();
552     }
553   }
554 }
555 
OnResetComplete()556 void VideoDecoderShim::OnResetComplete() {
557   DCHECK(RenderThreadImpl::current());
558   DCHECK(host_);
559 
560   while (!pending_frames_.empty())
561     pending_frames_.pop();
562   NotifyCompletedDecodes();
563 
564   // Dismiss any old textures now.
565   while (!textures_to_dismiss_.empty())
566     DismissTexture(*textures_to_dismiss_.begin());
567 
568   state_ = DECODING;
569   host_->NotifyResetDone();
570 }
571 
NotifyCompletedDecodes()572 void VideoDecoderShim::NotifyCompletedDecodes() {
573   while (!completed_decodes_.empty()) {
574     host_->NotifyEndOfBitstreamBuffer(completed_decodes_.front());
575     completed_decodes_.pop();
576   }
577 }
578 
DismissTexture(uint32_t texture_id)579 void VideoDecoderShim::DismissTexture(uint32_t texture_id) {
580   DCHECK(host_);
581   textures_to_dismiss_.erase(texture_id);
582   DCHECK(texture_id_map_.find(texture_id) != texture_id_map_.end());
583   DeleteTexture(texture_id_map_[texture_id]);
584   texture_id_map_.erase(texture_id);
585   host_->DismissPictureBuffer(texture_id);
586 }
587 
DeleteTexture(uint32_t texture_id)588 void VideoDecoderShim::DeleteTexture(uint32_t texture_id) {
589   gpu::gles2::GLES2Interface* gles2 = context_provider_->ContextGL();
590   gles2->DeleteTextures(1, &texture_id);
591 }
592 
FlushCommandBuffer()593 void VideoDecoderShim::FlushCommandBuffer() {
594   context_provider_->ContextGL()->Flush();
595 }
596 
597 }  // namespace content
598