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.
5 #include "media/video/capture/win/video_capture_device_win.h"
10 #include "base/command_line.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/win/metro.h"
14 #include "base/win/scoped_co_mem.h"
15 #include "base/win/scoped_variant.h"
16 #include "base/win/windows_version.h"
17 #include "media/base/media_switches.h"
18 #include "media/video/capture/win/video_capture_device_mf_win.h"
20 using base::win::ScopedCoMem;
21 using base::win::ScopedComPtr;
22 using base::win::ScopedVariant;
27 // Finds and creates a DirectShow Video Capture filter matching the device_name.
28 HRESULT GetDeviceFilter(const VideoCaptureDevice::Name& device_name,
29 IBaseFilter** filter) {
32 ScopedComPtr<ICreateDevEnum> dev_enum;
33 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
38 ScopedComPtr<IEnumMoniker> enum_moniker;
39 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
40 enum_moniker.Receive(), 0);
41 // CreateClassEnumerator returns S_FALSE on some Windows OS
42 // when no camera exist. Therefore the FAILED macro can't be used.
46 ScopedComPtr<IMoniker> moniker;
47 ScopedComPtr<IBaseFilter> capture_filter;
49 while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) {
50 ScopedComPtr<IPropertyBag> prop_bag;
51 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
57 // Find the description or friendly name.
58 static const wchar_t* kPropertyNames[] = {
59 L"DevicePath", L"Description", L"FriendlyName"
63 i < arraysize(kPropertyNames) && name.type() != VT_BSTR; ++i) {
64 prop_bag->Read(kPropertyNames[i], name.Receive(), 0);
66 if (name.type() == VT_BSTR) {
67 std::string device_path(base::SysWideToUTF8(V_BSTR(&name)));
68 if (device_path.compare(device_name.id()) == 0) {
69 // We have found the requested device
70 hr = moniker->BindToObject(0, 0, IID_IBaseFilter,
71 capture_filter.ReceiveVoid());
72 DVPLOG_IF(2, FAILED(hr)) << "Failed to bind camera filter.";
79 *filter = capture_filter.Detach();
80 if (!*filter && SUCCEEDED(hr))
81 hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
86 // Check if a Pin matches a category.
87 bool PinMatchesCategory(IPin* pin, REFGUID category) {
90 ScopedComPtr<IKsPropertySet> ks_property;
91 HRESULT hr = ks_property.QueryFrom(pin);
95 hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
96 &pin_category, sizeof(pin_category), &return_value);
97 if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
98 found = (pin_category == category);
104 // Finds a IPin on a IBaseFilter given the direction an category.
105 ScopedComPtr<IPin> GetPin(IBaseFilter* filter, PIN_DIRECTION pin_dir,
107 ScopedComPtr<IPin> pin;
108 ScopedComPtr<IEnumPins> pin_emum;
109 HRESULT hr = filter->EnumPins(pin_emum.Receive());
110 if (pin_emum == NULL)
113 // Get first unconnected pin.
114 hr = pin_emum->Reset(); // set to first pin
115 while ((hr = pin_emum->Next(1, pin.Receive(), NULL)) == S_OK) {
116 PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1);
117 hr = pin->QueryDirection(&this_pin_dir);
118 if (pin_dir == this_pin_dir) {
119 if (category == GUID_NULL || PinMatchesCategory(pin, category))
129 // Release the format block for a media type.
130 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
131 void FreeMediaType(AM_MEDIA_TYPE* mt) {
132 if (mt->cbFormat != 0) {
133 CoTaskMemFree(mt->pbFormat);
137 if (mt->pUnk != NULL) {
139 // pUnk should not be used.
145 // Delete a media type structure that was allocated on the heap.
146 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
147 void DeleteMediaType(AM_MEDIA_TYPE* mt) {
154 // A utility class that wraps the AM_MEDIA_TYPE type and guarantees that
155 // we free the structure when exiting the scope. DCHECKing is also done to
156 // avoid memory leaks.
157 class ScopedMediaType {
159 ScopedMediaType() : media_type_(NULL) {}
160 ~ScopedMediaType() { Free(); }
162 AM_MEDIA_TYPE* operator->() { return media_type_; }
163 AM_MEDIA_TYPE* get() { return media_type_; }
169 DeleteMediaType(media_type_);
173 AM_MEDIA_TYPE** Receive() {
174 DCHECK(!media_type_);
179 AM_MEDIA_TYPE* media_type_;
182 VideoPixelFormat TranslateMediaSubtypeToPixelFormat(const GUID& sub_type) {
184 const GUID& sub_type;
185 VideoPixelFormat format;
186 } pixel_formats[] = {
187 { kMediaSubTypeI420, PIXEL_FORMAT_I420 },
188 { MEDIASUBTYPE_IYUV, PIXEL_FORMAT_I420 },
189 { MEDIASUBTYPE_RGB24, PIXEL_FORMAT_RGB24 },
190 { MEDIASUBTYPE_YUY2, PIXEL_FORMAT_YUY2 },
191 { MEDIASUBTYPE_MJPG, PIXEL_FORMAT_MJPEG },
192 { MEDIASUBTYPE_UYVY, PIXEL_FORMAT_UYVY },
193 { MEDIASUBTYPE_ARGB32, PIXEL_FORMAT_ARGB },
195 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(pixel_formats); ++i) {
196 if (sub_type == pixel_formats[i].sub_type)
197 return pixel_formats[i].format;
201 StringFromGUID2(sub_type, guid_str, arraysize(guid_str));
202 DVLOG(2) << "Device (also) supports an unknown media type " << guid_str;
204 return PIXEL_FORMAT_UNKNOWN;
210 void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
211 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
212 // Use Media Foundation for Metro processes (after and including Win8) and
213 // DirectShow for any other versions, unless forced via flag. Media Foundation
214 // can also be forced if appropriate flag is set and we are in Windows 7 or
215 // 8 in non-Metro mode.
216 if ((base::win::IsMetroProcess() &&
217 !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) ||
218 (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
219 cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture))) {
220 VideoCaptureDeviceMFWin::GetDeviceNames(device_names);
222 VideoCaptureDeviceWin::GetDeviceNames(device_names);
227 void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device,
228 VideoCaptureFormats* formats) {
229 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
230 // Use Media Foundation for Metro processes (after and including Win8) and
231 // DirectShow for any other versions, unless forced via flag. Media Foundation
232 // can also be forced if appropriate flag is set and we are in Windows 7 or
233 // 8 in non-Metro mode.
234 if ((base::win::IsMetroProcess() &&
235 !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) ||
236 (base::win::GetVersion() >= base::win::VERSION_WIN7 &&
237 cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture))) {
238 VideoCaptureDeviceMFWin::GetDeviceSupportedFormats(device, formats);
240 VideoCaptureDeviceWin::GetDeviceSupportedFormats(device, formats);
245 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
246 VideoCaptureDevice* ret = NULL;
247 if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) {
248 DCHECK(VideoCaptureDeviceMFWin::PlatformSupported());
249 scoped_ptr<VideoCaptureDeviceMFWin> device(
250 new VideoCaptureDeviceMFWin(device_name));
251 DVLOG(1) << " MediaFoundation Device: " << device_name.name();
253 ret = device.release();
254 } else if (device_name.capture_api_type() == Name::DIRECT_SHOW) {
255 scoped_ptr<VideoCaptureDeviceWin> device(
256 new VideoCaptureDeviceWin(device_name));
257 DVLOG(1) << " DirectShow Device: " << device_name.name();
259 ret = device.release();
261 NOTREACHED() << " Couldn't recognize VideoCaptureDevice type";
268 void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) {
269 DCHECK(device_names);
271 ScopedComPtr<ICreateDevEnum> dev_enum;
272 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
277 ScopedComPtr<IEnumMoniker> enum_moniker;
278 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
279 enum_moniker.Receive(), 0);
280 // CreateClassEnumerator returns S_FALSE on some Windows OS
281 // when no camera exist. Therefore the FAILED macro can't be used.
285 device_names->clear();
287 // Name of a fake DirectShow filter that exist on computers with
289 static const char kGoogleCameraAdapter[] = "google camera adapter";
291 // Enumerate all video capture devices.
292 ScopedComPtr<IMoniker> moniker;
294 while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) {
295 ScopedComPtr<IPropertyBag> prop_bag;
296 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
302 // Find the description or friendly name.
304 hr = prop_bag->Read(L"Description", name.Receive(), 0);
306 hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
308 if (SUCCEEDED(hr) && name.type() == VT_BSTR) {
309 // Ignore all VFW drivers and the special Google Camera Adapter.
310 // Google Camera Adapter is not a real DirectShow camera device.
311 // VFW are very old Video for Windows drivers that can not be used.
312 const wchar_t* str_ptr = V_BSTR(&name);
313 const int name_length = arraysize(kGoogleCameraAdapter) - 1;
315 if ((wcsstr(str_ptr, L"(VFW)") == NULL) &&
316 lstrlenW(str_ptr) < name_length ||
317 (!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length,
318 kGoogleCameraAdapter)))) {
320 std::string device_name(base::SysWideToUTF8(str_ptr));
322 hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
323 if (FAILED(hr) || name.type() != VT_BSTR) {
326 DCHECK_EQ(name.type(), VT_BSTR);
327 id = base::SysWideToUTF8(V_BSTR(&name));
330 device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW));
338 void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device,
339 VideoCaptureFormats* formats) {
340 DVLOG(1) << "GetDeviceSupportedFormats for " << device.name();
341 ScopedComPtr<ICreateDevEnum> dev_enum;
342 HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
347 ScopedComPtr<IEnumMoniker> enum_moniker;
348 hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
349 enum_moniker.Receive(), 0);
350 // CreateClassEnumerator returns S_FALSE on some Windows OS when no camera
351 // exists. Therefore the FAILED macro can't be used.
355 // Walk the capture devices. No need to check for "google camera adapter",
356 // since this is already skipped in the enumeration of GetDeviceNames().
357 ScopedComPtr<IMoniker> moniker;
359 ScopedVariant device_id;
360 while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) {
361 ScopedComPtr<IPropertyBag> prop_bag;
362 hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
369 hr = prop_bag->Read(L"DevicePath", device_id.Receive(), 0);
371 DVLOG(1) << "Couldn't read a device's DevicePath.";
374 if (device.id() == base::SysWideToUTF8(V_BSTR(&device_id)))
380 base::win::ScopedComPtr<IBaseFilter> capture_filter;
381 hr = GetDeviceFilter(device, capture_filter.Receive());
382 if (!capture_filter) {
383 DVLOG(2) << "Failed to create capture filter.";
387 base::win::ScopedComPtr<IPin> output_capture_pin(
388 GetPin(capture_filter, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE));
389 if (!output_capture_pin) {
390 DVLOG(2) << "Failed to get capture output pin";
394 ScopedComPtr<IAMStreamConfig> stream_config;
395 hr = output_capture_pin.QueryInterface(stream_config.Receive());
397 DVLOG(2) << "Failed to get IAMStreamConfig interface from "
402 int count = 0, size = 0;
403 hr = stream_config->GetNumberOfCapabilities(&count, &size);
405 DVLOG(2) << "Failed to GetNumberOfCapabilities";
409 scoped_ptr<BYTE[]> caps(new BYTE[size]);
410 for (int i = 0; i < count; ++i) {
411 ScopedMediaType media_type;
412 hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get());
413 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
414 // macros here since they'll trigger incorrectly.
416 DVLOG(2) << "Failed to GetStreamCaps";
420 if (media_type->majortype == MEDIATYPE_Video &&
421 media_type->formattype == FORMAT_VideoInfo) {
423 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
424 VideoCaptureFormat format;
425 format.frame_size.SetSize(h->bmiHeader.biWidth,
426 h->bmiHeader.biHeight);
427 // Trust the frame rate from the VIDEOINFOHEADER.
428 format.frame_rate = (h->AvgTimePerFrame > 0) ?
429 static_cast<int>(kSecondsToReferenceTime / h->AvgTimePerFrame) :
431 format.pixel_format =
432 TranslateMediaSubtypeToPixelFormat(media_type->subtype);
433 formats->push_back(format);
434 DVLOG(1) << device.name() << " resolution: "
435 << format.frame_size.ToString() << ", fps: " << format.frame_rate
436 << ", pixel format: " << format.pixel_format;
442 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name)
443 : device_name_(device_name),
448 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() {
449 DCHECK(CalledOnValidThread());
451 media_control_->Stop();
453 if (graph_builder_) {
455 graph_builder_->RemoveFilter(sink_filter_);
460 graph_builder_->RemoveFilter(capture_filter_);
463 graph_builder_->RemoveFilter(mjpg_filter_);
467 bool VideoCaptureDeviceWin::Init() {
468 DCHECK(CalledOnValidThread());
469 HRESULT hr = GetDeviceFilter(device_name_, capture_filter_.Receive());
470 if (!capture_filter_) {
471 DVLOG(2) << "Failed to create capture filter.";
475 output_capture_pin_ =
476 GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE);
477 if (!output_capture_pin_) {
478 DVLOG(2) << "Failed to get capture output pin";
482 // Create the sink filter used for receiving Captured frames.
483 sink_filter_ = new SinkFilter(this);
484 if (sink_filter_ == NULL) {
485 DVLOG(2) << "Failed to create send filter";
489 input_sink_pin_ = sink_filter_->GetPin(0);
491 hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL,
492 CLSCTX_INPROC_SERVER);
494 DVLOG(2) << "Failed to create graph builder.";
498 hr = graph_builder_.QueryInterface(media_control_.Receive());
500 DVLOG(2) << "Failed to create media control builder.";
504 hr = graph_builder_->AddFilter(capture_filter_, NULL);
506 DVLOG(2) << "Failed to add the capture device to the graph.";
510 hr = graph_builder_->AddFilter(sink_filter_, NULL);
512 DVLOG(2)<< "Failed to add the send filter to the graph.";
516 return CreateCapabilityMap();
519 void VideoCaptureDeviceWin::AllocateAndStart(
520 const VideoCaptureParams& params,
521 scoped_ptr<VideoCaptureDevice::Client> client) {
522 DCHECK(CalledOnValidThread());
526 client_ = client.Pass();
528 // Get the camera capability that best match the requested resolution.
529 const VideoCaptureCapabilityWin& found_capability =
530 capabilities_.GetBestMatchedFormat(
531 params.requested_format.frame_size.width(),
532 params.requested_format.frame_size.height(),
533 params.requested_format.frame_rate);
534 VideoCaptureFormat format = found_capability.supported_format;
536 // Reduce the frame rate if the requested frame rate is lower
537 // than the capability.
538 if (format.frame_rate > params.requested_format.frame_rate)
539 format.frame_rate = params.requested_format.frame_rate;
541 ScopedComPtr<IAMStreamConfig> stream_config;
542 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
544 SetErrorState("Can't get the Capture format settings");
548 int count = 0, size = 0;
549 hr = stream_config->GetNumberOfCapabilities(&count, &size);
551 DVLOG(2) << "Failed to GetNumberOfCapabilities";
555 scoped_ptr<BYTE[]> caps(new BYTE[size]);
556 ScopedMediaType media_type;
558 // Get the windows capability from the capture device.
559 hr = stream_config->GetStreamCaps(
560 found_capability.stream_index, media_type.Receive(), caps.get());
562 if (media_type->formattype == FORMAT_VideoInfo) {
564 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
565 if (format.frame_rate > 0)
566 h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate;
568 // Set the sink filter to request this format.
569 sink_filter_->SetRequestedMediaFormat(format);
570 // Order the capture device to use this format.
571 hr = stream_config->SetFormat(media_type.get());
575 SetErrorState("Failed to set capture device output format");
577 if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) {
578 // Create MJPG filter if we need it.
579 hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC);
582 input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL);
583 output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL);
584 hr = graph_builder_->AddFilter(mjpg_filter_, NULL);
588 mjpg_filter_.Release();
589 input_mjpg_pin_.Release();
590 output_mjpg_pin_.Release();
594 if (format.pixel_format == PIXEL_FORMAT_MJPEG && mjpg_filter_.get()) {
595 // Connect the camera to the MJPEG decoder.
596 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_,
598 // Connect the MJPEG filter to the Capture filter.
599 hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_,
602 hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_,
607 SetErrorState("Failed to connect the Capture graph.");
611 hr = media_control_->Pause();
613 SetErrorState("Failed to Pause the Capture device. "
614 "Is it already occupied?");
618 // Get the format back from the sink filter after the filter have been
620 capture_format_ = sink_filter_->ResultingFormat();
623 hr = media_control_->Run();
625 SetErrorState("Failed to start the Capture device.");
632 void VideoCaptureDeviceWin::StopAndDeAllocate() {
633 DCHECK(CalledOnValidThread());
634 if (state_ != kCapturing)
637 HRESULT hr = media_control_->Stop();
639 SetErrorState("Failed to stop the capture graph.");
643 graph_builder_->Disconnect(output_capture_pin_);
644 graph_builder_->Disconnect(input_sink_pin_);
646 // If the _mjpg filter exist disconnect it even if it has not been used.
648 graph_builder_->Disconnect(input_mjpg_pin_);
649 graph_builder_->Disconnect(output_mjpg_pin_);
653 SetErrorState("Failed to Stop the Capture device");
660 // Implements SinkFilterObserver::SinkFilterObserver.
661 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer,
663 client_->OnIncomingCapturedData(
664 buffer, length, capture_format_, 0, base::TimeTicks::Now());
667 bool VideoCaptureDeviceWin::CreateCapabilityMap() {
668 DCHECK(CalledOnValidThread());
669 ScopedComPtr<IAMStreamConfig> stream_config;
670 HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
672 DVLOG(2) << "Failed to get IAMStreamConfig interface from "
677 // Get interface used for getting the frame rate.
678 ScopedComPtr<IAMVideoControl> video_control;
679 hr = capture_filter_.QueryInterface(video_control.Receive());
680 DVLOG_IF(2, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED";
682 int count = 0, size = 0;
683 hr = stream_config->GetNumberOfCapabilities(&count, &size);
685 DVLOG(2) << "Failed to GetNumberOfCapabilities";
689 scoped_ptr<BYTE[]> caps(new BYTE[size]);
690 for (int i = 0; i < count; ++i) {
691 ScopedMediaType media_type;
692 hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get());
693 // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
694 // macros here since they'll trigger incorrectly.
696 DVLOG(2) << "Failed to GetStreamCaps";
700 if (media_type->majortype == MEDIATYPE_Video &&
701 media_type->formattype == FORMAT_VideoInfo) {
702 VideoCaptureCapabilityWin capability(i);
704 reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
705 capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth,
706 h->bmiHeader.biHeight);
708 // Try to get a better |time_per_frame| from IAMVideoControl. If not, use
709 // the value from VIDEOINFOHEADER.
710 REFERENCE_TIME time_per_frame = h->AvgTimePerFrame;
712 ScopedCoMem<LONGLONG> max_fps;
714 SIZE size = {capability.supported_format.frame_size.width(),
715 capability.supported_format.frame_size.height()};
717 // GetFrameRateList doesn't return max frame rate always
718 // eg: Logitech Notebook. This may be due to a bug in that API
719 // because GetFrameRateList array is reversed in the above camera. So
720 // a util method written. Can't assume the first value will return
722 hr = video_control->GetFrameRateList(output_capture_pin_, i, size,
723 &list_size, &max_fps);
724 // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some
725 // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates
726 // into success, so explicitly check S_OK. See http://crbug.com/306237.
727 if (hr == S_OK && list_size > 0 && max_fps) {
728 time_per_frame = *std::min_element(max_fps.get(),
729 max_fps.get() + list_size);
733 capability.supported_format.frame_rate =
735 ? static_cast<int>(kSecondsToReferenceTime / time_per_frame)
738 // DirectShow works at the moment only on integer frame_rate but the
739 // best capability matching class works on rational frame rates.
740 capability.frame_rate_numerator = capability.supported_format.frame_rate;
741 capability.frame_rate_denominator = 1;
743 capability.supported_format.pixel_format =
744 TranslateMediaSubtypeToPixelFormat(media_type->subtype);
745 capabilities_.Add(capability);
749 return !capabilities_.empty();
752 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) {
753 DCHECK(CalledOnValidThread());
756 client_->OnError(reason);