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