| OLD | NEW |
| (Empty) |
| 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 "webkit/plugins/ppapi/ppb_video_capture_impl.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <string> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/memory/scoped_ptr.h" | |
| 13 #include "ppapi/c/dev/pp_video_capture_dev.h" | |
| 14 #include "ppapi/c/dev/ppb_video_capture_dev.h" | |
| 15 #include "ppapi/c/pp_completion_callback.h" | |
| 16 #include "ppapi/c/pp_errors.h" | |
| 17 #include "ppapi/shared_impl/ppapi_globals.h" | |
| 18 #include "ppapi/shared_impl/ppb_device_ref_shared.h" | |
| 19 #include "ppapi/shared_impl/resource_tracker.h" | |
| 20 #include "ppapi/shared_impl/tracked_callback.h" | |
| 21 #include "ppapi/thunk/enter.h" | |
| 22 #include "webkit/plugins/ppapi/common.h" | |
| 23 #include "webkit/plugins/ppapi/plugin_module.h" | |
| 24 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | |
| 25 #include "webkit/plugins/ppapi/ppb_buffer_impl.h" | |
| 26 #include "webkit/plugins/ppapi/resource_helper.h" | |
| 27 | |
| 28 using ppapi::DeviceRefData; | |
| 29 using ppapi::PpapiGlobals; | |
| 30 using ppapi::thunk::EnterResourceNoLock; | |
| 31 using ppapi::thunk::PPB_Buffer_API; | |
| 32 using ppapi::thunk::PPB_VideoCapture_API; | |
| 33 using ppapi::TrackedCallback; | |
| 34 | |
| 35 namespace { | |
| 36 | |
| 37 // Maximum number of buffers to actually allocate. | |
| 38 const uint32_t kMaxBuffers = 20; | |
| 39 | |
| 40 } // namespace | |
| 41 | |
| 42 namespace webkit { | |
| 43 namespace ppapi { | |
| 44 | |
| 45 PPB_VideoCapture_Impl::PPB_VideoCapture_Impl(PP_Instance instance) | |
| 46 : PPB_VideoCapture_Shared(instance), | |
| 47 buffer_count_hint_(0), | |
| 48 ppp_videocapture_(NULL), | |
| 49 capability_() { | |
| 50 } | |
| 51 | |
| 52 PPB_VideoCapture_Impl::~PPB_VideoCapture_Impl() { | |
| 53 Close(); | |
| 54 } | |
| 55 | |
| 56 bool PPB_VideoCapture_Impl::Init() { | |
| 57 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
| 58 if (!instance) | |
| 59 return false; | |
| 60 ppp_videocapture_ = static_cast<const PPP_VideoCapture_Dev*>( | |
| 61 instance->module()->GetPluginInterface(PPP_VIDEO_CAPTURE_DEV_INTERFACE)); | |
| 62 if (!ppp_videocapture_) | |
| 63 return false; | |
| 64 | |
| 65 return true; | |
| 66 } | |
| 67 | |
| 68 void PPB_VideoCapture_Impl::OnStarted(media::VideoCapture* capture) { | |
| 69 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STARTED, false)) | |
| 70 SendStatus(); | |
| 71 } | |
| 72 | |
| 73 void PPB_VideoCapture_Impl::OnStopped(media::VideoCapture* capture) { | |
| 74 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, false)) | |
| 75 SendStatus(); | |
| 76 } | |
| 77 | |
| 78 void PPB_VideoCapture_Impl::OnPaused(media::VideoCapture* capture) { | |
| 79 if (SetStatus(PP_VIDEO_CAPTURE_STATUS_PAUSED, false)) | |
| 80 SendStatus(); | |
| 81 } | |
| 82 | |
| 83 void PPB_VideoCapture_Impl::OnError(media::VideoCapture* capture, | |
| 84 int error_code) { | |
| 85 // Today, the media layer only sends "1" as an error. | |
| 86 DCHECK(error_code == 1); | |
| 87 // It either comes because some error was detected while starting (e.g. 2 | |
| 88 // conflicting "master" resolution), or because the browser failed to start | |
| 89 // the capture. | |
| 90 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); | |
| 91 ppp_videocapture_->OnError(pp_instance(), pp_resource(), PP_ERROR_FAILED); | |
| 92 } | |
| 93 | |
| 94 void PPB_VideoCapture_Impl::OnRemoved(media::VideoCapture* capture) { | |
| 95 } | |
| 96 | |
| 97 void PPB_VideoCapture_Impl::OnBufferReady( | |
| 98 media::VideoCapture* capture, | |
| 99 scoped_refptr<media::VideoCapture::VideoFrameBuffer> buffer) { | |
| 100 DCHECK(buffer.get()); | |
| 101 for (uint32_t i = 0; i < buffers_.size(); ++i) { | |
| 102 if (!buffers_[i].in_use) { | |
| 103 // TODO(ihf): Switch to a size calculation based on stride. | |
| 104 // Stride is filled out now but not more meaningful than size | |
| 105 // until wjia unifies VideoFrameBuffer and media::VideoFrame. | |
| 106 size_t size = std::min(static_cast<size_t>(buffers_[i].buffer->size()), | |
| 107 buffer->buffer_size); | |
| 108 memcpy(buffers_[i].data, buffer->memory_pointer, size); | |
| 109 buffers_[i].in_use = true; | |
| 110 platform_video_capture_->FeedBuffer(buffer); | |
| 111 ppp_videocapture_->OnBufferReady(pp_instance(), pp_resource(), i); | |
| 112 return; | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 // No free slot, just discard the frame and tell the media layer it can | |
| 117 // re-use the buffer. | |
| 118 platform_video_capture_->FeedBuffer(buffer); | |
| 119 } | |
| 120 | |
| 121 void PPB_VideoCapture_Impl::OnDeviceInfoReceived( | |
| 122 media::VideoCapture* capture, | |
| 123 const media::VideoCaptureParams& device_info) { | |
| 124 PP_VideoCaptureDeviceInfo_Dev info = { | |
| 125 static_cast<uint32_t>(device_info.width), | |
| 126 static_cast<uint32_t>(device_info.height), | |
| 127 static_cast<uint32_t>(device_info.frame_per_second) | |
| 128 }; | |
| 129 ReleaseBuffers(); | |
| 130 | |
| 131 // Allocate buffers. We keep a reference to them, that is released in | |
| 132 // ReleaseBuffers. | |
| 133 // YUV 4:2:0 | |
| 134 int uv_width = info.width / 2; | |
| 135 int uv_height = info.height / 2; | |
| 136 size_t size = info.width * info.height + 2 * uv_width * uv_height; | |
| 137 scoped_array<PP_Resource> resources(new PP_Resource[buffer_count_hint_]); | |
| 138 | |
| 139 buffers_.reserve(buffer_count_hint_); | |
| 140 for (size_t i = 0; i < buffer_count_hint_; ++i) { | |
| 141 resources[i] = PPB_Buffer_Impl::Create(pp_instance(), size); | |
| 142 if (!resources[i]) | |
| 143 break; | |
| 144 | |
| 145 EnterResourceNoLock<PPB_Buffer_API> enter(resources[i], true); | |
| 146 DCHECK(enter.succeeded()); | |
| 147 | |
| 148 BufferInfo info; | |
| 149 info.buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); | |
| 150 info.data = info.buffer->Map(); | |
| 151 if (!info.data) { | |
| 152 PpapiGlobals::Get()->GetResourceTracker()->ReleaseResource(resources[i]); | |
| 153 break; | |
| 154 } | |
| 155 buffers_.push_back(info); | |
| 156 } | |
| 157 | |
| 158 if (buffers_.empty()) { | |
| 159 // We couldn't allocate/map buffers at all. Send an error and stop the | |
| 160 // capture. | |
| 161 ppp_videocapture_->OnError(pp_instance(), pp_resource(), PP_ERROR_NOMEMORY); | |
| 162 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPING, true); | |
| 163 platform_video_capture_->StopCapture(this); | |
| 164 return; | |
| 165 } | |
| 166 | |
| 167 ppp_videocapture_->OnDeviceInfo(pp_instance(), pp_resource(), &info, | |
| 168 buffers_.size(), resources.get()); | |
| 169 } | |
| 170 | |
| 171 void PPB_VideoCapture_Impl::OnInitialized(media::VideoCapture* capture, | |
| 172 bool succeeded) { | |
| 173 DCHECK(capture == platform_video_capture_.get()); | |
| 174 | |
| 175 OnOpenComplete(succeeded ? PP_OK : PP_ERROR_FAILED); | |
| 176 } | |
| 177 | |
| 178 int32_t PPB_VideoCapture_Impl::InternalEnumerateDevices( | |
| 179 PP_Resource* devices, | |
| 180 scoped_refptr<TrackedCallback> callback) { | |
| 181 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
| 182 if (!instance) | |
| 183 return PP_ERROR_FAILED; | |
| 184 | |
| 185 devices_ = devices; | |
| 186 enumerate_devices_callback_ = callback; | |
| 187 instance->delegate()->EnumerateDevices( | |
| 188 PP_DEVICETYPE_DEV_VIDEOCAPTURE, | |
| 189 base::Bind(&PPB_VideoCapture_Impl::EnumerateDevicesCallbackFunc, | |
| 190 AsWeakPtr())); | |
| 191 return PP_OK_COMPLETIONPENDING; | |
| 192 } | |
| 193 | |
| 194 int32_t PPB_VideoCapture_Impl::InternalOpen( | |
| 195 const std::string& device_id, | |
| 196 const PP_VideoCaptureDeviceInfo_Dev& requested_info, | |
| 197 uint32_t buffer_count, | |
| 198 scoped_refptr<TrackedCallback> callback) { | |
| 199 // It is able to complete synchronously if the default device is used. | |
| 200 bool sync_completion = device_id.empty(); | |
| 201 | |
| 202 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
| 203 if (!instance) | |
| 204 return PP_ERROR_FAILED; | |
| 205 | |
| 206 SetRequestedInfo(requested_info, buffer_count); | |
| 207 | |
| 208 DCHECK(!platform_video_capture_.get()); | |
| 209 platform_video_capture_ = | |
| 210 instance->delegate()->CreateVideoCapture(device_id, this); | |
| 211 | |
| 212 if (sync_completion) { | |
| 213 OnInitialized(platform_video_capture_.get(), true); | |
| 214 return PP_OK; | |
| 215 } else { | |
| 216 open_callback_ = callback; | |
| 217 return PP_OK_COMPLETIONPENDING; | |
| 218 } | |
| 219 } | |
| 220 | |
| 221 int32_t PPB_VideoCapture_Impl::InternalStartCapture() { | |
| 222 DCHECK(buffers_.empty()); | |
| 223 platform_video_capture_->StartCapture(this, capability_); | |
| 224 return PP_OK; | |
| 225 } | |
| 226 | |
| 227 int32_t PPB_VideoCapture_Impl::InternalReuseBuffer(uint32_t buffer) { | |
| 228 if (buffer >= buffers_.size() || !buffers_[buffer].in_use) | |
| 229 return PP_ERROR_BADARGUMENT; | |
| 230 buffers_[buffer].in_use = false; | |
| 231 return PP_OK; | |
| 232 } | |
| 233 | |
| 234 int32_t PPB_VideoCapture_Impl::InternalStopCapture() { | |
| 235 ReleaseBuffers(); | |
| 236 platform_video_capture_->StopCapture(this); | |
| 237 return PP_OK; | |
| 238 } | |
| 239 | |
| 240 void PPB_VideoCapture_Impl::InternalClose() { | |
| 241 StopCapture(); | |
| 242 DCHECK(buffers_.empty()); | |
| 243 | |
| 244 DetachPlatformVideoCapture(); | |
| 245 } | |
| 246 | |
| 247 int32_t PPB_VideoCapture_Impl::InternalStartCapture0_1( | |
| 248 const PP_VideoCaptureDeviceInfo_Dev& requested_info, | |
| 249 uint32_t buffer_count) { | |
| 250 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
| 251 if (!instance) { | |
| 252 SetStatus(PP_VIDEO_CAPTURE_STATUS_STOPPED, true); | |
| 253 return PP_ERROR_FAILED; | |
| 254 } | |
| 255 | |
| 256 DCHECK(buffers_.empty()); | |
| 257 | |
| 258 SetRequestedInfo(requested_info, buffer_count); | |
| 259 | |
| 260 DetachPlatformVideoCapture(); | |
| 261 platform_video_capture_ = | |
| 262 instance->delegate()->CreateVideoCapture("", this); | |
| 263 platform_video_capture_->StartCapture(this, capability_); | |
| 264 | |
| 265 return PP_OK; | |
| 266 } | |
| 267 | |
| 268 const PPB_VideoCapture_Impl::DeviceRefDataVector& | |
| 269 PPB_VideoCapture_Impl::InternalGetDeviceRefData() const { | |
| 270 return devices_data_; | |
| 271 } | |
| 272 | |
| 273 void PPB_VideoCapture_Impl::ReleaseBuffers() { | |
| 274 ::ppapi::ResourceTracker* tracker = PpapiGlobals::Get()->GetResourceTracker(); | |
| 275 for (size_t i = 0; i < buffers_.size(); ++i) { | |
| 276 buffers_[i].buffer->Unmap(); | |
| 277 tracker->ReleaseResource(buffers_[i].buffer->pp_resource()); | |
| 278 } | |
| 279 buffers_.clear(); | |
| 280 } | |
| 281 | |
| 282 void PPB_VideoCapture_Impl::SendStatus() { | |
| 283 ppp_videocapture_->OnStatus(pp_instance(), pp_resource(), status_); | |
| 284 } | |
| 285 | |
| 286 void PPB_VideoCapture_Impl::SetRequestedInfo( | |
| 287 const PP_VideoCaptureDeviceInfo_Dev& device_info, | |
| 288 uint32_t buffer_count) { | |
| 289 // Clamp the buffer count to between 1 and |kMaxBuffers|. | |
| 290 buffer_count_hint_ = std::min(std::max(buffer_count, 1U), kMaxBuffers); | |
| 291 | |
| 292 capability_.width = device_info.width; | |
| 293 capability_.height = device_info.height; | |
| 294 capability_.frame_rate = device_info.frames_per_second; | |
| 295 capability_.expected_capture_delay = 0; // Ignored. | |
| 296 capability_.color = media::VideoCaptureCapability::kI420; | |
| 297 capability_.interlaced = false; // Ignored. | |
| 298 } | |
| 299 | |
| 300 void PPB_VideoCapture_Impl::DetachPlatformVideoCapture() { | |
| 301 if (platform_video_capture_.get()) { | |
| 302 platform_video_capture_->DetachEventHandler(); | |
| 303 platform_video_capture_ = NULL; | |
| 304 } | |
| 305 } | |
| 306 | |
| 307 void PPB_VideoCapture_Impl::EnumerateDevicesCallbackFunc( | |
| 308 int request_id, | |
| 309 bool succeeded, | |
| 310 const DeviceRefDataVector& devices) { | |
| 311 devices_data_.clear(); | |
| 312 if (succeeded) | |
| 313 devices_data_ = devices; | |
| 314 | |
| 315 PluginInstance* instance = ResourceHelper::GetPluginInstance(this); | |
| 316 if (instance) | |
| 317 instance->delegate()->StopEnumerateDevices(request_id); | |
| 318 | |
| 319 OnEnumerateDevicesComplete(succeeded ? PP_OK : PP_ERROR_FAILED, devices); | |
| 320 } | |
| 321 | |
| 322 PPB_VideoCapture_Impl::BufferInfo::BufferInfo() | |
| 323 : in_use(false), | |
| 324 data(NULL), | |
| 325 buffer() { | |
| 326 } | |
| 327 | |
| 328 PPB_VideoCapture_Impl::BufferInfo::~BufferInfo() { | |
| 329 } | |
| 330 | |
| 331 } // namespace ppapi | |
| 332 } // namespace webkit | |
| OLD | NEW |