Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(47)

Side by Side Diff: media/capture/video/win/video_capture_device_mf_win.cc

Issue 2143903003: [WIP] Move media/capture to device/capture (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/capture/video/win/video_capture_device_mf_win.h"
6
7 #include <mfapi.h>
8 #include <mferror.h>
9 #include <stddef.h>
10
11 #include <utility>
12
13 #include "base/location.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/sys_string_conversions.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/win/scoped_co_mem.h"
19 #include "base/win/windows_version.h"
20 #include "media/capture/video/win/capability_list_win.h"
21
22 using base::win::ScopedCoMem;
23 using base::win::ScopedComPtr;
24
25 namespace media {
26
27 // In Windows device identifiers, the USB VID and PID are preceded by the string
28 // "vid_" or "pid_". The identifiers are each 4 bytes long.
29 const char kVidPrefix[] = "vid_"; // Also contains '\0'.
30 const char kPidPrefix[] = "pid_"; // Also contains '\0'.
31 const size_t kVidPidSize = 4;
32
33 static bool GetFrameSize(IMFMediaType* type, gfx::Size* frame_size) {
34 UINT32 width32, height32;
35 if (FAILED(MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &width32, &height32)))
36 return false;
37 frame_size->SetSize(width32, height32);
38 return true;
39 }
40
41 static bool GetFrameRate(IMFMediaType* type, float* frame_rate) {
42 UINT32 numerator, denominator;
43 if (FAILED(MFGetAttributeRatio(type, MF_MT_FRAME_RATE, &numerator,
44 &denominator)) ||
45 !denominator) {
46 return false;
47 }
48 *frame_rate = static_cast<float>(numerator) / denominator;
49 return true;
50 }
51
52 static bool FillFormat(IMFMediaType* type, VideoCaptureFormat* format) {
53 GUID type_guid;
54 if (FAILED(type->GetGUID(MF_MT_SUBTYPE, &type_guid)) ||
55 !GetFrameSize(type, &format->frame_size) ||
56 !GetFrameRate(type, &format->frame_rate) ||
57 !VideoCaptureDeviceMFWin::FormatFromGuid(type_guid,
58 &format->pixel_format)) {
59 return false;
60 }
61
62 return true;
63 }
64
65 HRESULT FillCapabilities(IMFSourceReader* source,
66 CapabilityList* capabilities) {
67 DWORD stream_index = 0;
68 ScopedComPtr<IMFMediaType> type;
69 HRESULT hr;
70 while (SUCCEEDED(hr = source->GetNativeMediaType(
71 kFirstVideoStream, stream_index, type.Receive()))) {
72 VideoCaptureFormat format;
73 if (FillFormat(type.get(), &format))
74 capabilities->emplace_back(stream_index, format);
75 type.Release();
76 ++stream_index;
77 }
78
79 if (capabilities->empty() && (SUCCEEDED(hr) || hr == MF_E_NO_MORE_TYPES))
80 hr = HRESULT_FROM_WIN32(ERROR_EMPTY);
81
82 return (hr == MF_E_NO_MORE_TYPES) ? S_OK : hr;
83 }
84
85 class MFReaderCallback final
86 : public base::RefCountedThreadSafe<MFReaderCallback>,
87 public IMFSourceReaderCallback {
88 public:
89 MFReaderCallback(VideoCaptureDeviceMFWin* observer)
90 : observer_(observer), wait_event_(NULL) {}
91
92 void SetSignalOnFlush(base::WaitableEvent* event) { wait_event_ = event; }
93
94 STDMETHOD(QueryInterface)(REFIID riid, void** object) override {
95 if (riid != IID_IUnknown && riid != IID_IMFSourceReaderCallback)
96 return E_NOINTERFACE;
97 *object = static_cast<IMFSourceReaderCallback*>(this);
98 AddRef();
99 return S_OK;
100 }
101
102 STDMETHOD_(ULONG, AddRef)() override {
103 base::RefCountedThreadSafe<MFReaderCallback>::AddRef();
104 return 1U;
105 }
106
107 STDMETHOD_(ULONG, Release)() override {
108 base::RefCountedThreadSafe<MFReaderCallback>::Release();
109 return 1U;
110 }
111
112 STDMETHOD(OnReadSample)
113 (HRESULT status,
114 DWORD stream_index,
115 DWORD stream_flags,
116 LONGLONG raw_time_stamp,
117 IMFSample* sample) override {
118 base::TimeTicks reference_time(base::TimeTicks::Now());
119 base::TimeDelta timestamp =
120 base::TimeDelta::FromMicroseconds(raw_time_stamp / 10);
121 if (!sample) {
122 observer_->OnIncomingCapturedData(NULL, 0, 0, reference_time, timestamp);
123 return S_OK;
124 }
125
126 DWORD count = 0;
127 sample->GetBufferCount(&count);
128
129 for (DWORD i = 0; i < count; ++i) {
130 ScopedComPtr<IMFMediaBuffer> buffer;
131 sample->GetBufferByIndex(i, buffer.Receive());
132 if (buffer.get()) {
133 DWORD length = 0, max_length = 0;
134 BYTE* data = NULL;
135 buffer->Lock(&data, &max_length, &length);
136 observer_->OnIncomingCapturedData(data, length, 0, reference_time,
137 timestamp);
138 buffer->Unlock();
139 }
140 }
141 return S_OK;
142 }
143
144 STDMETHOD(OnFlush)(DWORD stream_index) override {
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) override {
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 (const auto& kFormat : kFormatMap) {
182 if (kFormat.guid == guid) {
183 *format = kFormat.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 std::string();
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 std::string();
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_.get());
224
225 ScopedComPtr<IMFAttributes> attributes;
226 MFCreateAttributes(attributes.Receive(), 1);
227 DCHECK(attributes.get());
228
229 callback_ = new MFReaderCallback(this);
230 attributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, callback_.get());
231
232 return SUCCEEDED(MFCreateSourceReaderFromMediaSource(
233 source.get(), attributes.get(), reader_.Receive()));
234 }
235
236 void VideoCaptureDeviceMFWin::AllocateAndStart(
237 const VideoCaptureParams& params,
238 std::unique_ptr<VideoCaptureDevice::Client> client) {
239 DCHECK(CalledOnValidThread());
240
241 base::AutoLock lock(lock_);
242
243 client_ = std::move(client);
244 DCHECK_EQ(capture_, false);
245
246 CapabilityList capabilities;
247 HRESULT hr = S_OK;
248 if (reader_.get()) {
249 hr = FillCapabilities(reader_.get(), &capabilities);
250 if (SUCCEEDED(hr)) {
251 const CapabilityWin found_capability =
252 GetBestMatchedCapability(params.requested_format, capabilities);
253 ScopedComPtr<IMFMediaType> type;
254 hr = reader_->GetNativeMediaType(
255 kFirstVideoStream, found_capability.stream_index, type.Receive());
256 if (SUCCEEDED(hr)) {
257 hr = reader_->SetCurrentMediaType(kFirstVideoStream, NULL, type.get());
258 if (SUCCEEDED(hr)) {
259 hr =
260 reader_->ReadSample(kFirstVideoStream, 0, NULL, NULL, NULL, NULL);
261 if (SUCCEEDED(hr)) {
262 capture_format_ = found_capability.supported_format;
263 capture_ = true;
264 return;
265 }
266 }
267 }
268 }
269 }
270
271 OnError(FROM_HERE, hr);
272 }
273
274 void VideoCaptureDeviceMFWin::StopAndDeAllocate() {
275 DCHECK(CalledOnValidThread());
276 base::WaitableEvent flushed(base::WaitableEvent::ResetPolicy::AUTOMATIC,
277 base::WaitableEvent::InitialState::NOT_SIGNALED);
278 const int kFlushTimeOutInMs = 1000;
279 bool wait = false;
280 {
281 base::AutoLock lock(lock_);
282 if (capture_) {
283 capture_ = false;
284 callback_->SetSignalOnFlush(&flushed);
285 wait = SUCCEEDED(
286 reader_->Flush(static_cast<DWORD>(MF_SOURCE_READER_ALL_STREAMS)));
287 if (!wait) {
288 callback_->SetSignalOnFlush(NULL);
289 }
290 }
291 client_.reset();
292 }
293
294 // If the device has been unplugged, the Flush() won't trigger the event
295 // and a timeout will happen.
296 // TODO(tommi): Hook up the IMFMediaEventGenerator notifications API and
297 // do not wait at all after getting MEVideoCaptureDeviceRemoved event.
298 // See issue/226396.
299 if (wait)
300 flushed.TimedWait(base::TimeDelta::FromMilliseconds(kFlushTimeOutInMs));
301 }
302
303 void VideoCaptureDeviceMFWin::OnIncomingCapturedData(
304 const uint8_t* data,
305 int length,
306 int rotation,
307 base::TimeTicks reference_time,
308 base::TimeDelta timestamp) {
309 base::AutoLock lock(lock_);
310 if (data && client_.get()) {
311 client_->OnIncomingCapturedData(data, length, capture_format_, rotation,
312 reference_time, timestamp);
313 }
314
315 if (capture_) {
316 HRESULT hr =
317 reader_->ReadSample(kFirstVideoStream, 0, NULL, NULL, NULL, NULL);
318 if (FAILED(hr)) {
319 // If running the *VideoCap* unit tests on repeat, this can sometimes
320 // fail with HRESULT_FROM_WINHRESULT_FROM_WIN32(ERROR_INVALID_FUNCTION).
321 // It's not clear to me why this is, but it is possible that it has
322 // something to do with this bug:
323 // http://support.microsoft.com/kb/979567
324 OnError(FROM_HERE, hr);
325 }
326 }
327 }
328
329 void VideoCaptureDeviceMFWin::OnError(
330 const tracked_objects::Location& from_here,
331 HRESULT hr) {
332 if (client_.get()) {
333 client_->OnError(
334 from_here,
335 base::StringPrintf("VideoCaptureDeviceMFWin: %s",
336 logging::SystemErrorCodeToString(hr).c_str()));
337 }
338 }
339
340 } // namespace media
OLDNEW
« no previous file with comments | « media/capture/video/win/video_capture_device_mf_win.h ('k') | media/capture/video/win/video_capture_device_win.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698