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