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 "ppapi/proxy/video_decoder_resource.h" 6 7 #include "base/bind.h" 8 #include "gpu/command_buffer/client/gles2_cmd_helper.h" 9 #include "gpu/command_buffer/client/gles2_implementation.h" 10 #include "gpu/command_buffer/common/mailbox.h" 11 #include "ipc/ipc_message.h" 12 #include "ppapi/c/pp_errors.h" 13 #include "ppapi/c/ppb_opengles2.h" 14 #include "ppapi/proxy/plugin_dispatcher.h" 15 #include "ppapi/proxy/ppapi_messages.h" 16 #include "ppapi/proxy/ppb_graphics_3d_proxy.h" 17 #include "ppapi/proxy/serialized_handle.h" 18 #include "ppapi/proxy/video_decoder_constants.h" 19 #include "ppapi/shared_impl/ppapi_globals.h" 20 #include "ppapi/shared_impl/ppb_graphics_3d_shared.h" 21 #include "ppapi/shared_impl/proxy_lock.h" 22 #include "ppapi/shared_impl/resource_tracker.h" 23 #include "ppapi/thunk/enter.h" 24 25 using ppapi::thunk::EnterResourceNoLock; 26 using ppapi::thunk::PPB_Graphics3D_API; 27 using ppapi::thunk::PPB_VideoDecoder_API; 28 29 namespace ppapi { 30 namespace proxy { 31 32 VideoDecoderResource::ShmBuffer::ShmBuffer( 33 scoped_ptr<base::SharedMemory> shm_ptr, 34 uint32_t size, 35 uint32_t shm_id) 36 : shm(shm_ptr.Pass()), addr(NULL), shm_id(shm_id) { 37 if (shm->Map(size)) 38 addr = shm->memory(); 39 } 40 41 VideoDecoderResource::ShmBuffer::~ShmBuffer() { 42 } 43 44 VideoDecoderResource::Texture::Texture(uint32_t texture_target, 45 const PP_Size& size) 46 : texture_target(texture_target), size(size) { 47 } 48 49 VideoDecoderResource::Texture::~Texture() { 50 } 51 52 VideoDecoderResource::Picture::Picture(int32_t decode_id, uint32_t texture_id) 53 : decode_id(decode_id), texture_id(texture_id) { 54 } 55 56 VideoDecoderResource::Picture::~Picture() { 57 } 58 59 VideoDecoderResource::VideoDecoderResource(Connection connection, 60 PP_Instance instance) 61 : PluginResource(connection, instance), 62 num_decodes_(0), 63 get_picture_(NULL), 64 gles2_impl_(NULL), 65 initialized_(false), 66 testing_(false), 67 // Set |decoder_last_error_| to PP_OK after successful initialization. 68 // This makes error checking a little more concise, since we can check 69 // that the decoder has been initialized and hasn't returned an error by 70 // just testing |decoder_last_error_|. 71 decoder_last_error_(PP_ERROR_FAILED) { 72 // Clear the decode_ids_ array. 73 memset(decode_ids_, 0, arraysize(decode_ids_)); 74 SendCreate(RENDERER, PpapiHostMsg_VideoDecoder_Create()); 75 } 76 77 VideoDecoderResource::~VideoDecoderResource() { 78 // Destroy any textures which haven't been dismissed. 79 TextureMap::iterator it = textures_.begin(); 80 for (; it != textures_.end(); ++it) 81 DeleteGLTexture(it->first); 82 } 83 84 PPB_VideoDecoder_API* VideoDecoderResource::AsPPB_VideoDecoder_API() { 85 return this; 86 } 87 88 int32_t VideoDecoderResource::Initialize0_1( 89 PP_Resource graphics_context, 90 PP_VideoProfile profile, 91 PP_Bool allow_software_fallback, 92 scoped_refptr<TrackedCallback> callback) { 93 return Initialize(graphics_context, 94 profile, 95 allow_software_fallback 96 ? PP_HARDWAREACCELERATION_WITHFALLBACK 97 : PP_HARDWAREACCELERATION_ONLY, 98 callback); 99 } 100 101 int32_t VideoDecoderResource::Initialize( 102 PP_Resource graphics_context, 103 PP_VideoProfile profile, 104 PP_HardwareAcceleration acceleration, 105 scoped_refptr<TrackedCallback> callback) { 106 if (initialized_) 107 return PP_ERROR_FAILED; 108 if (profile < 0 || profile > PP_VIDEOPROFILE_MAX) 109 return PP_ERROR_BADARGUMENT; 110 if (initialize_callback_.get()) 111 return PP_ERROR_INPROGRESS; 112 if (!graphics_context) 113 return PP_ERROR_BADRESOURCE; 114 115 HostResource host_resource; 116 if (!testing_) { 117 // Create a new Graphics3D resource that can create texture resources to 118 // share with the plugin. We can't use the plugin's Graphics3D, since we 119 // create textures on a proxy thread, and would interfere with the plugin. 120 thunk::EnterResourceCreationNoLock enter_create(pp_instance()); 121 if (enter_create.failed()) 122 return PP_ERROR_FAILED; 123 int32_t attrib_list[] = {PP_GRAPHICS3DATTRIB_NONE}; 124 graphics3d_ = 125 ScopedPPResource(ScopedPPResource::PassRef(), 126 enter_create.functions()->CreateGraphics3D( 127 pp_instance(), graphics_context, attrib_list)); 128 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics(graphics3d_.get(), 129 false); 130 if (enter_graphics.failed()) 131 return PP_ERROR_BADRESOURCE; 132 133 PPB_Graphics3D_Shared* ppb_graphics3d_shared = 134 static_cast<PPB_Graphics3D_Shared*>(enter_graphics.object()); 135 gles2_impl_ = ppb_graphics3d_shared->gles2_impl(); 136 host_resource = ppb_graphics3d_shared->host_resource(); 137 } 138 139 initialize_callback_ = callback; 140 141 Call<PpapiPluginMsg_VideoDecoder_InitializeReply>( 142 RENDERER, 143 PpapiHostMsg_VideoDecoder_Initialize( 144 host_resource, profile, acceleration), 145 base::Bind(&VideoDecoderResource::OnPluginMsgInitializeComplete, this)); 146 147 return PP_OK_COMPLETIONPENDING; 148 } 149 150 int32_t VideoDecoderResource::Decode(uint32_t decode_id, 151 uint32_t size, 152 const void* buffer, 153 scoped_refptr<TrackedCallback> callback) { 154 if (decoder_last_error_) 155 return decoder_last_error_; 156 if (flush_callback_.get() || reset_callback_.get()) 157 return PP_ERROR_FAILED; 158 if (decode_callback_.get()) 159 return PP_ERROR_INPROGRESS; 160 if (size > kMaximumBitstreamBufferSize) 161 return PP_ERROR_NOMEMORY; 162 163 // If we allow the plugin to call Decode again, we must have somewhere to 164 // copy their buffer. 165 DCHECK(!available_shm_buffers_.empty() || 166 shm_buffers_.size() < kMaximumPendingDecodes); 167 168 // Count up, wrapping back to 0 before overflowing. 169 int32_t uid = ++num_decodes_; 170 if (uid == std::numeric_limits<int32_t>::max()) 171 num_decodes_ = 0; 172 173 // Save decode_id in a ring buffer. The ring buffer is sized to store 174 // decode_id for the maximum picture delay. 175 decode_ids_[uid % kMaximumPictureDelay] = decode_id; 176 177 if (available_shm_buffers_.empty() || 178 available_shm_buffers_.back()->shm->mapped_size() < size) { 179 uint32_t shm_id; 180 if (shm_buffers_.size() < kMaximumPendingDecodes) { 181 // Signal the host to create a new shm buffer by passing an index outside 182 // the legal range. 183 shm_id = static_cast<uint32_t>(shm_buffers_.size()); 184 } else { 185 // Signal the host to grow a buffer by passing a legal index. Choose the 186 // last available shm buffer for simplicity. 187 shm_id = available_shm_buffers_.back()->shm_id; 188 available_shm_buffers_.pop_back(); 189 } 190 191 // Synchronously get shared memory. Use GenericSyncCall so we can get the 192 // reply params, which contain the handle. 193 uint32_t shm_size = 0; 194 IPC::Message reply; 195 ResourceMessageReplyParams reply_params; 196 int32_t result = 197 GenericSyncCall(RENDERER, 198 PpapiHostMsg_VideoDecoder_GetShm(shm_id, size), 199 &reply, 200 &reply_params); 201 if (result != PP_OK) 202 return PP_ERROR_FAILED; 203 if (!UnpackMessage<PpapiPluginMsg_VideoDecoder_GetShmReply>(reply, 204 &shm_size)) 205 return PP_ERROR_FAILED; 206 base::SharedMemoryHandle shm_handle = base::SharedMemory::NULLHandle(); 207 if (!reply_params.TakeSharedMemoryHandleAtIndex(0, &shm_handle)) 208 return PP_ERROR_NOMEMORY; 209 scoped_ptr<base::SharedMemory> shm( 210 new base::SharedMemory(shm_handle, false /* read_only */)); 211 scoped_ptr<ShmBuffer> shm_buffer( 212 new ShmBuffer(shm.Pass(), shm_size, shm_id)); 213 if (!shm_buffer->addr) 214 return PP_ERROR_NOMEMORY; 215 216 available_shm_buffers_.push_back(shm_buffer.get()); 217 if (shm_buffers_.size() < kMaximumPendingDecodes) { 218 shm_buffers_.push_back(shm_buffer.release()); 219 } else { 220 // Delete manually since ScopedVector won't delete the existing element if 221 // we just assign it. 222 delete shm_buffers_[shm_id]; 223 shm_buffers_[shm_id] = shm_buffer.release(); 224 } 225 } 226 227 // At this point we should have shared memory to hold the plugin's buffer. 228 DCHECK(!available_shm_buffers_.empty() && 229 available_shm_buffers_.back()->shm->mapped_size() >= size); 230 231 ShmBuffer* shm_buffer = available_shm_buffers_.back(); 232 available_shm_buffers_.pop_back(); 233 memcpy(shm_buffer->addr, buffer, size); 234 235 Call<PpapiPluginMsg_VideoDecoder_DecodeReply>( 236 RENDERER, 237 PpapiHostMsg_VideoDecoder_Decode(shm_buffer->shm_id, size, uid), 238 base::Bind(&VideoDecoderResource::OnPluginMsgDecodeComplete, this)); 239 240 // If we have another free buffer, or we can still create new buffers, let 241 // the plugin call Decode again. 242 if (!available_shm_buffers_.empty() || 243 shm_buffers_.size() < kMaximumPendingDecodes) 244 return PP_OK; 245 246 // All buffers are busy and we can't create more. Delay completion until a 247 // buffer is available. 248 decode_callback_ = callback; 249 return PP_OK_COMPLETIONPENDING; 250 } 251 252 int32_t VideoDecoderResource::GetPicture( 253 PP_VideoPicture* picture, 254 scoped_refptr<TrackedCallback> callback) { 255 if (decoder_last_error_) 256 return decoder_last_error_; 257 if (reset_callback_.get()) 258 return PP_ERROR_FAILED; 259 if (get_picture_callback_.get()) 260 return PP_ERROR_INPROGRESS; 261 262 // If the next picture is ready, return it synchronously. 263 if (!received_pictures_.empty()) { 264 WriteNextPicture(picture); 265 return PP_OK; 266 } 267 268 get_picture_callback_ = callback; 269 get_picture_ = picture; 270 return PP_OK_COMPLETIONPENDING; 271 } 272 273 void VideoDecoderResource::RecyclePicture(const PP_VideoPicture* picture) { 274 if (decoder_last_error_) 275 return; 276 277 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture(picture->texture_id)); 278 } 279 280 int32_t VideoDecoderResource::Flush(scoped_refptr<TrackedCallback> callback) { 281 if (decoder_last_error_) 282 return decoder_last_error_; 283 if (reset_callback_.get()) 284 return PP_ERROR_FAILED; 285 if (flush_callback_.get()) 286 return PP_ERROR_INPROGRESS; 287 flush_callback_ = callback; 288 289 Call<PpapiPluginMsg_VideoDecoder_FlushReply>( 290 RENDERER, 291 PpapiHostMsg_VideoDecoder_Flush(), 292 base::Bind(&VideoDecoderResource::OnPluginMsgFlushComplete, this)); 293 294 return PP_OK_COMPLETIONPENDING; 295 } 296 297 int32_t VideoDecoderResource::Reset(scoped_refptr<TrackedCallback> callback) { 298 if (decoder_last_error_) 299 return decoder_last_error_; 300 if (flush_callback_.get()) 301 return PP_ERROR_FAILED; 302 if (reset_callback_.get()) 303 return PP_ERROR_INPROGRESS; 304 reset_callback_ = callback; 305 306 // Cause any pending Decode or GetPicture callbacks to abort after we return, 307 // to avoid reentering the plugin. 308 if (TrackedCallback::IsPending(decode_callback_)) 309 decode_callback_->PostAbort(); 310 decode_callback_ = NULL; 311 if (TrackedCallback::IsPending(get_picture_callback_)) 312 get_picture_callback_->PostAbort(); 313 get_picture_callback_ = NULL; 314 Call<PpapiPluginMsg_VideoDecoder_ResetReply>( 315 RENDERER, 316 PpapiHostMsg_VideoDecoder_Reset(), 317 base::Bind(&VideoDecoderResource::OnPluginMsgResetComplete, this)); 318 319 return PP_OK_COMPLETIONPENDING; 320 } 321 322 void VideoDecoderResource::OnReplyReceived( 323 const ResourceMessageReplyParams& params, 324 const IPC::Message& msg) { 325 PPAPI_BEGIN_MESSAGE_MAP(VideoDecoderResource, msg) 326 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 327 PpapiPluginMsg_VideoDecoder_RequestTextures, OnPluginMsgRequestTextures) 328 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 329 PpapiPluginMsg_VideoDecoder_PictureReady, OnPluginMsgPictureReady) 330 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 331 PpapiPluginMsg_VideoDecoder_DismissPicture, OnPluginMsgDismissPicture) 332 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL( 333 PpapiPluginMsg_VideoDecoder_NotifyError, OnPluginMsgNotifyError) 334 PPAPI_DISPATCH_PLUGIN_RESOURCE_CALL_UNHANDLED( 335 PluginResource::OnReplyReceived(params, msg)) 336 PPAPI_END_MESSAGE_MAP() 337 } 338 339 void VideoDecoderResource::SetForTest() { 340 testing_ = true; 341 } 342 343 void VideoDecoderResource::OnPluginMsgRequestTextures( 344 const ResourceMessageReplyParams& params, 345 uint32_t num_textures, 346 const PP_Size& size, 347 uint32_t texture_target, 348 const std::vector<gpu::Mailbox>& mailboxes) { 349 DCHECK(num_textures); 350 DCHECK(mailboxes.empty() || mailboxes.size() == num_textures); 351 std::vector<uint32_t> texture_ids(num_textures); 352 if (gles2_impl_) { 353 gles2_impl_->GenTextures(num_textures, &texture_ids.front()); 354 for (uint32_t i = 0; i < num_textures; ++i) { 355 gles2_impl_->ActiveTexture(GL_TEXTURE0); 356 gles2_impl_->BindTexture(texture_target, texture_ids[i]); 357 gles2_impl_->TexParameteri( 358 texture_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 359 gles2_impl_->TexParameteri( 360 texture_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 361 gles2_impl_->TexParameterf( 362 texture_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 363 gles2_impl_->TexParameterf( 364 texture_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 365 366 if (texture_target == GL_TEXTURE_2D) { 367 gles2_impl_->TexImage2D(texture_target, 368 0, 369 GL_RGBA, 370 size.width, 371 size.height, 372 0, 373 GL_RGBA, 374 GL_UNSIGNED_BYTE, 375 NULL); 376 } 377 if (!mailboxes.empty()) { 378 gles2_impl_->ProduceTextureCHROMIUM( 379 GL_TEXTURE_2D, reinterpret_cast<const GLbyte*>(mailboxes[i].name)); 380 } 381 382 textures_.insert( 383 std::make_pair(texture_ids[i], Texture(texture_target, size))); 384 } 385 gles2_impl_->Flush(); 386 } else { 387 DCHECK(testing_); 388 // Create some fake texture ids so we can test picture handling. 389 for (uint32_t i = 0; i < num_textures; ++i) { 390 texture_ids[i] = i + 1; 391 textures_.insert( 392 std::make_pair(texture_ids[i], Texture(texture_target, size))); 393 } 394 } 395 396 Post(RENDERER, PpapiHostMsg_VideoDecoder_AssignTextures(size, texture_ids)); 397 } 398 399 void VideoDecoderResource::OnPluginMsgPictureReady( 400 const ResourceMessageReplyParams& params, 401 int32_t decode_id, 402 uint32_t texture_id) { 403 received_pictures_.push(Picture(decode_id, texture_id)); 404 405 if (TrackedCallback::IsPending(get_picture_callback_)) { 406 // The plugin may call GetPicture in its callback. 407 scoped_refptr<TrackedCallback> callback; 408 callback.swap(get_picture_callback_); 409 PP_VideoPicture* picture = get_picture_; 410 get_picture_ = NULL; 411 WriteNextPicture(picture); 412 callback->Run(PP_OK); 413 } 414 } 415 416 void VideoDecoderResource::OnPluginMsgDismissPicture( 417 const ResourceMessageReplyParams& params, 418 uint32_t texture_id) { 419 DeleteGLTexture(texture_id); 420 textures_.erase(texture_id); 421 } 422 423 void VideoDecoderResource::OnPluginMsgNotifyError( 424 const ResourceMessageReplyParams& params, 425 int32_t error) { 426 decoder_last_error_ = error; 427 // Cause any pending callbacks to run immediately. Reentrancy isn't a problem, 428 // since the plugin wasn't calling us. 429 RunCallbackWithError(&initialize_callback_); 430 RunCallbackWithError(&decode_callback_); 431 RunCallbackWithError(&get_picture_callback_); 432 RunCallbackWithError(&flush_callback_); 433 RunCallbackWithError(&reset_callback_); 434 } 435 436 void VideoDecoderResource::OnPluginMsgInitializeComplete( 437 const ResourceMessageReplyParams& params) { 438 decoder_last_error_ = params.result(); 439 if (decoder_last_error_ == PP_OK) 440 initialized_ = true; 441 442 // Let the plugin call Initialize again from its callback in case of failure. 443 scoped_refptr<TrackedCallback> callback; 444 callback.swap(initialize_callback_); 445 callback->Run(decoder_last_error_); 446 } 447 448 void VideoDecoderResource::OnPluginMsgDecodeComplete( 449 const ResourceMessageReplyParams& params, 450 uint32_t shm_id) { 451 if (shm_id >= shm_buffers_.size()) { 452 NOTREACHED(); 453 return; 454 } 455 // Make the shm buffer available. 456 available_shm_buffers_.push_back(shm_buffers_[shm_id]); 457 // If the plugin is waiting, let it call Decode again. 458 if (decode_callback_.get()) { 459 scoped_refptr<TrackedCallback> callback; 460 callback.swap(decode_callback_); 461 callback->Run(PP_OK); 462 } 463 } 464 465 void VideoDecoderResource::OnPluginMsgFlushComplete( 466 const ResourceMessageReplyParams& params) { 467 // All shm buffers should have been made available by now. 468 DCHECK_EQ(shm_buffers_.size(), available_shm_buffers_.size()); 469 470 if (get_picture_callback_.get()) { 471 scoped_refptr<TrackedCallback> callback; 472 callback.swap(get_picture_callback_); 473 callback->Abort(); 474 } 475 476 scoped_refptr<TrackedCallback> callback; 477 callback.swap(flush_callback_); 478 callback->Run(params.result()); 479 } 480 481 void VideoDecoderResource::OnPluginMsgResetComplete( 482 const ResourceMessageReplyParams& params) { 483 // All shm buffers should have been made available by now. 484 DCHECK_EQ(shm_buffers_.size(), available_shm_buffers_.size()); 485 // Recycle any pictures which haven't been passed to the plugin. 486 while (!received_pictures_.empty()) { 487 Post(RENDERER, PpapiHostMsg_VideoDecoder_RecyclePicture( 488 received_pictures_.front().texture_id)); 489 received_pictures_.pop(); 490 } 491 492 scoped_refptr<TrackedCallback> callback; 493 callback.swap(reset_callback_); 494 callback->Run(params.result()); 495 } 496 497 void VideoDecoderResource::RunCallbackWithError( 498 scoped_refptr<TrackedCallback>* callback) { 499 if (TrackedCallback::IsPending(*callback)) { 500 scoped_refptr<TrackedCallback> temp; 501 callback->swap(temp); 502 temp->Run(decoder_last_error_); 503 } 504 } 505 506 void VideoDecoderResource::DeleteGLTexture(uint32_t id) { 507 if (gles2_impl_) { 508 gles2_impl_->DeleteTextures(1, &id); 509 gles2_impl_->Flush(); 510 } 511 } 512 513 void VideoDecoderResource::WriteNextPicture(PP_VideoPicture* pp_picture) { 514 DCHECK(!received_pictures_.empty()); 515 Picture& picture = received_pictures_.front(); 516 // Internally, we identify decodes by a unique id, which the host returns 517 // to us in the picture. Use this to get the plugin's decode_id. 518 pp_picture->decode_id = decode_ids_[picture.decode_id % kMaximumPictureDelay]; 519 pp_picture->texture_id = picture.texture_id; 520 TextureMap::iterator it = textures_.find(picture.texture_id); 521 if (it != textures_.end()) { 522 pp_picture->texture_target = it->second.texture_target; 523 pp_picture->texture_size = it->second.size; 524 } else { 525 NOTREACHED(); 526 } 527 received_pictures_.pop(); 528 } 529 530 } // namespace proxy 531 } // namespace ppapi 532