| 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" | |
| 11 #include "base/memory/ref_counted.h" | 10 #include "base/memory/ref_counted.h" |
| 12 #include "base/strings/stringprintf.h" | 11 #include "base/strings/stringprintf.h" |
| 13 #include "base/strings/sys_string_conversions.h" | 12 #include "base/strings/sys_string_conversions.h" |
| 14 #include "base/synchronization/waitable_event.h" | 13 #include "base/synchronization/waitable_event.h" |
| 15 #include "base/win/scoped_co_mem.h" | 14 #include "base/win/scoped_co_mem.h" |
| 16 #include "base/win/windows_version.h" | 15 #include "base/win/windows_version.h" |
| 17 #include "media/video/capture/win/capability_list_win.h" | 16 #include "media/video/capture/win/capability_list_win.h" |
| 18 | 17 |
| 19 using base::win::ScopedCoMem; | 18 using base::win::ScopedCoMem; |
| 20 using base::win::ScopedComPtr; | 19 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( |
| 222 const base::win::ScopedComPtr<IMFMediaSource>& source) { |
| 374 DCHECK(CalledOnValidThread()); | 223 DCHECK(CalledOnValidThread()); |
| 375 DCHECK(!reader_); | 224 DCHECK(!reader_); |
| 376 | 225 |
| 377 ScopedComPtr<IMFMediaSource> source; | |
| 378 if (!CreateVideoCaptureDevice(name_.id().c_str(), source.Receive())) | |
| 379 return false; | |
| 380 | |
| 381 ScopedComPtr<IMFAttributes> attributes; | 226 ScopedComPtr<IMFAttributes> attributes; |
| 382 MFCreateAttributes(attributes.Receive(), 1); | 227 MFCreateAttributes(attributes.Receive(), 1); |
| 383 DCHECK(attributes); | 228 DCHECK(attributes); |
| 384 | 229 |
| 385 callback_ = new MFReaderCallback(this); | 230 callback_ = new MFReaderCallback(this); |
| 386 attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback_.get()); | 231 attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback_.get()); |
| 387 | 232 |
| 388 return SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attributes, | 233 return SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attributes, |
| 389 reader_.Receive())); | 234 reader_.Receive())); |
| 390 } | 235 } |
| (...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 485 } | 330 } |
| 486 | 331 |
| 487 void VideoCaptureDeviceMFWin::OnError(HRESULT hr) { | 332 void VideoCaptureDeviceMFWin::OnError(HRESULT hr) { |
| 488 std::string log_msg = base::StringPrintf("VideoCaptureDeviceMFWin: %x", hr); | 333 std::string log_msg = base::StringPrintf("VideoCaptureDeviceMFWin: %x", hr); |
| 489 DLOG(ERROR) << log_msg; | 334 DLOG(ERROR) << log_msg; |
| 490 if (client_.get()) | 335 if (client_.get()) |
| 491 client_->OnError(log_msg); | 336 client_->OnError(log_msg); |
| 492 } | 337 } |
| 493 | 338 |
| 494 } // namespace media | 339 } // namespace media |
| OLD | NEW |