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

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

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

Powered by Google App Engine
This is Rietveld 408576698