Commit dce6ab4f authored by tommi@chromium.org's avatar tommi@chromium.org

Video capture implementation using the Media Foundation API.

This replaces the DirectShow based implementation for Vista,Win7 and Win8.
DirectShow isn't supported in metro mode so once we've landed this,
we can revert the workaround we have in place for Win8.

The CapabilityList class is mostly moved code from the DS implementation.
The difference is that I'm not using a std::map<> since that wasn't
really necessary and instead adding one member (via inheritance) to
the capability struct on Windows that holds the stream id that is needed.

BUG=140545
TEST=All video capture tests in media_unittests now test this new implementation (except on XP).

Review URL: https://codereview.chromium.org/11419200

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@170912 0039d316-1c4b-4281-b951-d872f2087c98
parent 19778818
...@@ -183,6 +183,9 @@ ...@@ -183,6 +183,9 @@
'imagehlp.dll', 'imagehlp.dll',
'imm32.dll', 'imm32.dll',
'iphlpapi.dll', 'iphlpapi.dll',
'mf.dll',
'mfplat.dll',
'mfreadwrite.dll',
'setupapi.dll', 'setupapi.dll',
'urlmon.dll', 'urlmon.dll',
'winhttp.dll', 'winhttp.dll',
......
...@@ -314,6 +314,8 @@ ...@@ -314,6 +314,8 @@
'video/capture/video_capture_proxy.cc', 'video/capture/video_capture_proxy.cc',
'video/capture/video_capture_proxy.h', 'video/capture/video_capture_proxy.h',
'video/capture/video_capture_types.h', 'video/capture/video_capture_types.h',
'video/capture/win/capability_list_win.cc',
'video/capture/win/capability_list_win.h',
'video/capture/win/filter_base_win.cc', 'video/capture/win/filter_base_win.cc',
'video/capture/win/filter_base_win.h', 'video/capture/win/filter_base_win.h',
'video/capture/win/pin_base_win.cc', 'video/capture/win/pin_base_win.cc',
...@@ -323,6 +325,8 @@ ...@@ -323,6 +325,8 @@
'video/capture/win/sink_filter_win.h', 'video/capture/win/sink_filter_win.h',
'video/capture/win/sink_input_pin_win.cc', 'video/capture/win/sink_input_pin_win.cc',
'video/capture/win/sink_input_pin_win.h', 'video/capture/win/sink_input_pin_win.h',
'video/capture/win/video_capture_device_mf_win.cc',
'video/capture/win/video_capture_device_mf_win.h',
'video/capture/win/video_capture_device_win.cc', 'video/capture/win/video_capture_device_win.cc',
'video/capture/win/video_capture_device_win.h', 'video/capture/win/video_capture_device_win.h',
'video/picture.cc', 'video/picture.cc',
...@@ -556,6 +560,23 @@ ...@@ -556,6 +560,23 @@
'video/capture/video_capture_device_dummy.cc', 'video/capture/video_capture_device_dummy.cc',
'video/capture/video_capture_device_dummy.h', 'video/capture/video_capture_device_dummy.h',
], ],
'link_settings': {
'libraries': [
'-lmf.lib',
'-lmfplat.lib',
'-lmfreadwrite.lib',
'-lmfuuid.lib',
],
},
'msvs_settings': {
'VCLinkerTool': {
'DelayLoadDLLs': [
'mf.dll',
'mfplat.dll',
'mfreadwrite.dll',
],
},
},
}], }],
['proprietary_codecs==1 or branding=="Chrome"', { ['proprietary_codecs==1 or branding=="Chrome"', {
'sources': [ 'sources': [
......
// Copyright (c) 2012 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/capability_list_win.h"
#include <algorithm>
#include "base/logging.h"
namespace media {
namespace {
// Help structure used for comparing video capture capabilities.
struct ResolutionDiff {
const VideoCaptureCapabilityWin* capability;
int diff_height;
int diff_width;
int diff_frame_rate;
};
bool CompareHeight(const ResolutionDiff& item1, const ResolutionDiff& item2) {
return abs(item1.diff_height) < abs(item2.diff_height);
}
bool CompareWidth(const ResolutionDiff& item1, const ResolutionDiff& item2) {
return abs(item1.diff_width) < abs(item2.diff_width);
}
bool CompareFrameRate(const ResolutionDiff& item1,
const ResolutionDiff& item2) {
return abs(item1.diff_frame_rate) < abs(item2.diff_frame_rate);
}
bool CompareColor(const ResolutionDiff& item1, const ResolutionDiff& item2) {
return item1.capability->color < item2.capability->color;
}
} // namespace.
CapabilityList::CapabilityList() {
DetachFromThread();
}
CapabilityList::~CapabilityList() {}
// Appends an entry to the list.
void CapabilityList::Add(const VideoCaptureCapabilityWin& capability) {
DCHECK(CalledOnValidThread());
capabilities_.push_back(capability);
}
const VideoCaptureCapabilityWin& CapabilityList::GetBestMatchedCapability(
int requested_width,
int requested_height,
int requested_frame_rate) const {
DCHECK(CalledOnValidThread());
DCHECK(!capabilities_.empty());
std::list<ResolutionDiff> diff_list;
// Loop through the candidates to create a list of differentials between the
// requested resolution and the camera capability.
for (Capabilities::const_iterator it = capabilities_.begin();
it != capabilities_.end(); ++it) {
ResolutionDiff diff;
diff.capability = &(*it);
diff.diff_width = it->width - requested_width;
diff.diff_height = it->height - requested_height;
diff.diff_frame_rate = it->frame_rate - requested_frame_rate;
diff_list.push_back(diff);
}
// Sort the best height candidates.
diff_list.sort(&CompareHeight);
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;
}
} // namespace media
// Copyright (c) 2012 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.
// Windows specific implementation of VideoCaptureDevice.
// DirectShow is used for capturing. DirectShow provide its own threads
// for capturing.
#ifndef MEDIA_VIDEO_CAPTURE_WIN_CAPABILITY_LIST_WIN_H_
#define MEDIA_VIDEO_CAPTURE_WIN_CAPABILITY_LIST_WIN_H_
#include <list>
#include "base/threading/non_thread_safe.h"
#include "media/video/capture/video_capture_types.h"
namespace media {
struct VideoCaptureCapabilityWin : public VideoCaptureCapability {
explicit VideoCaptureCapabilityWin(int index) : stream_index(index) {}
int stream_index;
};
class CapabilityList : public base::NonThreadSafe {
public:
CapabilityList();
~CapabilityList();
bool empty() const { return capabilities_.empty(); }
// Appends an entry to the list.
void Add(const VideoCaptureCapabilityWin& capability);
// Loops through the list of capabilities and returns an index of the best
// matching capability. The algorithm prioritizes height, width, frame rate
// and color format in that order.
const VideoCaptureCapabilityWin& GetBestMatchedCapability(
int requested_width, int requested_height,
int requested_frame_rate) const;
private:
typedef std::list<VideoCaptureCapabilityWin> Capabilities;
Capabilities capabilities_;
DISALLOW_COPY_AND_ASSIGN(CapabilityList);
};
} // namespace media
#endif // MEDIA_VIDEO_CAPTURE_WIN_CAPABILITY_LIST_WIN_H_
This diff is collapsed.
// Copyright (c) 2012 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.
// Windows specific implementation of VideoCaptureDevice.
// DirectShow is used for capturing. DirectShow provide its own threads
// for capturing.
#ifndef MEDIA_VIDEO_CAPTURE_WIN_VIDEO_CAPTURE_DEVICE_MF_WIN_H_
#define MEDIA_VIDEO_CAPTURE_WIN_VIDEO_CAPTURE_DEVICE_MF_WIN_H_
#include <mfidl.h>
#include <mfreadwrite.h>
#include <vector>
#include "base/synchronization/lock.h"
#include "base/threading/non_thread_safe.h"
#include "base/win/scoped_comptr.h"
#include "media/video/capture/video_capture_device.h"
interface IMFSourceReader;
namespace media {
class MFReaderCallback;
class VideoCaptureDeviceMFWin
: public base::NonThreadSafe,
public VideoCaptureDevice {
public:
explicit VideoCaptureDeviceMFWin(const Name& device_name);
virtual ~VideoCaptureDeviceMFWin();
// Opens the device driver for this device.
// This function is used by the static VideoCaptureDevice::Create function.
bool Init();
// VideoCaptureDevice implementation.
virtual void Allocate(int width,
int height,
int frame_rate,
VideoCaptureDevice::EventHandler* observer) OVERRIDE;
virtual void Start() OVERRIDE;
virtual void Stop() OVERRIDE;
virtual void DeAllocate() OVERRIDE;
virtual const Name& device_name() OVERRIDE;
static void GetDeviceNames(Names* device_names);
// Captured a new video frame.
void OnIncomingCapturedFrame(const uint8* data, int length,
const base::Time& time_stamp);
private:
void OnError(HRESULT hr);
Name name_;
base::win::ScopedComPtr<IMFActivate> device_;
scoped_refptr<MFReaderCallback> callback_;
base::Lock lock_; // Used to guard the below variables.
VideoCaptureDevice::EventHandler* observer_;
base::win::ScopedComPtr<IMFSourceReader> reader_;
bool capture_;
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureDeviceMFWin);
};
} // namespace media
#endif // MEDIA_VIDEO_CAPTURE_WIN_VIDEO_CAPTURE_DEVICE_MF_WIN_H_
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "base/string_util.h" #include "base/string_util.h"
#include "base/sys_string_conversions.h" #include "base/sys_string_conversions.h"
#include "base/win/scoped_variant.h" #include "base/win/scoped_variant.h"
#include "base/win/windows_version.h"
#include "media/video/capture/win/video_capture_device_mf_win.h"
using base::win::ScopedComPtr; using base::win::ScopedComPtr;
using base::win::ScopedVariant; using base::win::ScopedVariant;
...@@ -142,42 +144,39 @@ void DeleteMediaType(AM_MEDIA_TYPE* mt) { ...@@ -142,42 +144,39 @@ void DeleteMediaType(AM_MEDIA_TYPE* mt) {
} }
} }
// Help structure used for comparing video capture capabilities. } // namespace
struct ResolutionDiff {
int capability_index;
int diff_height;
int diff_width;
int diff_frame_rate;
media::VideoCaptureCapability::Format color;
};
bool CompareHeight(const ResolutionDiff& item1, const ResolutionDiff& item2) {
return abs(item1.diff_height) < abs(item2.diff_height);
}
bool CompareWidth(const ResolutionDiff& item1, const ResolutionDiff& item2) {
return abs(item1.diff_width) < abs(item2.diff_width);
}
bool CompareFrameRate(const ResolutionDiff& item1, namespace media {
const ResolutionDiff& item2) {
return abs(item1.diff_frame_rate) < abs(item2.diff_frame_rate);
}
bool CompareColor(const ResolutionDiff& item1, const ResolutionDiff& item2) { // static
return (item1.color < item2.color); void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
VideoCaptureDeviceMFWin::GetDeviceNames(device_names);
} else {
VideoCaptureDeviceWin::GetDeviceNames(device_names);
}
} }
} // namespace // static
VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
namespace media { VideoCaptureDevice* ret = NULL;
if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
scoped_ptr<VideoCaptureDeviceMFWin> device(
new VideoCaptureDeviceMFWin(device_name));
if (device->Init())
ret = device.release();
} else {
scoped_ptr<VideoCaptureDeviceWin> device(
new VideoCaptureDeviceWin(device_name));
if (device->Init())
ret = device.release();
}
// Name of a fake DirectShow filter that exist on computers with return ret;
// GTalk installed. }
static const char kGoogleCameraAdapter[] = "google camera adapter";
// Gets the names of all video capture devices connected to this computer. // static
void VideoCaptureDevice::GetDeviceNames(Names* device_names) { void VideoCaptureDeviceWin::GetDeviceNames(Names* device_names) {
DCHECK(device_names); DCHECK(device_names);
ScopedComPtr<ICreateDevEnum> dev_enum; ScopedComPtr<ICreateDevEnum> dev_enum;
...@@ -196,6 +195,10 @@ void VideoCaptureDevice::GetDeviceNames(Names* device_names) { ...@@ -196,6 +195,10 @@ void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
device_names->clear(); device_names->clear();
// Name of a fake DirectShow filter that exist on computers with
// GTalk installed.
static const char kGoogleCameraAdapter[] = "google camera adapter";
// Enumerate all video capture devices. // Enumerate all video capture devices.
ScopedComPtr<IMoniker> moniker; ScopedComPtr<IMoniker> moniker;
int index = 0; int index = 0;
...@@ -241,15 +244,6 @@ void VideoCaptureDevice::GetDeviceNames(Names* device_names) { ...@@ -241,15 +244,6 @@ void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
} }
} }
VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
VideoCaptureDeviceWin* self = new VideoCaptureDeviceWin(device_name);
if (self && self->Init())
return self;
delete self;
return NULL;
}
VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name) VideoCaptureDeviceWin::VideoCaptureDeviceWin(const Name& device_name)
: device_name_(device_name), : device_name_(device_name),
state_(kIdle), state_(kIdle),
...@@ -338,10 +332,11 @@ void VideoCaptureDeviceWin::Allocate( ...@@ -338,10 +332,11 @@ void VideoCaptureDeviceWin::Allocate(
return; return;
observer_ = observer; observer_ = observer;
// Get the camera capability that best match the requested resolution. // Get the camera capability that best match the requested resolution.
const int capability_index = GetBestMatchedCapability(width, height, const VideoCaptureCapabilityWin& found_capability =
frame_rate); capabilities_.GetBestMatchedCapability(width, height, frame_rate);
VideoCaptureCapability capability = capabilities_[capability_index]; VideoCaptureCapability capability = found_capability;
// Reduce the frame rate if the requested frame rate is lower // Reduce the frame rate if the requested frame rate is lower
// than the capability. // than the capability.
...@@ -359,7 +354,7 @@ void VideoCaptureDeviceWin::Allocate( ...@@ -359,7 +354,7 @@ void VideoCaptureDeviceWin::Allocate(
} }
// Get the windows capability from the capture device. // Get the windows capability from the capture device.
hr = stream_config->GetStreamCaps(capability_index, &pmt, hr = stream_config->GetStreamCaps(found_capability.stream_index, &pmt,
reinterpret_cast<BYTE*>(&caps)); reinterpret_cast<BYTE*>(&caps));
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
if (pmt->formattype == FORMAT_VideoInfo) { if (pmt->formattype == FORMAT_VideoInfo) {
...@@ -526,7 +521,7 @@ bool VideoCaptureDeviceWin::CreateCapabilityMap() { ...@@ -526,7 +521,7 @@ bool VideoCaptureDeviceWin::CreateCapabilityMap() {
if (media_type->majortype == MEDIATYPE_Video && if (media_type->majortype == MEDIATYPE_Video &&
media_type->formattype == FORMAT_VideoInfo) { media_type->formattype == FORMAT_VideoInfo) {
VideoCaptureCapability capability; VideoCaptureCapabilityWin capability(i);
REFERENCE_TIME time_per_frame = 0; REFERENCE_TIME time_per_frame = 0;
VIDEOINFOHEADER* h = VIDEOINFOHEADER* h =
...@@ -586,79 +581,13 @@ bool VideoCaptureDeviceWin::CreateCapabilityMap() { ...@@ -586,79 +581,13 @@ bool VideoCaptureDeviceWin::CreateCapabilityMap() {
DVLOG(2) << "Device support unknown media type " << guid_str; DVLOG(2) << "Device support unknown media type " << guid_str;
continue; continue;
} }
capabilities_[i] = capability; capabilities_.Add(capability);
} }
DeleteMediaType(media_type); DeleteMediaType(media_type);
media_type = NULL; media_type = NULL;
} }
return capabilities_.size() > 0; return !capabilities_.empty();
}
// 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) {
DCHECK(CalledOnValidThread());
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) {
VideoCaptureCapability 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);
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) { void VideoCaptureDeviceWin::SetErrorState(const char* reason) {
......
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
#include "base/win/scoped_comptr.h" #include "base/win/scoped_comptr.h"
#include "media/video/capture/video_capture_device.h" #include "media/video/capture/video_capture_device.h"
#include "media/video/capture/video_capture_types.h" #include "media/video/capture/video_capture_types.h"
#include "media/video/capture/win/capability_list_win.h"
#include "media/video/capture/win/sink_filter_win.h" #include "media/video/capture/win/sink_filter_win.h"
#include "media/video/capture/win/sink_input_pin_win.h" #include "media/video/capture/win/sink_input_pin_win.h"
...@@ -48,6 +49,8 @@ class VideoCaptureDeviceWin ...@@ -48,6 +49,8 @@ class VideoCaptureDeviceWin
virtual void DeAllocate() OVERRIDE; virtual void DeAllocate() OVERRIDE;
virtual const Name& device_name() OVERRIDE; virtual const Name& device_name() OVERRIDE;
static void GetDeviceNames(Names* device_names);
private: private:
enum InternalState { enum InternalState {
kIdle, // The device driver is opened but camera is not in use. kIdle, // The device driver is opened but camera is not in use.
...@@ -56,13 +59,11 @@ class VideoCaptureDeviceWin ...@@ -56,13 +59,11 @@ class VideoCaptureDeviceWin
kError // Error accessing HW functions. kError // Error accessing HW functions.
// User needs to recover by destroying the object. // User needs to recover by destroying the object.
}; };
typedef std::map<int, VideoCaptureCapability> CapabilityMap;
// Implements SinkFilterObserver. // Implements SinkFilterObserver.
virtual void FrameReceived(const uint8* buffer, int length); virtual void FrameReceived(const uint8* buffer, int length);
bool CreateCapabilityMap(); bool CreateCapabilityMap();
int GetBestMatchedCapability(int width, int height, int frame_rate);
void SetErrorState(const char* reason); void SetErrorState(const char* reason);
Name device_name_; Name device_name_;
...@@ -82,7 +83,7 @@ class VideoCaptureDeviceWin ...@@ -82,7 +83,7 @@ class VideoCaptureDeviceWin
scoped_refptr<SinkFilter> sink_filter_; scoped_refptr<SinkFilter> sink_filter_;
// Map of all capabilities this device support. // Map of all capabilities this device support.
CapabilityMap capabilities_; CapabilityList capabilities_;
DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureDeviceWin); DISALLOW_IMPLICIT_CONSTRUCTORS(VideoCaptureDeviceWin);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment