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/string_util.h" | 10 #include "base/string_util.h" |
11 #include "base/sys_string_conversions.h" | 11 #include "base/sys_string_conversions.h" |
12 #include "base/win/scoped_variant.h" | 12 #include "base/win/scoped_variant.h" |
13 #include "base/win/windows_version.h" | |
14 #include "media/video/capture/win/video_capture_device_mf_win.h" | |
15 | 13 |
16 using base::win::ScopedComPtr; | 14 using base::win::ScopedComPtr; |
17 using base::win::ScopedVariant; | 15 using base::win::ScopedVariant; |
18 | 16 |
19 namespace { | 17 namespace { |
20 | 18 |
21 // Finds and creates a DirectShow Video Capture filter matching the device_name. | 19 // Finds and creates a DirectShow Video Capture filter matching the device_name. |
22 HRESULT GetDeviceFilter(const media::VideoCaptureDevice::Name& device_name, | 20 HRESULT GetDeviceFilter(const media::VideoCaptureDevice::Name& device_name, |
23 IBaseFilter** filter) { | 21 IBaseFilter** filter) { |
24 DCHECK(filter); | 22 DCHECK(filter); |
(...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
137 | 135 |
138 // Delete a media type structure that was allocated on the heap. | 136 // Delete a media type structure that was allocated on the heap. |
139 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx | 137 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx |
140 void DeleteMediaType(AM_MEDIA_TYPE* mt) { | 138 void DeleteMediaType(AM_MEDIA_TYPE* mt) { |
141 if (mt != NULL) { | 139 if (mt != NULL) { |
142 FreeMediaType(mt); | 140 FreeMediaType(mt); |
143 CoTaskMemFree(mt); | 141 CoTaskMemFree(mt); |
144 } | 142 } |
145 } | 143 } |
146 | 144 |
| 145 // Help structure used for comparing video capture capabilities. |
| 146 struct ResolutionDiff { |
| 147 int capability_index; |
| 148 int diff_height; |
| 149 int diff_width; |
| 150 int diff_frame_rate; |
| 151 media::VideoCaptureCapability::Format color; |
| 152 }; |
| 153 |
| 154 bool CompareHeight(const ResolutionDiff& item1, const ResolutionDiff& item2) { |
| 155 return abs(item1.diff_height) < abs(item2.diff_height); |
| 156 } |
| 157 |
| 158 bool CompareWidth(const ResolutionDiff& item1, const ResolutionDiff& item2) { |
| 159 return abs(item1.diff_width) < abs(item2.diff_width); |
| 160 } |
| 161 |
| 162 bool CompareFrameRate(const ResolutionDiff& item1, |
| 163 const ResolutionDiff& item2) { |
| 164 return abs(item1.diff_frame_rate) < abs(item2.diff_frame_rate); |
| 165 } |
| 166 |
| 167 bool CompareColor(const ResolutionDiff& item1, const ResolutionDiff& item2) { |
| 168 return (item1.color < item2.color); |
| 169 } |
| 170 |
147 } // namespace | 171 } // namespace |
148 | 172 |
149 namespace media { | 173 namespace media { |
150 | 174 |
151 // static | 175 // Name of a fake DirectShow filter that exist on computers with |
| 176 // GTalk installed. |
| 177 static const char kGoogleCameraAdapter[] = "google camera adapter"; |
| 178 |
| 179 // Gets the names of all video capture devices connected to this computer. |
152 void VideoCaptureDevice::GetDeviceNames(Names* device_names) { | 180 void VideoCaptureDevice::GetDeviceNames(Names* device_names) { |
153 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
154 VideoCaptureDeviceMFWin::GetDeviceNames(device_names); | |
155 } else { | |
156 VideoCaptureDeviceWin::GetDeviceNames(device_names); | |
157 } | |
158 } | |
159 | |
160 // static | |
161 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) { | |
162 VideoCaptureDevice* ret = NULL; | |
163 if (base::win::GetVersion() >= base::win::VERSION_VISTA) { | |
164 scoped_ptr<VideoCaptureDeviceMFWin> device( | |
165 new VideoCaptureDeviceMFWin(device_name)); | |
166 if (device->Init()) | |
167 ret = device.release(); | |
168 } else { | |
169 scoped_ptr<VideoCaptureDeviceWin> device( | |
170 new VideoCaptureDeviceWin(device_name)); | |
171 if (device->Init()) | |
172 ret = device.release(); | |
173 } | |
174 | |
175 return ret; | |
176 } | |
177 | |
178 // static | |
179 void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) { | |
180 DCHECK(device_names); | 181 DCHECK(device_names); |
181 | 182 |
182 ScopedComPtr<ICreateDevEnum> dev_enum; | 183 ScopedComPtr<ICreateDevEnum> dev_enum; |
183 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, | 184 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, |
184 CLSCTX_INPROC); | 185 CLSCTX_INPROC); |
185 if (FAILED(hr)) | 186 if (FAILED(hr)) |
186 return; | 187 return; |
187 | 188 |
188 ScopedComPtr<IEnumMoniker> enum_moniker; | 189 ScopedComPtr<IEnumMoniker> enum_moniker; |
189 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, | 190 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, |
190 enum_moniker.Receive(), 0); | 191 enum_moniker.Receive(), 0); |
191 // CreateClassEnumerator returns S_FALSE on some Windows OS | 192 // CreateClassEnumerator returns S_FALSE on some Windows OS |
192 // when no camera exist. Therefore the FAILED macro can't be used. | 193 // when no camera exist. Therefore the FAILED macro can't be used. |
193 if (hr != S_OK) | 194 if (hr != S_OK) |
194 return; | 195 return; |
195 | 196 |
196 device_names->clear(); | 197 device_names->clear(); |
197 | 198 |
198 // Name of a fake DirectShow filter that exist on computers with | |
199 // GTalk installed. | |
200 static const char kGoogleCameraAdapter[] = "google camera adapter"; | |
201 | |
202 // Enumerate all video capture devices. | 199 // Enumerate all video capture devices. |
203 ScopedComPtr<IMoniker> moniker; | 200 ScopedComPtr<IMoniker> moniker; |
204 int index = 0; | 201 int index = 0; |
205 while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) { | 202 while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) { |
206 Name device; | 203 Name device; |
207 ScopedComPtr<IPropertyBag> prop_bag; | 204 ScopedComPtr<IPropertyBag> prop_bag; |
208 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid()); | 205 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid()); |
209 if (FAILED(hr)) { | 206 if (FAILED(hr)) { |
210 moniker.Release(); | 207 moniker.Release(); |
211 continue; | 208 continue; |
(...skipping 25 matching lines...) Expand all Loading... |
237 device.unique_id = base::SysWideToUTF8(V_BSTR(&name)); | 234 device.unique_id = base::SysWideToUTF8(V_BSTR(&name)); |
238 } | 235 } |
239 | 236 |
240 device_names->push_back(device); | 237 device_names->push_back(device); |
241 } | 238 } |
242 } | 239 } |
243 moniker.Release(); | 240 moniker.Release(); |
244 } | 241 } |
245 } | 242 } |
246 | 243 |
| 244 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) { |
| 245 VideoCaptureDeviceWin* self = new VideoCaptureDeviceWin(device_name); |
| 246 if (self && self->Init()) |
| 247 return self; |
| 248 |
| 249 delete self; |
| 250 return NULL; |
| 251 } |
| 252 |
247 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) | 253 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) |
248 : device_name_(device_name), | 254 : device_name_(device_name), |
249 state_(kIdle), | 255 state_(kIdle), |
250 observer_(NULL) { | 256 observer_(NULL) { |
251 DetachFromThread(); | 257 DetachFromThread(); |
252 } | 258 } |
253 | 259 |
254 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { | 260 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { |
255 DCHECK(CalledOnValidThread()); | 261 DCHECK(CalledOnValidThread()); |
256 if (media_control_) | 262 if (media_control_) |
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
325 void VideoCaptureDeviceWin::Allocate( | 331 void VideoCaptureDeviceWin::Allocate( |
326 int width, | 332 int width, |
327 int height, | 333 int height, |
328 int frame_rate, | 334 int frame_rate, |
329 VideoCaptureDevice::EventHandler* observer) { | 335 VideoCaptureDevice::EventHandler* observer) { |
330 DCHECK(CalledOnValidThread()); | 336 DCHECK(CalledOnValidThread()); |
331 if (state_ != kIdle) | 337 if (state_ != kIdle) |
332 return; | 338 return; |
333 | 339 |
334 observer_ = observer; | 340 observer_ = observer; |
335 | |
336 // Get the camera capability that best match the requested resolution. | 341 // Get the camera capability that best match the requested resolution. |
337 const VideoCaptureCapabilityWin& found_capability = | 342 const int capability_index = GetBestMatchedCapability(width, height, |
338 capabilities_.GetBestMatchedCapability(width, height, frame_rate); | 343 frame_rate); |
339 VideoCaptureCapability capability = found_capability; | 344 VideoCaptureCapability capability = capabilities_[capability_index]; |
340 | 345 |
341 // Reduce the frame rate if the requested frame rate is lower | 346 // Reduce the frame rate if the requested frame rate is lower |
342 // than the capability. | 347 // than the capability. |
343 if (capability.frame_rate > frame_rate) | 348 if (capability.frame_rate > frame_rate) |
344 capability.frame_rate = frame_rate; | 349 capability.frame_rate = frame_rate; |
345 | 350 |
346 AM_MEDIA_TYPE* pmt = NULL; | 351 AM_MEDIA_TYPE* pmt = NULL; |
347 VIDEO_STREAM_CONFIG_CAPS caps; | 352 VIDEO_STREAM_CONFIG_CAPS caps; |
348 | 353 |
349 ScopedComPtr<IAMStreamConfig> stream_config; | 354 ScopedComPtr<IAMStreamConfig> stream_config; |
350 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); | 355 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
351 if (FAILED(hr)) { | 356 if (FAILED(hr)) { |
352 SetErrorState("Can't get the Capture format settings"); | 357 SetErrorState("Can't get the Capture format settings"); |
353 return; | 358 return; |
354 } | 359 } |
355 | 360 |
356 // Get the windows capability from the capture device. | 361 // Get the windows capability from the capture device. |
357 hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt, | 362 hr = stream_config->GetStreamCaps(capability_index, &pmt, |
358 reinterpret_cast<BYTE*>(&caps)); | 363 reinterpret_cast<BYTE*>(&caps)); |
359 if (SUCCEEDED(hr)) { | 364 if (SUCCEEDED(hr)) { |
360 if (pmt->formattype == FORMAT_VideoInfo) { | 365 if (pmt->formattype == FORMAT_VideoInfo) { |
361 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); | 366 VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); |
362 if (capability.frame_rate > 0) | 367 if (capability.frame_rate > 0) |
363 h->AvgTimePerFrame = kSecondsToReferenceTime / capability.frame_rate; | 368 h->AvgTimePerFrame = kSecondsToReferenceTime / capability.frame_rate; |
364 } | 369 } |
365 // Set the sink filter to request this capability. | 370 // Set the sink filter to request this capability. |
366 sink_filter_->SetRequestedMediaCapability(capability); | 371 sink_filter_->SetRequestedMediaCapability(capability); |
367 // Order the capture device to use this capability. | 372 // Order the capture device to use this capability. |
(...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
514 for (int i = 0; i < count; ++i) { | 519 for (int i = 0; i < count; ++i) { |
515 hr = stream_config->GetStreamCaps(i, &media_type, | 520 hr = stream_config->GetStreamCaps(i, &media_type, |
516 reinterpret_cast<BYTE*>(&caps)); | 521 reinterpret_cast<BYTE*>(&caps)); |
517 if (FAILED(hr)) { | 522 if (FAILED(hr)) { |
518 DVLOG(2) << "Failed to GetStreamCaps"; | 523 DVLOG(2) << "Failed to GetStreamCaps"; |
519 return false; | 524 return false; |
520 } | 525 } |
521 | 526 |
522 if (media_type->majortype == MEDIATYPE_Video && | 527 if (media_type->majortype == MEDIATYPE_Video && |
523 media_type->formattype == FORMAT_VideoInfo) { | 528 media_type->formattype == FORMAT_VideoInfo) { |
524 VideoCaptureCapabilityWin capability(i); | 529 VideoCaptureCapability capability; |
525 REFERENCE_TIME time_per_frame = 0; | 530 REFERENCE_TIME time_per_frame = 0; |
526 | 531 |
527 VIDEOINFOHEADER* h = | 532 VIDEOINFOHEADER* h = |
528 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); | 533 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); |
529 capability.width = h->bmiHeader.biWidth; | 534 capability.width = h->bmiHeader.biWidth; |
530 capability.height = h->bmiHeader.biHeight; | 535 capability.height = h->bmiHeader.biHeight; |
531 time_per_frame = h->AvgTimePerFrame; | 536 time_per_frame = h->AvgTimePerFrame; |
532 | 537 |
533 // Try to get the max frame rate from IAMVideoControl. | 538 // Try to get the max frame rate from IAMVideoControl. |
534 if (video_control.get()) { | 539 if (video_control.get()) { |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
574 } else if (media_type->subtype == MEDIASUBTYPE_YUY2) { | 579 } else if (media_type->subtype == MEDIASUBTYPE_YUY2) { |
575 capability.color = VideoCaptureCapability::kYUY2; | 580 capability.color = VideoCaptureCapability::kYUY2; |
576 } else if (media_type->subtype == MEDIASUBTYPE_MJPG) { | 581 } else if (media_type->subtype == MEDIASUBTYPE_MJPG) { |
577 capability.color = VideoCaptureCapability::kMJPEG; | 582 capability.color = VideoCaptureCapability::kMJPEG; |
578 } else { | 583 } else { |
579 WCHAR guid_str[128]; | 584 WCHAR guid_str[128]; |
580 StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str)); | 585 StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str)); |
581 DVLOG(2) << "Device support unknown media type " << guid_str; | 586 DVLOG(2) << "Device support unknown media type " << guid_str; |
582 continue; | 587 continue; |
583 } | 588 } |
584 capabilities_.Add(capability); | 589 capabilities_[i] = capability; |
585 } | 590 } |
586 DeleteMediaType(media_type); | 591 DeleteMediaType(media_type); |
587 media_type = NULL; | 592 media_type = NULL; |
588 } | 593 } |
589 | 594 |
590 return !capabilities_.empty(); | 595 return capabilities_.size() > 0; |
| 596 } |
| 597 |
| 598 // Loops through the list of capabilities and returns an index of the best |
| 599 // matching capability. |
| 600 // The algorithm prioritize height, width, frame rate and color format in that |
| 601 // order. |
| 602 int VideoCaptureDeviceWin::GetBestMatchedCapability(int requested_width, |
| 603 int requested_height, |
| 604 int requested_frame_rate) { |
| 605 DCHECK(CalledOnValidThread()); |
| 606 std::list<ResolutionDiff> diff_list; |
| 607 |
| 608 // Loop through the candidates to create a list of differentials between the |
| 609 // requested resolution and the camera capability. |
| 610 for (CapabilityMap::iterator iterator = capabilities_.begin(); |
| 611 iterator != capabilities_.end(); |
| 612 ++iterator) { |
| 613 VideoCaptureCapability capability = iterator->second; |
| 614 |
| 615 ResolutionDiff diff; |
| 616 diff.capability_index = iterator->first; |
| 617 diff.diff_width = capability.width - requested_width; |
| 618 diff.diff_height = capability.height - requested_height; |
| 619 diff.diff_frame_rate = capability.frame_rate - requested_frame_rate; |
| 620 diff.color = capability.color; |
| 621 diff_list.push_back(diff); |
| 622 } |
| 623 |
| 624 // Sort the best height candidates. |
| 625 diff_list.sort(&CompareHeight); |
| 626 int best_diff = diff_list.front().diff_height; |
| 627 for (std::list<ResolutionDiff>::iterator it = diff_list.begin(); |
| 628 it != diff_list.end(); ++it) { |
| 629 if (it->diff_height != best_diff) { |
| 630 // Remove all candidates but the best. |
| 631 diff_list.erase(it, diff_list.end()); |
| 632 break; |
| 633 } |
| 634 } |
| 635 |
| 636 // Sort the best width candidates. |
| 637 diff_list.sort(&CompareWidth); |
| 638 best_diff = diff_list.front().diff_width; |
| 639 for (std::list<ResolutionDiff>::iterator it = diff_list.begin(); |
| 640 it != diff_list.end(); ++it) { |
| 641 if (it->diff_width != best_diff) { |
| 642 // Remove all candidates but the best. |
| 643 diff_list.erase(it, diff_list.end()); |
| 644 break; |
| 645 } |
| 646 } |
| 647 |
| 648 // Sort the best frame rate candidates. |
| 649 diff_list.sort(&CompareFrameRate); |
| 650 best_diff = diff_list.front().diff_frame_rate; |
| 651 for (std::list<ResolutionDiff>::iterator it = diff_list.begin(); |
| 652 it != diff_list.end(); ++it) { |
| 653 if (it->diff_frame_rate != best_diff) { |
| 654 diff_list.erase(it, diff_list.end()); |
| 655 break; |
| 656 } |
| 657 } |
| 658 |
| 659 // Decide the best color format. |
| 660 diff_list.sort(&CompareColor); |
| 661 return diff_list.front().capability_index; |
591 } | 662 } |
592 | 663 |
593 void VideoCaptureDeviceWin::SetErrorState(const char* reason) { | 664 void VideoCaptureDeviceWin::SetErrorState(const char* reason) { |
594 DCHECK(CalledOnValidThread()); | 665 DCHECK(CalledOnValidThread()); |
595 DVLOG(1) << reason; | 666 DVLOG(1) << reason; |
596 state_ = kError; | 667 state_ = kError; |
597 observer_->OnError(); | 668 observer_->OnError(); |
598 } | 669 } |
599 | 670 |
600 } // namespace media | 671 } // namespace media |
OLD | NEW |