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