Chromium Code Reviews| Index: media/video/capture/win/video_capture_device_win.cc |
| =================================================================== |
| --- media/video/capture/win/video_capture_device_win.cc (revision 0) |
| +++ media/video/capture/win/video_capture_device_win.cc (revision 0) |
| @@ -0,0 +1,638 @@ |
| +// Copyright (c) 2011 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 <algorithm> |
| +#include <list> |
| + |
| +#include "base/sys_string_conversions.h" |
| +#include "base/win/scoped_variant.h" |
| + |
| +using base::win::ScopedComPtr; |
| +using base::win::ScopedVariant; |
| + |
| +namespace { |
| + |
| +// Finds and creates a DirectShow Video Capture filter matching the device_name. |
| +HRESULT GetDeviceFilter(const media::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); |
| + if (FAILED(hr)) |
| + 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.unique_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) ? true : false; |
| + } |
| + } |
| + return found; |
| +} |
| + |
| +// Finds a IPin on a IBaseFilter given the direction an category. |
| +HRESULT GetPin(IBaseFilter* filter, PIN_DIRECTION pin_dir, REFGUID category, |
| + IPin** pin) { |
| + DCHECK(pin); |
| + ScopedComPtr<IEnumPins> pin_emum; |
| + HRESULT hr = filter->EnumPins(pin_emum.Receive()); |
| + if (pin_emum == NULL) |
| + return hr; |
| + |
| + // Get first unconnected pin. |
| + hr = pin_emum->Reset(); // set to first pin |
| + while ((hr = pin_emum->Next(1, pin, 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 S_OK; |
| + } |
| + (*pin)->Release(); |
| + } |
| + |
| + return E_FAIL; |
| +} |
| + |
| +// 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((PVOID)mt->pbFormat); |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
reinterpret_cast<void*>()
... actually, do you nee
Per K
2011/06/27 11:47:50
Done.
|
| + mt->cbFormat = 0; |
| + mt->pbFormat = NULL; |
| + } |
| + if (mt->pUnk != NULL) { |
| + // pUnk should not be used. |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
in that case, maybe add a NOTREACHED()?
Per K
2011/06/27 11:47:50
Done.
|
| + mt->pUnk->Release(); |
| + mt->pUnk = NULL; |
| + } |
| +} |
| + |
| +// 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 *pmt) { |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
fix *
Per K
2011/06/27 11:47:50
Done.
|
| + if (pmt != NULL) { |
| + FreeMediaType(pmt); |
| + CoTaskMemFree(pmt); |
| + } |
| +} |
| + |
| +// Help structure used for comparing video capture capabilities. |
| +struct ResolutionDiff { |
| + int capability_index; |
| + int diff_height; |
| + int diff_width; |
| + int diff_frame_rate; |
| + media::VideoCaptureDevice::Format color; |
| +}; |
| + |
| +bool CompareHeight(ResolutionDiff item1, ResolutionDiff item2) { |
| + return abs(item1.diff_height) < abs(item2.diff_height); |
| +} |
| + |
| +bool CompareWidth(ResolutionDiff item1, ResolutionDiff item2) { |
| + return abs(item1.diff_width) < abs(item2.diff_width); |
| +} |
| + |
| +bool CompareFrameRate(ResolutionDiff item1, ResolutionDiff item2) { |
| + return abs(item1.diff_frame_rate) < abs(item2.diff_frame_rate); |
| +} |
| + |
| +bool CompareColor(ResolutionDiff item1, ResolutionDiff item2) { |
| + return (item1.color < item2.color); |
| +} |
| + |
| +} // namespace |
| + |
| +namespace media { |
| + |
| +// Gets the names of all video capture devices connected to this computer. |
| +void VideoCaptureDevice::GetDeviceNames(Names* device_names) { |
| + DCHECK(device_names); |
| + |
| + app::win::ScopedCOMInitializer coinit; |
| + ScopedComPtr<ICreateDevEnum> dev_enum; |
| + HRESULT hr = dev_enum.CreateInstance(CLSID_SystemDeviceEnum, NULL, |
| + CLSCTX_INPROC); |
| + if (FAILED(hr)) |
| + return; |
| + |
| + ScopedComPtr<IEnumMoniker> enum_moniker; |
| + hr = dev_enum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, |
| + enum_moniker.Receive(), 0); |
| + if (FAILED(hr)) |
| + return; |
| + |
| + device_names->clear(); |
| + |
| + // Enumerate all video capture devices. |
| + ScopedComPtr<IMoniker> moniker; |
| + int index = 0; |
| + while (enum_moniker->Next(1, moniker.Receive(), NULL) == S_OK) { |
| + Name device; |
| + 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. |
| + ScopedVariant name; |
| + hr = prop_bag->Read(L"Description", name.Receive(), 0); |
| + if (FAILED(hr)) |
| + hr = prop_bag->Read(L"FriendlyName", name.Receive(), 0); |
| + |
| + if (SUCCEEDED(hr) && name.type() == VT_BSTR) { |
| + // ignore all VFW drivers and the special Google Camera Adapter |
| + const wchar_t* str_ptr = V_BSTR(&name); |
| + static const wchar_t kGoogleCameraAdapter[] = L"Google Camera Adapter"; |
| + if ((wcsstr(str_ptr, L"(VFW)") == NULL) && |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
add a note on what the string might look like if w
Per K
2011/06/27 11:47:50
Done.
|
| + (_wcsnicmp(str_ptr, kGoogleCameraAdapter, |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
use strncasecmp or perhaps LowerCaseEqualsASCII (s
Per K
2011/06/27 11:47:50
Done.
|
| + arraysize(kGoogleCameraAdapter) - 1) != 0)) { |
| + device.device_name = base::SysWideToUTF8(str_ptr); |
| + name.Reset(); |
| + hr = prop_bag->Read(L"DevicePath", name.Receive(), 0); |
| + if (FAILED(hr)) { |
| + device.unique_id = device.device_name; |
| + } else if (name.type() == VT_BSTR) { |
| + device.unique_id = base::SysWideToUTF8(V_BSTR(&name)); |
| + } |
| + |
| + device_names->push_back(device); |
| + } |
| + } |
| + moniker.Release(); |
| + } |
| +} |
| + |
| +VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) { |
| + VideoCaptureDeviceWin* self = new VideoCaptureDeviceWin(device_name); |
| + if (!self || !self->Init()) { |
| + delete self; |
| + return NULL; |
| + } |
| + return self; |
| +} |
| + |
| +VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) |
| + : device_name_(device_name), |
| + state_(kIdle) { |
| +} |
| + |
| +VideoCaptureDeviceWin::~VideoCaptureDeviceWin() { |
| + 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() { |
| + HRESULT hr = GetDeviceFilter(device_name_, capture_filter_.Receive()); |
| + if (!capture_filter_) { |
| + DVLOG(2) << "Failed to create capture filter."; |
| + return false; |
| + } |
| + |
| + hr = GetPin(capture_filter_, PINDIR_OUTPUT, PIN_CATEGORY_CAPTURE, |
| + output_capture_pin_.Receive()); |
| + 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::Allocate( |
| + int width, |
| + int height, |
| + int frame_rate, |
| + VideoCaptureDevice::EventHandler* observer) { |
| + if (state_ != kIdle) |
| + return; |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
Should SetErrorState be called?
Per K
2011/06/27 11:47:50
Nope. This means that the VideoCaptureDevice have
|
| + |
| + observer_ = observer; |
| + // Get the camera capability that best match the requested resolution. |
| + const int capability_index = GetBestMatchedCapability(width, height, |
| + frame_rate); |
| + Capability capability = capabilities_[capability_index]; |
| + |
| + // Reduce the frame rate if the requested frame rate is lower |
| + // than the capability. |
| + if (capability.frame_rate > frame_rate) |
| + capability.frame_rate = frame_rate; |
| + |
| + AM_MEDIA_TYPE* pmt = NULL; |
| + VIDEO_STREAM_CONFIG_CAPS caps; |
| + |
| + ScopedComPtr<IAMStreamConfig> stream_config; |
| + HRESULT hr = output_capture_pin_.QueryInterface(stream_config.Receive()); |
| + if (FAILED(hr)) { |
| + SetErrorState("Can't get the Capture format settings"); |
| + return; |
| + } |
| + |
| + // Get the windows capability from the capture device. |
| + hr = stream_config->GetStreamCaps(capability_index, &pmt, |
| + reinterpret_cast<BYTE*>(&caps)); |
| + if (SUCCEEDED(hr)) { |
| + if (pmt->formattype == FORMAT_VideoInfo) { |
| + VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); |
| + if (capability.frame_rate > 0) |
| + h->AvgTimePerFrame = kSecondsToReferenceTime / capability.frame_rate; |
| + } |
| + // Set the sink filter to request this capability. |
| + sink_filter_->SetRequestedMediaCapability(capability); |
| + // Order the capture device to use this capability. |
| + hr = stream_config->SetFormat(pmt); |
| + } |
| + |
| + if (FAILED(hr)) |
| + SetErrorState("Failed to set capture device output format"); |
| + |
| + if (capability.color == VideoCaptureDevice::kMJPEG && !mjpg_filter_.get()) { |
| + // Create MJPG filter if we need it. |
| + hr = mjpg_filter_.CreateInstance(CLSID_MjpegDec, NULL, CLSCTX_INPROC); |
| + |
| + if (SUCCEEDED(hr)) { |
| + GetPin(mjpg_filter_, PINDIR_INPUT, GUID_NULL, input_mjpg_pin_.Receive()); |
| + GetPin(mjpg_filter_, PINDIR_OUTPUT, GUID_NULL, |
| + output_mjpg_pin_.Receive()); |
| + hr = graph_builder_->AddFilter(mjpg_filter_, NULL); |
| + } |
| + |
| + if (FAILED(hr)) { |
| + mjpg_filter_.Release(); |
| + input_mjpg_pin_.Release(); |
| + output_mjpg_pin_.Release(); |
| + } |
| + } |
| + |
| + if (capability.color == VideoCaptureDevice::kMJPEG && 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_, |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
When we get here, it is possible that |hr| indicat
Per K
2011/06/27 11:47:50
The idea is that we would still try to Connect the
|
| + NULL); |
| + } |
| + |
| + if (FAILED(hr)) { |
| + SetErrorState("Failed to connect the Capture graph."); |
| + return; |
| + } |
| + |
| + hr = media_control_->Pause(); |
| + if (FAILED(hr)) { |
| + SetErrorState("Failed to Pause the Capture device. " |
| + "Is it already occupied?"); |
| + return; |
| + } |
| + |
| + // Get the capability back from the sink filter after the filter have been |
| + // connected. |
| + const Capability& used_capability = sink_filter_->ResultingCapability(); |
| + observer_->OnFrameInfo(used_capability); |
| + |
| + state_ = kAllocated; |
| +} |
| + |
| +void VideoCaptureDeviceWin::Start() { |
| + if (state_ != kAllocated) |
| + return; |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
SetErrorState?
Per K
2011/06/27 11:47:50
Nope. This means that the VideoCaptureDevice have
|
| + |
| + HRESULT hr = media_control_->Run(); |
| + if (FAILED(hr)) { |
| + SetErrorState("Failed to start the Capture device."); |
| + return; |
| + } |
| + |
| + state_ = kAllocated; |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
bug?
state_ will already be kAllocated at this poi
Per K
2011/06/27 11:47:50
Done.
|
| +} |
| + |
| +void VideoCaptureDeviceWin::Stop() { |
| + if (state_ != kCapturing) |
| + return; |
| + |
| + HRESULT hr = media_control_->Stop(); |
| + if (FAILED(hr)) { |
| + SetErrorState("Failed to stop the capture graph."); |
| + return; |
| + } |
| + |
| + state_ = kAllocated; |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
is it OK to change the state even though Stop() fa
Per K
2011/06/27 11:47:50
I think you mean in DeAllocate? I just changed so
|
| +} |
| + |
| +void VideoCaptureDeviceWin::DeAllocate() { |
| + if (state_ == kIdle) |
| + return; |
| + |
| + HRESULT hr = media_control_->Stop(); |
| + 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"); |
| + |
| + state_ = kIdle; |
| +} |
| + |
| +const VideoCaptureDevice::Name& VideoCaptureDeviceWin::device_name() { |
| + return device_name_; |
| +} |
| + |
| +// Implements SinkFilterObserver::SinkFilterObserver. |
| +void VideoCaptureDeviceWin::FrameReceived(const uint8* buffer, |
| + int length) { |
| + observer_->OnIncomingCapturedFrame(buffer, length, base::Time::Now()); |
| +} |
| + |
| +bool VideoCaptureDeviceWin::CreateCapabilityMap() { |
| + 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"; |
| + |
| + AM_MEDIA_TYPE* pmt = NULL; |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
no hungarian
Per K
2011/06/27 11:47:50
Done.
|
| + VIDEO_STREAM_CONFIG_CAPS caps; |
| + int count, size; |
| + |
| + hr = stream_config->GetNumberOfCapabilities(&count, &size); |
| + if (FAILED(hr)) { |
| + DVLOG(2) << "Failed to GetNumberOfCapabilities"; |
| + return false; |
| + } |
| + |
| + for (int i = 0; i < count; ++i) { |
| + hr = stream_config->GetStreamCaps(i, &pmt, |
| + reinterpret_cast<BYTE*>(&caps)); |
| + if (FAILED(hr)) { |
| + DVLOG(2) << "Failed to GetStreamCaps"; |
| + return false; |
| + } |
| + |
| + if (pmt->majortype == MEDIATYPE_Video && |
| + pmt->formattype == FORMAT_VideoInfo) { |
| + Capability capability; |
| + REFERENCE_TIME time_per_frame = 0; |
| + |
| + VIDEOINFOHEADER* h = reinterpret_cast<VIDEOINFOHEADER*>(pmt->pbFormat); |
| + capability.width = h->bmiHeader.biWidth; |
| + capability.height = h->bmiHeader.biHeight; |
| + time_per_frame = h->AvgTimePerFrame; |
| + |
| + // Try to get the max frame rate from IAMVideoControl. |
| + if (video_control.get()) { |
| + LONGLONG* max_fps_ptr; |
| + LONG list_size; |
| + SIZE size; |
| + size.cx = capability.width; |
| + size.cy = capability.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_ptr); |
| + |
| + if (SUCCEEDED(hr) && list_size > 0) { |
| + int min_time = *std::min_element(max_fps_ptr, |
| + max_fps_ptr + list_size); |
| + capability.frame_rate = (min_time > 0) ? |
| + kSecondsToReferenceTime / min_time : 0; |
| + } else { |
| + // Get frame rate from VIDEOINFOHEADER. |
| + capability.frame_rate = (time_per_frame > 0) ? |
| + static_cast<int>(kSecondsToReferenceTime / time_per_frame) : 0; |
| + } |
| + } else { |
| + // Get frame rate from VIDEOINFOHEADER since IAMVideoControl is |
| + // not supported. |
| + capability.frame_rate = (time_per_frame > 0) ? |
| + static_cast<int>(kSecondsToReferenceTime / time_per_frame) : 0; |
| + } |
| + |
| + // We can't switch MEDIATYPE :~(. |
| + if (pmt->subtype == kMediaSubTypeI420) { |
| + capability.color = VideoCaptureDevice::kI420; |
| + } else if (pmt->subtype == MEDIASUBTYPE_IYUV) { // identical to kI420. |
| + capability.color = VideoCaptureDevice::kI420; |
| + } else if (pmt->subtype == MEDIASUBTYPE_RGB24) { |
| + capability.color = VideoCaptureDevice::kRGB24; |
| + } else if (pmt->subtype == MEDIASUBTYPE_YUY2) { |
| + capability.color = VideoCaptureDevice::kYUY2; |
| + } else if (pmt->subtype == MEDIASUBTYPE_MJPG) { |
| + capability.color = VideoCaptureDevice::kMJPEG; |
| + } else { |
| + WCHAR guid_str[128]; |
| + StringFromGUID2(pmt->subtype, guid_str, arraysize(guid_str)); |
| + DVLOG(2) << "Device support unknown media type " << guid_str; |
| + continue; |
| + } |
| + capabilities_[i] = capability; |
| + } |
| + DeleteMediaType(pmt); |
| + pmt = NULL; |
| + } |
| + |
| + return capabilities_.size() > 0; |
| +} |
| + |
| +// Loops through the list of capabilities and returns an index of the best |
| +// matching capability. |
| +// The algorithm prioritize height, width, frame rate and color format in that |
| +// order. |
| +int VideoCaptureDeviceWin::GetBestMatchedCapability(int requested_width, |
| + int requested_height, |
| + int requested_frame_rate) { |
| + std::list<ResolutionDiff> diff_list; |
| + |
| + // Loop through the candidates to create a list of differentials between the |
| + // requested resolution and the camera capability. |
| + for (CapabilityMap::iterator iterator = capabilities_.begin(); |
| + iterator != capabilities_.end(); |
| + ++iterator) { |
| + Capability capability = iterator->second; |
| + |
| + ResolutionDiff diff; |
| + diff.capability_index = iterator->first; |
| + diff.diff_width = capability.width - requested_width; |
| + diff.diff_height = capability.height - requested_height; |
| + diff.diff_frame_rate = capability.frame_rate - requested_frame_rate; |
| + diff.color = capability.color; |
| + diff_list.push_back(diff); |
| + } |
| + |
| + // Sort the best height candidates. |
| + diff_list.sort(CompareHeight); |
|
tommi (sloooow) - chröme
2011/06/23 14:05:56
should this be
diff_list.sort(&CompareHeight)?
(an
Per K
2011/06/27 11:47:50
Interesting. Both seems to work. According to the
|
| + int best_diff = diff_list.front().diff_height; |
| + for (std::list<ResolutionDiff>::iterator it = diff_list.begin(); |
| + it != diff_list.end(); ++it) { |
| + if (it->diff_height != best_diff) { |
| + // Remove all candidates but the best. |
| + diff_list.erase(it, diff_list.end()); |
| + break; |
| + } |
| + } |
| + |
| + // Sort the best width candidates. |
| + diff_list.sort(CompareWidth); |
| + best_diff = diff_list.front().diff_width; |
| + for (std::list<ResolutionDiff>::iterator it = diff_list.begin(); |
| + it != diff_list.end(); ++it) { |
| + if (it->diff_width != best_diff) { |
| + // Remove all candidates but the best. |
| + diff_list.erase(it, diff_list.end()); |
| + break; |
| + } |
| + } |
| + |
| + // Sort the best frame rate candidates. |
| + diff_list.sort(CompareFrameRate); |
| + best_diff = diff_list.front().diff_frame_rate; |
| + for (std::list<ResolutionDiff>::iterator it = diff_list.begin(); |
| + it != diff_list.end(); ++it) { |
| + if (it->diff_frame_rate != best_diff) { |
| + diff_list.erase(it, diff_list.end()); |
| + break; |
| + } |
| + } |
| + |
| + // Decide the best color format. |
| + diff_list.sort(CompareColor); |
| + return diff_list.front().capability_index; |
| +} |
| + |
| +void VideoCaptureDeviceWin::SetErrorState(const char* reason) { |
| + DLOG(ERROR) << reason; |
| + state_ = kError; |
| + observer_->OnError(); |
| +} |
| + |
| +} // namespace media |