OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2014 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 "media/video/capture/win/video_capture_device_pxc_win.h" |
| 6 |
| 7 #include "base/bind.h" |
| 8 #include "base/logging.h" |
| 9 #include "base/strings/string_number_conversions.h" |
| 10 #include "base/strings/string_split.h" |
| 11 #include "base/strings/string_util.h" |
| 12 #include "base/strings/stringprintf.h" |
| 13 #include "base/strings/utf_string_conversions.h" |
| 14 |
| 15 namespace media { |
| 16 namespace { |
| 17 |
| 18 // Release a PXCSession will call CoUninitiliaze which causes calling thread |
| 19 // (Audio Thread) to fail on COM API calling. Hold a global PXCSession to avoid |
| 20 // this. |
| 21 static PXCSmartPtr<PXCSession> g_session; |
| 22 |
| 23 const char kDepthCameraName[] = "depth"; |
| 24 const char kDepthCameraID[] = "PXCCaptureDepthCamera"; |
| 25 |
| 26 const char CHROMA_ZERO_LEVEL = 127; |
| 27 |
| 28 bool GetFirstDepthDeviceAndStream( |
| 29 PXCCapture::Device** depth_device, |
| 30 PXCCapture::VideoStream** depth_stream) { |
| 31 pxcStatus status; |
| 32 PXCSession::ImplDesc video_capture_desc; |
| 33 memset(&video_capture_desc, 0, sizeof(video_capture_desc)); |
| 34 video_capture_desc.group = PXCSession::IMPL_GROUP_SENSOR; |
| 35 video_capture_desc.subgroup = PXCSession::IMPL_SUBGROUP_VIDEO_CAPTURE; |
| 36 for (int module_index = 0; ; module_index++) { |
| 37 PXCSession::ImplDesc desc; |
| 38 status = g_session->QueryImpl(&video_capture_desc, module_index, &desc); |
| 39 if (status < PXC_STATUS_NO_ERROR) { |
| 40 DVLOG(1) << "No more PXC modules."; |
| 41 break; |
| 42 } |
| 43 |
| 44 PXCSmartPtr<PXCCapture> capture; |
| 45 status = g_session->CreateImpl<PXCCapture>(&desc, &capture); |
| 46 if (status < PXC_STATUS_NO_ERROR) { |
| 47 DVLOG(2) << "Failed to create PXCCapture instance."; |
| 48 continue; |
| 49 } |
| 50 |
| 51 for (int device_index = 0; ; device_index++) { |
| 52 PXCCapture::DeviceInfo device_info; |
| 53 status = capture->QueryDevice(device_index, &device_info); |
| 54 if (status < PXC_STATUS_NO_ERROR) { |
| 55 DVLOG(1) << "No more PXC capture devices."; |
| 56 break; |
| 57 } |
| 58 |
| 59 PXCSmartPtr<PXCCapture::Device> device; |
| 60 status = capture->CreateDevice(device_index, &device); |
| 61 if (status < PXC_STATUS_NO_ERROR) { |
| 62 DVLOG(2) << "Failed to create PXCCapture::Device instance."; |
| 63 continue; |
| 64 } |
| 65 |
| 66 for (int stream_index = 0; ; stream_index++) { |
| 67 PXCCapture::Device::StreamInfo stream_info; |
| 68 status = device->QueryStream(stream_index, &stream_info); |
| 69 if (status < PXC_STATUS_NO_ERROR) { |
| 70 DVLOG(1) << "No more PXC streams."; |
| 71 break; |
| 72 } |
| 73 |
| 74 if (stream_info.cuid == PXCCapture::VideoStream::CUID && |
| 75 stream_info.imageType == PXCImage::IMAGE_TYPE_DEPTH) { |
| 76 PXCSmartPtr<PXCCapture::VideoStream> stream; |
| 77 status = |
| 78 device->CreateStream<PXCCapture::VideoStream>( |
| 79 stream_index, &stream); |
| 80 if (status < PXC_STATUS_NO_ERROR) { |
| 81 DVLOG(2) << "Failed to create PXCCapture::VideoStream instance."; |
| 82 break; |
| 83 } |
| 84 |
| 85 *depth_device = device.ReleasePtr(); |
| 86 *depth_stream = stream.ReleasePtr(); |
| 87 return true; |
| 88 } |
| 89 } // Enumerate streams. |
| 90 } // Enumerate devices. |
| 91 } // Enumerate modules. |
| 92 return false; |
| 93 } |
| 94 |
| 95 } // namespace |
| 96 |
| 97 // static |
| 98 bool VideoCaptureDevicePXCWin::PlatformSupported() { |
| 99 if (g_session.IsValid()) |
| 100 return true; |
| 101 |
| 102 pxcStatus status = PXCSession_Create(&g_session); |
| 103 if (status < PXC_STATUS_NO_ERROR) { |
| 104 DVLOG(2) << "Failed to create a PXC Session."; |
| 105 return false; |
| 106 } |
| 107 return true; |
| 108 } |
| 109 |
| 110 // static |
| 111 void VideoCaptureDevicePXCWin::AppendDeviceNames(Names* device_names) { |
| 112 if (!VideoCaptureDevicePXCWin::PlatformSupported()) |
| 113 return; |
| 114 |
| 115 PXCSmartPtr<PXCCapture::Device> device; |
| 116 PXCSmartPtr<PXCCapture::VideoStream> stream; |
| 117 if (!GetFirstDepthDeviceAndStream(&device, &stream)) { |
| 118 DVLOG(2) << "Failed to get PXC depth device and stream."; |
| 119 return; |
| 120 } |
| 121 |
| 122 Name name(kDepthCameraName, kDepthCameraID, Name::PXC_CAPTURE); |
| 123 DVLOG(1) << "PXC depth video capture device, " |
| 124 << name.name() << " : " << name.id(); |
| 125 device_names->push_back(name); |
| 126 } |
| 127 |
| 128 //static |
| 129 bool VideoCaptureDevicePXCWin::IsDepthDevice(const Name& device_name) { |
| 130 return device_name.name() == kDepthCameraName; |
| 131 } |
| 132 |
| 133 //static |
| 134 void VideoCaptureDevicePXCWin::GetDeviceSupportedFormats( |
| 135 const Name& device_name, VideoCaptureFormats* formats) { |
| 136 PXCSmartPtr<PXCCapture::Device> device; |
| 137 PXCSmartPtr<PXCCapture::VideoStream> stream; |
| 138 if (!GetFirstDepthDeviceAndStream(&device, &stream)) { |
| 139 DVLOG(2) << "Failed to get PXC depth device and stream."; |
| 140 return; |
| 141 } |
| 142 |
| 143 pxcStatus status; |
| 144 for (int porfile_index = 0; ; porfile_index++) { |
| 145 PXCCapture::VideoStream::ProfileInfo video_profile; |
| 146 status = stream->QueryProfile(porfile_index, &video_profile); |
| 147 if (status < PXC_STATUS_NO_ERROR) { |
| 148 DVLOG(1) << "No more PXC stream profiles."; |
| 149 break; |
| 150 } |
| 151 VideoCaptureFormat format; |
| 152 format.frame_size.SetSize(video_profile.imageInfo.width, |
| 153 video_profile.imageInfo.height); |
| 154 uint32 frame_rate_min = |
| 155 video_profile.frameRateMin.denominator ? |
| 156 video_profile.frameRateMin.numerator / |
| 157 video_profile.frameRateMin.denominator |
| 158 : 0; |
| 159 uint32 frame_rate_max = |
| 160 video_profile.frameRateMax.denominator ? |
| 161 video_profile.frameRateMax.numerator / |
| 162 video_profile.frameRateMax.denominator |
| 163 : 0; |
| 164 format.frame_rate = (frame_rate_min + frame_rate_max) / 2; |
| 165 format.pixel_format = PIXEL_FORMAT_YV12; |
| 166 formats->push_back(format); |
| 167 DVLOG(1) << device_name.name() << " resolution: " |
| 168 << format.frame_size.ToString() << ", fps: " << format.frame_rate |
| 169 << ", pixel format: " << format.pixel_format; |
| 170 } |
| 171 } |
| 172 |
| 173 VideoCaptureDevicePXCWin::VideoCaptureDevicePXCWin(const Name& device_name) |
| 174 : state_(kIdle), |
| 175 device_name_(device_name), |
| 176 pxc_capture_thread_("PxcCaptureThread") { |
| 177 } |
| 178 |
| 179 VideoCaptureDevicePXCWin::~VideoCaptureDevicePXCWin() { |
| 180 } |
| 181 |
| 182 bool VideoCaptureDevicePXCWin::Init() { |
| 183 return true; |
| 184 } |
| 185 |
| 186 void VideoCaptureDevicePXCWin::AllocateAndStart( |
| 187 const VideoCaptureParams& params, |
| 188 scoped_ptr<Client> client) { |
| 189 if (pxc_capture_thread_.IsRunning()) { |
| 190 return; // Wrong state. |
| 191 } |
| 192 pxc_capture_thread_.Start(); |
| 193 pxc_capture_thread_.message_loop()->PostTask( |
| 194 FROM_HERE, |
| 195 base::Bind(&VideoCaptureDevicePXCWin::OnAllocateAndStart, |
| 196 base::Unretained(this), |
| 197 params.requested_format.frame_size.width(), |
| 198 params.requested_format.frame_size.height(), |
| 199 params.requested_format.frame_rate, |
| 200 base::Passed(&client))); |
| 201 } |
| 202 |
| 203 void VideoCaptureDevicePXCWin::StopAndDeAllocate() { |
| 204 if (!pxc_capture_thread_.IsRunning()) { |
| 205 return; // Wrong state. |
| 206 } |
| 207 pxc_capture_thread_.message_loop()->PostTask( |
| 208 FROM_HERE, |
| 209 base::Bind(&VideoCaptureDevicePXCWin::OnStopAndDeAllocate, |
| 210 base::Unretained(this))); |
| 211 pxc_capture_thread_.Stop(); |
| 212 } |
| 213 |
| 214 void VideoCaptureDevicePXCWin::OnAllocateAndStart( |
| 215 int width, int height, int frame_rate, scoped_ptr<Client> client) { |
| 216 DCHECK_EQ(pxc_capture_thread_.message_loop(), base::MessageLoop::current()); |
| 217 |
| 218 client_ = client.Pass(); |
| 219 |
| 220 PXCSmartPtr<PXCCapture::Device> device; |
| 221 PXCSmartPtr<PXCCapture::VideoStream> stream; |
| 222 |
| 223 if (!GetFirstDepthDeviceAndStream(&device, &stream)) { |
| 224 SetErrorState("Failed to get PXC depth device and stream."); |
| 225 return; |
| 226 } |
| 227 |
| 228 pxcStatus status; |
| 229 // Try to find the best profile. |
| 230 PXCCapture::VideoStream::ProfileInfo best_profile; |
| 231 bool best_profile_found = false; |
| 232 uint32 best = 0xFFFFFFFF; |
| 233 for (int porfile_index = 0; ; porfile_index++) { |
| 234 PXCCapture::VideoStream::ProfileInfo video_profile; |
| 235 status = stream->QueryProfile(porfile_index, &video_profile); |
| 236 if (status < PXC_STATUS_NO_ERROR) { |
| 237 // No more profiles. |
| 238 break; |
| 239 } |
| 240 |
| 241 uint32 frame_rate_min = |
| 242 video_profile.frameRateMin.denominator ? |
| 243 video_profile.frameRateMin.numerator / |
| 244 video_profile.frameRateMin.denominator |
| 245 : 0; |
| 246 uint32 frame_rate_max = |
| 247 video_profile.frameRateMax.denominator ? |
| 248 video_profile.frameRateMax.numerator / |
| 249 video_profile.frameRateMax.denominator |
| 250 : 0; |
| 251 uint32 current = |
| 252 abs(static_cast<int>(width - video_profile.imageInfo.width)) + |
| 253 abs(static_cast<int>(height - video_profile.imageInfo.height)) + |
| 254 abs(static_cast<int>(frame_rate - frame_rate_min)) + |
| 255 abs(static_cast<int>(frame_rate - frame_rate_max)); |
| 256 |
| 257 if (current < best) { |
| 258 best = current; |
| 259 best_profile_found = true; |
| 260 best_profile = video_profile; |
| 261 } |
| 262 } // Enumerate profiles. |
| 263 |
| 264 if (!best_profile_found) { |
| 265 SetErrorState("Cannot find appropriate stream."); |
| 266 return; |
| 267 } |
| 268 |
| 269 status = stream->SetProfile(&best_profile); |
| 270 if (status < PXC_STATUS_NO_ERROR) { |
| 271 SetErrorState("Failed to set stream profile."); |
| 272 return; |
| 273 } |
| 274 stream_ = stream.ReleasePtr(); |
| 275 |
| 276 device->QueryProperty( |
| 277 PXCCapture::Device::PROPERTY_DEPTH_SATURATION_VALUE, |
| 278 &depth_saturation_value_); |
| 279 device->QueryProperty( |
| 280 PXCCapture::Device::PROPERTY_DEPTH_LOW_CONFIDENCE_VALUE, |
| 281 &depth_low_confidence_value_); |
| 282 device->QueryProperty( |
| 283 PXCCapture::Device::PROPERTY_DEPTH_UNIT, |
| 284 &depth_unit_in_micrometers_); |
| 285 device->QueryPropertyAsRange( |
| 286 PXCCapture::Device::PROPERTY_DEPTH_SENSOR_RANGE, |
| 287 &depth_range_in_millimeters_); |
| 288 DVLOG(1) << "Depth Device Properties: " |
| 289 << "\nPROPERTY_DEPTH_SATURATION_VALUE: " |
| 290 << depth_saturation_value_ |
| 291 << "\nPROPERTY_DEPTH_LOW_CONFIDENCE_VALUE: " |
| 292 << depth_low_confidence_value_ |
| 293 << "\nPROPERTY_DEPTH_UNIT: " |
| 294 << depth_unit_in_micrometers_ |
| 295 << "\nPROPERTY_DEPTH_SENSOR_RANGE: " |
| 296 << depth_range_in_millimeters_.min << ":" |
| 297 << depth_range_in_millimeters_.max; |
| 298 |
| 299 DVLOG(1) << "Allocate PXC Stream: " |
| 300 << " width = " << best_profile.imageInfo.width |
| 301 << " height = " << best_profile.imageInfo.height |
| 302 << " frame_rate = " << frame_rate |
| 303 << " color = " << PIXEL_FORMAT_YV12; |
| 304 |
| 305 // Store our current width and height. |
| 306 capture_format_.frame_size.SetSize(best_profile.imageInfo.width, |
| 307 best_profile.imageInfo.height); |
| 308 capture_format_.frame_rate = frame_rate; |
| 309 capture_format_.pixel_format = PIXEL_FORMAT_YV12; |
| 310 |
| 311 yv12_image_.reset( |
| 312 new uint8[capture_format_.frame_size.width() * |
| 313 capture_format_.frame_size.height() * |
| 314 3 / 2]); |
| 315 |
| 316 // Start capturing. |
| 317 state_ = kCapturing; |
| 318 pxc_capture_thread_.message_loop()->PostTask( |
| 319 FROM_HERE, |
| 320 base::Bind(&VideoCaptureDevicePXCWin::OnCaptureTask, |
| 321 base::Unretained(this))); |
| 322 return; |
| 323 } |
| 324 |
| 325 void VideoCaptureDevicePXCWin::OnStopAndDeAllocate() { |
| 326 DCHECK_EQ(pxc_capture_thread_.message_loop(), base::MessageLoop::current()); |
| 327 |
| 328 state_ = kIdle; |
| 329 stream_.ReleaseRef(); |
| 330 client_.reset(); |
| 331 yv12_image_.reset(); |
| 332 } |
| 333 |
| 334 void VideoCaptureDevicePXCWin::OnCaptureTask() { |
| 335 DCHECK_EQ(pxc_capture_thread_.message_loop(), base::MessageLoop::current()); |
| 336 |
| 337 if (state_ != kCapturing || !stream_.IsValid()) |
| 338 return; |
| 339 |
| 340 PXCSmartSP sp; |
| 341 PXCSmartPtr<PXCImage> image; |
| 342 pxcStatus status = stream_->ReadStreamAsync(&image, &sp); |
| 343 if (status < PXC_STATUS_NO_ERROR) { |
| 344 SetErrorState("Failed to read stream."); |
| 345 return; |
| 346 } |
| 347 |
| 348 status = sp->Synchronize(); |
| 349 if (status < PXC_STATUS_NO_ERROR) { |
| 350 SetErrorState("Read synchronization EOF."); |
| 351 return; |
| 352 } |
| 353 |
| 354 PXCImage::ImageInfo info; |
| 355 status = image->QueryInfo(&info); |
| 356 if (status < PXC_STATUS_NO_ERROR) { |
| 357 SetErrorState("Failed to get image info."); |
| 358 return; |
| 359 } |
| 360 |
| 361 PXCImage::ImageData data; |
| 362 status = image->AcquireAccess( |
| 363 PXCImage::ACCESS_READ, PXCImage::COLOR_FORMAT_DEPTH, &data); |
| 364 if (status < PXC_STATUS_NO_ERROR) { |
| 365 SetErrorState("Failed to acquire access to image data."); |
| 366 return; |
| 367 } |
| 368 |
| 369 DCHECK_EQ(data.type, PXCImage::SURFACE_TYPE_SYSTEM_MEMORY); |
| 370 |
| 371 unsigned int yv12_data_length = info.width * info.height * 3 / 2; |
| 372 uint8* yv12_data = yv12_image_.get(); |
| 373 memset(yv12_data, CHROMA_ZERO_LEVEL, sizeof(uint8) * yv12_data_length); |
| 374 int16* depth_data = reinterpret_cast<int16*>(data.planes[0]); |
| 375 for (unsigned int i = 0; i < info.width * info.height; i++) { |
| 376 if (depth_data[i] == depth_saturation_value_ || |
| 377 depth_data[i] == depth_low_confidence_value_) { |
| 378 // Discard the invalid depth value. |
| 379 yv12_data[i] = 0xFF; |
| 380 continue; |
| 381 } |
| 382 |
| 383 // The depth value is 16bits integer value. Throw away the lowest 4 |
| 384 // resolution bits and truncate to 8 bits. |
| 385 // This aligns with VideoCaptureTango.java implemenation for Tango's depth |
| 386 // camera. In this case Chroma components are unused. No need to write them |
| 387 // explicitly since they're filled to 128 on creation. |
| 388 yv12_data[i] = ((depth_data[i] & 0xFFF0) >> 4) & 0xFF;; |
| 389 } |
| 390 |
| 391 client_->OnIncomingCapturedData( |
| 392 yv12_data, yv12_data_length, capture_format_, 0, base::TimeTicks::Now()); |
| 393 |
| 394 image->ReleaseAccess(&data); |
| 395 |
| 396 pxc_capture_thread_.message_loop()->PostTask( |
| 397 FROM_HERE, |
| 398 base::Bind(&VideoCaptureDevicePXCWin::OnCaptureTask, |
| 399 base::Unretained(this))); |
| 400 } |
| 401 |
| 402 void VideoCaptureDevicePXCWin::SetErrorState(const std::string& reason) { |
| 403 DVLOG(1) << reason; |
| 404 state_ = kError; |
| 405 client_->OnError(reason); |
| 406 } |
| 407 |
| 408 } // namespace media |
OLD | NEW |