OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/video/capture/win/video_capture_device_mf_win.h" | 5 #include "media/video/capture/win/video_capture_device_mf_win.h" |
6 | 6 |
7 #include <mfapi.h> | 7 #include <mfapi.h> |
8 #include <mferror.h> | 8 #include <mferror.h> |
9 | 9 |
10 #include "base/lazy_instance.h" | 10 #include "base/lazy_instance.h" // remove me !!! |
tommi (sloooow) - chröme
2014/05/15 15:18:33
ping :)
mcasas
2014/05/15 16:03:23
Done.
| |
11 #include "base/memory/ref_counted.h" | 11 #include "base/memory/ref_counted.h" |
12 #include "base/strings/stringprintf.h" | 12 #include "base/strings/stringprintf.h" |
13 #include "base/strings/sys_string_conversions.h" | 13 #include "base/strings/sys_string_conversions.h" |
14 #include "base/synchronization/waitable_event.h" | 14 #include "base/synchronization/waitable_event.h" |
15 #include "base/win/scoped_co_mem.h" | 15 #include "base/win/scoped_co_mem.h" |
16 #include "base/win/windows_version.h" | 16 #include "base/win/windows_version.h" |
17 #include "media/video/capture/win/capability_list_win.h" | 17 #include "media/video/capture/win/capability_list_win.h" |
18 | 18 |
19 using base::win::ScopedCoMem; | 19 using base::win::ScopedCoMem; |
20 using base::win::ScopedComPtr; | |
21 | 20 |
22 namespace media { | 21 namespace media { |
23 namespace { | |
24 | 22 |
25 // In Windows device identifiers, the USB VID and PID are preceded by the string | 23 // In Windows device identifiers, the USB VID and PID are preceded by the string |
26 // "vid_" or "pid_". The identifiers are each 4 bytes long. | 24 // "vid_" or "pid_". The identifiers are each 4 bytes long. |
27 const char kVidPrefix[] = "vid_"; // Also contains '\0'. | 25 const char kVidPrefix[] = "vid_"; // Also contains '\0'. |
28 const char kPidPrefix[] = "pid_"; // Also contains '\0'. | 26 const char kPidPrefix[] = "pid_"; // Also contains '\0'. |
29 const size_t kVidPidSize = 4; | 27 const size_t kVidPidSize = 4; |
30 | 28 |
31 class MFInitializerSingleton { | 29 static bool GetFrameSize(IMFMediaType* type, gfx::Size* frame_size) { |
32 public: | |
33 MFInitializerSingleton() { MFStartup(MF_VERSION, MFSTARTUP_LITE); } | |
34 ~MFInitializerSingleton() { MFShutdown(); } | |
35 }; | |
36 | |
37 static base::LazyInstance<MFInitializerSingleton> g_mf_initialize = | |
38 LAZY_INSTANCE_INITIALIZER; | |
39 | |
40 void EnsureMFInit() { | |
41 g_mf_initialize.Get(); | |
42 } | |
43 | |
44 bool PrepareVideoCaptureAttributes(IMFAttributes** attributes, int count) { | |
45 EnsureMFInit(); | |
46 | |
47 if (FAILED(MFCreateAttributes(attributes, count))) | |
48 return false; | |
49 | |
50 return SUCCEEDED((*attributes)->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, | |
51 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID)); | |
52 } | |
53 | |
54 bool EnumerateVideoDevices(IMFActivate*** devices, | |
55 UINT32* count) { | |
56 ScopedComPtr<IMFAttributes> attributes; | |
57 if (!PrepareVideoCaptureAttributes(attributes.Receive(), 1)) | |
58 return false; | |
59 | |
60 return SUCCEEDED(MFEnumDeviceSources(attributes, devices, count)); | |
61 } | |
62 | |
63 bool CreateVideoCaptureDevice(const char* sym_link, IMFMediaSource** source) { | |
64 ScopedComPtr<IMFAttributes> attributes; | |
65 if (!PrepareVideoCaptureAttributes(attributes.Receive(), 2)) | |
66 return false; | |
67 | |
68 attributes->SetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, | |
69 base::SysUTF8ToWide(sym_link).c_str()); | |
70 | |
71 return SUCCEEDED(MFCreateDeviceSource(attributes, source)); | |
72 } | |
73 | |
74 bool FormatFromGuid(const GUID& guid, VideoPixelFormat* format) { | |
75 struct { | |
76 const GUID& guid; | |
77 const VideoPixelFormat format; | |
78 } static const kFormatMap[] = { | |
79 { MFVideoFormat_I420, PIXEL_FORMAT_I420 }, | |
80 { MFVideoFormat_YUY2, PIXEL_FORMAT_YUY2 }, | |
81 { MFVideoFormat_UYVY, PIXEL_FORMAT_UYVY }, | |
82 { MFVideoFormat_RGB24, PIXEL_FORMAT_RGB24 }, | |
83 { MFVideoFormat_ARGB32, PIXEL_FORMAT_ARGB }, | |
84 { MFVideoFormat_MJPG, PIXEL_FORMAT_MJPEG }, | |
85 { MFVideoFormat_YV12, PIXEL_FORMAT_YV12 }, | |
86 }; | |
87 | |
88 for (int i = 0; i < arraysize(kFormatMap); ++i) { | |
89 if (kFormatMap[i].guid == guid) { | |
90 *format = kFormatMap[i].format; | |
91 return true; | |
92 } | |
93 } | |
94 | |
95 return false; | |
96 } | |
97 | |
98 bool GetFrameSize(IMFMediaType* type, gfx::Size* frame_size) { | |
99 UINT32 width32, height32; | 30 UINT32 width32, height32; |
100 if (FAILED(MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width32, &height32))) | 31 if (FAILED(MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width32, &height32))) |
101 return false; | 32 return false; |
102 frame_size->SetSize(width32, height32); | 33 frame_size->SetSize(width32, height32); |
103 return true; | 34 return true; |
104 } | 35 } |
105 | 36 |
106 bool GetFrameRate(IMFMediaType* type, | 37 static bool GetFrameRate(IMFMediaType* type, |
107 int* frame_rate_numerator, | 38 int* frame_rate_numerator, |
108 int* frame_rate_denominator) { | 39 int* frame_rate_denominator) { |
109 UINT32 numerator, denominator; | 40 UINT32 numerator, denominator; |
110 if (FAILED(MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, | 41 if (FAILED(MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, |
111 &denominator))|| | 42 &denominator))|| |
112 !denominator) { | 43 !denominator) { |
113 return false; | 44 return false; |
114 } | 45 } |
115 *frame_rate_numerator = numerator; | 46 *frame_rate_numerator = numerator; |
116 *frame_rate_denominator = denominator; | 47 *frame_rate_denominator = denominator; |
117 return true; | 48 return true; |
118 } | 49 } |
119 | 50 |
120 bool FillCapabilitiesFromType(IMFMediaType* type, | 51 static bool FillCapabilitiesFromType(IMFMediaType* type, |
121 VideoCaptureCapabilityWin* capability) { | 52 VideoCaptureCapabilityWin* capability) { |
122 GUID type_guid; | 53 GUID type_guid; |
123 if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &type_guid)) || | 54 if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &type_guid)) || |
124 !GetFrameSize(type, &capability->supported_format.frame_size) || | 55 !GetFrameSize(type, &capability->supported_format.frame_size) || |
125 !GetFrameRate(type, | 56 !GetFrameRate(type, |
126 &capability->frame_rate_numerator, | 57 &capability->frame_rate_numerator, |
127 &capability->frame_rate_denominator) || | 58 &capability->frame_rate_denominator) || |
128 !FormatFromGuid(type_guid, &capability->supported_format.pixel_format)) { | 59 !VideoCaptureDeviceMFWin::FormatFromGuid(type_guid, |
60 &capability->supported_format.pixel_format)) { | |
129 return false; | 61 return false; |
130 } | 62 } |
131 // Keep the integer version of the frame_rate for (potential) returns. | 63 // Keep the integer version of the frame_rate for (potential) returns. |
132 capability->supported_format.frame_rate = | 64 capability->supported_format.frame_rate = |
133 capability->frame_rate_numerator / capability->frame_rate_denominator; | 65 capability->frame_rate_numerator / capability->frame_rate_denominator; |
134 | 66 |
135 return true; | 67 return true; |
136 } | 68 } |
137 | 69 |
138 HRESULT FillCapabilities(IMFSourceReader* source, | 70 HRESULT FillCapabilities(IMFSourceReader* source, |
139 CapabilityList* capabilities) { | 71 CapabilityList* capabilities) { |
140 DWORD stream_index = 0; | 72 DWORD stream_index = 0; |
141 ScopedComPtr<IMFMediaType> type; | 73 ScopedComPtr<IMFMediaType> type; |
142 HRESULT hr; | 74 HRESULT hr; |
143 while (SUCCEEDED(hr = source->GetNativeMediaType( | 75 while (SUCCEEDED(hr = source->GetNativeMediaType( |
144 MF_SOURCE_READER_FIRST_VIDEO_STREAM, stream_index, type.Receive()))) { | 76 MF_SOURCE_READER_FIRST_VIDEO_STREAM, stream_index, type.Receive()))) { |
145 VideoCaptureCapabilityWin capability(stream_index++); | 77 VideoCaptureCapabilityWin capability(stream_index++); |
146 if (FillCapabilitiesFromType(type, &capability)) | 78 if (FillCapabilitiesFromType(type, &capability)) |
147 capabilities->Add(capability); | 79 capabilities->Add(capability); |
148 type.Release(); | 80 type.Release(); |
149 } | 81 } |
150 | 82 |
151 if (capabilities->empty() && (SUCCEEDED(hr) || hr == MF_E_NO_MORE_TYPES)) | 83 if (capabilities->empty() && (SUCCEEDED(hr) || hr == MF_E_NO_MORE_TYPES)) |
152 hr = HRESULT_FROM_WIN32(ERROR_EMPTY); | 84 hr = HRESULT_FROM_WIN32(ERROR_EMPTY); |
153 | 85 |
154 return (hr == MF_E_NO_MORE_TYPES) ? S_OK : hr; | 86 return (hr == MF_E_NO_MORE_TYPES) ? S_OK : hr; |
155 } | 87 } |
156 | 88 |
157 bool LoadMediaFoundationDlls() { | |
158 static const wchar_t* const kMfDLLs[] = { | |
159 L"%WINDIR%\\system32\\mf.dll", | |
160 L"%WINDIR%\\system32\\mfplat.dll", | |
161 L"%WINDIR%\\system32\\mfreadwrite.dll", | |
162 }; | |
163 | |
164 for (int i = 0; i < arraysize(kMfDLLs); ++i) { | |
165 wchar_t path[MAX_PATH] = {0}; | |
166 ExpandEnvironmentStringsW(kMfDLLs[i], path, arraysize(path)); | |
167 if (!LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH)) | |
168 return false; | |
169 } | |
170 | |
171 return true; | |
172 } | |
173 | |
174 } // namespace | |
175 | 89 |
176 class MFReaderCallback FINAL | 90 class MFReaderCallback FINAL |
177 : public base::RefCountedThreadSafe<MFReaderCallback>, | 91 : public base::RefCountedThreadSafe<MFReaderCallback>, |
178 public IMFSourceReaderCallback { | 92 public IMFSourceReaderCallback { |
179 public: | 93 public: |
180 MFReaderCallback(VideoCaptureDeviceMFWin* observer) | 94 MFReaderCallback(VideoCaptureDeviceMFWin* observer) |
181 : observer_(observer), wait_event_(NULL) { | 95 : observer_(observer), wait_event_(NULL) { |
182 } | 96 } |
183 | 97 |
184 void SetSignalOnFlush(base::WaitableEvent* event) { | 98 void SetSignalOnFlush(base::WaitableEvent* event) { |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
243 | 157 |
244 private: | 158 private: |
245 friend class base::RefCountedThreadSafe<MFReaderCallback>; | 159 friend class base::RefCountedThreadSafe<MFReaderCallback>; |
246 ~MFReaderCallback() {} | 160 ~MFReaderCallback() {} |
247 | 161 |
248 VideoCaptureDeviceMFWin* observer_; | 162 VideoCaptureDeviceMFWin* observer_; |
249 base::WaitableEvent* wait_event_; | 163 base::WaitableEvent* wait_event_; |
250 }; | 164 }; |
251 | 165 |
252 // static | 166 // static |
253 bool VideoCaptureDeviceMFWin::PlatformSupported() { | 167 bool VideoCaptureDeviceMFWin::FormatFromGuid(const GUID& guid, |
254 // Even though the DLLs might be available on Vista, we get crashes | 168 VideoPixelFormat* format) { |
255 // when running our tests on the build bots. | 169 struct { |
256 if (base::win::GetVersion() < base::win::VERSION_WIN7) | 170 const GUID& guid; |
257 return false; | 171 const VideoPixelFormat format; |
172 } static const kFormatMap[] = { | |
173 { MFVideoFormat_I420, PIXEL_FORMAT_I420 }, | |
174 { MFVideoFormat_YUY2, PIXEL_FORMAT_YUY2 }, | |
175 { MFVideoFormat_UYVY, PIXEL_FORMAT_UYVY }, | |
176 { MFVideoFormat_RGB24, PIXEL_FORMAT_RGB24 }, | |
177 { MFVideoFormat_ARGB32, PIXEL_FORMAT_ARGB }, | |
178 { MFVideoFormat_MJPG, PIXEL_FORMAT_MJPEG }, | |
179 { MFVideoFormat_YV12, PIXEL_FORMAT_YV12 }, | |
180 }; | |
258 | 181 |
259 static bool g_dlls_available = LoadMediaFoundationDlls(); | 182 for (int i = 0; i < arraysize(kFormatMap); ++i) { |
260 return g_dlls_available; | 183 if (kFormatMap[i].guid == guid) { |
261 } | 184 *format = kFormatMap[i].format; |
262 | 185 return true; |
263 // static | |
264 void VideoCaptureDeviceMFWin::GetDeviceNames(Names* device_names) { | |
265 ScopedCoMem<IMFActivate*> devices; | |
266 UINT32 count; | |
267 if (!EnumerateVideoDevices(&devices, &count)) | |
268 return; | |
269 | |
270 HRESULT hr; | |
271 for (UINT32 i = 0; i < count; ++i) { | |
272 UINT32 name_size, id_size; | |
273 ScopedCoMem<wchar_t> name, id; | |
274 if (SUCCEEDED(hr = devices[i]->GetAllocatedString( | |
275 MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, &name, &name_size)) && | |
276 SUCCEEDED(hr = devices[i]->GetAllocatedString( | |
277 MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, &id, | |
278 &id_size))) { | |
279 std::wstring name_w(name, name_size), id_w(id, id_size); | |
280 Name device(base::SysWideToUTF8(name_w), base::SysWideToUTF8(id_w), | |
281 Name::MEDIA_FOUNDATION); | |
282 device_names->push_back(device); | |
283 } else { | |
284 DLOG(WARNING) << "GetAllocatedString failed: " << std::hex << hr; | |
285 } | 186 } |
286 devices[i]->Release(); | |
287 } | |
288 } | |
289 | |
290 // static | |
291 void VideoCaptureDeviceMFWin::GetDeviceSupportedFormats(const Name& device, | |
292 VideoCaptureFormats* formats) { | |
293 ScopedComPtr<IMFMediaSource> source; | |
294 if (!CreateVideoCaptureDevice(device.id().c_str(), source.Receive())) | |
295 return; | |
296 | |
297 HRESULT hr; | |
298 base::win::ScopedComPtr<IMFSourceReader> reader; | |
299 if (FAILED(hr = MFCreateSourceReaderFromMediaSource(source, NULL, | |
300 reader.Receive()))) { | |
301 DLOG(ERROR) << "MFCreateSourceReaderFromMediaSource: " << std::hex << hr; | |
302 return; | |
303 } | 187 } |
304 | 188 |
305 DWORD stream_index = 0; | 189 return false; |
306 ScopedComPtr<IMFMediaType> type; | |
307 while (SUCCEEDED(hr = reader->GetNativeMediaType( | |
308 MF_SOURCE_READER_FIRST_VIDEO_STREAM, stream_index, type.Receive()))) { | |
309 UINT32 width, height; | |
310 hr = MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width, &height); | |
311 if (FAILED(hr)) { | |
312 DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr; | |
313 return; | |
314 } | |
315 VideoCaptureFormat capture_format; | |
316 capture_format.frame_size.SetSize(width, height); | |
317 | |
318 UINT32 numerator, denominator; | |
319 hr = MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, &denominator); | |
320 if (FAILED(hr)) { | |
321 DLOG(ERROR) << "MFGetAttributeSize: " << std::hex << hr; | |
322 return; | |
323 } | |
324 capture_format.frame_rate = denominator ? numerator / denominator : 0; | |
325 | |
326 GUID type_guid; | |
327 hr = type->GetGUID(MF_MT_SUBTYPE, &type_guid); | |
328 if (FAILED(hr)) { | |
329 DLOG(ERROR) << "GetGUID: " << std::hex << hr; | |
330 return; | |
331 } | |
332 FormatFromGuid(type_guid, &capture_format.pixel_format); | |
333 type.Release(); | |
334 formats->push_back(capture_format); | |
335 ++stream_index; | |
336 | |
337 DVLOG(1) << device.name() << " resolution: " | |
338 << capture_format.frame_size.ToString() << ", fps: " | |
339 << capture_format.frame_rate << ", pixel format: " | |
340 << capture_format.pixel_format; | |
341 } | |
342 } | 190 } |
343 | 191 |
344 const std::string VideoCaptureDevice::Name::GetModel() const { | 192 const std::string VideoCaptureDevice::Name::GetModel() const { |
345 const size_t vid_prefix_size = sizeof(kVidPrefix) - 1; | 193 const size_t vid_prefix_size = sizeof(kVidPrefix) - 1; |
346 const size_t pid_prefix_size = sizeof(kPidPrefix) - 1; | 194 const size_t pid_prefix_size = sizeof(kPidPrefix) - 1; |
347 const size_t vid_location = unique_id_.find(kVidPrefix); | 195 const size_t vid_location = unique_id_.find(kVidPrefix); |
348 if (vid_location == std::string::npos || | 196 if (vid_location == std::string::npos || |
349 vid_location + vid_prefix_size + kVidPidSize > unique_id_.size()) { | 197 vid_location + vid_prefix_size + kVidPidSize > unique_id_.size()) { |
350 return ""; | 198 return ""; |
351 } | 199 } |
(...skipping 11 matching lines...) Expand all Loading... | |
363 | 211 |
364 VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(const Name& device_name) | 212 VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(const Name& device_name) |
365 : name_(device_name), capture_(0) { | 213 : name_(device_name), capture_(0) { |
366 DetachFromThread(); | 214 DetachFromThread(); |
367 } | 215 } |
368 | 216 |
369 VideoCaptureDeviceMFWin::~VideoCaptureDeviceMFWin() { | 217 VideoCaptureDeviceMFWin::~VideoCaptureDeviceMFWin() { |
370 DCHECK(CalledOnValidThread()); | 218 DCHECK(CalledOnValidThread()); |
371 } | 219 } |
372 | 220 |
373 bool VideoCaptureDeviceMFWin::Init() { | 221 bool VideoCaptureDeviceMFWin::Init(ScopedComPtr<IMFMediaSource> source) { |
374 DCHECK(CalledOnValidThread()); | 222 DCHECK(CalledOnValidThread()); |
375 DCHECK(!reader_); | 223 DCHECK(!reader_); |
376 | 224 |
377 ScopedComPtr<IMFMediaSource> source; | |
378 if (!CreateVideoCaptureDevice(name_.id().c_str(), source.Receive())) | |
379 return false; | |
380 | |
381 ScopedComPtr<IMFAttributes> attributes; | 225 ScopedComPtr<IMFAttributes> attributes; |
382 MFCreateAttributes(attributes.Receive(), 1); | 226 MFCreateAttributes(attributes.Receive(), 1); |
383 DCHECK(attributes); | 227 DCHECK(attributes); |
384 | 228 |
385 callback_ = new MFReaderCallback(this); | 229 callback_ = new MFReaderCallback(this); |
386 attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback_.get()); | 230 attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback_.get()); |
387 | 231 |
388 return SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attributes, | 232 return SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attributes, |
389 reader_.Receive())); | 233 reader_.Receive())); |
390 } | 234 } |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
485 } | 329 } |
486 | 330 |
487 void VideoCaptureDeviceMFWin::OnError(HRESULT hr) { | 331 void VideoCaptureDeviceMFWin::OnError(HRESULT hr) { |
488 std::string log_msg = base::StringPrintf("VideoCaptureDeviceMFWin: %x", hr); | 332 std::string log_msg = base::StringPrintf("VideoCaptureDeviceMFWin: %x", hr); |
489 DLOG(ERROR) << log_msg; | 333 DLOG(ERROR) << log_msg; |
490 if (client_.get()) | 334 if (client_.get()) |
491 client_->OnError(log_msg); | 335 client_->OnError(log_msg); |
492 } | 336 } |
493 | 337 |
494 } // namespace media | 338 } // namespace media |
OLD | NEW |