| Index: content/renderer/pepper/pepper_video_capture_host.cc | 
| diff --git a/content/renderer/pepper/pepper_video_capture_host.cc b/content/renderer/pepper/pepper_video_capture_host.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..19866d677476cd0e674d213c6d9bfa8e8587f649 | 
| --- /dev/null | 
| +++ b/content/renderer/pepper/pepper_video_capture_host.cc | 
| @@ -0,0 +1,460 @@ | 
| +// Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "content/renderer/pepper/pepper_video_capture_host.h" | 
| + | 
| +#include "ppapi/host/dispatch_host_message.h" | 
| +#include "ppapi/host/ppapi_host.h" | 
| +#include "ppapi/proxy/host_dispatcher.h" | 
| +#include "ppapi/proxy/ppapi_messages.h" | 
| +#include "ppapi/shared_impl/host_resource.h" | 
| +#include "ppapi/thunk/enter.h" | 
| +#include "ppapi/thunk/ppb_buffer_api.h" | 
| +#include "webkit/plugins/ppapi/host_globals.h" | 
| +#include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | 
| +#include "webkit/plugins/ppapi/resource_helper.h" | 
| + | 
| +using ppapi::DeviceRefData; | 
| +using ppapi::HostResource; | 
| +using ppapi::TrackedCallback; | 
| +using ppapi::thunk::EnterResourceNoLock; | 
| +using ppapi::thunk::PPB_Buffer_API; | 
| +using ppapi::thunk::PPB_BufferTrusted_API; | 
| +using webkit::ppapi::HostGlobals; | 
| +using webkit::ppapi::ResourceHelper; | 
| +using webkit::ppapi::PPB_Buffer_Impl; | 
| +using webkit::ppapi::PluginInstance; | 
| + | 
| +namespace { | 
| + | 
| +// Maximum number of buffers to actually allocate. | 
| +const uint32_t kMaxBuffers = 20; | 
| + | 
| +}  // namespace | 
| + | 
| +namespace content { | 
| + | 
| +PepperVideoCaptureHost::PepperVideoCaptureHost(RendererPpapiHost* host, | 
| +                                               PP_Instance instance, | 
| +                                               PP_Resource resource) | 
| +    : ResourceHost(host->GetPpapiHost(), instance, resource), | 
| +      renderer_ppapi_host_(host), | 
| +      buffer_count_hint_(0), | 
| +      status_(PP_VIDEO_CAPTURE_STATUS_STOPPED) { | 
| +} | 
| + | 
| +PepperVideoCaptureHost::~PepperVideoCaptureHost() { | 
| +  Close(); | 
| +} | 
| + | 
| +bool PepperVideoCaptureHost::Init() { | 
| +  PluginInstance* instance = GetPluginInstance(); | 
| +  return !!instance; | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::OnResourceMessageReceived( | 
| +    const IPC::Message& msg, | 
| +    ppapi::host::HostMessageContext* context) { | 
| +  IPC_BEGIN_MESSAGE_MAP(PepperVideoCaptureHost, msg) | 
| +    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( | 
| +        PpapiHostMsg_VideoCapture_EnumerateDevices, | 
| +        OnEnumerateDevices) | 
| +    PPAPI_DISPATCH_HOST_RESOURCE_CALL( | 
| +        PpapiHostMsg_VideoCapture_Open, | 
| +        OnOpen) | 
| +    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( | 
| +        PpapiHostMsg_VideoCapture_StartCapture, | 
| +        OnStartCapture) | 
| +    PPAPI_DISPATCH_HOST_RESOURCE_CALL( | 
| +        PpapiHostMsg_VideoCapture_ReuseBuffer, | 
| +        OnReuseBuffer) | 
| +    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( | 
| +        PpapiHostMsg_VideoCapture_StopCapture, | 
| +        OnStopCapture) | 
| +    PPAPI_DISPATCH_HOST_RESOURCE_CALL_0( | 
| +        PpapiHostMsg_VideoCapture_Close, | 
| +        OnClose) | 
| +  IPC_END_MESSAGE_MAP() | 
| +  return PP_ERROR_FAILED; | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnInitialized(media::VideoCapture* capture, | 
| +                                           bool succeeded) { | 
| +  DCHECK(capture == platform_video_capture_.get()); | 
| + | 
| +  if (succeeded) { | 
| +    open_reply_context_.params.set_result(PP_OK); | 
| +  } else { | 
| +    DetachPlatformVideoCapture(); | 
| +    open_reply_context_.params.set_result(PP_ERROR_FAILED); | 
| +  } | 
| + | 
| +  host()->SendReply(open_reply_context_, | 
| +                    PpapiPluginMsg_VideoCapture_OpenReply()); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnStarted(media::VideoCapture* capture) { | 
| +  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false)) | 
| +    SendStatus(); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnStopped(media::VideoCapture* capture) { | 
| +  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false)) | 
| +    SendStatus(); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnPaused(media::VideoCapture* capture) { | 
| +  if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false)) | 
| +    SendStatus(); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnError(media::VideoCapture* capture, | 
| +                                     int error_code) { | 
| +  // Today, the media layer only sends "1" as an error. | 
| +  DCHECK(error_code == 1); | 
| +  // It either comes because some error was detected while starting (e.g. 2 | 
| +  // conflicting "master" resolution), or because the browser failed to start | 
| +  // the capture. | 
| +  SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); | 
| +  host()->SendUnsolicitedReply(pp_resource(), | 
| +      PpapiPluginMsg_VideoCapture_OnError(PP_ERROR_FAILED)); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnRemoved(media::VideoCapture* capture) { | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnBufferReady( | 
| +    media::VideoCapture* capture, | 
| +    scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) { | 
| +  DCHECK(buffer.get()); | 
| +  for (uint32_t i = 0; i < buffers_.size(); ++i) { | 
| +    if (!buffers_[i].in_use) { | 
| +      // TODO(ihf): Switch to a size calculation based on stride. | 
| +      // Stride is filled out now but not more meaningful than size | 
| +      // until wjia unifies VideoFrameBuffer and media::VideoFrame. | 
| +      size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()), | 
| +          buffer->buffer_size); | 
| +      memcpy(buffers_[i].data, buffer->memory_pointer, size); | 
| +      buffers_[i].in_use = true; | 
| +      platform_video_capture_->FeedBuffer(buffer); | 
| +      host()->SendUnsolicitedReply(pp_resource(), | 
| +          PpapiPluginMsg_VideoCapture_OnBufferReady(i)); | 
| +      return; | 
| +    } | 
| +  } | 
| + | 
| +  // No free slot, just discard the frame and tell the media layer it can | 
| +  // re-use the buffer. | 
| +  platform_video_capture_->FeedBuffer(buffer); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::OnDeviceInfoReceived( | 
| +    media::VideoCapture* capture, | 
| +    const media::VideoCaptureParams& device_info) { | 
| +  PP_VideoCaptureDeviceInfo_Dev info = { | 
| +    static_cast<uint32_t>(device_info.width), | 
| +    static_cast<uint32_t>(device_info.height), | 
| +    static_cast<uint32_t>(device_info.frame_per_second) | 
| +  }; | 
| +  ReleaseBuffers(); | 
| + | 
| +  // YUV 4:2:0 | 
| +  int uv_width = info.width / 2; | 
| +  int uv_height = info.height / 2; | 
| +  size_t size = info.width * info.height + 2 * uv_width * uv_height; | 
| + | 
| +  ppapi::proxy::ResourceMessageReplyParams params(pp_resource(), 0); | 
| + | 
| +  // Allocate buffers. We keep a reference to them, that is released in | 
| +  // ReleaseBuffers. In the mean time, we prepare the resource and handle here | 
| +  // for sending below. | 
| +  std::vector<HostResource> buffer_host_resources; | 
| +  buffers_.reserve(buffer_count_hint_); | 
| +  ::ppapi::ResourceTracker* tracker = | 
| +      HostGlobals::Get()->GetResourceTracker(); | 
| +  ppapi::proxy::HostDispatcher* dispatcher = | 
| +      ppapi::proxy::HostDispatcher::GetForInstance(pp_instance()); | 
| +  for (size_t i = 0; i < buffer_count_hint_; ++i) { | 
| +    PP_Resource res = PPB_Buffer_Impl::Create(pp_instance(), size); | 
| +    if (!res) | 
| +      break; | 
| + | 
| +    EnterResourceNoLock<PPB_Buffer_API> enter(res, true); | 
| +    DCHECK(enter.succeeded()); | 
| + | 
| +    BufferInfo buf; | 
| +    buf.buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); | 
| +    buf.data = buf.buffer->Map(); | 
| +    if (!buf.data) { | 
| +      tracker->ReleaseResource(res); | 
| +      break; | 
| +    } | 
| +    buffers_.push_back(buf); | 
| + | 
| +    // Add to HostResource array to be sent. | 
| +    { | 
| +      HostResource host_resource; | 
| +      host_resource.SetHostResource(pp_instance(), res); | 
| +      buffer_host_resources.push_back(host_resource); | 
| + | 
| +      // Add a reference for the plugin, which is resposible for releasing it. | 
| +      tracker->AddRefResource(res); | 
| +    } | 
| + | 
| +    // Add the serialized shared memory handle to params. FileDescriptor is | 
| +    // treated in special case. | 
| +    { | 
| +      EnterResourceNoLock<PPB_BufferTrusted_API> enter(res, true); | 
| +      DCHECK(enter.succeeded()); | 
| +      int handle; | 
| +      int32_t result = enter.object()->GetSharedMemory(&handle); | 
| +      DCHECK(result == PP_OK); | 
| +      // TODO(piman/brettw): Change trusted interface to return a PP_FileHandle, | 
| +      // those casts are ugly. | 
| +      base::PlatformFile platform_file = | 
| +#if defined(OS_WIN) | 
| +          reinterpret_cast<HANDLE>(static_cast<intptr_t>(handle)); | 
| +#elif defined(OS_POSIX) | 
| +          handle; | 
| +#else | 
| +#error Not implemented. | 
| +#endif | 
| +      params.AppendHandle( | 
| +          ppapi::proxy::SerializedHandle( | 
| +              dispatcher->ShareHandleWithRemote(platform_file, false), | 
| +              size)); | 
| +    } | 
| +  } | 
| + | 
| +  if (buffers_.empty()) { | 
| +    // We couldn't allocate/map buffers at all. Send an error and stop the | 
| +    // capture. | 
| +    SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true); | 
| +    platform_video_capture_->StopCapture(this); | 
| +    OnError(capture, PP_ERROR_NOMEMORY); | 
| +    return; | 
| +  } | 
| + | 
| +  host()->Send(new PpapiPluginMsg_ResourceReply( | 
| +      params, PpapiPluginMsg_VideoCapture_OnDeviceInfo( | 
| +          info, buffer_host_resources, size))); | 
| +} | 
| + | 
| +PluginInstance* PepperVideoCaptureHost::GetPluginInstance() const { | 
| +  return renderer_ppapi_host_->GetPluginInstance(pp_instance()); | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::OnEnumerateDevices( | 
| +    ppapi::host::HostMessageContext* context) { | 
| +  PluginInstance* instance = GetPluginInstance(); | 
| +  if (!instance) | 
| +    return PP_ERROR_FAILED; | 
| + | 
| +  enum_reply_context_ = context->MakeReplyMessageContext(); | 
| +  instance->delegate()->EnumerateDevices( | 
| +      PP_DEVICETYPE_DEV_VIDEOCAPTURE, | 
| +      base::Bind(&PepperVideoCaptureHost::EnumerateDevicesCallbackFunc, | 
| +                 AsWeakPtr())); | 
| +  return PP_OK_COMPLETIONPENDING; | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::OnOpen( | 
| +    ppapi::host::HostMessageContext* context, | 
| +    const std::string& device_id, | 
| +    const PP_VideoCaptureDeviceInfo_Dev& requested_info, | 
| +    uint32_t buffer_count) { | 
| +  if (platform_video_capture_.get()) | 
| +    return PP_ERROR_FAILED; | 
| + | 
| +  PluginInstance* instance = GetPluginInstance(); | 
| +  if (!instance) | 
| +    return PP_ERROR_FAILED; | 
| + | 
| +  SetRequestedInfo(requested_info, buffer_count); | 
| + | 
| +  platform_video_capture_ = | 
| +      instance->delegate()->CreateVideoCapture(device_id, this); | 
| + | 
| +  open_reply_context_ = context->MakeReplyMessageContext(); | 
| + | 
| +  // It is able to complete synchronously if the default device is used. | 
| +  bool sync_completion = device_id.empty(); | 
| +  if (sync_completion) { | 
| +    // Send OpenACK directly, but still need to return PP_OK_COMPLETIONPENDING | 
| +    // to make PluginResource happy. | 
| +    OnInitialized(platform_video_capture_.get(), true); | 
| +  } | 
| + | 
| +  return PP_OK_COMPLETIONPENDING; | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::OnStartCapture( | 
| +    ppapi::host::HostMessageContext* context) { | 
| +  if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTING, false) || | 
| +      !platform_video_capture_.get()) | 
| +    return PP_ERROR_FAILED; | 
| + | 
| +  DCHECK(buffers_.empty()); | 
| + | 
| +  // It's safe to call this regardless it's capturing or not, because | 
| +  // PepperPlatformVideoCaptureImpl maintains the state. | 
| +  platform_video_capture_->StartCapture(this, capability_); | 
| +  return PP_OK; | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::OnReuseBuffer( | 
| +    ppapi::host::HostMessageContext* context, | 
| +    uint32_t buffer) { | 
| +  if (buffer >= buffers_.size() || !buffers_[buffer].in_use) | 
| +    return PP_ERROR_BADARGUMENT; | 
| +  buffers_[buffer].in_use = false; | 
| +  return PP_OK; | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::OnStopCapture( | 
| +    ppapi::host::HostMessageContext* context) { | 
| +  return StopCapture(); | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::OnClose( | 
| +    ppapi::host::HostMessageContext* context) { | 
| +  return Close(); | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::StopCapture() { | 
| +  if (!SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, false)) | 
| +    return PP_ERROR_FAILED; | 
| + | 
| +  DCHECK(platform_video_capture_.get()); | 
| + | 
| +  ReleaseBuffers(); | 
| +  // It's safe to call this regardless it's capturing or not, because | 
| +  // PepperPlatformVideoCaptureImpl maintains the state. | 
| +  platform_video_capture_->StopCapture(this); | 
| +  return PP_OK; | 
| +} | 
| + | 
| +int32_t PepperVideoCaptureHost::Close() { | 
| +  if (!platform_video_capture_.get()) | 
| +    return PP_OK; | 
| + | 
| +  StopCapture(); | 
| +  DCHECK(buffers_.empty()); | 
| +  DetachPlatformVideoCapture(); | 
| +  return PP_OK; | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::ReleaseBuffers() { | 
| +  ::ppapi::ResourceTracker* tracker = HostGlobals::Get()->GetResourceTracker(); | 
| +  for (size_t i = 0; i < buffers_.size(); ++i) { | 
| +    buffers_[i].buffer->Unmap(); | 
| +    tracker->ReleaseResource(buffers_[i].buffer->pp_resource()); | 
| +  } | 
| +  buffers_.clear(); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::SendStatus() { | 
| +  host()->SendUnsolicitedReply(pp_resource(), | 
| +      PpapiPluginMsg_VideoCapture_OnStatus(status_)); | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::SetRequestedInfo( | 
| +    const PP_VideoCaptureDeviceInfo_Dev& device_info, | 
| +    uint32_t buffer_count) { | 
| +  // Clamp the buffer count to between 1 and |kMaxBuffers|. | 
| +  buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers); | 
| + | 
| +  capability_.width = device_info.width; | 
| +  capability_.height = device_info.height; | 
| +  capability_.frame_rate = device_info.frames_per_second; | 
| +  capability_.expected_capture_delay = 0;  // Ignored. | 
| +  capability_.color = media::VideoCaptureCapability::kI420; | 
| +  capability_.interlaced = false;  // Ignored. | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::DetachPlatformVideoCapture() { | 
| +  if (platform_video_capture_.get()) { | 
| +    platform_video_capture_->DetachEventHandler(); | 
| +    platform_video_capture_ = NULL; | 
| +  } | 
| +} | 
| + | 
| +void PepperVideoCaptureHost::EnumerateDevicesCallbackFunc( | 
| +    int request_id, | 
| +    bool succeeded, | 
| +    const std::vector<ppapi::DeviceRefData>& devices) { | 
| +  PluginInstance* instance = GetPluginInstance(); | 
| +  if (instance) | 
| +    instance->delegate()->StopEnumerateDevices(request_id); | 
| + | 
| +  if (succeeded) { | 
| +    enum_reply_context_.params.set_result(PP_OK); | 
| +    host()->SendReply(enum_reply_context_, | 
| +                      PpapiPluginMsg_VideoCapture_EnumerateDevicesReply( | 
| +                          devices)); | 
| +  } else { | 
| +    enum_reply_context_.params.set_result(PP_ERROR_FAILED); | 
| +    host()->SendReply(enum_reply_context_, | 
| +                      PpapiPluginMsg_VideoCapture_EnumerateDevicesReply( | 
| +                          std::vector<DeviceRefData>())); | 
| +  } | 
| +} | 
| + | 
| +bool PepperVideoCaptureHost::SetStatus(PP_VideoCaptureStatus_Dev status, | 
| +                                       bool forced) { | 
| +  if (!forced) { | 
| +    switch (status) { | 
| +      case PP_VIDEO_CAPTURE_STATUS_STOPPED: | 
| +        if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPING) | 
| +          return false; | 
| +        break; | 
| +      case PP_VIDEO_CAPTURE_STATUS_STARTING: | 
| +        if (status_ != PP_VIDEO_CAPTURE_STATUS_STOPPED) | 
| +          return false; | 
| +        break; | 
| +      case PP_VIDEO_CAPTURE_STATUS_STARTED: | 
| +        switch (status_) { | 
| +          case PP_VIDEO_CAPTURE_STATUS_STARTING: | 
| +          case PP_VIDEO_CAPTURE_STATUS_PAUSED: | 
| +            break; | 
| +          default: | 
| +            return false; | 
| +        } | 
| +        break; | 
| +      case PP_VIDEO_CAPTURE_STATUS_PAUSED: | 
| +        switch (status_) { | 
| +          case PP_VIDEO_CAPTURE_STATUS_STARTING: | 
| +          case PP_VIDEO_CAPTURE_STATUS_STARTED: | 
| +            break; | 
| +          default: | 
| +            return false; | 
| +        } | 
| +        break; | 
| +      case PP_VIDEO_CAPTURE_STATUS_STOPPING: | 
| +        switch (status_) { | 
| +          case PP_VIDEO_CAPTURE_STATUS_STARTING: | 
| +          case PP_VIDEO_CAPTURE_STATUS_STARTED: | 
| +          case PP_VIDEO_CAPTURE_STATUS_PAUSED: | 
| +            break; | 
| +          default: | 
| +            return false; | 
| +        } | 
| +        break; | 
| +    } | 
| +  } | 
| + | 
| +  status_ = status; | 
| +  return true; | 
| +} | 
| + | 
| +PepperVideoCaptureHost::BufferInfo::BufferInfo() | 
| +    : in_use(false), | 
| +      data(NULL), | 
| +      buffer() { | 
| +} | 
| + | 
| +PepperVideoCaptureHost::BufferInfo::~BufferInfo() { | 
| +} | 
| + | 
| +}  // namespace content | 
|  |