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