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

Side by Side Diff: media/capture/video/mac/video_capture_device_decklink_mac.mm

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 2014 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/mac/video_capture_device_decklink_mac.h"
6
7 #include <utility>
8
9 #include "base/logging.h"
10 #include "base/macros.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/synchronization/lock.h"
14 #include "third_party/decklink/mac/include/DeckLinkAPI.h"
15
16 namespace {
17
18 // DeckLink SDK uses ScopedComPtr-style APIs. Chrome ScopedComPtr is only
19 // available for Windows builds. This is a verbatim knock-off of the needed
20 // parts of base::win::ScopedComPtr<> for ref counting.
21 template <class T>
22 class ScopedDeckLinkPtr : public scoped_refptr<T> {
23 private:
24 using scoped_refptr<T>::ptr_;
25
26 public:
27 T** Receive() {
28 DCHECK(!ptr_) << "Object leak. Pointer must be NULL";
29 return &ptr_;
30 }
31
32 void** ReceiveVoid() { return reinterpret_cast<void**>(Receive()); }
33
34 void Release() {
35 if (ptr_ != NULL) {
36 ptr_->Release();
37 ptr_ = NULL;
38 }
39 }
40 };
41
42 // This class is used to interact directly with DeckLink SDK for video capture.
43 // Implements the reference counted interface IUnknown. Has a weak reference to
44 // VideoCaptureDeviceDeckLinkMac for sending captured frames, error messages and
45 // logs.
46 class DeckLinkCaptureDelegate
47 : public IDeckLinkInputCallback,
48 public base::RefCountedThreadSafe<DeckLinkCaptureDelegate> {
49 public:
50 DeckLinkCaptureDelegate(const media::VideoCaptureDevice::Name& device_name,
51 media::VideoCaptureDeviceDeckLinkMac* frame_receiver);
52
53 void AllocateAndStart(const media::VideoCaptureParams& params);
54 void StopAndDeAllocate();
55
56 // Remove the VideoCaptureDeviceDeckLinkMac's weak reference.
57 void ResetVideoCaptureDeviceReference();
58
59 private:
60 // IDeckLinkInputCallback interface implementation.
61 HRESULT VideoInputFormatChanged(
62 BMDVideoInputFormatChangedEvents notification_events,
63 IDeckLinkDisplayMode* new_display_mode,
64 BMDDetectedVideoInputFormatFlags detected_signal_flags) override;
65 HRESULT VideoInputFrameArrived(
66 IDeckLinkVideoInputFrame* video_frame,
67 IDeckLinkAudioInputPacket* audio_packet) override;
68
69 // IUnknown interface implementation.
70 HRESULT QueryInterface(REFIID iid, void** ppv) override;
71 ULONG AddRef() override;
72 ULONG Release() override;
73
74 // Forwarder to VideoCaptureDeviceDeckLinkMac::SendErrorString().
75 void SendErrorString(const tracked_objects::Location& from_here,
76 const std::string& reason);
77
78 // Forwarder to VideoCaptureDeviceDeckLinkMac::SendLogString().
79 void SendLogString(const std::string& message);
80
81 const media::VideoCaptureDevice::Name device_name_;
82
83 // Protects concurrent setting and using of |frame_receiver_|.
84 base::Lock lock_;
85 // Weak reference to the captured frames client, used also for error messages
86 // and logging. Initialized on construction and used until cleared by calling
87 // ResetVideoCaptureDeviceReference().
88 media::VideoCaptureDeviceDeckLinkMac* frame_receiver_;
89
90 // This is used to control the video capturing device input interface.
91 ScopedDeckLinkPtr<IDeckLinkInput> decklink_input_;
92 // |decklink_| represents a physical device attached to the host.
93 ScopedDeckLinkPtr<IDeckLink> decklink_;
94
95 base::TimeTicks first_ref_time_;
96
97 // Checks for Device (a.k.a. Audio) thread.
98 base::ThreadChecker thread_checker_;
99
100 friend class scoped_refptr<DeckLinkCaptureDelegate>;
101 friend class base::RefCountedThreadSafe<DeckLinkCaptureDelegate>;
102
103 ~DeckLinkCaptureDelegate() override;
104
105 DISALLOW_COPY_AND_ASSIGN(DeckLinkCaptureDelegate);
106 };
107
108 static float GetDisplayModeFrameRate(
109 const ScopedDeckLinkPtr<IDeckLinkDisplayMode>& display_mode) {
110 BMDTimeValue time_value, time_scale;
111 float display_mode_frame_rate = 0.0f;
112 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK &&
113 time_value > 0) {
114 display_mode_frame_rate = static_cast<float>(time_scale) / time_value;
115 }
116 // Interlaced formats are going to be marked as double the frame rate,
117 // which follows the general naming convention.
118 if (display_mode->GetFieldDominance() == bmdLowerFieldFirst ||
119 display_mode->GetFieldDominance() == bmdUpperFieldFirst) {
120 display_mode_frame_rate *= 2.0f;
121 }
122 return display_mode_frame_rate;
123 }
124
125 DeckLinkCaptureDelegate::DeckLinkCaptureDelegate(
126 const media::VideoCaptureDevice::Name& device_name,
127 media::VideoCaptureDeviceDeckLinkMac* frame_receiver)
128 : device_name_(device_name), frame_receiver_(frame_receiver) {
129 }
130
131 DeckLinkCaptureDelegate::~DeckLinkCaptureDelegate() {
132 }
133
134 void DeckLinkCaptureDelegate::AllocateAndStart(
135 const media::VideoCaptureParams& params) {
136 DCHECK(thread_checker_.CalledOnValidThread());
137 scoped_refptr<IDeckLinkIterator> decklink_iter(
138 CreateDeckLinkIteratorInstance());
139 DLOG_IF(ERROR, !decklink_iter.get()) << "Error creating DeckLink iterator";
140 if (!decklink_iter.get())
141 return;
142
143 ScopedDeckLinkPtr<IDeckLink> decklink_local;
144 while (decklink_iter->Next(decklink_local.Receive()) == S_OK) {
145 CFStringRef device_model_name = NULL;
146 if ((decklink_local->GetModelName(&device_model_name) == S_OK) ||
147 (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) {
148 break;
149 }
150 }
151 if (!decklink_local.get()) {
152 SendErrorString(FROM_HERE, "Device id not found in the system");
153 return;
154 }
155
156 ScopedDeckLinkPtr<IDeckLinkInput> decklink_input_local;
157 if (decklink_local->QueryInterface(
158 IID_IDeckLinkInput, decklink_input_local.ReceiveVoid()) != S_OK) {
159 SendErrorString(FROM_HERE, "Error querying input interface.");
160 return;
161 }
162
163 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
164 if (decklink_input_local->GetDisplayModeIterator(
165 display_mode_iter.Receive()) != S_OK) {
166 SendErrorString(FROM_HERE, "Error creating Display Mode Iterator");
167 return;
168 }
169
170 ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode;
171 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
172 float min_diff = FLT_MAX;
173 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
174 const float diff = labs(display_mode->GetWidth() -
175 params.requested_format.frame_size.width()) +
176 labs(params.requested_format.frame_size.height() -
177 display_mode->GetHeight()) +
178 fabs(params.requested_format.frame_rate -
179 GetDisplayModeFrameRate(display_mode));
180 if (diff < min_diff) {
181 chosen_display_mode = display_mode;
182 min_diff = diff;
183 }
184 display_mode.Release();
185 }
186 if (!chosen_display_mode.get()) {
187 SendErrorString(FROM_HERE, "Could not find a display mode");
188 return;
189 }
190 #if !defined(NDEBUG)
191 DVLOG(1) << "Requested format: "
192 << media::VideoCaptureFormat::ToString(params.requested_format);
193 CFStringRef format_name = NULL;
194 if (chosen_display_mode->GetName(&format_name) == S_OK)
195 DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name);
196 #endif
197
198 // Enable video input. Configure for no input video format change detection,
199 // this in turn will disable calls to VideoInputFormatChanged().
200 if (decklink_input_local->EnableVideoInput(
201 chosen_display_mode->GetDisplayMode(), bmdFormat8BitYUV,
202 bmdVideoInputFlagDefault) != S_OK) {
203 SendErrorString(FROM_HERE, "Could not select the video format we like.");
204 return;
205 }
206
207 decklink_input_local->SetCallback(this);
208 if (decklink_input_local->StartStreams() != S_OK)
209 SendErrorString(FROM_HERE, "Could not start capturing");
210
211 decklink_.swap(decklink_local);
212 decklink_input_.swap(decklink_input_local);
213 }
214
215 void DeckLinkCaptureDelegate::StopAndDeAllocate() {
216 DCHECK(thread_checker_.CalledOnValidThread());
217 if (!decklink_input_.get())
218 return;
219 if (decklink_input_->StopStreams() != S_OK)
220 SendLogString("Problem stopping capture.");
221 decklink_input_->SetCallback(NULL);
222 decklink_input_->DisableVideoInput();
223 decklink_input_.Release();
224 decklink_.Release();
225 ResetVideoCaptureDeviceReference();
226 }
227
228 HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged(
229 BMDVideoInputFormatChangedEvents notification_events,
230 IDeckLinkDisplayMode* new_display_mode,
231 BMDDetectedVideoInputFormatFlags detected_signal_flags) {
232 DCHECK(thread_checker_.CalledOnValidThread());
233 return S_OK;
234 }
235
236 HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(
237 IDeckLinkVideoInputFrame* video_frame,
238 IDeckLinkAudioInputPacket* /* audio_packet */) {
239 // Capture frames are manipulated as an IDeckLinkVideoFrame.
240 uint8_t* video_data = NULL;
241 video_frame->GetBytes(reinterpret_cast<void**>(&video_data));
242
243 media::VideoPixelFormat pixel_format =
244 media::PIXEL_FORMAT_UNKNOWN;
245 switch (video_frame->GetPixelFormat()) {
246 case bmdFormat8BitYUV: // A.k.a. '2vuy';
247 pixel_format = media::PIXEL_FORMAT_UYVY;
248 break;
249 case bmdFormat8BitARGB:
250 pixel_format = media::PIXEL_FORMAT_ARGB;
251 break;
252 default:
253 SendErrorString(FROM_HERE, "Unsupported pixel format");
254 break;
255 }
256
257 const media::VideoCaptureFormat capture_format(
258 gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()),
259 0.0f, // Frame rate is not needed for captured data callback.
260 pixel_format);
261 base::TimeTicks now = base::TimeTicks::Now();
262 if (first_ref_time_.is_null())
263 first_ref_time_ = now;
264 base::AutoLock lock(lock_);
265 if (frame_receiver_) {
266 const BMDTimeScale micros_time_scale = base::Time::kMicrosecondsPerSecond;
267 BMDTimeValue frame_time;
268 BMDTimeValue frame_duration;
269 base::TimeDelta timestamp;
270 if (SUCCEEDED(video_frame->GetStreamTime(&frame_time, &frame_duration,
271 micros_time_scale))) {
272 timestamp = base::TimeDelta::FromMicroseconds(frame_time);
273 } else {
274 timestamp = now - first_ref_time_;
275 }
276 frame_receiver_->OnIncomingCapturedData(
277 video_data, video_frame->GetRowBytes() * video_frame->GetHeight(),
278 capture_format,
279 0, // Rotation.
280 now, timestamp);
281 }
282 return S_OK;
283 }
284
285 HRESULT DeckLinkCaptureDelegate::QueryInterface(REFIID iid, void** ppv) {
286 DCHECK(thread_checker_.CalledOnValidThread());
287 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
288 if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0 ||
289 memcmp(&iid, &IID_IDeckLinkInputCallback, sizeof(REFIID)) == 0) {
290 *ppv = static_cast<IDeckLinkInputCallback*>(this);
291 AddRef();
292 return S_OK;
293 }
294 return E_NOINTERFACE;
295 }
296
297 ULONG DeckLinkCaptureDelegate::AddRef() {
298 DCHECK(thread_checker_.CalledOnValidThread());
299 base::RefCountedThreadSafe<DeckLinkCaptureDelegate>::AddRef();
300 return 1;
301 }
302
303 ULONG DeckLinkCaptureDelegate::Release() {
304 DCHECK(thread_checker_.CalledOnValidThread());
305 bool ret_value = !HasOneRef();
306 base::RefCountedThreadSafe<DeckLinkCaptureDelegate>::Release();
307 return ret_value;
308 }
309
310 void DeckLinkCaptureDelegate::SendErrorString(
311 const tracked_objects::Location& from_here,
312 const std::string& reason) {
313 base::AutoLock lock(lock_);
314 if (frame_receiver_)
315 frame_receiver_->SendErrorString(from_here, reason);
316 }
317
318 void DeckLinkCaptureDelegate::SendLogString(const std::string& message) {
319 base::AutoLock lock(lock_);
320 if (frame_receiver_)
321 frame_receiver_->SendLogString(message);
322 }
323
324 void DeckLinkCaptureDelegate::ResetVideoCaptureDeviceReference() {
325 DCHECK(thread_checker_.CalledOnValidThread());
326 base::AutoLock lock(lock_);
327 frame_receiver_ = NULL;
328 }
329
330 } // namespace
331
332 namespace media {
333
334 static std::string JoinDeviceNameAndFormat(CFStringRef name,
335 CFStringRef format) {
336 return base::SysCFStringRefToUTF8(name) + " - " +
337 base::SysCFStringRefToUTF8(format);
338 }
339
340 // static
341 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices(
342 VideoCaptureDevice::Names* device_names) {
343 scoped_refptr<IDeckLinkIterator> decklink_iter(
344 CreateDeckLinkIteratorInstance());
345 // At this point, not being able to create a DeckLink iterator means that
346 // there are no Blackmagic DeckLink devices in the system, don't print error.
347 DVLOG_IF(1, !decklink_iter.get()) << "Could not create DeckLink iterator";
348 if (!decklink_iter.get())
349 return;
350
351 ScopedDeckLinkPtr<IDeckLink> decklink;
352 while (decklink_iter->Next(decklink.Receive()) == S_OK) {
353 ScopedDeckLinkPtr<IDeckLink> decklink_local;
354 decklink_local.swap(decklink);
355
356 CFStringRef device_model_name = NULL;
357 HRESULT hr = decklink_local->GetModelName(&device_model_name);
358 DVLOG_IF(1, hr != S_OK) << "Error reading Blackmagic device model name";
359 CFStringRef device_display_name = NULL;
360 hr = decklink_local->GetDisplayName(&device_display_name);
361 DVLOG_IF(1, hr != S_OK) << "Error reading Blackmagic device display name";
362 DVLOG_IF(1, hr == S_OK) << "Blackmagic device found with name: "
363 << base::SysCFStringRefToUTF8(device_display_name);
364
365 if (!device_model_name && !device_display_name)
366 continue;
367
368 ScopedDeckLinkPtr<IDeckLinkInput> decklink_input;
369 if (decklink_local->QueryInterface(IID_IDeckLinkInput,
370 decklink_input.ReceiveVoid()) != S_OK) {
371 DLOG(ERROR) << "Error Blackmagic querying input interface.";
372 return;
373 }
374
375 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
376 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) !=
377 S_OK) {
378 continue;
379 }
380
381 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
382 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
383 CFStringRef format_name = NULL;
384 if (display_mode->GetName(&format_name) == S_OK) {
385 VideoCaptureDevice::Name name(
386 JoinDeviceNameAndFormat(device_display_name, format_name),
387 JoinDeviceNameAndFormat(device_model_name, format_name),
388 VideoCaptureDevice::Name::DECKLINK,
389 VideoCaptureDevice::Name::OTHER_TRANSPORT);
390 device_names->push_back(name);
391 DVLOG(1) << "Blackmagic camera enumerated: " << name.name();
392 }
393 display_mode.Release();
394 }
395 }
396 }
397
398 // static
399 void VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities(
400 const VideoCaptureDevice::Name& device,
401 VideoCaptureFormats* supported_formats) {
402 scoped_refptr<IDeckLinkIterator> decklink_iter(
403 CreateDeckLinkIteratorInstance());
404 DLOG_IF(ERROR, !decklink_iter.get()) << "Error creating DeckLink iterator";
405 if (!decklink_iter.get())
406 return;
407
408 ScopedDeckLinkPtr<IDeckLink> decklink;
409 while (decklink_iter->Next(decklink.Receive()) == S_OK) {
410 ScopedDeckLinkPtr<IDeckLink> decklink_local;
411 decklink_local.swap(decklink);
412
413 ScopedDeckLinkPtr<IDeckLinkInput> decklink_input;
414 if (decklink_local->QueryInterface(IID_IDeckLinkInput,
415 decklink_input.ReceiveVoid()) != S_OK) {
416 DLOG(ERROR) << "Error Blackmagic querying input interface.";
417 return;
418 }
419
420 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
421 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) !=
422 S_OK) {
423 continue;
424 }
425
426 CFStringRef device_model_name = NULL;
427 if (decklink_local->GetModelName(&device_model_name) != S_OK)
428 continue;
429
430 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
431 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
432 CFStringRef format_name = NULL;
433 if (display_mode->GetName(&format_name) == S_OK &&
434 device.id() !=
435 JoinDeviceNameAndFormat(device_model_name, format_name)) {
436 display_mode.Release();
437 continue;
438 }
439
440 // IDeckLinkDisplayMode does not have information on pixel format, this
441 // is only available on capture.
442 const media::VideoCaptureFormat format(
443 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()),
444 GetDisplayModeFrameRate(display_mode),
445 PIXEL_FORMAT_UNKNOWN);
446 supported_formats->push_back(format);
447 DVLOG(2) << device.name() << " " << VideoCaptureFormat::ToString(format);
448 display_mode.Release();
449 }
450 return;
451 }
452 }
453
454 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac(
455 const Name& device_name)
456 : decklink_capture_delegate_(
457 new DeckLinkCaptureDelegate(device_name, this)) {
458 }
459
460 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {
461 decklink_capture_delegate_->ResetVideoCaptureDeviceReference();
462 }
463
464 void VideoCaptureDeviceDeckLinkMac::OnIncomingCapturedData(
465 const uint8_t* data,
466 size_t length,
467 const VideoCaptureFormat& frame_format,
468 int rotation, // Clockwise.
469 base::TimeTicks reference_time,
470 base::TimeDelta timestamp) {
471 base::AutoLock lock(lock_);
472 if (client_) {
473 client_->OnIncomingCapturedData(data, length, frame_format, rotation,
474 reference_time, timestamp);
475 }
476 }
477
478 void VideoCaptureDeviceDeckLinkMac::SendErrorString(
479 const tracked_objects::Location& from_here,
480 const std::string& reason) {
481 DCHECK(thread_checker_.CalledOnValidThread());
482 base::AutoLock lock(lock_);
483 if (client_)
484 client_->OnError(from_here, reason);
485 }
486
487 void VideoCaptureDeviceDeckLinkMac::SendLogString(const std::string& message) {
488 DCHECK(thread_checker_.CalledOnValidThread());
489 base::AutoLock lock(lock_);
490 if (client_)
491 client_->OnLog(message);
492 }
493
494 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart(
495 const VideoCaptureParams& params,
496 std::unique_ptr<VideoCaptureDevice::Client> client) {
497 DCHECK(thread_checker_.CalledOnValidThread());
498 client_ = std::move(client);
499 if (decklink_capture_delegate_.get())
500 decklink_capture_delegate_->AllocateAndStart(params);
501 }
502
503 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() {
504 if (decklink_capture_delegate_.get())
505 decklink_capture_delegate_->StopAndDeAllocate();
506 }
507
508 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698