Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / media / video / capture / win / video_capture_device_win.cc
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 "media/video/capture/win/video_capture_device_win.h"
6
7 #include <algorithm>
8 #include <list>
9
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"
19
20 using base::win::ScopedCoMem;
21 using base::win::ScopedComPtr;
22 using base::win::ScopedVariant;
23
24 namespace media {
25 namespace {
26
27 // Finds and creates a DirectShow Video Capture filter matching the device_name.
28 HRESULT GetDeviceFilter(const VideoCaptureDevice::Name& device_name,
29                         IBaseFilter** filter) {
30   DCHECK(filter);
31
32   ScopedComPtr<ICreateDevEnum> dev_enum;
33   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
34                                        CLSCTX_INPROC);
35   if (FAILED(hr))
36     return hr;
37
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.
43   if (hr != S_OK)
44     return NULL;
45
46   ScopedComPtr<IMoniker> moniker;
47   ScopedComPtr<IBaseFilter> capture_filter;
48   DWORD fetched = 0;
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());
52     if (FAILED(hr)) {
53       moniker.Release();
54       continue;
55     }
56
57     // Find the description or friendly name.
58     static const wchar_t* kPropertyNames[] = {
59       L"DevicePath", L"Description", L"FriendlyName"
60     };
61     ScopedVariant name;
62     for (size_t i = 0;
63          i < arraysize(kPropertyNames) && name.type() != VT_BSTR; ++i) {
64       prop_bag->Read(kPropertyNames[i], name.Receive(), 0);
65     }
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.";
73         break;
74       }
75     }
76     moniker.Release();
77   }
78
79   *filter = capture_filter.Detach();
80   if (!*filter && SUCCEEDED(hr))
81     hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
82
83   return hr;
84 }
85
86 // Check if a Pin matches a category.
87 bool PinMatchesCategory(IPin* pin, REFGUID category) {
88   DCHECK(pin);
89   bool found = false;
90   ScopedComPtr<IKsPropertySet> ks_property;
91   HRESULT hr = ks_property.QueryFrom(pin);
92   if (SUCCEEDED(hr)) {
93     GUID pin_category;
94     DWORD return_value;
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);
99     }
100   }
101   return found;
102 }
103
104 // Finds a IPin on a IBaseFilter given the direction an category.
105 ScopedComPtr<IPin> GetPin(IBaseFilter* filter, PIN_DIRECTION pin_dir,
106                           REFGUID category) {
107   ScopedComPtr<IPin> pin;
108   ScopedComPtr<IEnumPins> pin_emum;
109   HRESULT hr = filter->EnumPins(pin_emum.Receive());
110   if (pin_emum == NULL)
111     return pin;
112
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))
120         return pin;
121     }
122     pin.Release();
123   }
124
125   DCHECK(!pin);
126   return pin;
127 }
128
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);
134     mt->cbFormat = 0;
135     mt->pbFormat = NULL;
136   }
137   if (mt->pUnk != NULL) {
138     NOTREACHED();
139     // pUnk should not be used.
140     mt->pUnk->Release();
141     mt->pUnk = NULL;
142   }
143 }
144
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) {
148   if (mt != NULL) {
149     FreeMediaType(mt);
150     CoTaskMemFree(mt);
151   }
152 }
153
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 {
158  public:
159   ScopedMediaType() : media_type_(NULL) {}
160   ~ScopedMediaType() { Free(); }
161
162   AM_MEDIA_TYPE* operator->() { return media_type_; }
163   AM_MEDIA_TYPE* get() { return media_type_; }
164
165   void Free() {
166     if (!media_type_)
167       return;
168
169     DeleteMediaType(media_type_);
170     media_type_= NULL;
171   }
172
173   AM_MEDIA_TYPE** Receive() {
174     DCHECK(!media_type_);
175     return &media_type_;
176   }
177
178  private:
179   AM_MEDIA_TYPE* media_type_;
180 };
181
182 VideoPixelFormat TranslateMediaSubtypeToPixelFormat(const GUID& sub_type) {
183   static struct {
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 },
194   };
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;
198   }
199 #ifndef NDEBUG
200   WCHAR guid_str[128];
201   StringFromGUID2(sub_type, guid_str, arraysize(guid_str));
202   DVLOG(2) << "Device (also) supports an unknown media type " << guid_str;
203 #endif
204   return PIXEL_FORMAT_UNKNOWN;
205 }
206
207 }  // namespace
208
209 // static
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);
221   } else {
222     VideoCaptureDeviceWin::GetDeviceNames(device_names);
223   }
224 }
225
226 // static
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);
239   } else {
240     VideoCaptureDeviceWin::GetDeviceSupportedFormats(device, formats);
241   }
242 }
243
244 // static
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();
252     if (device->Init())
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();
258     if (device->Init())
259       ret = device.release();
260   } else{
261     NOTREACHED() << " Couldn't recognize VideoCaptureDevice type";
262   }
263
264   return ret;
265 }
266
267 // static
268 void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) {
269   DCHECK(device_names);
270
271   ScopedComPtr<ICreateDevEnum> dev_enum;
272   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
273                                        CLSCTX_INPROC);
274   if (FAILED(hr))
275     return;
276
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.
282   if (hr != S_OK)
283     return;
284
285   device_names->clear();
286
287   // Name of a fake DirectShow filter that exist on computers with
288   // GTalk installed.
289   static const char kGoogleCameraAdapter[] = "google camera adapter";
290
291   // Enumerate all video capture devices.
292   ScopedComPtr<IMoniker> moniker;
293   int index = 0;
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());
297     if (FAILED(hr)) {
298       moniker.Release();
299       continue;
300     }
301
302     // Find the description or friendly name.
303     ScopedVariant name;
304     hr = prop_bag->Read(L"Description", name.Receive(), 0);
305     if (FAILED(hr))
306       hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
307
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;
314
315       if ((wcsstr(str_ptr, L"(VFW)") == NULL) &&
316           lstrlenW(str_ptr) < name_length ||
317           (!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length,
318                                   kGoogleCameraAdapter)))) {
319         std::string id;
320         std::string device_name(base::SysWideToUTF8(str_ptr));
321         name.Reset();
322         hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
323         if (FAILED(hr) || name.type() != VT_BSTR) {
324           id = device_name;
325         } else {
326           DCHECK_EQ(name.type(), VT_BSTR);
327           id = base::SysWideToUTF8(V_BSTR(&name));
328         }
329
330         device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW));
331       }
332     }
333     moniker.Release();
334   }
335 }
336
337 // static
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,
343                                        CLSCTX_INPROC);
344   if (FAILED(hr))
345     return;
346
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.
352   if (hr != S_OK)
353     return;
354
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;
358   int index = 0;
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());
363     if (FAILED(hr)) {
364       moniker.Release();
365       continue;
366     }
367
368     device_id.Reset();
369     hr = prop_bag->Read(L"DevicePath", device_id.Receive(), 0);
370     if (FAILED(hr)) {
371       DVLOG(1) << "Couldn't read a device's DevicePath.";
372       return;
373     }
374     if (device.id() == base::SysWideToUTF8(V_BSTR(&device_id)))
375       break;
376     moniker.Release();
377   }
378
379   if (moniker.get()) {
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.";
384       return;
385     }
386
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";
391       return;
392     }
393
394     ScopedComPtr<IAMStreamConfig> stream_config;
395     hr = output_capture_pin.QueryInterface(stream_config.Receive());
396     if (FAILED(hr)) {
397       DVLOG(2) << "Failed to get IAMStreamConfig interface from "
398                   "capture device";
399       return;
400     }
401
402     int count = 0, size = 0;
403     hr = stream_config->GetNumberOfCapabilities(&count, &size);
404     if (FAILED(hr)) {
405       DVLOG(2) << "Failed to GetNumberOfCapabilities";
406       return;
407     }
408
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.
415       if (hr != S_OK) {
416         DVLOG(2) << "Failed to GetStreamCaps";
417         return;
418       }
419
420       if (media_type->majortype == MEDIATYPE_Video &&
421           media_type->formattype == FORMAT_VideoInfo) {
422         VIDEOINFOHEADER* h =
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) :
430             0;
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;
437       }
438     }
439   }
440 }
441
442 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name)
443     : device_name_(device_name),
444       state_(kIdle) {
445   DetachFromThread();
446 }
447
448 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() {
449   DCHECK(CalledOnValidThread());
450   if (media_control_)
451     media_control_->Stop();
452
453   if (graph_builder_) {
454     if (sink_filter_) {
455       graph_builder_->RemoveFilter(sink_filter_);
456       sink_filter_ = NULL;
457     }
458
459     if (capture_filter_)
460       graph_builder_->RemoveFilter(capture_filter_);
461
462     if (mjpg_filter_)
463       graph_builder_->RemoveFilter(mjpg_filter_);
464   }
465 }
466
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.";
472     return false;
473   }
474
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";
479     return false;
480   }
481
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";
486     return false;
487   }
488
489   input_sink_pin_ = sink_filter_->GetPin(0);
490
491   hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL,
492                                      CLSCTX_INPROC_SERVER);
493   if (FAILED(hr)) {
494     DVLOG(2) << "Failed to create graph builder.";
495     return false;
496   }
497
498   hr = graph_builder_.QueryInterface(media_control_.Receive());
499   if (FAILED(hr)) {
500     DVLOG(2) << "Failed to create media control builder.";
501     return false;
502   }
503
504   hr = graph_builder_->AddFilter(capture_filter_, NULL);
505   if (FAILED(hr)) {
506     DVLOG(2) << "Failed to add the capture device to the graph.";
507     return false;
508   }
509
510   hr = graph_builder_->AddFilter(sink_filter_, NULL);
511   if (FAILED(hr)) {
512     DVLOG(2)<< "Failed to add the send filter to the graph.";
513     return false;
514   }
515
516   return CreateCapabilityMap();
517 }
518
519 void VideoCaptureDeviceWin::AllocateAndStart(
520     const VideoCaptureParams& params,
521     scoped_ptr<VideoCaptureDevice::Client> client) {
522   DCHECK(CalledOnValidThread());
523   if (state_ != kIdle)
524     return;
525
526   client_ = client.Pass();
527
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;
535
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;
540
541   ScopedComPtr<IAMStreamConfig> stream_config;
542   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
543   if (FAILED(hr)) {
544     SetErrorState("Can't get the Capture format settings");
545     return;
546   }
547
548   int count = 0, size = 0;
549   hr = stream_config->GetNumberOfCapabilities(&count, &size);
550   if (FAILED(hr)) {
551     DVLOG(2) << "Failed to GetNumberOfCapabilities";
552     return;
553   }
554
555   scoped_ptr<BYTE[]> caps(new BYTE[size]);
556   ScopedMediaType media_type;
557
558   // Get the windows capability from the capture device.
559   hr = stream_config->GetStreamCaps(
560       found_capability.stream_index, media_type.Receive(), caps.get());
561   if (SUCCEEDED(hr)) {
562     if (media_type->formattype == FORMAT_VideoInfo) {
563       VIDEOINFOHEADER* h =
564           reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
565       if (format.frame_rate > 0)
566         h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate;
567     }
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());
572   }
573
574   if (FAILED(hr))
575     SetErrorState("Failed to set capture device output format");
576
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);
580
581     if (SUCCEEDED(hr)) {
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);
585     }
586
587     if (FAILED(hr)) {
588       mjpg_filter_.Release();
589       input_mjpg_pin_.Release();
590       output_mjpg_pin_.Release();
591     }
592   }
593
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_,
597                                        NULL);
598     // Connect the MJPEG filter to the Capture filter.
599     hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_,
600                                         NULL);
601   } else {
602     hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_,
603                                        NULL);
604   }
605
606   if (FAILED(hr)) {
607     SetErrorState("Failed to connect the Capture graph.");
608     return;
609   }
610
611   hr = media_control_->Pause();
612   if (FAILED(hr)) {
613     SetErrorState("Failed to Pause the Capture device. "
614                   "Is it already occupied?");
615     return;
616   }
617
618   // Get the format back from the sink filter after the filter have been
619   // connected.
620   capture_format_ = sink_filter_->ResultingFormat();
621
622   // Start capturing.
623   hr = media_control_->Run();
624   if (FAILED(hr)) {
625     SetErrorState("Failed to start the Capture device.");
626     return;
627   }
628
629   state_ = kCapturing;
630 }
631
632 void VideoCaptureDeviceWin::StopAndDeAllocate() {
633   DCHECK(CalledOnValidThread());
634   if (state_ != kCapturing)
635     return;
636
637   HRESULT hr = media_control_->Stop();
638   if (FAILED(hr)) {
639     SetErrorState("Failed to stop the capture graph.");
640     return;
641   }
642
643   graph_builder_->Disconnect(output_capture_pin_);
644   graph_builder_->Disconnect(input_sink_pin_);
645
646   // If the _mjpg filter exist disconnect it even if it has not been used.
647   if (mjpg_filter_) {
648     graph_builder_->Disconnect(input_mjpg_pin_);
649     graph_builder_->Disconnect(output_mjpg_pin_);
650   }
651
652   if (FAILED(hr)) {
653     SetErrorState("Failed to Stop the Capture device");
654     return;
655   }
656   client_.reset();
657   state_ = kIdle;
658 }
659
660 // Implements SinkFilterObserver::SinkFilterObserver.
661 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer,
662                                           int length) {
663   client_->OnIncomingCapturedData(
664       buffer, length, capture_format_, 0, base::TimeTicks::Now());
665 }
666
667 bool VideoCaptureDeviceWin::CreateCapabilityMap() {
668   DCHECK(CalledOnValidThread());
669   ScopedComPtr<IAMStreamConfig> stream_config;
670   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
671   if (FAILED(hr)) {
672     DVLOG(2) << "Failed to get IAMStreamConfig interface from "
673                 "capture device";
674     return false;
675   }
676
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";
681
682   int count = 0, size = 0;
683   hr = stream_config->GetNumberOfCapabilities(&count, &size);
684   if (FAILED(hr)) {
685     DVLOG(2) << "Failed to GetNumberOfCapabilities";
686     return false;
687   }
688
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.
695     if (hr != S_OK) {
696       DVLOG(2) << "Failed to GetStreamCaps";
697       return false;
698     }
699
700     if (media_type->majortype == MEDIATYPE_Video &&
701         media_type->formattype == FORMAT_VideoInfo) {
702       VideoCaptureCapabilityWin capability(i);
703       VIDEOINFOHEADER* h =
704           reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
705       capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth,
706                                                      h->bmiHeader.biHeight);
707
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;
711       if (video_control) {
712         ScopedCoMem<LONGLONG> max_fps;
713         LONG list_size = 0;
714         SIZE size = {capability.supported_format.frame_size.width(),
715                      capability.supported_format.frame_size.height()};
716
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
721         // the max fps.
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);
730         }
731       }
732
733       capability.supported_format.frame_rate =
734           (time_per_frame > 0)
735               ? static_cast<int>(kSecondsToReferenceTime / time_per_frame)
736               : 0;
737
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;
742
743       capability.supported_format.pixel_format =
744           TranslateMediaSubtypeToPixelFormat(media_type->subtype);
745       capabilities_.Add(capability);
746     }
747   }
748
749   return !capabilities_.empty();
750 }
751
752 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) {
753   DCHECK(CalledOnValidThread());
754   DVLOG(1) << reason;
755   state_ = kError;
756   client_->OnError(reason);
757 }
758 }  // namespace media