Chromium Code Reviews| Index: media/video/capture/win/video_capture_device_factory_win.cc |
| diff --git a/media/video/capture/win/video_capture_device_win.cc b/media/video/capture/win/video_capture_device_factory_win.cc |
| similarity index 29% |
| copy from media/video/capture/win/video_capture_device_win.cc |
| copy to media/video/capture/win/video_capture_device_factory_win.cc |
| index e78c60450dd9bd7e2f9dba5135121109e8527dbf..d03613b1eefc493f31285317c17ad492e77efd0e 100644 |
| --- a/media/video/capture/win/video_capture_device_win.cc |
| +++ b/media/video/capture/win/video_capture_device_factory_win.cc |
| @@ -1,16 +1,14 @@ |
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -#include "media/video/capture/win/video_capture_device_win.h" |
| - |
| -#include <ks.h> |
| -#include <ksmedia.h> |
| +#include "media/video/capture/win/video_capture_device_factory_win.h" |
| -#include <algorithm> |
| -#include <list> |
| +#include <mfapi.h> |
| +#include <mferror.h> |
| #include "base/command_line.h" |
| +#include "base/lazy_instance.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/sys_string_conversions.h" |
| #include "base/win/metro.h" |
| @@ -19,257 +17,84 @@ |
| #include "base/win/windows_version.h" |
| #include "media/base/media_switches.h" |
| #include "media/video/capture/win/video_capture_device_mf_win.h" |
| +#include "media/video/capture/win/video_capture_device_win.h" |
| using base::win::ScopedCoMem; |
| using base::win::ScopedComPtr; |
| using base::win::ScopedVariant; |
| namespace media { |
| -namespace { |
| - |
| -// Finds and creates a DirectShow Video Capture filter matching the device_name. |
| -HRESULT GetDeviceFilter(const VideoCaptureDevice::Name& device_name, |
| - IBaseFilter** filter) { |
| - DCHECK(filter); |
| - |
| - ScopedComPtr<ICreateDevEnum> dev_enum; |
| - HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, |
| - CLSCTX_INPROC); |
| - if (FAILED(hr)) |
| - return hr; |
| - ScopedComPtr<IEnumMoniker> enum_moniker; |
| - hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, |
| - enum_moniker.Receive(), 0); |
| - // CreateClassEnumerator returns S_FALSE on some Windows OS |
| - // when no camera exist. Therefore the FAILED macro can't be used. |
| - if (hr != S_OK) |
| - return NULL; |
| - |
| - ScopedComPtr<IMoniker> moniker; |
| - ScopedComPtr<IBaseFilter> capture_filter; |
| - DWORD fetched = 0; |
| - while (enum_moniker->Next(1, moniker.Receive(), &fetched) == S_OK) { |
| - ScopedComPtr<IPropertyBag> prop_bag; |
| - hr = moniker->BindToStorage(0, 0, IID_IPropertyBag, prop_bag.ReceiveVoid()); |
| - if (FAILED(hr)) { |
| - moniker.Release(); |
| - continue; |
| - } |
| - |
| - // Find the description or friendly name. |
| - static const wchar_t* kPropertyNames[] = { |
| - L"DevicePath", L"Description", L"FriendlyName" |
| - }; |
| - ScopedVariant name; |
| - for (size_t i = 0; |
| - i < arraysize(kPropertyNames) && name.type() != VT_BSTR; ++i) { |
| - prop_bag->Read(kPropertyNames[i], name.Receive(), 0); |
| - } |
| - if (name.type() == VT_BSTR) { |
| - std::string device_path(base::SysWideToUTF8(V_BSTR(&name))); |
| - if (device_path.compare(device_name.id()) == 0) { |
| - // We have found the requested device |
| - hr = moniker->BindToObject(0, 0, IID_IBaseFilter, |
| - capture_filter.ReceiveVoid()); |
| - DVPLOG_IF(2, FAILED(hr)) << "Failed to bind camera filter."; |
| - break; |
| - } |
| - } |
| - moniker.Release(); |
| - } |
| - |
| - *filter = capture_filter.Detach(); |
| - if (!*filter && SUCCEEDED(hr)) |
| - hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
| - |
| - return hr; |
| -} |
| - |
| -// Check if a Pin matches a category. |
| -bool PinMatchesCategory(IPin* pin, REFGUID category) { |
| - DCHECK(pin); |
| - bool found = false; |
| - ScopedComPtr<IKsPropertySet> ks_property; |
| - HRESULT hr = ks_property.QueryFrom(pin); |
| - if (SUCCEEDED(hr)) { |
| - GUID pin_category; |
| - DWORD return_value; |
| - hr = ks_property->Get(AMPROPSETID_Pin, AMPROPERTY_PIN_CATEGORY, NULL, 0, |
| - &pin_category, sizeof(pin_category), &return_value); |
| - if (SUCCEEDED(hr) && (return_value == sizeof(pin_category))) { |
| - found = (pin_category == category); |
| - } |
| - } |
| - return found; |
| -} |
| +// Lazy Instance to initialize the MediaFoundation Library. |
| +class MFInitializerSingleton { |
| + public: |
| + MFInitializerSingleton() { MFStartup(MF_VERSION, MFSTARTUP_LITE); } |
| + ~MFInitializerSingleton() { MFShutdown(); } |
| +}; |
| -// Finds a IPin on a IBaseFilter given the direction an category. |
| -ScopedComPtr<IPin> GetPin(IBaseFilter* filter, PIN_DIRECTION pin_dir, |
| - REFGUID category) { |
| - ScopedComPtr<IPin> pin; |
| - ScopedComPtr<IEnumPins> pin_emum; |
| - HRESULT hr = filter->EnumPins(pin_emum.Receive()); |
| - if (pin_emum == NULL) |
| - return pin; |
| - |
| - // Get first unconnected pin. |
| - hr = pin_emum->Reset(); // set to first pin |
| - while ((hr = pin_emum->Next(1, pin.Receive(), NULL)) == S_OK) { |
| - PIN_DIRECTION this_pin_dir = static_cast<PIN_DIRECTION>(-1); |
| - hr = pin->QueryDirection(&this_pin_dir); |
| - if (pin_dir == this_pin_dir) { |
| - if (category == GUID_NULL || PinMatchesCategory(pin, category)) |
| - return pin; |
| - } |
| - pin.Release(); |
| - } |
| +static base::LazyInstance<MFInitializerSingleton> g_mf_initialize = |
| + LAZY_INSTANCE_INITIALIZER; |
| - DCHECK(!pin); |
| - return pin; |
| +static void EnsureMediaFoundationInit() { |
| + g_mf_initialize.Get(); |
| } |
| -// Release the format block for a media type. |
| -// http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx |
| -void FreeMediaType(AM_MEDIA_TYPE* mt) { |
| - if (mt->cbFormat != 0) { |
| - CoTaskMemFree(mt->pbFormat); |
| - mt->cbFormat = 0; |
| - mt->pbFormat = NULL; |
| - } |
| - if (mt->pUnk != NULL) { |
| - NOTREACHED(); |
| - // pUnk should not be used. |
| - mt->pUnk->Release(); |
| - mt->pUnk = NULL; |
| - } |
| -} |
| +// TODO(mcasas): This method is only used from PlatformSupportsMediaFoundation, |
| +// who is either hiding behind a DCHECK or used from tests only. Consider |
| +// removing this function altogether and rely only on MFInitializerSingleton for |
| +// initializing MF. |
|
tommi (sloooow) - chröme
2014/05/15 15:18:33
See the comments where LoadMediaFoundationDlls is
mcasas
2014/05/15 16:03:23
Works for me!
|
| +static bool LoadMediaFoundationDlls() { |
| + static const wchar_t* const kMfDLLs[] = { |
| + L"%WINDIR%\\system32\\mf.dll", |
| + L"%WINDIR%\\system32\\mfplat.dll", |
| + L"%WINDIR%\\system32\\mfreadwrite.dll", |
| + }; |
| -// Delete a media type structure that was allocated on the heap. |
| -// http://msdn.microsoft.com/en-us/library/dd375432(VS.85).aspx |
| -void DeleteMediaType(AM_MEDIA_TYPE* mt) { |
| - if (mt != NULL) { |
| - FreeMediaType(mt); |
| - CoTaskMemFree(mt); |
| + for (int i = 0; i < arraysize(kMfDLLs); ++i) { |
| + wchar_t path[MAX_PATH] = {0}; |
| + ExpandEnvironmentStringsW(kMfDLLs[i], path, arraysize(path)); |
| + if (!LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)) |
| + return false; |
| } |
| + return true; |
| } |
| -// A utility class that wraps the AM_MEDIA_TYPE type and guarantees that |
| -// we free the structure when exiting the scope. DCHECKing is also done to |
| -// avoid memory leaks. |
| -class ScopedMediaType { |
| - public: |
| - ScopedMediaType() : media_type_(NULL) {} |
| - ~ScopedMediaType() { Free(); } |
| - |
| - AM_MEDIA_TYPE* operator->() { return media_type_; } |
| - AM_MEDIA_TYPE* get() { return media_type_; } |
| - |
| - void Free() { |
| - if (!media_type_) |
| - return; |
| - |
| - DeleteMediaType(media_type_); |
| - media_type_= NULL; |
| - } |
| - |
| - AM_MEDIA_TYPE** Receive() { |
| - DCHECK(!media_type_); |
| - return &media_type_; |
| - } |
| +static bool PrepareVideoCaptureAttributesMediaFoundation( |
| + IMFAttributes** attributes, |
| + int count) { |
| + EnsureMediaFoundationInit(); |
| - private: |
| - AM_MEDIA_TYPE* media_type_; |
| -}; |
| + if (FAILED(MFCreateAttributes(attributes, count))) |
| + return false; |
| -VideoPixelFormat TranslateMediaSubtypeToPixelFormat(const GUID& sub_type) { |
| - static struct { |
| - const GUID& sub_type; |
| - VideoPixelFormat format; |
| - } pixel_formats[] = { |
| - { kMediaSubTypeI420, PIXEL_FORMAT_I420 }, |
| - { MEDIASUBTYPE_IYUV, PIXEL_FORMAT_I420 }, |
| - { MEDIASUBTYPE_RGB24, PIXEL_FORMAT_RGB24 }, |
| - { MEDIASUBTYPE_YUY2, PIXEL_FORMAT_YUY2 }, |
| - { MEDIASUBTYPE_MJPG, PIXEL_FORMAT_MJPEG }, |
| - { MEDIASUBTYPE_UYVY, PIXEL_FORMAT_UYVY }, |
| - { MEDIASUBTYPE_ARGB32, PIXEL_FORMAT_ARGB }, |
| - }; |
| - for (size_t i = 0; i < ARRAYSIZE_UNSAFE(pixel_formats); ++i) { |
| - if (sub_type == pixel_formats[i].sub_type) |
| - return pixel_formats[i].format; |
| - } |
| -#ifndef NDEBUG |
| - WCHAR guid_str[128]; |
| - StringFromGUID2(sub_type, guid_str, arraysize(guid_str)); |
| - DVLOG(2) << "Device (also) supports an unknown media type " << guid_str; |
| -#endif |
| - return PIXEL_FORMAT_UNKNOWN; |
| + return SUCCEEDED((*attributes)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, |
| + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)); |
| } |
| -} // namespace |
| +static bool CreateVideoCaptureDeviceMediaFoundation(const char* sym_link, |
| + IMFMediaSource** source) { |
| + ScopedComPtr<IMFAttributes> attributes; |
| + if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 2)) |
| + return false; |
| -// static |
| -void VideoCaptureDevice::GetDeviceNames(Names* device_names) { |
| - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| - // Use Media Foundation for Metro processes (after and including Win8) and |
| - // DirectShow for any other versions, unless forced via flag. Media Foundation |
| - // can also be forced if appropriate flag is set and we are in Windows 7 or |
| - // 8 in non-Metro mode. |
| - if ((base::win::IsMetroProcess() && |
| - !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) || |
| - (base::win::GetVersion() >= base::win::VERSION_WIN7 && |
| - cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture))) { |
| - VideoCaptureDeviceMFWin::GetDeviceNames(device_names); |
| - } else { |
| - VideoCaptureDeviceWin::GetDeviceNames(device_names); |
| - } |
| -} |
| + attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, |
| + base::SysUTF8ToWide(sym_link).c_str()); |
| -// static |
| -void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device, |
| - VideoCaptureFormats* formats) { |
| - const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| - // Use Media Foundation for Metro processes (after and including Win8) and |
| - // DirectShow for any other versions, unless forced via flag. Media Foundation |
| - // can also be forced if appropriate flag is set and we are in Windows 7 or |
| - // 8 in non-Metro mode. |
| - if ((base::win::IsMetroProcess() && |
| - !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) || |
| - (base::win::GetVersion() >= base::win::VERSION_WIN7 && |
| - cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture))) { |
| - VideoCaptureDeviceMFWin::GetDeviceSupportedFormats(device, formats); |
| - } else { |
| - VideoCaptureDeviceWin::GetDeviceSupportedFormats(device, formats); |
| - } |
| + return SUCCEEDED(MFCreateDeviceSource(attributes, source)); |
| } |
| -// static |
| -VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) { |
| - VideoCaptureDevice* ret = NULL; |
| - if (device_name.capture_api_type() == Name::MEDIA_FOUNDATION) { |
| - DCHECK(VideoCaptureDeviceMFWin::PlatformSupported()); |
| - scoped_ptr<VideoCaptureDeviceMFWin> device( |
| - new VideoCaptureDeviceMFWin(device_name)); |
| - DVLOG(1) << " MediaFoundation Device: " << device_name.name(); |
| - if (device->Init()) |
| - ret = device.release(); |
| - } else if (device_name.capture_api_type() == Name::DIRECT_SHOW) { |
| - scoped_ptr<VideoCaptureDeviceWin> device( |
| - new VideoCaptureDeviceWin(device_name)); |
| - DVLOG(1) << " DirectShow Device: " << device_name.name(); |
| - if (device->Init()) |
| - ret = device.release(); |
| - } else{ |
| - NOTREACHED() << " Couldn't recognize VideoCaptureDevice type"; |
| - } |
| +static bool EnumerateVideoDevicesMediaFoundation(IMFActivate*** devices, |
| + UINT32* count) { |
| + ScopedComPtr<IMFAttributes> attributes; |
| + if (!PrepareVideoCaptureAttributesMediaFoundation(attributes.Receive(), 1)) |
| + return false; |
| - return ret; |
| + return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count)); |
| } |
| -// static |
| -void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) { |
| +static void GetDeviceNamesDirectShow(VideoCaptureDevice::Names* device_names) { |
| DCHECK(device_names); |
| + DVLOG(1) << " GetDeviceNamesDirectShow"; |
| ScopedComPtr<ICreateDevEnum> dev_enum; |
| HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, |
| @@ -330,17 +155,47 @@ void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) { |
| id = base::SysWideToUTF8(V_BSTR(&name)); |
| } |
| - device_names->push_back(Name(device_name, id, Name::DIRECT_SHOW)); |
| + device_names->push_back(VideoCaptureDevice::Name(device_name, id, |
| + VideoCaptureDevice::Name::DIRECT_SHOW)); |
| } |
| } |
| moniker.Release(); |
| } |
| } |
| -// static |
| -void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device, |
| +static void GetDeviceNamesMediaFoundation( |
| + VideoCaptureDevice::Names* device_names) { |
| + DVLOG(1) << " GetDeviceNamesMediaFoundation"; |
| + ScopedCoMem<IMFActivate*> devices; |
| + UINT32 count; |
| + if (!EnumerateVideoDevicesMediaFoundation(&devices, &count)) |
| + return; |
| + |
| + HRESULT hr; |
| + for (UINT32 i = 0; i < count; ++i) { |
| + UINT32 name_size, id_size; |
| + ScopedCoMem<wchar_t> name, id; |
| + if (SUCCEEDED(hr = devices[i]->GetAllocatedString( |
| + MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size)) && |
| + SUCCEEDED(hr = devices[i]->GetAllocatedString( |
| + MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id, |
| + &id_size))) { |
| + std::wstring name_w(name, name_size), id_w(id, id_size); |
| + VideoCaptureDevice::Name device(base::SysWideToUTF8(name_w), |
| + base::SysWideToUTF8(id_w), |
| + VideoCaptureDevice::Name::MEDIA_FOUNDATION); |
| + device_names->push_back(device); |
| + } else { |
| + DLOG(WARNING) << "GetAllocatedString failed: " << std::hex << hr; |
| + } |
| + devices[i]->Release(); |
| + } |
| +} |
| + |
| +static void GetDeviceSupportedFormatsDirectShow( |
| + const VideoCaptureDevice::Name& device, |
| VideoCaptureFormats* formats) { |
| - DVLOG(1) << "GetDeviceSupportedFormats for " << device.name(); |
| + DVLOG(1) << "GetDeviceSupportedFormatsDirectShow for " << device.name(); |
| ScopedComPtr<ICreateDevEnum> dev_enum; |
| HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, |
| CLSCTX_INPROC); |
| @@ -381,14 +236,17 @@ void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device, |
| if (moniker.get()) { |
| base::win::ScopedComPtr<IBaseFilter> capture_filter; |
| - hr = GetDeviceFilter(device, capture_filter.Receive()); |
| + hr = VideoCaptureDeviceWin::GetDeviceFilter(device, |
| + capture_filter.Receive()); |
| if (!capture_filter) { |
| DVLOG(2) << "Failed to create capture filter."; |
| return; |
| } |
| base::win::ScopedComPtr<IPin> output_capture_pin( |
| - GetPin(capture_filter, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE)); |
| + VideoCaptureDeviceWin::GetPin(capture_filter, |
| + PINDIR_OUTPUT, |
| + PIN_CATEGORY_CAPTURE)); |
| if (!output_capture_pin) { |
| DVLOG(2) << "Failed to get capture output pin"; |
| return; |
| @@ -411,7 +269,7 @@ void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device, |
| scoped_ptr<BYTE[]> caps(new BYTE[size]); |
| for (int i = 0; i < count; ++i) { |
| - ScopedMediaType media_type; |
| + VideoCaptureDeviceWin::ScopedMediaType media_type; |
| hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get()); |
| // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED() |
| // macros here since they'll trigger incorrectly. |
| @@ -424,7 +282,8 @@ void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device, |
| media_type->formattype == FORMAT_VideoInfo) { |
| VideoCaptureFormat format; |
| format.pixel_format = |
| - TranslateMediaSubtypeToPixelFormat(media_type->subtype); |
| + VideoCaptureDeviceWin::TranslateMediaSubtypeToPixelFormat( |
| + media_type->subtype); |
| if (format.pixel_format == PIXEL_FORMAT_UNKNOWN) |
| continue; |
| VIDEOINFOHEADER* h = |
| @@ -444,355 +303,143 @@ void VideoCaptureDeviceWin::GetDeviceSupportedFormats(const Name& device, |
| } |
| } |
| -VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) |
| - : device_name_(device_name), |
| - state_(kIdle) { |
| - DetachFromThread(); |
| -} |
| - |
| -VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { |
| - DCHECK(CalledOnValidThread()); |
| - if (media_control_) |
| - media_control_->Stop(); |
| - |
| - if (graph_builder_) { |
| - if (sink_filter_) { |
| - graph_builder_->RemoveFilter(sink_filter_); |
| - sink_filter_ = NULL; |
| - } |
| - |
| - if (capture_filter_) |
| - graph_builder_->RemoveFilter(capture_filter_); |
| - |
| - if (mjpg_filter_) |
| - graph_builder_->RemoveFilter(mjpg_filter_); |
| - } |
| -} |
| - |
| -bool VideoCaptureDeviceWin::Init() { |
| - DCHECK(CalledOnValidThread()); |
| - HRESULT hr = GetDeviceFilter(device_name_, capture_filter_.Receive()); |
| - if (!capture_filter_) { |
| - DVLOG(2) << "Failed to create capture filter."; |
| - return false; |
| - } |
| - |
| - output_capture_pin_ = |
| - GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE); |
| - if (!output_capture_pin_) { |
| - DVLOG(2) << "Failed to get capture output pin"; |
| - return false; |
| - } |
| - |
| - // Create the sink filter used for receiving Captured frames. |
| - sink_filter_ = new SinkFilter(this); |
| - if (sink_filter_ == NULL) { |
| - DVLOG(2) << "Failed to create send filter"; |
| - return false; |
| - } |
| - |
| - input_sink_pin_ = sink_filter_->GetPin(0); |
| - |
| - hr = graph_builder_.CreateInstance(CLSID_FilterGraph, NULL, |
| - CLSCTX_INPROC_SERVER); |
| - if (FAILED(hr)) { |
| - DVLOG(2) << "Failed to create graph builder."; |
| - return false; |
| - } |
| - |
| - hr = graph_builder_.QueryInterface(media_control_.Receive()); |
| - if (FAILED(hr)) { |
| - DVLOG(2) << "Failed to create media control builder."; |
| - return false; |
| - } |
| - |
| - hr = graph_builder_->AddFilter(capture_filter_, NULL); |
| - if (FAILED(hr)) { |
| - DVLOG(2) << "Failed to add the capture device to the graph."; |
| - return false; |
| - } |
| - |
| - hr = graph_builder_->AddFilter(sink_filter_, NULL); |
| - if (FAILED(hr)) { |
| - DVLOG(2)<< "Failed to add the send filter to the graph."; |
| - return false; |
| - } |
| - |
| - return CreateCapabilityMap(); |
| -} |
| - |
| -void VideoCaptureDeviceWin::AllocateAndStart( |
| - const VideoCaptureParams& params, |
| - scoped_ptr<VideoCaptureDevice::Client> client) { |
| - DCHECK(CalledOnValidThread()); |
| - if (state_ != kIdle) |
| - return; |
| - |
| - client_ = client.Pass(); |
| - |
| - // Get the camera capability that best match the requested resolution. |
| - const VideoCaptureCapabilityWin& found_capability = |
| - capabilities_.GetBestMatchedFormat( |
| - params.requested_format.frame_size.width(), |
| - params.requested_format.frame_size.height(), |
| - params.requested_format.frame_rate); |
| - VideoCaptureFormat format = found_capability.supported_format; |
| - |
| - // Reduce the frame rate if the requested frame rate is lower |
| - // than the capability. |
| - if (format.frame_rate > params.requested_format.frame_rate) |
| - format.frame_rate = params.requested_format.frame_rate; |
| - |
| - ScopedComPtr<IAMStreamConfig> stream_config; |
| - HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
| - if (FAILED(hr)) { |
| - SetErrorState("Can't get the Capture format settings"); |
| +static void GetDeviceSupportedFormatsMediaFoundation( |
| + const VideoCaptureDevice::Name& device, |
| + VideoCaptureFormats* formats) { |
| + DVLOG(1) << "GetDeviceSupportedFormatsMediaFoundation for " << device.name(); |
| + ScopedComPtr<IMFMediaSource> source; |
| + if (!CreateVideoCaptureDeviceMediaFoundation(device.id().c_str(), |
| + source.Receive())) { |
| return; |
| } |
| - int count = 0, size = 0; |
| - hr = stream_config->GetNumberOfCapabilities(&count, &size); |
| - if (FAILED(hr)) { |
| - DVLOG(2) << "Failed to GetNumberOfCapabilities"; |
| + HRESULT hr; |
| + base::win::ScopedComPtr<IMFSourceReader> reader; |
| + if (FAILED(hr = MFCreateSourceReaderFromMediaSource(source, NULL, |
| + reader.Receive()))) { |
| + DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource: " << std::hex << hr; |
| return; |
| } |
| - scoped_ptr<BYTE[]> caps(new BYTE[size]); |
| - ScopedMediaType media_type; |
| - |
| - // Get the windows capability from the capture device. |
| - hr = stream_config->GetStreamCaps( |
| - found_capability.stream_index, media_type.Receive(), caps.get()); |
| - if (SUCCEEDED(hr)) { |
| - if (media_type->formattype == FORMAT_VideoInfo) { |
| - VIDEOINFOHEADER* h = |
| - reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); |
| - if (format.frame_rate > 0) |
| - h->AvgTimePerFrame = kSecondsToReferenceTime / format.frame_rate; |
| + DWORD stream_index = 0; |
| + ScopedComPtr<IMFMediaType> type; |
| + while (SUCCEEDED(hr = reader->GetNativeMediaType( |
| + MF_SOURCE_READER_FIRST_VIDEO_STREAM, stream_index, type.Receive()))) { |
| + UINT32 width, height; |
| + hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height); |
| + if (FAILED(hr)) { |
| + DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr; |
| + return; |
| } |
| - // Set the sink filter to request this format. |
| - sink_filter_->SetRequestedMediaFormat(format); |
| - // Order the capture device to use this format. |
| - hr = stream_config->SetFormat(media_type.get()); |
| - } |
| - |
| - if (FAILED(hr)) |
| - SetErrorState("Failed to set capture device output format"); |
| - |
| - if (format.pixel_format == PIXEL_FORMAT_MJPEG && !mjpg_filter_.get()) { |
| - // Create MJPG filter if we need it. |
| - hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); |
| + VideoCaptureFormat capture_format; |
| + capture_format.frame_size.SetSize(width, height); |
| - if (SUCCEEDED(hr)) { |
| - input_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL); |
| - output_mjpg_pin_ = GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL); |
| - hr = graph_builder_->AddFilter(mjpg_filter_, NULL); |
| + UINT32 numerator, denominator; |
| + hr = MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator); |
| + if (FAILED(hr)) { |
| + DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr; |
| + return; |
| } |
| + capture_format.frame_rate = denominator ? numerator / denominator : 0; |
| + GUID type_guid; |
| + hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid); |
| if (FAILED(hr)) { |
| - mjpg_filter_.Release(); |
| - input_mjpg_pin_.Release(); |
| - output_mjpg_pin_.Release(); |
| + DLOG(ERROR) << "GetGUID: " << std::hex << hr; |
| + return; |
| } |
| - } |
| - |
| - SetAntiFlickerInCaptureFilter(); |
| - |
| - if (format.pixel_format == PIXEL_FORMAT_MJPEG && mjpg_filter_.get()) { |
| - // Connect the camera to the MJPEG decoder. |
| - hr = graph_builder_->ConnectDirect(output_capture_pin_, input_mjpg_pin_, |
| - NULL); |
| - // Connect the MJPEG filter to the Capture filter. |
| - hr += graph_builder_->ConnectDirect(output_mjpg_pin_, input_sink_pin_, |
| - NULL); |
| - } else { |
| - hr = graph_builder_->ConnectDirect(output_capture_pin_, input_sink_pin_, |
| - NULL); |
| - } |
| + VideoCaptureDeviceMFWin::FormatFromGuid(type_guid, |
| + &capture_format.pixel_format); |
| + type.Release(); |
| + formats->push_back(capture_format); |
| + ++stream_index; |
| - if (FAILED(hr)) { |
| - SetErrorState("Failed to connect the Capture graph."); |
| - return; |
| + DVLOG(1) << device.name() << " resolution: " |
| + << capture_format.frame_size.ToString() << ", fps: " |
| + << capture_format.frame_rate << ", pixel format: " |
| + << capture_format.pixel_format; |
| } |
| - |
| - hr = media_control_->Pause(); |
| - if (FAILED(hr)) { |
| - SetErrorState("Failed to Pause the Capture device. " |
| - "Is it already occupied?"); |
| - return; |
| - } |
| - |
| - // Get the format back from the sink filter after the filter have been |
| - // connected. |
| - capture_format_ = sink_filter_->ResultingFormat(); |
| - |
| - // Start capturing. |
| - hr = media_control_->Run(); |
| - if (FAILED(hr)) { |
| - SetErrorState("Failed to start the Capture device."); |
| - return; |
| - } |
| - |
| - state_ = kCapturing; |
| } |
| -void VideoCaptureDeviceWin::StopAndDeAllocate() { |
| - DCHECK(CalledOnValidThread()); |
| - if (state_ != kCapturing) |
| - return; |
| - |
| - HRESULT hr = media_control_->Stop(); |
| - if (FAILED(hr)) { |
| - SetErrorState("Failed to stop the capture graph."); |
| - return; |
| - } |
| - |
| - graph_builder_->Disconnect(output_capture_pin_); |
| - graph_builder_->Disconnect(input_sink_pin_); |
| - |
| - // If the _mjpg filter exist disconnect it even if it has not been used. |
| - if (mjpg_filter_) { |
| - graph_builder_->Disconnect(input_mjpg_pin_); |
| - graph_builder_->Disconnect(output_mjpg_pin_); |
| - } |
| - |
| - if (FAILED(hr)) { |
| - SetErrorState("Failed to Stop the Capture device"); |
| - return; |
| - } |
| - client_.reset(); |
| - state_ = kIdle; |
| -} |
| - |
| -// Implements SinkFilterObserver::SinkFilterObserver. |
| -void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, |
| - int length) { |
| - client_->OnIncomingCapturedData( |
| - buffer, length, capture_format_, 0, base::TimeTicks::Now()); |
| -} |
| - |
| -bool VideoCaptureDeviceWin::CreateCapabilityMap() { |
| - DCHECK(CalledOnValidThread()); |
| - ScopedComPtr<IAMStreamConfig> stream_config; |
| - HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
| - if (FAILED(hr)) { |
| - DVLOG(2) << "Failed to get IAMStreamConfig interface from " |
| - "capture device"; |
| - return false; |
| - } |
| - |
| - // Get interface used for getting the frame rate. |
| - ScopedComPtr<IAMVideoControl> video_control; |
| - hr = capture_filter_.QueryInterface(video_control.Receive()); |
| - DVLOG_IF(2, FAILED(hr)) << "IAMVideoControl Interface NOT SUPPORTED"; |
| - |
| - int count = 0, size = 0; |
| - hr = stream_config->GetNumberOfCapabilities(&count, &size); |
| - if (FAILED(hr)) { |
| - DVLOG(2) << "Failed to GetNumberOfCapabilities"; |
| +// Returns true iff the current platform supports the Media Foundation API |
| +// and that the DLLs are available. On Vista this API is an optional download |
| +// but the API is advertised as a part of Windows 7 and onwards. However, |
| +// we've seen that the required DLLs are not available in some Win7 |
| +// distributions such as Windows 7 N and Windows 7 KN. |
| +// static |
| +bool VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation() { |
| + // Even though the DLLs might be available on Vista, we get crashes |
| + // when running our tests on the build bots. |
| + if (base::win::GetVersion() < base::win::VERSION_WIN7) |
| return false; |
| - } |
| - scoped_ptr<BYTE[]> caps(new BYTE[size]); |
| - for (int i = 0; i < count; ++i) { |
| - ScopedMediaType media_type; |
| - hr = stream_config->GetStreamCaps(i, media_type.Receive(), caps.get()); |
| - // GetStreamCaps() may return S_FALSE, so don't use FAILED() or SUCCEED() |
| - // macros here since they'll trigger incorrectly. |
| - if (hr != S_OK) { |
| - DVLOG(2) << "Failed to GetStreamCaps"; |
| - return false; |
| - } |
| - |
| - if (media_type->majortype == MEDIATYPE_Video && |
| - media_type->formattype == FORMAT_VideoInfo) { |
| - VideoCaptureCapabilityWin capability(i); |
| - capability.supported_format.pixel_format = |
| - TranslateMediaSubtypeToPixelFormat(media_type->subtype); |
| - if (capability.supported_format.pixel_format == PIXEL_FORMAT_UNKNOWN) |
| - continue; |
| - |
| - VIDEOINFOHEADER* h = |
| - reinterpret_cast<VIDEOINFOHEADER*>(media_type->pbFormat); |
| - capability.supported_format.frame_size.SetSize(h->bmiHeader.biWidth, |
| - h->bmiHeader.biHeight); |
| - |
| - // Try to get a better |time_per_frame| from IAMVideoControl. If not, use |
| - // the value from VIDEOINFOHEADER. |
| - REFERENCE_TIME time_per_frame = h->AvgTimePerFrame; |
| - if (video_control) { |
| - ScopedCoMem<LONGLONG> max_fps; |
| - LONG list_size = 0; |
| - SIZE size = {capability.supported_format.frame_size.width(), |
| - capability.supported_format.frame_size.height()}; |
| - |
| - // GetFrameRateList doesn't return max frame rate always |
| - // eg: Logitech Notebook. This may be due to a bug in that API |
| - // because GetFrameRateList array is reversed in the above camera. So |
| - // a util method written. Can't assume the first value will return |
| - // the max fps. |
| - hr = video_control->GetFrameRateList(output_capture_pin_, i, size, |
| - &list_size, &max_fps); |
| - // Sometimes |list_size| will be > 0, but max_fps will be NULL. Some |
| - // drivers may return an HRESULT of S_FALSE which SUCCEEDED() translates |
| - // into success, so explicitly check S_OK. See http://crbug.com/306237. |
| - if (hr == S_OK && list_size > 0 && max_fps) { |
| - time_per_frame = *std::min_element(max_fps.get(), |
| - max_fps.get() + list_size); |
| - } |
| - } |
| - |
| - capability.supported_format.frame_rate = |
| - (time_per_frame > 0) |
| - ? static_cast<int>(kSecondsToReferenceTime / time_per_frame) |
| - : 0; |
| - |
| - // DirectShow works at the moment only on integer frame_rate but the |
| - // best capability matching class works on rational frame rates. |
| - capability.frame_rate_numerator = capability.supported_format.frame_rate; |
| - capability.frame_rate_denominator = 1; |
| + static bool g_dlls_available = LoadMediaFoundationDlls(); |
| + return g_dlls_available; |
| +} |
| - capabilities_.Add(capability); |
| +scoped_ptr<VideoCaptureDevice> VideoCaptureDeviceFactoryWin::Create( |
| + const VideoCaptureDevice::Name& device_name) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + scoped_ptr<VideoCaptureDevice> device; |
| + if (device_name.capture_api_type() == |
| + VideoCaptureDevice::Name::MEDIA_FOUNDATION) { |
| + DCHECK(PlatformSupportsMediaFoundation()); |
| + device.reset(new VideoCaptureDeviceMFWin(device_name)); |
| + DVLOG(1) << " MediaFoundation Device: " << device_name.name(); |
| + ScopedComPtr<IMFMediaSource> source; |
| + if (!CreateVideoCaptureDeviceMediaFoundation(device_name.id().c_str(), |
| + source.Receive())) { |
| + return scoped_ptr<VideoCaptureDevice>(); |
| } |
| + if (!static_cast<VideoCaptureDeviceMFWin*>(device.get())->Init(source)) |
| + device.reset(); |
| + } else if (device_name.capture_api_type() == |
| + VideoCaptureDevice::Name::DIRECT_SHOW) { |
| + device.reset(new VideoCaptureDeviceWin(device_name)); |
| + DVLOG(1) << " DirectShow Device: " << device_name.name(); |
| + if (!static_cast<VideoCaptureDeviceWin*>(device.get())->Init()) |
| + device.reset(); |
| + } else { |
| + NOTREACHED() << " Couldn't recognize VideoCaptureDevice type"; |
| } |
| - |
| - return !capabilities_.empty(); |
| + return device.Pass(); |
| } |
| -// Set the power line frequency removal in |capture_filter_| if available. |
| -void VideoCaptureDeviceWin::SetAntiFlickerInCaptureFilter() { |
| - const int power_line_frequency = GetPowerLineFrequencyForLocation(); |
| - if (power_line_frequency != kPowerLine50Hz && |
| - power_line_frequency != kPowerLine60Hz) { |
| - return; |
| - } |
| - ScopedComPtr<IKsPropertySet> ks_propset; |
| - DWORD type_support = 0; |
| - HRESULT hr; |
| - if (SUCCEEDED(hr = ks_propset.QueryFrom(capture_filter_)) && |
| - SUCCEEDED(hr = ks_propset->QuerySupported(PROPSETID_VIDCAP_VIDEOPROCAMP, |
| - KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, &type_support)) && |
| - (type_support & KSPROPERTY_SUPPORT_SET)) { |
| - KSPROPERTY_VIDEOPROCAMP_S data = {}; |
| - data.Property.Set = PROPSETID_VIDCAP_VIDEOPROCAMP; |
| - data.Property.Id = KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY; |
| - data.Property.Flags = KSPROPERTY_TYPE_SET; |
| - data.Value = (power_line_frequency == kPowerLine50Hz) ? 1 : 2; |
| - data.Flags = KSPROPERTY_VIDEOPROCAMP_FLAGS_MANUAL; |
| - hr = ks_propset->Set(PROPSETID_VIDCAP_VIDEOPROCAMP, |
| - KSPROPERTY_VIDEOPROCAMP_POWERLINE_FREQUENCY, |
| - &data, sizeof(data), &data, sizeof(data)); |
| - DVLOG_IF(ERROR, FAILED(hr)) << "Anti-flicker setting failed."; |
| - DVLOG_IF(2, SUCCEEDED(hr)) << "Anti-flicker set correctly."; |
| +void VideoCaptureDeviceFactoryWin::GetDeviceNames( |
| + VideoCaptureDevice::Names* device_names) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| + // Use Media Foundation for Metro processes (after and including Win8) and |
| + // DirectShow for any other versions, unless forced via flag. Media Foundation |
| + // can also be forced if appropriate flag is set and we are in Windows 7 or |
| + // 8 in non-Metro mode. |
| + if ((base::win::IsMetroProcess() && |
| + !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) || |
| + (base::win::GetVersion() >= base::win::VERSION_WIN7 && |
| + cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture))) { |
| + GetDeviceNamesMediaFoundation(device_names); |
| } else { |
| - DVLOG(2) << "Anti-flicker setting not supported."; |
| + GetDeviceNamesDirectShow(device_names); |
| } |
| } |
| -void VideoCaptureDeviceWin::SetErrorState(const std::string& reason) { |
| - DCHECK(CalledOnValidThread()); |
| - DVLOG(1) << reason; |
| - state_ = kError; |
| - client_->OnError(reason); |
| +void VideoCaptureDeviceFactoryWin::GetDeviceSupportedFormats( |
| + const VideoCaptureDevice::Name& device, |
| + VideoCaptureFormats* formats) { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); |
| + // Use Media Foundation for Metro processes (after and including Win8) and |
| + // DirectShow for any other versions, unless forced via flag. Media Foundation |
| + // can also be forced if appropriate flag is set and we are in Windows 7 or |
| + // 8 in non-Metro mode. |
| + if ((base::win::IsMetroProcess() && |
| + !cmd_line->HasSwitch(switches::kForceDirectShowVideoCapture)) || |
| + (base::win::GetVersion() >= base::win::VERSION_WIN7 && |
| + cmd_line->HasSwitch(switches::kForceMediaFoundationVideoCapture))) { |
| + GetDeviceSupportedFormatsMediaFoundation(device, formats); |
| + } else { |
| + GetDeviceSupportedFormatsDirectShow(device, formats); |
| + } |
| } |
| + |
| } // namespace media |