Upstream version 5.34.92.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 "media/base/media_switches.h"
17 #include "media/video/capture/win/video_capture_device_mf_win.h"
18
19 using base::win::ScopedCoMem;
20 using base::win::ScopedComPtr;
21 using base::win::ScopedVariant;
22
23 namespace media {
24 namespace {
25
26 // Finds and creates a DirectShow Video Capture filter matching the device_name.
27 HRESULT GetDeviceFilter(const VideoCaptureDevice::Name& device_name,
28                         IBaseFilter** filter) {
29   DCHECK(filter);
30
31   ScopedComPtr<ICreateDevEnum> dev_enum;
32   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
33                                        CLSCTX_INPROC);
34   if (FAILED(hr))
35     return hr;
36
37   ScopedComPtr<IEnumMoniker> enum_moniker;
38   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
39                                        enum_moniker.Receive(), 0);
40   // CreateClassEnumerator returns S_FALSE on some Windows OS
41   // when no camera exist. Therefore the FAILED macro can't be used.
42   if (hr != S_OK)
43     return NULL;
44
45   ScopedComPtr<IMoniker> moniker;
46   ScopedComPtr<IBaseFilter> capture_filter;
47   DWORD fetched = 0;
48   while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) {
49     ScopedComPtr<IPropertyBag> prop_bag;
50     hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
51     if (FAILED(hr)) {
52       moniker.Release();
53       continue;
54     }
55
56     // Find the description or friendly name.
57     static const wchar_t* kPropertyNames[] = {
58       L"DevicePath", L"Description", L"FriendlyName"
59     };
60     ScopedVariant name;
61     for (size_t i = 0;
62          i < arraysize(kPropertyNames) && name.type() != VT_BSTR; ++i) {
63       prop_bag->Read(kPropertyNames[i], name.Receive(), 0);
64     }
65     if (name.type() == VT_BSTR) {
66       std::string device_path(base::SysWideToUTF8(V_BSTR(&name)));
67       if (device_path.compare(device_name.id()) == 0) {
68         // We have found the requested device
69         hr = moniker->BindToObject(0, 0, IID_IBaseFilter,
70                                    capture_filter.ReceiveVoid());
71         DVPLOG_IF(2, FAILED(hr)) << "Failed to bind camera filter.";
72         break;
73       }
74     }
75     moniker.Release();
76   }
77
78   *filter = capture_filter.Detach();
79   if (!*filter && SUCCEEDED(hr))
80     hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
81
82   return hr;
83 }
84
85 // Check if a Pin matches a category.
86 bool PinMatchesCategory(IPin* pin, REFGUID category) {
87   DCHECK(pin);
88   bool found = false;
89   ScopedComPtr<IKsPropertySet> ks_property;
90   HRESULT hr = ks_property.QueryFrom(pin);
91   if (SUCCEEDED(hr)) {
92     GUID pin_category;
93     DWORD return_value;
94     hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0,
95                           &pin_category, sizeof(pin_category), &return_value);
96     if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) {
97       found = (pin_category == category);
98     }
99   }
100   return found;
101 }
102
103 // Finds a IPin on a IBaseFilter given the direction an category.
104 HRESULT GetPin(IBaseFilter* filter, PIN_DIRECTION pin_dir, REFGUID category,
105                IPin** pin) {
106   DCHECK(pin);
107   ScopedComPtr<IEnumPins> pin_emum;
108   HRESULT hr = filter->EnumPins(pin_emum.Receive());
109   if (pin_emum == NULL)
110     return hr;
111
112   // Get first unconnected pin.
113   hr = pin_emum->Reset();  // set to first pin
114   while ((hr = pin_emum->Next(1, pin, NULL)) == S_OK) {
115     PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1);
116     hr = (*pin)->QueryDirection(&this_pin_dir);
117     if (pin_dir == this_pin_dir) {
118       if (category == GUID_NULL || PinMatchesCategory(*pin, category))
119         return S_OK;
120     }
121     (*pin)->Release();
122   }
123
124   return E_FAIL;
125 }
126
127 // Release the format block for a media type.
128 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
129 void FreeMediaType(AM_MEDIA_TYPE* mt) {
130   if (mt->cbFormat != 0) {
131     CoTaskMemFree(mt->pbFormat);
132     mt->cbFormat = 0;
133     mt->pbFormat = NULL;
134   }
135   if (mt->pUnk != NULL) {
136     NOTREACHED();
137     // pUnk should not be used.
138     mt->pUnk->Release();
139     mt->pUnk = NULL;
140   }
141 }
142
143 // Delete a media type structure that was allocated on the heap.
144 // http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx
145 void DeleteMediaType(AM_MEDIA_TYPE* mt) {
146   if (mt != NULL) {
147     FreeMediaType(mt);
148     CoTaskMemFree(mt);
149   }
150 }
151
152 }  // namespace
153
154 // static
155 void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
156   const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
157   // Use Media Foundation for Metro processes (after and including Win8)
158   // and DirectShow for any other platforms.
159   if (base::win::IsMetroProcess() &&
160       !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) {
161     VideoCaptureDeviceMFWin::GetDeviceNames(device_names);
162   } else {
163     VideoCaptureDeviceWin::GetDeviceNames(device_names);
164   }
165 }
166
167 // static
168 void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device,
169     VideoCaptureFormats* formats) {
170   NOTIMPLEMENTED();
171 }
172
173 // static
174 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
175   VideoCaptureDevice* ret = NULL;
176   if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) {
177     DCHECK(VideoCaptureDeviceMFWin::PlatformSupported());
178     scoped_ptr<VideoCaptureDeviceMFWin> device(
179         new VideoCaptureDeviceMFWin(device_name));
180     DVLOG(1) << " MediaFoundation Device: " << device_name.name();
181     if (device->Init())
182       ret = device.release();
183   } else if (device_name.capture_api_type() == Name::DIRECT_SHOW) {
184     scoped_ptr<VideoCaptureDeviceWin> device(
185         new VideoCaptureDeviceWin(device_name));
186     DVLOG(1) << " DirectShow Device: " << device_name.name();
187     if (device->Init())
188       ret = device.release();
189   } else{
190     NOTREACHED() << " Couldn't recognize VideoCaptureDevice type";
191   }
192
193   return ret;
194 }
195
196 // static
197 void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) {
198   DCHECK(device_names);
199
200   ScopedComPtr<ICreateDevEnum> dev_enum;
201   HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL,
202                                        CLSCTX_INPROC);
203   if (FAILED(hr))
204     return;
205
206   ScopedComPtr<IEnumMoniker> enum_moniker;
207   hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory,
208                                        enum_moniker.Receive(), 0);
209   // CreateClassEnumerator returns S_FALSE on some Windows OS
210   // when no camera exist. Therefore the FAILED macro can't be used.
211   if (hr != S_OK)
212     return;
213
214   device_names->clear();
215
216   // Name of a fake DirectShow filter that exist on computers with
217   // GTalk installed.
218   static const char kGoogleCameraAdapter[] = "google camera adapter";
219
220   // Enumerate all video capture devices.
221   ScopedComPtr<IMoniker> moniker;
222   int index = 0;
223   while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) {
224     ScopedComPtr<IPropertyBag> prop_bag;
225     hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid());
226     if (FAILED(hr)) {
227       moniker.Release();
228       continue;
229     }
230
231     // Find the description or friendly name.
232     ScopedVariant name;
233     hr = prop_bag->Read(L"Description", name.Receive(), 0);
234     if (FAILED(hr))
235       hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0);
236
237     if (SUCCEEDED(hr) && name.type() == VT_BSTR) {
238       // Ignore all VFW drivers and the special Google Camera Adapter.
239       // Google Camera Adapter is not a real DirectShow camera device.
240       // VFW is very old Video for Windows drivers that can not be used.
241       const wchar_t* str_ptr = V_BSTR(&name);
242       const int name_length = arraysize(kGoogleCameraAdapter) - 1;
243
244       if ((wcsstr(str_ptr, L"(VFW)") == NULL) &&
245           lstrlenW(str_ptr) < name_length ||
246           (!(LowerCaseEqualsASCII(str_ptr, str_ptr + name_length,
247                                   kGoogleCameraAdapter)))) {
248         std::string id;
249         std::string device_name(base::SysWideToUTF8(str_ptr));
250         name.Reset();
251         hr = prop_bag->Read(L"DevicePath", name.Receive(), 0);
252         if (FAILED(hr) || name.type() != VT_BSTR) {
253           id = device_name;
254         } else {
255           DCHECK_EQ(name.type(), VT_BSTR);
256           id = base::SysWideToUTF8(V_BSTR(&name));
257         }
258
259         device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW));
260       }
261     }
262     moniker.Release();
263   }
264 }
265
266 VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name)
267     : device_name_(device_name),
268       state_(kIdle) {
269   DetachFromThread();
270 }
271
272 VideoCaptureDeviceWin::~VideoCaptureDeviceWin() {
273   DCHECK(CalledOnValidThread());
274   if (media_control_)
275     media_control_->Stop();
276
277   if (graph_builder_) {
278     if (sink_filter_) {
279       graph_builder_->RemoveFilter(sink_filter_);
280       sink_filter_ = NULL;
281     }
282
283     if (capture_filter_)
284       graph_builder_->RemoveFilter(capture_filter_);
285
286     if (mjpg_filter_)
287       graph_builder_->RemoveFilter(mjpg_filter_);
288   }
289 }
290
291 bool VideoCaptureDeviceWin::Init() {
292   DCHECK(CalledOnValidThread());
293   HRESULT hr = GetDeviceFilter(device_name_, capture_filter_.Receive());
294   if (!capture_filter_) {
295     DVLOG(2) << "Failed to create capture filter.";
296     return false;
297   }
298
299   hr = GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE,
300               output_capture_pin_.Receive());
301   if (!output_capture_pin_) {
302     DVLOG(2) << "Failed to get capture output pin";
303     return false;
304   }
305
306   // Create the sink filter used for receiving Captured frames.
307   sink_filter_ = new SinkFilter(this);
308   if (sink_filter_ == NULL) {
309     DVLOG(2) << "Failed to create send filter";
310     return false;
311   }
312
313   input_sink_pin_ = sink_filter_->GetPin(0);
314
315   hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL,
316                                      CLSCTX_INPROC_SERVER);
317   if (FAILED(hr)) {
318     DVLOG(2) << "Failed to create graph builder.";
319     return false;
320   }
321
322   hr = graph_builder_.QueryInterface(media_control_.Receive());
323   if (FAILED(hr)) {
324     DVLOG(2) << "Failed to create media control builder.";
325     return false;
326   }
327
328   hr = graph_builder_->AddFilter(capture_filter_, NULL);
329   if (FAILED(hr)) {
330     DVLOG(2) << "Failed to add the capture device to the graph.";
331     return false;
332   }
333
334   hr = graph_builder_->AddFilter(sink_filter_, NULL);
335   if (FAILED(hr)) {
336     DVLOG(2)<< "Failed to add the send filter to the graph.";
337     return false;
338   }
339
340   return CreateCapabilityMap();
341 }
342
343 void VideoCaptureDeviceWin::AllocateAndStart(
344     const VideoCaptureParams& params,
345     scoped_ptr<VideoCaptureDevice::Client> client) {
346   DCHECK(CalledOnValidThread());
347   if (state_ != kIdle)
348     return;
349
350   client_ = client.Pass();
351
352   // Get the camera capability that best match the requested resolution.
353   const VideoCaptureCapabilityWin& found_capability =
354       capabilities_.GetBestMatchedFormat(
355           params.requested_format.frame_size.width(),
356           params.requested_format.frame_size.height(),
357           params.requested_format.frame_rate);
358   VideoCaptureFormat format = found_capability.supported_format;
359
360   // Reduce the frame rate if the requested frame rate is lower
361   // than the capability.
362   if (format.frame_rate > params.requested_format.frame_rate)
363     format.frame_rate = params.requested_format.frame_rate;
364
365   AM_MEDIA_TYPE* pmt = NULL;
366   VIDEO_STREAM_CONFIG_CAPS caps;
367
368   ScopedComPtr<IAMStreamConfig> stream_config;
369   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
370   if (FAILED(hr)) {
371     SetErrorState("Can't get the Capture format settings");
372     return;
373   }
374
375   // Get the windows capability from the capture device.
376   hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt,
377                                     reinterpret_cast<BYTE*>(&caps));
378   if (SUCCEEDED(hr)) {
379     if (pmt->formattype == FORMAT_VideoInfo) {
380       VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat);
381       if (format.frame_rate > 0)
382         h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate;
383     }
384     // Set the sink filter to request this format.
385     sink_filter_->SetRequestedMediaFormat(format);
386     // Order the capture device to use this format.
387     hr = stream_config->SetFormat(pmt);
388   }
389
390   if (FAILED(hr))
391     SetErrorState("Failed to set capture device output format");
392
393   if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) {
394     // Create MJPG filter if we need it.
395     hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC);
396
397     if (SUCCEEDED(hr)) {
398       GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, input_mjpg_pin_.Receive());
399       GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL,
400              output_mjpg_pin_.Receive());
401       hr = graph_builder_->AddFilter(mjpg_filter_, NULL);
402     }
403
404     if (FAILED(hr)) {
405       mjpg_filter_.Release();
406       input_mjpg_pin_.Release();
407       output_mjpg_pin_.Release();
408     }
409   }
410
411   if (format.pixel_format == PIXEL_FORMAT_MJPEG && mjpg_filter_.get()) {
412     // Connect the camera to the MJPEG decoder.
413     hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_,
414                                        NULL);
415     // Connect the MJPEG filter to the Capture filter.
416     hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_,
417                                         NULL);
418   } else {
419     hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_,
420                                        NULL);
421   }
422
423   if (FAILED(hr)) {
424     SetErrorState("Failed to connect the Capture graph.");
425     return;
426   }
427
428   hr = media_control_->Pause();
429   if (FAILED(hr)) {
430     SetErrorState("Failed to Pause the Capture device. "
431                   "Is it already occupied?");
432     return;
433   }
434
435   // Get the format back from the sink filter after the filter have been
436   // connected.
437   capture_format_ = sink_filter_->ResultingFormat();
438
439   // Start capturing.
440   hr = media_control_->Run();
441   if (FAILED(hr)) {
442     SetErrorState("Failed to start the Capture device.");
443     return;
444   }
445
446   state_ = kCapturing;
447 }
448
449 void VideoCaptureDeviceWin::StopAndDeAllocate() {
450   DCHECK(CalledOnValidThread());
451   if (state_ != kCapturing)
452     return;
453
454   HRESULT hr = media_control_->Stop();
455   if (FAILED(hr)) {
456     SetErrorState("Failed to stop the capture graph.");
457     return;
458   }
459
460   graph_builder_->Disconnect(output_capture_pin_);
461   graph_builder_->Disconnect(input_sink_pin_);
462
463   // If the _mjpg filter exist disconnect it even if it has not been used.
464   if (mjpg_filter_) {
465     graph_builder_->Disconnect(input_mjpg_pin_);
466     graph_builder_->Disconnect(output_mjpg_pin_);
467   }
468
469   if (FAILED(hr)) {
470     SetErrorState("Failed to Stop the Capture device");
471     return;
472   }
473   client_.reset();
474   state_ = kIdle;
475 }
476
477 // Implements SinkFilterObserver::SinkFilterObserver.
478 void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer,
479                                           int length) {
480   client_->OnIncomingCapturedFrame(
481       buffer, length, base::TimeTicks::Now(), 0, capture_format_);
482 }
483
484 bool VideoCaptureDeviceWin::CreateCapabilityMap() {
485   DCHECK(CalledOnValidThread());
486   ScopedComPtr<IAMStreamConfig> stream_config;
487   HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive());
488   if (FAILED(hr)) {
489     DVLOG(2) << "Failed to get IAMStreamConfig interface from "
490                 "capture device";
491     return false;
492   }
493
494   // Get interface used for getting the frame rate.
495   ScopedComPtr<IAMVideoControl> video_control;
496   hr = capture_filter_.QueryInterface(video_control.Receive());
497   DVLOG_IF(2, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED";
498
499   AM_MEDIA_TYPE* media_type = NULL;
500   VIDEO_STREAM_CONFIG_CAPS caps;
501   int count, size;
502
503   hr = stream_config->GetNumberOfCapabilities(&count, &size);
504   if (FAILED(hr)) {
505     DVLOG(2) << "Failed to GetNumberOfCapabilities";
506     return false;
507   }
508
509   for (int i = 0; i < count; ++i) {
510     hr = stream_config->GetStreamCaps(i, &media_type,
511                                       reinterpret_cast<BYTE*>(&caps));
512     // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED()
513     // macros here since they'll trigger incorrectly.
514     if (hr != S_OK) {
515       DVLOG(2) << "Failed to GetStreamCaps";
516       return false;
517     }
518
519     if (media_type->majortype == MEDIATYPE_Video &&
520         media_type->formattype == FORMAT_VideoInfo) {
521       VideoCaptureCapabilityWin capability(i);
522       VIDEOINFOHEADER* h =
523           reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat);
524       capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth,
525                                                      h->bmiHeader.biHeight);
526
527       // Try to get a better |time_per_frame| from IAMVideoControl.  If not, use
528       // the value from VIDEOINFOHEADER.
529       REFERENCE_TIME time_per_frame = h->AvgTimePerFrame;
530       if (video_control) {
531         ScopedCoMem<LONGLONG> max_fps;
532         LONG list_size = 0;
533         SIZE size = {capability.supported_format.frame_size.width(),
534                      capability.supported_format.frame_size.height()};
535
536         // GetFrameRateList doesn't return max frame rate always
537         // eg: Logitech Notebook. This may be due to a bug in that API
538         // because GetFrameRateList array is reversed in the above camera. So
539         // a util method written. Can't assume the first value will return
540         // the max fps.
541         hr = video_control->GetFrameRateList(output_capture_pin_, i, size,
542                                              &list_size, &max_fps);
543         // Sometimes |list_size| will be > 0, but max_fps will be NULL.  Some
544         // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates
545         // into success, so explicitly check S_OK.  See http://crbug.com/306237.
546         if (hr == S_OK && list_size > 0 && max_fps) {
547           time_per_frame = *std::min_element(max_fps.get(),
548                                              max_fps.get() + list_size);
549         }
550       }
551
552       capability.supported_format.frame_rate =
553           (time_per_frame > 0)
554               ? static_cast<int>(kSecondsToReferenceTime / time_per_frame)
555               : 0;
556
557       // DirectShow works at the moment only on integer frame_rate but the
558       // best capability matching class works on rational frame rates.
559       capability.frame_rate_numerator = capability.supported_format.frame_rate;
560       capability.frame_rate_denominator = 1;
561
562       // We can't switch MEDIATYPE :~(.
563       if (media_type->subtype == kMediaSubTypeI420) {
564         capability.supported_format.pixel_format = PIXEL_FORMAT_I420;
565       } else if (media_type->subtype == MEDIASUBTYPE_IYUV) {
566         // This is identical to PIXEL_FORMAT_I420.
567         capability.supported_format.pixel_format = PIXEL_FORMAT_I420;
568       } else if (media_type->subtype == MEDIASUBTYPE_RGB24) {
569         capability.supported_format.pixel_format = PIXEL_FORMAT_RGB24;
570       } else if (media_type->subtype == MEDIASUBTYPE_YUY2) {
571         capability.supported_format.pixel_format = PIXEL_FORMAT_YUY2;
572       } else if (media_type->subtype == MEDIASUBTYPE_MJPG) {
573         capability.supported_format.pixel_format = PIXEL_FORMAT_MJPEG;
574       } else if (media_type->subtype == MEDIASUBTYPE_UYVY) {
575         capability.supported_format.pixel_format = PIXEL_FORMAT_UYVY;
576       } else if (media_type->subtype == MEDIASUBTYPE_ARGB32) {
577         capability.supported_format.pixel_format = PIXEL_FORMAT_ARGB;
578       } else {
579         WCHAR guid_str[128];
580         StringFromGUID2(media_type->subtype, guid_str, arraysize(guid_str));
581         DVLOG(2) << "Device supports (also) an unknown media type " << guid_str;
582         continue;
583       }
584       capabilities_.Add(capability);
585     }
586     DeleteMediaType(media_type);
587     media_type = NULL;
588   }
589
590   return !capabilities_.empty();
591 }
592
593 void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) {
594   DCHECK(CalledOnValidThread());
595   DVLOG(1) << reason;
596   state_ = kError;
597   client_->OnError(reason);
598 }
599 }  // namespace media