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 "ppapi/host/dispatch_host_message.h" 14#include "ppapi/host/ppapi_host.h" 15#include "ppapi/proxy/host_dispatcher.h" 16#include "ppapi/proxy/ppapi_messages.h" 17#include "ppapi/shared_impl/host_resource.h" 18#include "ppapi/thunk/enter.h" 19#include "ppapi/thunk/ppb_buffer_api.h" 20#include "third_party/WebKit/public/web/WebDocument.h" 21#include "third_party/WebKit/public/web/WebElement.h" 22#include "third_party/WebKit/public/web/WebPluginContainer.h" 23 24using ppapi::HostResource; 25using ppapi::TrackedCallback; 26using ppapi::thunk::EnterResourceNoLock; 27using ppapi::thunk::PPB_Buffer_API; 28 29namespace { 30 31// Maximum number of buffers to actually allocate. 32const uint32_t kMaxBuffers = 20; 33 34} // namespace 35 36namespace content { 37 38PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHostImpl* host, 39 PP_Instance instance, 40 PP_Resource resource) 41 : ResourceHost(host->GetPpapiHost(), instance, resource), 42 renderer_ppapi_host_(host), 43 buffer_count_hint_(0), 44 status_(PP_VIDEO_CAPTURE_STATUS_STOPPED), 45 enumeration_helper_( 46 this, 47 PepperMediaDeviceManager::GetForRenderView( 48 host->GetRenderViewForInstance(pp_instance())), 49 PP_DEVICETYPE_DEV_VIDEOCAPTURE) { 50} 51 52PepperVideoCaptureHost::~PepperVideoCaptureHost() { 53 Close(); 54} 55 56bool PepperVideoCaptureHost::Init() { 57 return !!renderer_ppapi_host_->GetPluginInstance(pp_instance()); 58} 59 60int32_t PepperVideoCaptureHost::OnResourceMessageReceived( 61 const IPC::Message& msg, 62 ppapi::host::HostMessageContext* context) { 63 int32_t result = PP_ERROR_FAILED; 64 if (enumeration_helper_.HandleResourceMessage(msg, context, &result)) 65 return result; 66 67 IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg) 68 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 69 PpapiHostMsg_VideoCapture_Open, 70 OnOpen) 71 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 72 PpapiHostMsg_VideoCapture_StartCapture, 73 OnStartCapture) 74 PPAPI_DISPATCH_HOST_RESOURCE_CALL( 75 PpapiHostMsg_VideoCapture_ReuseBuffer, 76 OnReuseBuffer) 77 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 78 PpapiHostMsg_VideoCapture_StopCapture, 79 OnStopCapture) 80 PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( 81 PpapiHostMsg_VideoCapture_Close, 82 OnClose) 83 IPC_END_MESSAGE_MAP() 84 return PP_ERROR_FAILED; 85} 86 87void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture, 88 bool succeeded) { 89 DCHECK(capture == platform_video_capture_.get()); 90 91 if (succeeded) { 92 open_reply_context_.params.set_result(PP_OK); 93 } else { 94 DetachPlatformVideoCapture(); 95 open_reply_context_.params.set_result(PP_ERROR_FAILED); 96 } 97 98 host()->SendReply(open_reply_context_, 99 PpapiPluginMsg_VideoCapture_OpenReply()); 100} 101 102void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) { 103 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false)) 104 SendStatus(); 105} 106 107void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) { 108 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false)) 109 SendStatus(); 110} 111 112void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) { 113 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false)) 114 SendStatus(); 115} 116 117void PepperVideoCaptureHost::OnError(media::VideoCapture* capture, 118 int error_code) { 119 // Today, the media layer only sends "1" as an error. 120 DCHECK(error_code == 1); 121 // It either comes because some error was detected while starting (e.g. 2 122 // conflicting "master" resolution), or because the browser failed to start 123 // the capture. 124 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); 125 host()->SendUnsolicitedReply(pp_resource(), 126 PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED)); 127} 128 129void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) { 130} 131 132void PepperVideoCaptureHost::OnBufferReady( 133 media::VideoCapture* capture, 134 scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) { 135 DCHECK(buffer.get()); 136 for (uint32_t i = 0; i < buffers_.size(); ++i) { 137 if (!buffers_[i].in_use) { 138 // TODO(ihf): Switch to a size calculation based on stride. 139 // Stride is filled out now but not more meaningful than size 140 // until wjia unifies VideoFrameBuffer and media::VideoFrame. 141 size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()), 142 buffer->buffer_size); 143 memcpy(buffers_[i].data, buffer->memory_pointer, size); 144 buffers_[i].in_use = true; 145 platform_video_capture_->FeedBuffer(buffer); 146 host()->SendUnsolicitedReply(pp_resource(), 147 PpapiPluginMsg_VideoCapture_OnBufferReady(i)); 148 return; 149 } 150 } 151 152 // No free slot, just discard the frame and tell the media layer it can 153 // re-use the buffer. 154 platform_video_capture_->FeedBuffer(buffer); 155} 156 157void PepperVideoCaptureHost::OnDeviceInfoReceived( 158 media::VideoCapture* capture, 159 const media::VideoCaptureParams& device_info) { 160 PP_VideoCaptureDeviceInfo_Dev info = { 161 static_cast<uint32_t>(device_info.width), 162 static_cast<uint32_t>(device_info.height), 163 static_cast<uint32_t>(device_info.frame_per_second) 164 }; 165 ReleaseBuffers(); 166 167 // YUV 4:2:0 168 int uv_width = info.width / 2; 169 int uv_height = info.height / 2; 170 size_t size = info.width * info.height + 2 * uv_width * uv_height; 171 172 ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0); 173 174 // Allocate buffers. We keep a reference to them, that is released in 175 // ReleaseBuffers. In the mean time, we prepare the resource and handle here 176 // for sending below. 177 std::vector<HostResource> buffer_host_resources; 178 buffers_.reserve(buffer_count_hint_); 179 ppapi::ResourceTracker* tracker = 180 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( 229 ppapi::proxy::SerializedHandle( 230 dispatcher->ShareHandleWithRemote(platform_file, false), 231 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(this); 240 OnError(capture, PP_ERROR_NOMEMORY); 241 return; 242 } 243 244 host()->Send(new PpapiPluginMsg_ResourceReply( 245 params, PpapiPluginMsg_VideoCapture_OnDeviceInfo( 246 info, buffer_host_resources, size))); 247} 248 249int32_t PepperVideoCaptureHost::OnOpen( 250 ppapi::host::HostMessageContext* context, 251 const std::string& device_id, 252 const PP_VideoCaptureDeviceInfo_Dev& requested_info, 253 uint32_t buffer_count) { 254 if (platform_video_capture_.get()) 255 return PP_ERROR_FAILED; 256 257 SetRequestedInfo(requested_info, buffer_count); 258 259 PepperPluginInstance* instance = 260 renderer_ppapi_host_->GetPluginInstance(pp_instance()); 261 if (!instance) 262 return PP_ERROR_FAILED; 263 264 RenderViewImpl* render_view = static_cast<RenderViewImpl*>( 265 renderer_ppapi_host_->GetRenderViewForInstance(pp_instance())); 266 267 platform_video_capture_ = new PepperPlatformVideoCapture( 268 render_view->AsWeakPtr(), device_id, 269 instance->GetContainer()->element().document().url(), this); 270 271 open_reply_context_ = context->MakeReplyMessageContext(); 272 273 return PP_OK_COMPLETIONPENDING; 274} 275 276int32_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(this, capability_); 287 return PP_OK; 288} 289 290int32_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 299int32_t PepperVideoCaptureHost::OnStopCapture( 300 ppapi::host::HostMessageContext* context) { 301 return StopCapture(); 302} 303 304int32_t PepperVideoCaptureHost::OnClose( 305 ppapi::host::HostMessageContext* context) { 306 return Close(); 307} 308 309int32_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(this); 319 return PP_OK; 320} 321 322int32_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 332void 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 341void PepperVideoCaptureHost::SendStatus() { 342 host()->SendUnsolicitedReply(pp_resource(), 343 PpapiPluginMsg_VideoCapture_OnStatus(status_)); 344} 345 346void 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 352 capability_.width = device_info.width; 353 capability_.height = device_info.height; 354 capability_.frame_rate = device_info.frames_per_second; 355 capability_.expected_capture_delay = 0; // Ignored. 356 capability_.color = media::VideoCaptureCapability::kI420; 357 capability_.interlaced = false; // Ignored. 358} 359 360void PepperVideoCaptureHost::DetachPlatformVideoCapture() { 361 if (platform_video_capture_.get()) { 362 platform_video_capture_->DetachEventHandler(); 363 platform_video_capture_ = NULL; 364 } 365} 366 367bool 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 414PepperVideoCaptureHost::BufferInfo::BufferInfo() 415 : in_use(false), 416 data(NULL), 417 buffer() { 418} 419 420PepperVideoCaptureHost::BufferInfo::~BufferInfo() { 421} 422 423} // namespace content 424