OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/video/capture/win/video_capture_device_win.h" | 5 #include "media/video/capture/win/video_capture_device_win.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <list> | 8 #include <list> |
9 | 9 |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
143 | 143 |
144 // Delete a media type structure that was allocated on the heap. | 144 // Delete a media type structure that was allocated on the heap. |
145 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx | 145 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx |
146 void DeleteMediaType(AM_MEDIA_TYPE* mt) { | 146 void DeleteMediaType(AM_MEDIA_TYPE* mt) { |
147 if (mt != NULL) { | 147 if (mt != NULL) { |
148 FreeMediaType(mt); | 148 FreeMediaType(mt); |
149 CoTaskMemFree(mt); | 149 CoTaskMemFree(mt); |
150 } | 150 } |
151 } | 151 } |
152 | 152 |
153 VideoPixelFormat TranslateMediaSubtypeToPixelFormat(GUID subtype) { | |
tommi (sloooow) - chröme
2014/02/17 12:58:29
sub_type? Also, pass by const&?
mcasas
2014/02/17 14:56:20
Done.
| |
154 // We can't use a GUID in a switch-case, this applies to |subtype|. | |
155 if (subtype == kMediaSubTypeI420) { | |
tommi (sloooow) - chröme
2014/02/17 12:58:29
I would like to see this data driven instead of mu
mcasas
2014/02/17 14:56:20
Done.
| |
156 return PIXEL_FORMAT_I420; | |
157 } else if (subtype == MEDIASUBTYPE_IYUV) { | |
158 // This is identical to PIXEL_FORMAT_I420. | |
159 return PIXEL_FORMAT_I420; | |
160 } else if (subtype == MEDIASUBTYPE_RGB24) { | |
161 return PIXEL_FORMAT_RGB24; | |
162 } else if (subtype == MEDIASUBTYPE_YUY2) { | |
163 return PIXEL_FORMAT_YUY2; | |
164 } else if (subtype == MEDIASUBTYPE_MJPG) { | |
165 return PIXEL_FORMAT_MJPEG; | |
166 } else if (subtype == MEDIASUBTYPE_UYVY) { | |
167 return PIXEL_FORMAT_UYVY; | |
168 } else if (subtype == MEDIASUBTYPE_ARGB32) { | |
169 return PIXEL_FORMAT_ARGB; | |
170 } else { | |
171 WCHAR guid_str[128]; | |
172 StringFromGUID2(subtype, guid_str, arraysize(guid_str)); | |
tommi (sloooow) - chröme
2014/02/17 12:58:29
(I know this is just moved code)
This should be in
mcasas
2014/02/17 14:56:20
Done.
| |
173 DVLOG(2) << "Device supports (also) an unknown media type " << guid_str; | |
174 return PIXEL_FORMAT_UNKNOWN; | |
175 } | |
176 } | |
177 | |
153 } // namespace | 178 } // namespace |
154 | 179 |
155 // static | 180 // static |
156 void VideoCaptureDevice::GetDeviceNames(Names* device_names) { | 181 void VideoCaptureDevice::GetDeviceNames(Names* device_names) { |
157 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); | 182 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
158 // Use Media Foundation for Metro processes (after and including Win8) and | 183 // Use Media Foundation for Metro processes (after and including Win8) and |
159 // DirectShow for any other versions, unless forced via flag. Media Foundation | 184 // DirectShow for any other versions, unless forced via flag. Media Foundation |
160 // can also be forced if appropriate flag is set and we are in Windows 7 or | 185 // can also be forced if appropriate flag is set and we are in Windows 7 or |
161 // 8 in non-Metro mode. | 186 // 8 in non-Metro mode. |
162 if ((base::win::IsMetroProcess() && | 187 if ((base::win::IsMetroProcess() && |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
247 | 272 |
248 // Find the description or friendly name. | 273 // Find the description or friendly name. |
249 ScopedVariant name; | 274 ScopedVariant name; |
250 hr = prop_bag->Read(L"Description", name.Receive(), 0); | 275 hr = prop_bag->Read(L"Description", name.Receive(), 0); |
251 if (FAILED(hr)) | 276 if (FAILED(hr)) |
252 hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0); | 277 hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0); |
253 | 278 |
254 if (SUCCEEDED(hr) && name.type() == VT_BSTR) { | 279 if (SUCCEEDED(hr) && name.type() == VT_BSTR) { |
255 // Ignore all VFW drivers and the special Google Camera Adapter. | 280 // Ignore all VFW drivers and the special Google Camera Adapter. |
256 // Google Camera Adapter is not a real DirectShow camera device. | 281 // Google Camera Adapter is not a real DirectShow camera device. |
257 // VFW is very old Video for Windows drivers that can not be used. | 282 // VFW are very old Video for Windows drivers that can not be used. |
258 const wchar_t* str_ptr = V_BSTR(&name); | 283 const wchar_t* str_ptr = V_BSTR(&name); |
259 const int name_length = arraysize(kGoogleCameraAdapter) - 1; | 284 const int name_length = arraysize(kGoogleCameraAdapter) - 1; |
260 | 285 |
261 if ((wcsstr(str_ptr, L"(VFW)") == NULL) && | 286 if ((wcsstr(str_ptr, L"(VFW)") == NULL) && |
262 lstrlenW(str_ptr) < name_length || | 287 lstrlenW(str_ptr) < name_length || |
263 (!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length, | 288 (!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length, |
264 kGoogleCameraAdapter)))) { | 289 kGoogleCameraAdapter)))) { |
265 std::string id; | 290 std::string id; |
266 std::string device_name(base::SysWideToUTF8(str_ptr)); | 291 std::string device_name(base::SysWideToUTF8(str_ptr)); |
267 name.Reset(); | 292 name.Reset(); |
268 hr = prop_bag->Read(L"DevicePath", name.Receive(), 0); | 293 hr = prop_bag->Read(L"DevicePath", name.Receive(), 0); |
269 if (FAILED(hr) || name.type() != VT_BSTR) { | 294 if (FAILED(hr) || name.type() != VT_BSTR) { |
270 id = device_name; | 295 id = device_name; |
271 } else { | 296 } else { |
272 DCHECK_EQ(name.type(), VT_BSTR); | 297 DCHECK_EQ(name.type(), VT_BSTR); |
273 id = base::SysWideToUTF8(V_BSTR(&name)); | 298 id = base::SysWideToUTF8(V_BSTR(&name)); |
274 } | 299 } |
275 | 300 |
276 device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW)); | 301 device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW)); |
277 } | 302 } |
278 } | 303 } |
279 moniker.Release(); | 304 moniker.Release(); |
280 } | 305 } |
281 } | 306 } |
282 | 307 |
283 // static | 308 // static |
284 void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device, | 309 void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device, |
285 VideoCaptureFormats* formats) { | 310 VideoCaptureFormats* formats) { |
286 NOTIMPLEMENTED(); | 311 DVLOG(1) << "GetDeviceSupportedFormats for " << device.name(); |
312 ScopedComPtr<ICreateDevEnum> dev_enum; | |
313 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, | |
314 CLSCTX_INPROC); | |
315 if (FAILED(hr)) | |
316 return; | |
317 | |
318 ScopedComPtr<IEnumMoniker> enum_moniker; | |
319 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, | |
320 enum_moniker.Receive(), 0); | |
321 // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera | |
322 // exists. Therefore the FAILED macro can't be used. | |
323 if (hr != S_OK) | |
324 return; | |
325 | |
326 // Walk the capture devices. No need to check for "google camera adapter". | |
tommi (sloooow) - chröme
2014/02/17 12:58:29
maybe elaborate on why not?
mcasas
2014/02/17 14:56:20
Done.
| |
327 ScopedComPtr<IMoniker> moniker; | |
328 int index = 0; | |
329 ScopedVariant device_id; | |
330 while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) { | |
331 ScopedComPtr<IPropertyBag> prop_bag; | |
332 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid()); | |
333 if (FAILED(hr)) { | |
334 moniker.Release(); | |
335 continue; | |
336 } | |
337 | |
338 device_id.Reset(); | |
339 hr = prop_bag->Read(L"DevicePath", device_id.Receive(), 0); | |
340 if (FAILED(hr)) { | |
341 DVLOG(1) << "Couldn't read a device's DevicePath."; | |
342 return; | |
343 } | |
344 if (device.id() == base::SysWideToUTF8(V_BSTR(&device_id))) | |
345 break; | |
346 moniker.Release(); | |
347 } | |
348 | |
349 if (moniker.get()) { | |
350 base::win::ScopedComPtr<IBaseFilter> capture_filter; | |
351 hr = GetDeviceFilter(device, capture_filter.Receive()); | |
352 if (!capture_filter) { | |
353 DVLOG(2) << "Failed to create capture filter."; | |
354 return; | |
355 } | |
356 | |
357 base::win::ScopedComPtr<IPin> output_capture_pin; | |
358 hr = GetPin(capture_filter, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, | |
359 output_capture_pin.Receive()); | |
360 if (!output_capture_pin) { | |
361 DVLOG(2) << "Failed to get capture output pin"; | |
362 return; | |
363 } | |
364 | |
365 ScopedComPtr<IAMStreamConfig> stream_config; | |
366 hr = output_capture_pin.QueryInterface(stream_config.Receive()); | |
367 if (FAILED(hr)) { | |
368 DVLOG(2) << "Failed to get IAMStreamConfig interface from " | |
369 "capture device"; | |
370 return; | |
371 } | |
372 | |
373 int count, size; | |
374 hr = stream_config->GetNumberOfCapabilities(&count, &size); | |
375 if (FAILED(hr)) { | |
376 DVLOG(2) << "Failed to GetNumberOfCapabilities"; | |
377 return; | |
378 } | |
379 | |
380 AM_MEDIA_TYPE* media_type = NULL; | |
381 VIDEO_STREAM_CONFIG_CAPS caps; | |
382 for (int i = 0; i < count; ++i) { | |
383 hr = stream_config->GetStreamCaps(i, &media_type, | |
384 reinterpret_cast<BYTE*>(&caps)); | |
385 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED() | |
386 // macros here since they'll trigger incorrectly. | |
387 if (hr != S_OK) { | |
388 DVLOG(2) << "Failed to GetStreamCaps"; | |
389 return; | |
390 } | |
391 | |
392 if (media_type->majortype == MEDIATYPE_Video && | |
393 media_type->formattype == FORMAT_VideoInfo) { | |
394 VIDEOINFOHEADER* h = | |
395 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); | |
396 VideoCaptureFormat format; | |
397 format.frame_size.SetSize(h->bmiHeader.biWidth, | |
398 h->bmiHeader.biHeight); | |
399 // Trust the frame rate from the VIDEOINFOHEADER. | |
400 format.frame_rate = (h->AvgTimePerFrame > 0) | |
401 ? static_cast<int>(kSecondsToReferenceTime / h->AvgTimePerFrame) | |
tommi (sloooow) - chröme
2014/02/17 12:58:29
nit: I think the convention is to have the operato
mcasas
2014/02/17 14:56:20
Done.
| |
402 : 0; | |
403 format.pixel_format = | |
404 TranslateMediaSubtypeToPixelFormat(media_type->subtype); | |
405 formats->push_back(format); | |
406 DVLOG(1) << device.name() << " resolution: " | |
407 << format.frame_size.ToString() << ", fps: " << format.frame_rate | |
408 << ", pixel format: " << format.pixel_format; | |
409 } | |
410 } | |
411 } | |
287 } | 412 } |
288 | 413 |
289 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) | 414 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) |
290 : device_name_(device_name), | 415 : device_name_(device_name), |
291 state_(kIdle) { | 416 state_(kIdle) { |
292 DetachFromThread(); | 417 DetachFromThread(); |
293 } | 418 } |
294 | 419 |
295 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { | 420 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { |
296 DCHECK(CalledOnValidThread()); | 421 DCHECK(CalledOnValidThread()); |
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
575 capability.supported_format.frame_rate = | 700 capability.supported_format.frame_rate = |
576 (time_per_frame > 0) | 701 (time_per_frame > 0) |
577 ? static_cast<int>(kSecondsToReferenceTime / time_per_frame) | 702 ? static_cast<int>(kSecondsToReferenceTime / time_per_frame) |
578 : 0; | 703 : 0; |
579 | 704 |
580 // DirectShow works at the moment only on integer frame_rate but the | 705 // DirectShow works at the moment only on integer frame_rate but the |
581 // best capability matching class works on rational frame rates. | 706 // best capability matching class works on rational frame rates. |
582 capability.frame_rate_numerator = capability.supported_format.frame_rate; | 707 capability.frame_rate_numerator = capability.supported_format.frame_rate; |
583 capability.frame_rate_denominator = 1; | 708 capability.frame_rate_denominator = 1; |
584 | 709 |
585 // We can't switch MEDIATYPE :~(. | 710 capability.supported_format.pixel_format = |
586 if (media_type->subtype == kMediaSubTypeI420) { | 711 TranslateMediaSubtypeToPixelFormat(media_type->subtype); |
587 capability.supported_format.pixel_format = PIXEL_FORMAT_I420; | |
588 } else if (media_type->subtype == MEDIASUBTYPE_IYUV) { | |
589 // This is identical to PIXEL_FORMAT_I420. | |
590 capability.supported_format.pixel_format = PIXEL_FORMAT_I420; | |
591 } else if (media_type->subtype == MEDIASUBTYPE_RGB24) { | |
592 capability.supported_format.pixel_format = PIXEL_FORMAT_RGB24; | |
593 } else if (media_type->subtype == MEDIASUBTYPE_YUY2) { | |
594 capability.supported_format.pixel_format = PIXEL_FORMAT_YUY2; | |
595 } else if (media_type->subtype == MEDIASUBTYPE_MJPG) { | |
596 capability.supported_format.pixel_format = PIXEL_FORMAT_MJPEG; | |
597 } else if (media_type->subtype == MEDIASUBTYPE_UYVY) { | |
598 capability.supported_format.pixel_format = PIXEL_FORMAT_UYVY; | |
599 } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) { | |
600 capability.supported_format.pixel_format = PIXEL_FORMAT_ARGB; | |
601 } else { | |
602 WCHAR guid_str[128]; | |
603 StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str)); | |
604 DVLOG(2) << "Device supports (also) an unknown media type " << guid_str; | |
605 continue; | |
606 } | |
607 capabilities_.Add(capability); | 712 capabilities_.Add(capability); |
608 } | 713 } |
609 DeleteMediaType(media_type); | 714 DeleteMediaType(media_type); |
610 media_type = NULL; | 715 media_type = NULL; |
611 } | 716 } |
612 | 717 |
613 return !capabilities_.empty(); | 718 return !capabilities_.empty(); |
614 } | 719 } |
615 | 720 |
616 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { | 721 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { |
617 DCHECK(CalledOnValidThread()); | 722 DCHECK(CalledOnValidThread()); |
618 DVLOG(1) << reason; | 723 DVLOG(1) << reason; |
619 state_ = kError; | 724 state_ = kError; |
620 client_->OnError(reason); | 725 client_->OnError(reason); |
621 } | 726 } |
622 } // namespace media | 727 } // namespace media |
OLD | NEW |