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.
5 #include "media/capture/video/win/video_capture_device_mf_win.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/win/scoped_co_mem.h"
15 #include "base/win/windows_version.h"
16 #include "media/capture/video/win/capability_list_win.h"
18 using base::win::ScopedCoMem
;
19 using base::win::ScopedComPtr
;
23 // In Windows device identifiers, the USB VID and PID are preceded by the string
24 // "vid_" or "pid_". The identifiers are each 4 bytes long.
25 const char kVidPrefix
[] = "vid_"; // Also contains '\0'.
26 const char kPidPrefix
[] = "pid_"; // Also contains '\0'.
27 const size_t kVidPidSize
= 4;
29 static bool GetFrameSize(IMFMediaType
* type
, gfx::Size
* frame_size
) {
30 UINT32 width32
, height32
;
31 if (FAILED(MFGetAttributeSize(type
, MF_MT_FRAME_SIZE
, &width32
, &height32
)))
33 frame_size
->SetSize(width32
, height32
);
37 static bool GetFrameRate(IMFMediaType
* type
, float* frame_rate
) {
38 UINT32 numerator
, denominator
;
39 if (FAILED(MFGetAttributeRatio(type
, MF_MT_FRAME_RATE
, &numerator
,
44 *frame_rate
= static_cast<float>(numerator
) / denominator
;
48 static bool FillFormat(IMFMediaType
* type
, VideoCaptureFormat
* format
) {
50 if (FAILED(type
->GetGUID(MF_MT_SUBTYPE
, &type_guid
)) ||
51 !GetFrameSize(type
, &format
->frame_size
) ||
52 !GetFrameRate(type
, &format
->frame_rate
) ||
53 !VideoCaptureDeviceMFWin::FormatFromGuid(type_guid
,
54 &format
->pixel_format
)) {
61 HRESULT
FillCapabilities(IMFSourceReader
* source
,
62 CapabilityList
* capabilities
) {
63 DWORD stream_index
= 0;
64 ScopedComPtr
<IMFMediaType
> type
;
66 while (SUCCEEDED(hr
= source
->GetNativeMediaType(
67 kFirstVideoStream
, stream_index
, type
.Receive()))) {
68 VideoCaptureFormat format
;
69 if (FillFormat(type
.get(), &format
))
70 capabilities
->emplace_back(stream_index
, format
);
75 if (capabilities
->empty() && (SUCCEEDED(hr
) || hr
== MF_E_NO_MORE_TYPES
))
76 hr
= HRESULT_FROM_WIN32(ERROR_EMPTY
);
78 return (hr
== MF_E_NO_MORE_TYPES
) ? S_OK
: hr
;
81 class MFReaderCallback final
82 : public base::RefCountedThreadSafe
<MFReaderCallback
>,
83 public IMFSourceReaderCallback
{
85 MFReaderCallback(VideoCaptureDeviceMFWin
* observer
)
86 : observer_(observer
), wait_event_(NULL
) {}
88 void SetSignalOnFlush(base::WaitableEvent
* event
) { wait_event_
= event
; }
90 STDMETHOD(QueryInterface
)(REFIID riid
, void** object
) override
{
91 if (riid
!= IID_IUnknown
&& riid
!= IID_IMFSourceReaderCallback
)
93 *object
= static_cast<IMFSourceReaderCallback
*>(this);
98 STDMETHOD_(ULONG
, AddRef
)() override
{
99 base::RefCountedThreadSafe
<MFReaderCallback
>::AddRef();
103 STDMETHOD_(ULONG
, Release
)() override
{
104 base::RefCountedThreadSafe
<MFReaderCallback
>::Release();
108 STDMETHOD(OnReadSample
)(HRESULT status
,
112 IMFSample
* sample
) override
{
113 base::TimeTicks
stamp(base::TimeTicks::Now());
115 observer_
->OnIncomingCapturedData(NULL
, 0, 0, stamp
);
120 sample
->GetBufferCount(&count
);
122 for (DWORD i
= 0; i
< count
; ++i
) {
123 ScopedComPtr
<IMFMediaBuffer
> buffer
;
124 sample
->GetBufferByIndex(i
, buffer
.Receive());
126 DWORD length
= 0, max_length
= 0;
128 buffer
->Lock(&data
, &max_length
, &length
);
129 observer_
->OnIncomingCapturedData(data
, length
, 0, stamp
);
136 STDMETHOD(OnFlush
)(DWORD stream_index
) override
{
138 wait_event_
->Signal();
144 STDMETHOD(OnEvent
)(DWORD stream_index
, IMFMediaEvent
* event
) override
{
150 friend class base::RefCountedThreadSafe
<MFReaderCallback
>;
151 ~MFReaderCallback() {}
153 VideoCaptureDeviceMFWin
* observer_
;
154 base::WaitableEvent
* wait_event_
;
158 bool VideoCaptureDeviceMFWin::FormatFromGuid(const GUID
& guid
,
159 VideoCapturePixelFormat
* format
) {
162 const VideoCapturePixelFormat format
;
163 } static const kFormatMap
[] = {
164 {MFVideoFormat_I420
, VIDEO_CAPTURE_PIXEL_FORMAT_I420
},
165 {MFVideoFormat_YUY2
, VIDEO_CAPTURE_PIXEL_FORMAT_YUY2
},
166 {MFVideoFormat_UYVY
, VIDEO_CAPTURE_PIXEL_FORMAT_UYVY
},
167 {MFVideoFormat_RGB24
, VIDEO_CAPTURE_PIXEL_FORMAT_RGB24
},
168 {MFVideoFormat_ARGB32
, VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
},
169 {MFVideoFormat_MJPG
, VIDEO_CAPTURE_PIXEL_FORMAT_MJPEG
},
170 {MFVideoFormat_YV12
, VIDEO_CAPTURE_PIXEL_FORMAT_YV12
},
173 for (int i
= 0; i
< arraysize(kFormatMap
); ++i
) {
174 if (kFormatMap
[i
].guid
== guid
) {
175 *format
= kFormatMap
[i
].format
;
183 const std::string
VideoCaptureDevice::Name::GetModel() const {
184 const size_t vid_prefix_size
= sizeof(kVidPrefix
) - 1;
185 const size_t pid_prefix_size
= sizeof(kPidPrefix
) - 1;
186 const size_t vid_location
= unique_id_
.find(kVidPrefix
);
187 if (vid_location
== std::string::npos
||
188 vid_location
+ vid_prefix_size
+ kVidPidSize
> unique_id_
.size()) {
189 return std::string();
191 const size_t pid_location
= unique_id_
.find(kPidPrefix
);
192 if (pid_location
== std::string::npos
||
193 pid_location
+ pid_prefix_size
+ kVidPidSize
> unique_id_
.size()) {
194 return std::string();
196 std::string id_vendor
=
197 unique_id_
.substr(vid_location
+ vid_prefix_size
, kVidPidSize
);
198 std::string id_product
=
199 unique_id_
.substr(pid_location
+ pid_prefix_size
, kVidPidSize
);
200 return id_vendor
+ ":" + id_product
;
203 VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(const Name
& device_name
)
204 : name_(device_name
), capture_(0) {
208 VideoCaptureDeviceMFWin::~VideoCaptureDeviceMFWin() {
209 DCHECK(CalledOnValidThread());
212 bool VideoCaptureDeviceMFWin::Init(
213 const base::win::ScopedComPtr
<IMFMediaSource
>& source
) {
214 DCHECK(CalledOnValidThread());
215 DCHECK(!reader_
.get());
217 ScopedComPtr
<IMFAttributes
> attributes
;
218 MFCreateAttributes(attributes
.Receive(), 1);
219 DCHECK(attributes
.get());
221 callback_
= new MFReaderCallback(this);
222 attributes
->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK
, callback_
.get());
224 return SUCCEEDED(MFCreateSourceReaderFromMediaSource(
225 source
.get(), attributes
.get(), reader_
.Receive()));
228 void VideoCaptureDeviceMFWin::AllocateAndStart(
229 const VideoCaptureParams
& params
,
230 scoped_ptr
<VideoCaptureDevice::Client
> client
) {
231 DCHECK(CalledOnValidThread());
233 base::AutoLock
lock(lock_
);
235 client_
= client
.Pass();
236 DCHECK_EQ(capture_
, false);
238 CapabilityList capabilities
;
241 hr
= FillCapabilities(reader_
.get(), &capabilities
);
243 const CapabilityWin found_capability
=
244 GetBestMatchedCapability(params
.requested_format
, capabilities
);
245 ScopedComPtr
<IMFMediaType
> type
;
246 hr
= reader_
->GetNativeMediaType(
247 kFirstVideoStream
, found_capability
.stream_index
, type
.Receive());
249 hr
= reader_
->SetCurrentMediaType(kFirstVideoStream
, NULL
, type
.get());
252 reader_
->ReadSample(kFirstVideoStream
, 0, NULL
, NULL
, NULL
, NULL
);
254 capture_format_
= found_capability
.supported_format
;
266 void VideoCaptureDeviceMFWin::StopAndDeAllocate() {
267 DCHECK(CalledOnValidThread());
268 base::WaitableEvent
flushed(false, false);
269 const int kFlushTimeOutInMs
= 1000;
272 base::AutoLock
lock(lock_
);
275 callback_
->SetSignalOnFlush(&flushed
);
277 reader_
->Flush(static_cast<DWORD
>(MF_SOURCE_READER_ALL_STREAMS
)));
279 callback_
->SetSignalOnFlush(NULL
);
285 // If the device has been unplugged, the Flush() won't trigger the event
286 // and a timeout will happen.
287 // TODO(tommi): Hook up the IMFMediaEventGenerator notifications API and
288 // do not wait at all after getting MEVideoCaptureDeviceRemoved event.
291 flushed
.TimedWait(base::TimeDelta::FromMilliseconds(kFlushTimeOutInMs
));
294 void VideoCaptureDeviceMFWin::OnIncomingCapturedData(
298 const base::TimeTicks
& time_stamp
) {
299 base::AutoLock
lock(lock_
);
300 if (data
&& client_
.get()) {
301 client_
->OnIncomingCapturedData(data
, length
, capture_format_
, rotation
,
307 reader_
->ReadSample(kFirstVideoStream
, 0, NULL
, NULL
, NULL
, NULL
);
309 // If running the *VideoCap* unit tests on repeat, this can sometimes
310 // fail with HRESULT_FROM_WINHRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION).
311 // It's not clear to me why this is, but it is possible that it has
312 // something to do with this bug:
313 // http://support.microsoft.com/kb/979567
319 void VideoCaptureDeviceMFWin::OnError(HRESULT hr
) {
322 base::StringPrintf("VideoCaptureDeviceMFWin: %s",
323 logging::SystemErrorCodeToString(hr
).c_str()));