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. 4 5 #include "media/video/capture/win/video_capture_device_mf_win.h" 6 7 #include <mfapi.h> 8 #include <mferror.h> 9 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/video/capture/win/capability_list_win.h" 17 18 using base::win::ScopedCoMem; 19 using base::win::ScopedComPtr; 20 21 namespace media { 22 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; 28 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))) 32 return false; 33 frame_size->SetSize(width32, height32); 34 return true; 35 } 36 37 static bool GetFrameRate(IMFMediaType* type, 38 int* frame_rate_numerator, 39 int* frame_rate_denominator) { 40 UINT32 numerator, denominator; 41 if (FAILED(MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator, 42 &denominator))|| 43 !denominator) { 44 return false; 45 } 46 *frame_rate_numerator = numerator; 47 *frame_rate_denominator = denominator; 48 return true; 49 } 50 51 static bool FillCapabilitiesFromType(IMFMediaType* type, 52 VideoCaptureCapabilityWin* capability) { 53 GUID type_guid; 54 if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &type_guid)) || 55 !GetFrameSize(type, &capability->supported_format.frame_size) || 56 !GetFrameRate(type, 57 &capability->frame_rate_numerator, 58 &capability->frame_rate_denominator) || 59 !VideoCaptureDeviceMFWin::FormatFromGuid(type_guid, 60 &capability->supported_format.pixel_format)) { 61 return false; 62 } 63 capability->supported_format.frame_rate = 64 capability->frame_rate_numerator / capability->frame_rate_denominator; 65 66 return true; 67 } 68 69 HRESULT FillCapabilities(IMFSourceReader* source, 70 CapabilityList* capabilities) { 71 DWORD stream_index = 0; 72 ScopedComPtr<IMFMediaType> type; 73 HRESULT hr; 74 while (SUCCEEDED(hr = source->GetNativeMediaType( 75 MF_SOURCE_READER_FIRST_VIDEO_STREAM, stream_index, type.Receive()))) { 76 VideoCaptureCapabilityWin capability(stream_index++); 77 if (FillCapabilitiesFromType(type, &capability)) 78 capabilities->Add(capability); 79 type.Release(); 80 } 81 82 if (capabilities->empty() && (SUCCEEDED(hr) || hr == MF_E_NO_MORE_TYPES)) 83 hr = HRESULT_FROM_WIN32(ERROR_EMPTY); 84 85 return (hr == MF_E_NO_MORE_TYPES) ? S_OK : hr; 86 } 87 88 89 class MFReaderCallback FINAL 90 : public base::RefCountedThreadSafe<MFReaderCallback>, 91 public IMFSourceReaderCallback { 92 public: 93 MFReaderCallback(VideoCaptureDeviceMFWin* observer) 94 : observer_(observer), wait_event_(NULL) { 95 } 96 97 void SetSignalOnFlush(base::WaitableEvent* event) { 98 wait_event_ = event; 99 } 100 101 STDMETHOD(QueryInterface)(REFIID riid, void** object) { 102 if (riid != IID_IUnknown && riid != IID_IMFSourceReaderCallback) 103 return E_NOINTERFACE; 104 *object = static_cast<IMFSourceReaderCallback*>(this); 105 AddRef(); 106 return S_OK; 107 } 108 109 STDMETHOD_(ULONG, AddRef)() { 110 base::RefCountedThreadSafe<MFReaderCallback>::AddRef(); 111 return 1U; 112 } 113 114 STDMETHOD_(ULONG, Release)() { 115 base::RefCountedThreadSafe<MFReaderCallback>::Release(); 116 return 1U; 117 } 118 119 STDMETHOD(OnReadSample)(HRESULT status, DWORD stream_index, 120 DWORD stream_flags, LONGLONG time_stamp, IMFSample* sample) { 121 base::TimeTicks stamp(base::TimeTicks::Now()); 122 if (!sample) { 123 observer_->OnIncomingCapturedData(NULL, 0, 0, stamp); 124 return S_OK; 125 } 126 127 DWORD count = 0; 128 sample->GetBufferCount(&count); 129 130 for (DWORD i = 0; i < count; ++i) { 131 ScopedComPtr<IMFMediaBuffer> buffer; 132 sample->GetBufferByIndex(i, buffer.Receive()); 133 if (buffer) { 134 DWORD length = 0, max_length = 0; 135 BYTE* data = NULL; 136 buffer->Lock(&data, &max_length, &length); 137 observer_->OnIncomingCapturedData(data, length, 0, stamp); 138 buffer->Unlock(); 139 } 140 } 141 return S_OK; 142 } 143 144 STDMETHOD(OnFlush)(DWORD stream_index) { 145 if (wait_event_) { 146 wait_event_->Signal(); 147 wait_event_ = NULL; 148 } 149 return S_OK; 150 } 151 152 STDMETHOD(OnEvent)(DWORD stream_index, IMFMediaEvent* event) { 153 NOTIMPLEMENTED(); 154 return S_OK; 155 } 156 157 private: 158 friend class base::RefCountedThreadSafe<MFReaderCallback>; 159 ~MFReaderCallback() {} 160 161 VideoCaptureDeviceMFWin* observer_; 162 base::WaitableEvent* wait_event_; 163 }; 164 165 // static 166 bool VideoCaptureDeviceMFWin::FormatFromGuid(const GUID& guid, 167 VideoPixelFormat* format) { 168 struct { 169 const GUID& guid; 170 const VideoPixelFormat format; 171 } static const kFormatMap[] = { 172 { MFVideoFormat_I420, PIXEL_FORMAT_I420 }, 173 { MFVideoFormat_YUY2, PIXEL_FORMAT_YUY2 }, 174 { MFVideoFormat_UYVY, PIXEL_FORMAT_UYVY }, 175 { MFVideoFormat_RGB24, PIXEL_FORMAT_RGB24 }, 176 { MFVideoFormat_ARGB32, PIXEL_FORMAT_ARGB }, 177 { MFVideoFormat_MJPG, PIXEL_FORMAT_MJPEG }, 178 { MFVideoFormat_YV12, PIXEL_FORMAT_YV12 }, 179 }; 180 181 for (int i = 0; i < arraysize(kFormatMap); ++i) { 182 if (kFormatMap[i].guid == guid) { 183 *format = kFormatMap[i].format; 184 return true; 185 } 186 } 187 188 return false; 189 } 190 191 const std::string VideoCaptureDevice::Name::GetModel() const { 192 const size_t vid_prefix_size = sizeof(kVidPrefix) - 1; 193 const size_t pid_prefix_size = sizeof(kPidPrefix) - 1; 194 const size_t vid_location = unique_id_.find(kVidPrefix); 195 if (vid_location == std::string::npos || 196 vid_location + vid_prefix_size + kVidPidSize > unique_id_.size()) { 197 return ""; 198 } 199 const size_t pid_location = unique_id_.find(kPidPrefix); 200 if (pid_location == std::string::npos || 201 pid_location + pid_prefix_size + kVidPidSize > unique_id_.size()) { 202 return ""; 203 } 204 std::string id_vendor = 205 unique_id_.substr(vid_location + vid_prefix_size, kVidPidSize); 206 std::string id_product = 207 unique_id_.substr(pid_location + pid_prefix_size, kVidPidSize); 208 return id_vendor + ":" + id_product; 209 } 210 211 VideoCaptureDeviceMFWin::VideoCaptureDeviceMFWin(const Name& device_name) 212 : name_(device_name), capture_(0) { 213 DetachFromThread(); 214 } 215 216 VideoCaptureDeviceMFWin::~VideoCaptureDeviceMFWin() { 217 DCHECK(CalledOnValidThread()); 218 } 219 220 bool VideoCaptureDeviceMFWin::Init( 221 const base::win::ScopedComPtr<IMFMediaSource>& source) { 222 DCHECK(CalledOnValidThread()); 223 DCHECK(!reader_); 224 225 ScopedComPtr<IMFAttributes> attributes; 226 MFCreateAttributes(attributes.Receive(), 1); 227 DCHECK(attributes); 228 229 callback_ = new MFReaderCallback(this); 230 attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback_.get()); 231 232 return SUCCEEDED(MFCreateSourceReaderFromMediaSource(source, attributes, 233 reader_.Receive())); 234 } 235 236 void VideoCaptureDeviceMFWin::AllocateAndStart( 237 const VideoCaptureParams& params, 238 scoped_ptr<VideoCaptureDevice::Client> client) { 239 DCHECK(CalledOnValidThread()); 240 241 base::AutoLock lock(lock_); 242 243 client_ = client.Pass(); 244 DCHECK_EQ(capture_, false); 245 246 CapabilityList capabilities; 247 HRESULT hr = S_OK; 248 if (!reader_ || FAILED(hr = FillCapabilities(reader_, &capabilities))) { 249 OnError(hr); 250 return; 251 } 252 253 VideoCaptureCapabilityWin found_capability = 254 capabilities.GetBestMatchedFormat( 255 params.requested_format.frame_size.width(), 256 params.requested_format.frame_size.height(), 257 params.requested_format.frame_rate); 258 259 ScopedComPtr<IMFMediaType> type; 260 if (FAILED(hr = reader_->GetNativeMediaType( 261 MF_SOURCE_READER_FIRST_VIDEO_STREAM, found_capability.stream_index, 262 type.Receive())) || 263 FAILED(hr = reader_->SetCurrentMediaType( 264 MF_SOURCE_READER_FIRST_VIDEO_STREAM, NULL, type))) { 265 OnError(hr); 266 return; 267 } 268 269 if (FAILED(hr = reader_->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, 270 NULL, NULL, NULL, NULL))) { 271 OnError(hr); 272 return; 273 } 274 capture_format_ = found_capability.supported_format; 275 capture_ = true; 276 } 277 278 void VideoCaptureDeviceMFWin::StopAndDeAllocate() { 279 DCHECK(CalledOnValidThread()); 280 base::WaitableEvent flushed(false, false); 281 const int kFlushTimeOutInMs = 1000; 282 bool wait = false; 283 { 284 base::AutoLock lock(lock_); 285 if (capture_) { 286 capture_ = false; 287 callback_->SetSignalOnFlush(&flushed); 288 HRESULT hr = reader_->Flush(MF_SOURCE_READER_ALL_STREAMS); 289 wait = SUCCEEDED(hr); 290 if (!wait) { 291 callback_->SetSignalOnFlush(NULL); 292 } 293 } 294 client_.reset(); 295 } 296 297 // If the device has been unplugged, the Flush() won't trigger the event 298 // and a timeout will happen. 299 // TODO(tommi): Hook up the IMFMediaEventGenerator notifications API and 300 // do not wait at all after getting MEVideoCaptureDeviceRemoved event. 301 // See issue/226396. 302 if (wait) 303 flushed.TimedWait(base::TimeDelta::FromMilliseconds(kFlushTimeOutInMs)); 304 } 305 306 void VideoCaptureDeviceMFWin::OnIncomingCapturedData( 307 const uint8* data, 308 int length, 309 int rotation, 310 const base::TimeTicks& time_stamp) { 311 base::AutoLock lock(lock_); 312 if (data && client_.get()) { 313 client_->OnIncomingCapturedData( 314 data, length, capture_format_, rotation, time_stamp); 315 } 316 317 if (capture_) { 318 HRESULT hr = reader_->ReadSample(MF_SOURCE_READER_FIRST_VIDEO_STREAM, 0, 319 NULL, NULL, NULL, NULL); 320 if (FAILED(hr)) { 321 // If running the *VideoCap* unit tests on repeat, this can sometimes 322 // fail with HRESULT_FROM_WINHRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION). 323 // It's not clear to me why this is, but it is possible that it has 324 // something to do with this bug: 325 // http://support.microsoft.com/kb/979567 326 OnError(hr); 327 } 328 } 329 } 330 331 void VideoCaptureDeviceMFWin::OnError(HRESULT hr) { 332 std::string log_msg = base::StringPrintf("VideoCaptureDeviceMFWin: %x", hr); 333 DLOG(ERROR) << log_msg; 334 if (client_.get()) 335 client_->OnError(log_msg); 336 } 337 338 } // namespace media 339