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 "content/renderer/pepper/pepper_video_capture_host.h" 6 7 #include "content/renderer/pepper/host_globals.h" 8 #include "content/renderer/pepper/pepper_media_device_manager.h" 9 #include "content/renderer/pepper/pepper_platform_video_capture.h" 10 #include "content/renderer/pepper/pepper_plugin_instance_impl.h" 11 #include "content/renderer/pepper/renderer_ppapi_host_impl.h" 12 #include "content/renderer/render_frame_impl.h" 13 #include "media/base/limits.h" 14 #include "media/base/video_frame.h" 15 #include "ppapi/host/dispatch_host_message.h" 16 #include "ppapi/host/ppapi_host.h" 17 #include "ppapi/proxy/host_dispatcher.h" 18 #include "ppapi/proxy/ppapi_messages.h" 19 #include "ppapi/shared_impl/host_resource.h" 20 #include "ppapi/thunk/enter.h" 21 #include "ppapi/thunk/ppb_buffer_api.h" 22 23 using ppapi::HostResource; 24 using ppapi::TrackedCallback; 25 using ppapi::thunk::EnterResourceNoLock; 26 using ppapi::thunk::PPB_Buffer_API; 27 28 namespace { 29 30 // Maximum number of buffers to actually allocate. 31 const uint32_t kMaxBuffers = 20; 32 33 } // namespace 34 35 namespace content { 36 37 PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl* host, 38 PP_Instance instance, 39 PP_Resource resource) 40 : ResourceHost(host->GetPpapiHost(), instance, resource), 41 renderer_ppapi_host_(host), 42 buffer_count_hint_(0), 43 status_(PP_VIDEO_CAPTURE_STATUS_STOPPED), 44 enumeration_helper_(this, 45 PepperMediaDeviceManager::GetForRenderFrame( 46 host->GetRenderFrameForInstance(pp_instance())), 47 PP_DEVICETYPE_DEV_VIDEOCAPTURE, 48 host->GetDocumentURL(instance)) { 49 } 50 51 PepperVideoCaptureHost::~PepperVideoCaptureHost() { 52 Close(); 53 } 54 55 bool PepperVideoCaptureHost::Init() { 56 return !!renderer_ppapi_host_->GetPluginInstance(pp_instance()); 57 } 58 59 int32_t PepperVideoCaptureHost::OnResourceMessageReceived( 60 const IPC::Message& msg, 61 ppapi::host::HostMessageContext* context) { 62 int32_t result = PP_ERROR_FAILED; 63 if (enumeration_helper_.HandleResourceMessage(msg, context, &result)) 64 return result; 65 66 PPAPI_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg) 67 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_Open, OnOpen) 68 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StartCapture, 69 OnStartCapture) 70 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_VideoCapture_ReuseBuffer, 71 OnReuseBuffer) 72 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_StopCapture, 73 OnStopCapture) 74 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0(PpapiHostMsg_VideoCapture_Close, 75 OnClose) 76 PPAPI_END_MESSAGE_MAP() 77 return PP_ERROR_FAILED; 78 } 79 80 void PepperVideoCaptureHost::OnInitialized(bool succeeded) { 81 if (succeeded) { 82 open_reply_context_.params.set_result(PP_OK); 83 } else { 84 DetachPlatformVideoCapture(); 85 open_reply_context_.params.set_result(PP_ERROR_FAILED); 86 } 87 88 host()->SendReply(open_reply_context_, 89 PpapiPluginMsg_VideoCapture_OpenReply()); 90 } 91 92 void PepperVideoCaptureHost::OnStarted() { 93 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false)) 94 SendStatus(); 95 } 96 97 void PepperVideoCaptureHost::OnStopped() { 98 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false)) 99 SendStatus(); 100 } 101 102 void PepperVideoCaptureHost::OnPaused() { 103 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false)) 104 SendStatus(); 105 } 106 107 void PepperVideoCaptureHost::OnError() { 108 PostErrorReply(); 109 } 110 111 void PepperVideoCaptureHost::PostErrorReply() { 112 // It either comes because some error was detected while starting (e.g. 2 113 // conflicting "master" resolution), or because the browser failed to start 114 // the capture. 115 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); 116 host()->SendUnsolicitedReply( 117 pp_resource(), 118 PpapiPluginMsg_VideoCapture_OnError( 119 static_cast<uint32_t>(PP_ERROR_FAILED))); 120 } 121 122 void PepperVideoCaptureHost::OnFrameReady( 123 const scoped_refptr<media::VideoFrame>& frame, 124 media::VideoCaptureFormat format) { 125 DCHECK(frame.get()); 126 127 if (alloc_size_ != frame->coded_size() || buffers_.empty()) { 128 AllocBuffers(frame->coded_size(), format.frame_rate); 129 alloc_size_ = frame->coded_size(); 130 } 131 132 for (uint32_t i = 0; i < buffers_.size(); ++i) { 133 if (!buffers_[i].in_use) { 134 DCHECK_EQ(frame->format(), media::VideoFrame::I420); 135 if (buffers_[i].buffer->size() < 136 media::VideoFrame::AllocationSize(frame->format(), 137 frame->coded_size())) { 138 // TODO(ihf): handle size mismatches gracefully here. 139 return; 140 } 141 uint8* dst = reinterpret_cast<uint8*>(buffers_[i].data); 142 COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, y_plane_should_be_0); 143 COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, u_plane_should_be_1); 144 COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, v_plane_should_be_2); 145 for (size_t j = 0; j < media::VideoFrame::NumPlanes(frame->format()); 146 ++j) { 147 const uint8* src = frame->data(j); 148 const size_t row_bytes = frame->row_bytes(j); 149 const size_t src_stride = frame->stride(j); 150 for (int k = 0; k < frame->rows(j); ++k) { 151 memcpy(dst, src, row_bytes); 152 dst += row_bytes; 153 src += src_stride; 154 } 155 } 156 buffers_[i].in_use = true; 157 host()->SendUnsolicitedReply( 158 pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i)); 159 return; 160 } 161 } 162 } 163 164 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size& resolution, 165 int frame_rate) { 166 PP_VideoCaptureDeviceInfo_Dev info = { 167 static_cast<uint32_t>(resolution.width()), 168 static_cast<uint32_t>(resolution.height()), 169 static_cast<uint32_t>(frame_rate)}; 170 ReleaseBuffers(); 171 172 const size_t size = media::VideoFrame::AllocationSize( 173 media::VideoFrame::I420, gfx::Size(info.width, info.height)); 174 175 ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0); 176 177 // Allocate buffers. We keep a reference to them, that is released in 178 // ReleaseBuffers. In the mean time, we prepare the resource and handle here 179 // for sending below. 180 std::vector<HostResource> buffer_host_resources; 181 buffers_.reserve(buffer_count_hint_); 182 ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker(); 183 ppapi::proxy::HostDispatcher* dispatcher = 184 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance()); 185 for (size_t i = 0; i < buffer_count_hint_; ++i) { 186 PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size); 187 if (!res) 188 break; 189 190 EnterResourceNoLock<PPB_Buffer_API> enter(res, true); 191 DCHECK(enter.succeeded()); 192 193 BufferInfo buf; 194 buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); 195 buf.data = buf.buffer->Map(); 196 if (!buf.data) { 197 tracker->ReleaseResource(res); 198 break; 199 } 200 buffers_.push_back(buf); 201 202 // Add to HostResource array to be sent. 203 { 204 HostResource host_resource; 205 host_resource.SetHostResource(pp_instance(), res); 206 buffer_host_resources.push_back(host_resource); 207 208 // Add a reference for the plugin, which is resposible for releasing it. 209 tracker->AddRefResource(res); 210 } 211 212 // Add the serialized shared memory handle to params. FileDescriptor is 213 // treated in special case. 214 { 215 EnterResourceNoLock<PPB_Buffer_API> enter(res, true); 216 DCHECK(enter.succeeded()); 217 int handle; 218 int32_t result = enter.object()->GetSharedMemory(&handle); 219 DCHECK(result == PP_OK); 220 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle, 221 // those casts are ugly. 222 base::PlatformFile platform_file = 223 #if defined(OS_WIN) 224 reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle)); 225 #elif defined(OS_POSIX) 226 handle; 227 #else 228 #error Not implemented. 229 #endif 230 params.AppendHandle(ppapi::proxy::SerializedHandle( 231 dispatcher->ShareHandleWithRemote(platform_file, false), size)); 232 } 233 } 234 235 if (buffers_.empty()) { 236 // We couldn't allocate/map buffers at all. Send an error and stop the 237 // capture. 238 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true); 239 platform_video_capture_->StopCapture(); 240 PostErrorReply(); 241 return; 242 } 243 244 host()->Send( 245 new PpapiPluginMsg_ResourceReply(params, 246 PpapiPluginMsg_VideoCapture_OnDeviceInfo( 247 info, buffer_host_resources, size))); 248 } 249 250 int32_t PepperVideoCaptureHost::OnOpen( 251 ppapi::host::HostMessageContext* context, 252 const std::string& device_id, 253 const PP_VideoCaptureDeviceInfo_Dev& requested_info, 254 uint32_t buffer_count) { 255 if (platform_video_capture_.get()) 256 return PP_ERROR_FAILED; 257 258 SetRequestedInfo(requested_info, buffer_count); 259 260 GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance()); 261 if (!document_url.is_valid()) 262 return PP_ERROR_FAILED; 263 264 platform_video_capture_.reset(new PepperPlatformVideoCapture( 265 renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance())-> 266 GetRoutingID(), 267 device_id, 268 document_url, 269 this)); 270 271 open_reply_context_ = context->MakeReplyMessageContext(); 272 273 return PP_OK_COMPLETIONPENDING; 274 } 275 276 int32_t PepperVideoCaptureHost::OnStartCapture( 277 ppapi::host::HostMessageContext* context) { 278 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) || 279 !platform_video_capture_.get()) 280 return PP_ERROR_FAILED; 281 282 DCHECK(buffers_.empty()); 283 284 // It's safe to call this regardless it's capturing or not, because 285 // PepperPlatformVideoCapture maintains the state. 286 platform_video_capture_->StartCapture(video_capture_params_); 287 return PP_OK; 288 } 289 290 int32_t PepperVideoCaptureHost::OnReuseBuffer( 291 ppapi::host::HostMessageContext* context, 292 uint32_t buffer) { 293 if (buffer >= buffers_.size() || !buffers_[buffer].in_use) 294 return PP_ERROR_BADARGUMENT; 295 buffers_[buffer].in_use = false; 296 return PP_OK; 297 } 298 299 int32_t PepperVideoCaptureHost::OnStopCapture( 300 ppapi::host::HostMessageContext* context) { 301 return StopCapture(); 302 } 303 304 int32_t PepperVideoCaptureHost::OnClose( 305 ppapi::host::HostMessageContext* context) { 306 return Close(); 307 } 308 309 int32_t PepperVideoCaptureHost::StopCapture() { 310 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false)) 311 return PP_ERROR_FAILED; 312 313 DCHECK(platform_video_capture_.get()); 314 315 ReleaseBuffers(); 316 // It's safe to call this regardless it's capturing or not, because 317 // PepperPlatformVideoCapture maintains the state. 318 platform_video_capture_->StopCapture(); 319 return PP_OK; 320 } 321 322 int32_t PepperVideoCaptureHost::Close() { 323 if (!platform_video_capture_.get()) 324 return PP_OK; 325 326 StopCapture(); 327 DCHECK(buffers_.empty()); 328 DetachPlatformVideoCapture(); 329 return PP_OK; 330 } 331 332 void PepperVideoCaptureHost::ReleaseBuffers() { 333 ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker(); 334 for (size_t i = 0; i < buffers_.size(); ++i) { 335 buffers_[i].buffer->Unmap(); 336 tracker->ReleaseResource(buffers_[i].buffer->pp_resource()); 337 } 338 buffers_.clear(); 339 } 340 341 void PepperVideoCaptureHost::SendStatus() { 342 host()->SendUnsolicitedReply(pp_resource(), 343 PpapiPluginMsg_VideoCapture_OnStatus(status_)); 344 } 345 346 void PepperVideoCaptureHost::SetRequestedInfo( 347 const PP_VideoCaptureDeviceInfo_Dev& device_info, 348 uint32_t buffer_count) { 349 // Clamp the buffer count to between 1 and |kMaxBuffers|. 350 buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers); 351 // Clamp the frame rate to between 1 and |kMaxFramesPerSecond - 1|. 352 int frames_per_second = 353 std::min(std::max(device_info.frames_per_second, 1U), 354 static_cast<uint32_t>(media::limits::kMaxFramesPerSecond - 1)); 355 356 video_capture_params_.requested_format = media::VideoCaptureFormat( 357 gfx::Size(device_info.width, device_info.height), 358 frames_per_second, 359 media::PIXEL_FORMAT_I420); 360 } 361 362 void PepperVideoCaptureHost::DetachPlatformVideoCapture() { 363 if (platform_video_capture_) { 364 platform_video_capture_->DetachEventHandler(); 365 platform_video_capture_.reset(); 366 } 367 } 368 369 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status, 370 bool forced) { 371 if (!forced) { 372 switch (status) { 373 case PP_VIDEO_CAPTURE_STATUS_STOPPED: 374 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING) 375 return false; 376 break; 377 case PP_VIDEO_CAPTURE_STATUS_STARTING: 378 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED) 379 return false; 380 break; 381 case PP_VIDEO_CAPTURE_STATUS_STARTED: 382 switch (status_) { 383 case PP_VIDEO_CAPTURE_STATUS_STARTING: 384 case PP_VIDEO_CAPTURE_STATUS_PAUSED: 385 break; 386 default: 387 return false; 388 } 389 break; 390 case PP_VIDEO_CAPTURE_STATUS_PAUSED: 391 switch (status_) { 392 case PP_VIDEO_CAPTURE_STATUS_STARTING: 393 case PP_VIDEO_CAPTURE_STATUS_STARTED: 394 break; 395 default: 396 return false; 397 } 398 break; 399 case PP_VIDEO_CAPTURE_STATUS_STOPPING: 400 switch (status_) { 401 case PP_VIDEO_CAPTURE_STATUS_STARTING: 402 case PP_VIDEO_CAPTURE_STATUS_STARTED: 403 case PP_VIDEO_CAPTURE_STATUS_PAUSED: 404 break; 405 default: 406 return false; 407 } 408 break; 409 } 410 } 411 412 status_ = status; 413 return true; 414 } 415 416 PepperVideoCaptureHost::BufferInfo::BufferInfo() 417 : in_use(false), data(NULL), buffer() { 418 } 419 420 PepperVideoCaptureHost::BufferInfo::~BufferInfo() { 421 } 422 423 } // namespace content 424