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 |