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/pepper_video_decoder_host.h" 6 7 #include "base/bind.h" 8 #include "base/memory/shared_memory.h" 9 #include "content/common/gpu/client/gpu_channel_host.h" 10 #include "content/public/renderer/render_thread.h" 11 #include "content/public/renderer/renderer_ppapi_host.h" 12 #include "content/renderer/pepper/ppb_graphics_3d_impl.h" 13 #include "content/renderer/pepper/video_decoder_shim.h" 14 #include "media/video/video_decode_accelerator.h" 15 #include "ppapi/c/pp_completion_callback.h" 16 #include "ppapi/c/pp_errors.h" 17 #include "ppapi/host/dispatch_host_message.h" 18 #include "ppapi/host/ppapi_host.h" 19 #include "ppapi/proxy/ppapi_messages.h" 20 #include "ppapi/proxy/video_decoder_constants.h" 21 #include "ppapi/thunk/enter.h" 22 #include "ppapi/thunk/ppb_graphics_3d_api.h" 23 24 using ppapi::proxy::SerializedHandle; 25 using ppapi::thunk::EnterResourceNoLock; 26 using ppapi::thunk::PPB_Graphics3D_API; 27 28 namespace content { 29 30 namespace { 31 32 media::VideoCodecProfile PepperToMediaVideoProfile(PP_VideoProfile profile) { 33 switch (profile) { 34 case PP_VIDEOPROFILE_H264BASELINE: 35 return media::H264PROFILE_BASELINE; 36 case PP_VIDEOPROFILE_H264MAIN: 37 return media::H264PROFILE_MAIN; 38 case PP_VIDEOPROFILE_H264EXTENDED: 39 return media::H264PROFILE_EXTENDED; 40 case PP_VIDEOPROFILE_H264HIGH: 41 return media::H264PROFILE_HIGH; 42 case PP_VIDEOPROFILE_H264HIGH10PROFILE: 43 return media::H264PROFILE_HIGH10PROFILE; 44 case PP_VIDEOPROFILE_H264HIGH422PROFILE: 45 return media::H264PROFILE_HIGH422PROFILE; 46 case PP_VIDEOPROFILE_H264HIGH444PREDICTIVEPROFILE: 47 return media::H264PROFILE_HIGH444PREDICTIVEPROFILE; 48 case PP_VIDEOPROFILE_H264SCALABLEBASELINE: 49 return media::H264PROFILE_SCALABLEBASELINE; 50 case PP_VIDEOPROFILE_H264SCALABLEHIGH: 51 return media::H264PROFILE_SCALABLEHIGH; 52 case PP_VIDEOPROFILE_H264STEREOHIGH: 53 return media::H264PROFILE_STEREOHIGH; 54 case PP_VIDEOPROFILE_H264MULTIVIEWHIGH: 55 return media::H264PROFILE_MULTIVIEWHIGH; 56 case PP_VIDEOPROFILE_VP8_ANY: 57 return media::VP8PROFILE_ANY; 58 case PP_VIDEOPROFILE_VP9_ANY: 59 return media::VP9PROFILE_ANY; 60 // No default case, to catch unhandled PP_VideoProfile values. 61 } 62 63 return media::VIDEO_CODEC_PROFILE_UNKNOWN; 64 } 65 66 } // namespace 67 68 PepperVideoDecoderHost::PendingDecode::PendingDecode( 69 uint32_t shm_id, 70 const ppapi::host::ReplyMessageContext& reply_context) 71 : shm_id(shm_id), reply_context(reply_context) { 72 } 73 74 PepperVideoDecoderHost::PendingDecode::~PendingDecode() { 75 } 76 77 PepperVideoDecoderHost::PepperVideoDecoderHost(RendererPpapiHost* host, 78 PP_Instance instance, 79 PP_Resource resource) 80 : ResourceHost(host->GetPpapiHost(), instance, resource), 81 renderer_ppapi_host_(host), 82 initialized_(false) { 83 } 84 85 PepperVideoDecoderHost::~PepperVideoDecoderHost() { 86 } 87 88 int32_t PepperVideoDecoderHost::OnResourceMessageReceived( 89 const IPC::Message& msg, 90 ppapi::host::HostMessageContext* context) { 91 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoDecoderHost, msg) 92 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Initialize, 93 OnHostMsgInitialize) 94 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_GetShm, 95 OnHostMsgGetShm) 96 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_Decode, 97 OnHostMsgDecode) 98 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_AssignTextures, 99 OnHostMsgAssignTextures) 100 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoDecoder_RecyclePicture, 101 OnHostMsgRecyclePicture) 102 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Flush, 103 OnHostMsgFlush) 104 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoDecoder_Reset, 105 OnHostMsgReset) 106 PPAPI_END_MESSAGE_MAP() 107 return PP_ERROR_FAILED; 108 } 109 110 int32_t PepperVideoDecoderHost::OnHostMsgInitialize( 111 ppapi::host::HostMessageContext* context, 112 const ppapi::HostResource& graphics_context, 113 PP_VideoProfile profile, 114 PP_HardwareAcceleration acceleration) { 115 if (initialized_) 116 return PP_ERROR_FAILED; 117 118 EnterResourceNoLock<PPB_Graphics3D_API> enter_graphics( 119 graphics_context.host_resource(), true); 120 if (enter_graphics.failed()) 121 return PP_ERROR_FAILED; 122 PPB_Graphics3D_Impl* graphics3d = 123 static_cast<PPB_Graphics3D_Impl*>(enter_graphics.object()); 124 125 int command_buffer_route_id = graphics3d->GetCommandBufferRouteId(); 126 if (!command_buffer_route_id) 127 return PP_ERROR_FAILED; 128 129 media::VideoCodecProfile media_profile = PepperToMediaVideoProfile(profile); 130 131 if (acceleration != PP_HARDWAREACCELERATION_NONE) { 132 // This is not synchronous, but subsequent IPC messages will be buffered, so 133 // it is okay to immediately send IPC messages through the returned channel. 134 GpuChannelHost* channel = graphics3d->channel(); 135 DCHECK(channel); 136 decoder_ = channel->CreateVideoDecoder(command_buffer_route_id); 137 if (decoder_ && decoder_->Initialize(media_profile, this)) { 138 initialized_ = true; 139 return PP_OK; 140 } 141 decoder_.reset(); 142 if (acceleration == PP_HARDWAREACCELERATION_ONLY) 143 return PP_ERROR_NOTSUPPORTED; 144 } 145 146 #if defined(OS_ANDROID) 147 return PP_ERROR_NOTSUPPORTED; 148 #else 149 decoder_.reset(new VideoDecoderShim(this)); 150 initialize_reply_context_ = context->MakeReplyMessageContext(); 151 decoder_->Initialize(media_profile, this); 152 153 return PP_OK_COMPLETIONPENDING; 154 #endif 155 } 156 157 int32_t PepperVideoDecoderHost::OnHostMsgGetShm( 158 ppapi::host::HostMessageContext* context, 159 uint32_t shm_id, 160 uint32_t shm_size) { 161 if (!initialized_) 162 return PP_ERROR_FAILED; 163 164 // Make the buffers larger since we hope to reuse them. 165 shm_size = std::max( 166 shm_size, 167 static_cast<uint32_t>(ppapi::proxy::kMinimumBitstreamBufferSize)); 168 if (shm_size > ppapi::proxy::kMaximumBitstreamBufferSize) 169 return PP_ERROR_FAILED; 170 171 if (shm_id >= ppapi::proxy::kMaximumPendingDecodes) 172 return PP_ERROR_FAILED; 173 // The shm_id must be inside or at the end of shm_buffers_. 174 if (shm_id > shm_buffers_.size()) 175 return PP_ERROR_FAILED; 176 // Reject an attempt to reallocate a busy shm buffer. 177 if (shm_id < shm_buffers_.size() && shm_buffer_busy_[shm_id]) 178 return PP_ERROR_FAILED; 179 180 content::RenderThread* render_thread = content::RenderThread::Get(); 181 scoped_ptr<base::SharedMemory> shm( 182 render_thread->HostAllocateSharedMemoryBuffer(shm_size).Pass()); 183 if (!shm || !shm->Map(shm_size)) 184 return PP_ERROR_FAILED; 185 186 base::SharedMemoryHandle shm_handle = shm->handle(); 187 if (shm_id == shm_buffers_.size()) { 188 shm_buffers_.push_back(shm.release()); 189 shm_buffer_busy_.push_back(false); 190 } else { 191 // Remove the old buffer. Delete manually since ScopedVector won't delete 192 // the existing element if we just assign over it. 193 delete shm_buffers_[shm_id]; 194 shm_buffers_[shm_id] = shm.release(); 195 } 196 197 #if defined(OS_WIN) 198 base::PlatformFile platform_file = shm_handle; 199 #elif defined(OS_POSIX) 200 base::PlatformFile platform_file = shm_handle.fd; 201 #else 202 #error Not implemented. 203 #endif 204 SerializedHandle handle( 205 renderer_ppapi_host_->ShareHandleWithRemote(platform_file, false), 206 shm_size); 207 ppapi::host::ReplyMessageContext reply_context = 208 context->MakeReplyMessageContext(); 209 reply_context.params.AppendHandle(handle); 210 host()->SendReply(reply_context, 211 PpapiPluginMsg_VideoDecoder_GetShmReply(shm_size)); 212 213 return PP_OK_COMPLETIONPENDING; 214 } 215 216 int32_t PepperVideoDecoderHost::OnHostMsgDecode( 217 ppapi::host::HostMessageContext* context, 218 uint32_t shm_id, 219 uint32_t size, 220 int32_t decode_id) { 221 if (!initialized_) 222 return PP_ERROR_FAILED; 223 DCHECK(decoder_); 224 // |shm_id| is just an index into shm_buffers_. Make sure it's in range. 225 if (static_cast<size_t>(shm_id) >= shm_buffers_.size()) 226 return PP_ERROR_FAILED; 227 // Reject an attempt to pass a busy buffer to the decoder again. 228 if (shm_buffer_busy_[shm_id]) 229 return PP_ERROR_FAILED; 230 // Reject non-unique decode_id values. 231 if (pending_decodes_.find(decode_id) != pending_decodes_.end()) 232 return PP_ERROR_FAILED; 233 234 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid()) 235 return PP_ERROR_FAILED; 236 237 pending_decodes_.insert(std::make_pair( 238 decode_id, PendingDecode(shm_id, context->MakeReplyMessageContext()))); 239 240 shm_buffer_busy_[shm_id] = true; 241 decoder_->Decode( 242 media::BitstreamBuffer(decode_id, shm_buffers_[shm_id]->handle(), size)); 243 244 return PP_OK_COMPLETIONPENDING; 245 } 246 247 int32_t PepperVideoDecoderHost::OnHostMsgAssignTextures( 248 ppapi::host::HostMessageContext* context, 249 const PP_Size& size, 250 const std::vector<uint32_t>& texture_ids) { 251 if (!initialized_) 252 return PP_ERROR_FAILED; 253 DCHECK(decoder_); 254 255 std::vector<media::PictureBuffer> picture_buffers; 256 for (uint32 i = 0; i < texture_ids.size(); i++) { 257 media::PictureBuffer buffer( 258 texture_ids[i], // Use the texture_id to identify the buffer. 259 gfx::Size(size.width, size.height), 260 texture_ids[i]); 261 picture_buffers.push_back(buffer); 262 } 263 decoder_->AssignPictureBuffers(picture_buffers); 264 return PP_OK; 265 } 266 267 int32_t PepperVideoDecoderHost::OnHostMsgRecyclePicture( 268 ppapi::host::HostMessageContext* context, 269 uint32_t texture_id) { 270 if (!initialized_) 271 return PP_ERROR_FAILED; 272 DCHECK(decoder_); 273 274 decoder_->ReusePictureBuffer(texture_id); 275 return PP_OK; 276 } 277 278 int32_t PepperVideoDecoderHost::OnHostMsgFlush( 279 ppapi::host::HostMessageContext* context) { 280 if (!initialized_) 281 return PP_ERROR_FAILED; 282 DCHECK(decoder_); 283 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid()) 284 return PP_ERROR_FAILED; 285 286 flush_reply_context_ = context->MakeReplyMessageContext(); 287 decoder_->Flush(); 288 289 return PP_OK_COMPLETIONPENDING; 290 } 291 292 int32_t PepperVideoDecoderHost::OnHostMsgReset( 293 ppapi::host::HostMessageContext* context) { 294 if (!initialized_) 295 return PP_ERROR_FAILED; 296 DCHECK(decoder_); 297 if (flush_reply_context_.is_valid() || reset_reply_context_.is_valid()) 298 return PP_ERROR_FAILED; 299 300 reset_reply_context_ = context->MakeReplyMessageContext(); 301 decoder_->Reset(); 302 303 return PP_OK_COMPLETIONPENDING; 304 } 305 306 void PepperVideoDecoderHost::ProvidePictureBuffers( 307 uint32 requested_num_of_buffers, 308 const gfx::Size& dimensions, 309 uint32 texture_target) { 310 RequestTextures(requested_num_of_buffers, 311 dimensions, 312 texture_target, 313 std::vector<gpu::Mailbox>()); 314 } 315 316 void PepperVideoDecoderHost::PictureReady(const media::Picture& picture) { 317 // So far picture.visible_rect is not used. If used, visible_rect should 318 // be validated since it comes from GPU process and may not be trustworthy. 319 host()->SendUnsolicitedReply( 320 pp_resource(), 321 PpapiPluginMsg_VideoDecoder_PictureReady(picture.bitstream_buffer_id(), 322 picture.picture_buffer_id())); 323 } 324 325 void PepperVideoDecoderHost::DismissPictureBuffer(int32 picture_buffer_id) { 326 host()->SendUnsolicitedReply( 327 pp_resource(), 328 PpapiPluginMsg_VideoDecoder_DismissPicture(picture_buffer_id)); 329 } 330 331 void PepperVideoDecoderHost::NotifyEndOfBitstreamBuffer( 332 int32 bitstream_buffer_id) { 333 PendingDecodeMap::iterator it = pending_decodes_.find(bitstream_buffer_id); 334 if (it == pending_decodes_.end()) { 335 NOTREACHED(); 336 return; 337 } 338 const PendingDecode& pending_decode = it->second; 339 host()->SendReply( 340 pending_decode.reply_context, 341 PpapiPluginMsg_VideoDecoder_DecodeReply(pending_decode.shm_id)); 342 shm_buffer_busy_[pending_decode.shm_id] = false; 343 pending_decodes_.erase(it); 344 } 345 346 void PepperVideoDecoderHost::NotifyFlushDone() { 347 DCHECK(pending_decodes_.empty()); 348 host()->SendReply(flush_reply_context_, 349 PpapiPluginMsg_VideoDecoder_FlushReply()); 350 flush_reply_context_ = ppapi::host::ReplyMessageContext(); 351 } 352 353 void PepperVideoDecoderHost::NotifyResetDone() { 354 DCHECK(pending_decodes_.empty()); 355 host()->SendReply(reset_reply_context_, 356 PpapiPluginMsg_VideoDecoder_ResetReply()); 357 reset_reply_context_ = ppapi::host::ReplyMessageContext(); 358 } 359 360 void PepperVideoDecoderHost::NotifyError( 361 media::VideoDecodeAccelerator::Error error) { 362 int32_t pp_error = PP_ERROR_FAILED; 363 switch (error) { 364 case media::VideoDecodeAccelerator::UNREADABLE_INPUT: 365 pp_error = PP_ERROR_MALFORMED_INPUT; 366 break; 367 case media::VideoDecodeAccelerator::ILLEGAL_STATE: 368 case media::VideoDecodeAccelerator::INVALID_ARGUMENT: 369 case media::VideoDecodeAccelerator::PLATFORM_FAILURE: 370 case media::VideoDecodeAccelerator::LARGEST_ERROR_ENUM: 371 pp_error = PP_ERROR_RESOURCE_FAILED; 372 break; 373 // No default case, to catch unhandled enum values. 374 } 375 host()->SendUnsolicitedReply( 376 pp_resource(), PpapiPluginMsg_VideoDecoder_NotifyError(pp_error)); 377 } 378 379 void PepperVideoDecoderHost::OnInitializeComplete(int32_t result) { 380 if (!initialized_) { 381 if (result == PP_OK) 382 initialized_ = true; 383 initialize_reply_context_.params.set_result(result); 384 host()->SendReply(initialize_reply_context_, 385 PpapiPluginMsg_VideoDecoder_InitializeReply()); 386 } 387 } 388 389 const uint8_t* PepperVideoDecoderHost::DecodeIdToAddress(uint32_t decode_id) { 390 PendingDecodeMap::const_iterator it = pending_decodes_.find(decode_id); 391 DCHECK(it != pending_decodes_.end()); 392 uint32_t shm_id = it->second.shm_id; 393 return static_cast<uint8_t*>(shm_buffers_[shm_id]->memory()); 394 } 395 396 void PepperVideoDecoderHost::RequestTextures( 397 uint32 requested_num_of_buffers, 398 const gfx::Size& dimensions, 399 uint32 texture_target, 400 const std::vector<gpu::Mailbox>& mailboxes) { 401 host()->SendUnsolicitedReply( 402 pp_resource(), 403 PpapiPluginMsg_VideoDecoder_RequestTextures( 404 requested_num_of_buffers, 405 PP_MakeSize(dimensions.width(), dimensions.height()), 406 texture_target, 407 mailboxes)); 408 } 409 410 } // namespace content 411