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_view_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::GetForRenderView( 46 host->GetRenderViewForInstance(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(), PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED)); 118 } 119 120 void PepperVideoCaptureHost::OnFrameReady( 121 const scoped_refptr<media::VideoFrame>& frame, 122 media::VideoCaptureFormat format) { 123 DCHECK(frame.get()); 124 125 if (alloc_size_ != frame->coded_size() || buffers_.empty()) { 126 AllocBuffers(frame->coded_size(), format.frame_rate); 127 alloc_size_ = frame->coded_size(); 128 } 129 130 for (uint32_t i = 0; i < buffers_.size(); ++i) { 131 if (!buffers_[i].in_use) { 132 DCHECK_EQ(frame->format(), media::VideoFrame::I420); 133 if (buffers_[i].buffer->size() < 134 media::VideoFrame::AllocationSize(frame->format(), 135 frame->coded_size())) { 136 // TODO(ihf): handle size mismatches gracefully here. 137 return; 138 } 139 uint8* dst = reinterpret_cast<uint8*>(buffers_[i].data); 140 COMPILE_ASSERT(media::VideoFrame::kYPlane == 0, y_plane_should_be_0); 141 COMPILE_ASSERT(media::VideoFrame::kUPlane == 1, u_plane_should_be_1); 142 COMPILE_ASSERT(media::VideoFrame::kVPlane == 2, v_plane_should_be_2); 143 for (size_t j = 0; j < media::VideoFrame::NumPlanes(frame->format()); 144 ++j) { 145 const uint8* src = frame->data(j); 146 const size_t row_bytes = frame->row_bytes(j); 147 const size_t src_stride = frame->stride(j); 148 for (int k = 0; k < frame->rows(j); ++k) { 149 memcpy(dst, src, row_bytes); 150 dst += row_bytes; 151 src += src_stride; 152 } 153 } 154 buffers_[i].in_use = true; 155 host()->SendUnsolicitedReply( 156 pp_resource(), PpapiPluginMsg_VideoCapture_OnBufferReady(i)); 157 return; 158 } 159 } 160 } 161 162 void PepperVideoCaptureHost::AllocBuffers(const gfx::Size& resolution, 163 int frame_rate) { 164 PP_VideoCaptureDeviceInfo_Dev info = { 165 static_cast<uint32_t>(resolution.width()), 166 static_cast<uint32_t>(resolution.height()), 167 static_cast<uint32_t>(frame_rate)}; 168 ReleaseBuffers(); 169 170 const size_t size = media::VideoFrame::AllocationSize( 171 media::VideoFrame::I420, gfx::Size(info.width, info.height)); 172 173 ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0); 174 175 // Allocate buffers. We keep a reference to them, that is released in 176 // ReleaseBuffers. In the mean time, we prepare the resource and handle here 177 // for sending below. 178 std::vector<HostResource> buffer_host_resources; 179 buffers_.reserve(buffer_count_hint_); 180 ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker(); 181 ppapi::proxy::HostDispatcher* dispatcher = 182 ppapi::proxy::HostDispatcher::GetForInstance(pp_instance()); 183 for (size_t i = 0; i < buffer_count_hint_; ++i) { 184 PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size); 185 if (!res) 186 break; 187 188 EnterResourceNoLock<PPB_Buffer_API> enter(res, true); 189 DCHECK(enter.succeeded()); 190 191 BufferInfo buf; 192 buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); 193 buf.data = buf.buffer->Map(); 194 if (!buf.data) { 195 tracker->ReleaseResource(res); 196 break; 197 } 198 buffers_.push_back(buf); 199 200 // Add to HostResource array to be sent. 201 { 202 HostResource host_resource; 203 host_resource.SetHostResource(pp_instance(), res); 204 buffer_host_resources.push_back(host_resource); 205 206 // Add a reference for the plugin, which is resposible for releasing it. 207 tracker->AddRefResource(res); 208 } 209 210 // Add the serialized shared memory handle to params. FileDescriptor is 211 // treated in special case. 212 { 213 EnterResourceNoLock<PPB_Buffer_API> enter(res, true); 214 DCHECK(enter.succeeded()); 215 int handle; 216 int32_t result = enter.object()->GetSharedMemory(&handle); 217 DCHECK(result == PP_OK); 218 // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle, 219 // those casts are ugly. 220 base::PlatformFile platform_file = 221 #if defined(OS_WIN) 222 reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle)); 223 #elif defined(OS_POSIX) 224 handle; 225 #else 226 #error Not implemented. 227 #endif 228 params.AppendHandle(ppapi::proxy::SerializedHandle( 229 dispatcher->ShareHandleWithRemote(platform_file, false), size)); 230 } 231 } 232 233 if (buffers_.empty()) { 234 // We couldn't allocate/map buffers at all. Send an error and stop the 235 // capture. 236 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true); 237 platform_video_capture_->StopCapture(); 238 PostErrorReply(); 239 return; 240 } 241 242 host()->Send( 243 new PpapiPluginMsg_ResourceReply(params, 244 PpapiPluginMsg_VideoCapture_OnDeviceInfo( 245 info, buffer_host_resources, size))); 246 } 247 248 int32_t PepperVideoCaptureHost::OnOpen( 249 ppapi::host::HostMessageContext* context, 250 const std::string& device_id, 251 const PP_VideoCaptureDeviceInfo_Dev& requested_info, 252 uint32_t buffer_count) { 253 if (platform_video_capture_.get()) 254 return PP_ERROR_FAILED; 255 256 SetRequestedInfo(requested_info, buffer_count); 257 258 GURL document_url = renderer_ppapi_host_->GetDocumentURL(pp_instance()); 259 if (!document_url.is_valid()) 260 return PP_ERROR_FAILED; 261 262 RenderViewImpl* render_view = static_cast<RenderViewImpl*>( 263 renderer_ppapi_host_->GetRenderViewForInstance(pp_instance())); 264 265 platform_video_capture_.reset(new PepperPlatformVideoCapture( 266 render_view->AsWeakPtr(), device_id, document_url, this)); 267 268 open_reply_context_ = context->MakeReplyMessageContext(); 269 270 return PP_OK_COMPLETIONPENDING; 271 } 272 273 int32_t PepperVideoCaptureHost::OnStartCapture( 274 ppapi::host::HostMessageContext* context) { 275 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) || 276 !platform_video_capture_.get()) 277 return PP_ERROR_FAILED; 278 279 DCHECK(buffers_.empty()); 280 281 // It's safe to call this regardless it's capturing or not, because 282 // PepperPlatformVideoCapture maintains the state. 283 platform_video_capture_->StartCapture(video_capture_params_); 284 return PP_OK; 285 } 286 287 int32_t PepperVideoCaptureHost::OnReuseBuffer( 288 ppapi::host::HostMessageContext* context, 289 uint32_t buffer) { 290 if (buffer >= buffers_.size() || !buffers_[buffer].in_use) 291 return PP_ERROR_BADARGUMENT; 292 buffers_[buffer].in_use = false; 293 return PP_OK; 294 } 295 296 int32_t PepperVideoCaptureHost::OnStopCapture( 297 ppapi::host::HostMessageContext* context) { 298 return StopCapture(); 299 } 300 301 int32_t PepperVideoCaptureHost::OnClose( 302 ppapi::host::HostMessageContext* context) { 303 return Close(); 304 } 305 306 int32_t PepperVideoCaptureHost::StopCapture() { 307 if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false)) 308 return PP_ERROR_FAILED; 309 310 DCHECK(platform_video_capture_.get()); 311 312 ReleaseBuffers(); 313 // It's safe to call this regardless it's capturing or not, because 314 // PepperPlatformVideoCapture maintains the state. 315 platform_video_capture_->StopCapture(); 316 return PP_OK; 317 } 318 319 int32_t PepperVideoCaptureHost::Close() { 320 if (!platform_video_capture_.get()) 321 return PP_OK; 322 323 StopCapture(); 324 DCHECK(buffers_.empty()); 325 DetachPlatformVideoCapture(); 326 return PP_OK; 327 } 328 329 void PepperVideoCaptureHost::ReleaseBuffers() { 330 ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker(); 331 for (size_t i = 0; i < buffers_.size(); ++i) { 332 buffers_[i].buffer->Unmap(); 333 tracker->ReleaseResource(buffers_[i].buffer->pp_resource()); 334 } 335 buffers_.clear(); 336 } 337 338 void PepperVideoCaptureHost::SendStatus() { 339 host()->SendUnsolicitedReply(pp_resource(), 340 PpapiPluginMsg_VideoCapture_OnStatus(status_)); 341 } 342 343 void PepperVideoCaptureHost::SetRequestedInfo( 344 const PP_VideoCaptureDeviceInfo_Dev& device_info, 345 uint32_t buffer_count) { 346 // Clamp the buffer count to between 1 and |kMaxBuffers|. 347 buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers); 348 // Clamp the frame rate to between 1 and |kMaxFramesPerSecond - 1|. 349 int frames_per_second = 350 std::min(std::max(device_info.frames_per_second, 1U), 351 static_cast<uint32_t>(media::limits::kMaxFramesPerSecond - 1)); 352 353 video_capture_params_.requested_format = media::VideoCaptureFormat( 354 gfx::Size(device_info.width, device_info.height), 355 frames_per_second, 356 media::PIXEL_FORMAT_I420); 357 video_capture_params_.allow_resolution_change = false; 358 } 359 360 void PepperVideoCaptureHost::DetachPlatformVideoCapture() { 361 if (platform_video_capture_) { 362 platform_video_capture_->DetachEventHandler(); 363 platform_video_capture_.reset(); 364 } 365 } 366 367 bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status, 368 bool forced) { 369 if (!forced) { 370 switch (status) { 371 case PP_VIDEO_CAPTURE_STATUS_STOPPED: 372 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING) 373 return false; 374 break; 375 case PP_VIDEO_CAPTURE_STATUS_STARTING: 376 if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED) 377 return false; 378 break; 379 case PP_VIDEO_CAPTURE_STATUS_STARTED: 380 switch (status_) { 381 case PP_VIDEO_CAPTURE_STATUS_STARTING: 382 case PP_VIDEO_CAPTURE_STATUS_PAUSED: 383 break; 384 default: 385 return false; 386 } 387 break; 388 case PP_VIDEO_CAPTURE_STATUS_PAUSED: 389 switch (status_) { 390 case PP_VIDEO_CAPTURE_STATUS_STARTING: 391 case PP_VIDEO_CAPTURE_STATUS_STARTED: 392 break; 393 default: 394 return false; 395 } 396 break; 397 case PP_VIDEO_CAPTURE_STATUS_STOPPING: 398 switch (status_) { 399 case PP_VIDEO_CAPTURE_STATUS_STARTING: 400 case PP_VIDEO_CAPTURE_STATUS_STARTED: 401 case PP_VIDEO_CAPTURE_STATUS_PAUSED: 402 break; 403 default: 404 return false; 405 } 406 break; 407 } 408 } 409 410 status_ = status; 411 return true; 412 } 413 414 PepperVideoCaptureHost::BufferInfo::BufferInfo() 415 : in_use(false), data(NULL), buffer() { 416 } 417 418 PepperVideoCaptureHost::BufferInfo::~BufferInfo() { 419 } 420 421 } // namespace content 422