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

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

Issue 535983002: Mac Video Capture: Support for Blackmagic DeckLink SDK video capture. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@crbug408493__1__Enumerate_blackmagic_devices__2__branched_from_master
Patch Set: Nit. Created 6 years, 2 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
1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 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/mac/video_capture_device_decklink_mac.h" 5 #include "media/video/capture/mac/video_capture_device_decklink_mac.h"
6 6
7 #include "base/logging.h" 7 #include "base/logging.h"
8 #include "base/memory/ref_counted.h" 8 #include "base/memory/ref_counted.h"
9 #include "base/synchronization/lock.h"
9 #include "base/strings/sys_string_conversions.h" 10 #include "base/strings/sys_string_conversions.h"
10 #include "third_party/decklink/mac/include/DeckLinkAPI.h" 11 #include "third_party/decklink/mac/include/DeckLinkAPI.h"
11 12
12 namespace { 13 namespace {
13 14
14 // DeckLink SDK uses ScopedComPtr-style APIs. Chrome ScopedComPtr is only 15 // DeckLink SDK uses ScopedComPtr-style APIs. Chrome ScopedComPtr is only
15 // available for Windows builds. This is a verbatim knock-off of the needed 16 // available for Windows builds. This is a verbatim knock-off of the needed
16 // parts of base::win::ScopedComPtr<> for ref counting. 17 // parts of base::win::ScopedComPtr<> for ref counting.
17 template <class T> 18 template <class T>
18 class ScopedDeckLinkPtr : public scoped_refptr<T> { 19 class ScopedDeckLinkPtr : public scoped_refptr<T> {
19 public: 20 private:
20 using scoped_refptr<T>::ptr_; 21 using scoped_refptr<T>::ptr_;
21 22
23 public:
22 T** Receive() { 24 T** Receive() {
23 DCHECK(!ptr_) << "Object leak. Pointer must be NULL"; 25 DCHECK(!ptr_) << "Object leak. Pointer must be NULL";
24 return &ptr_; 26 return &ptr_;
25 } 27 }
26 28
27 void** ReceiveVoid() { 29 void** ReceiveVoid() {
28 return reinterpret_cast<void**>(Receive()); 30 return reinterpret_cast<void**>(Receive());
29 } 31 }
30 32
31 void Release() { 33 void Release() {
32 if (ptr_ != NULL) { 34 if (ptr_ != NULL) {
33 ptr_->Release(); 35 ptr_->Release();
34 ptr_ = NULL; 36 ptr_ = NULL;
35 } 37 }
36 } 38 }
37 }; 39 };
38 40
41 // This class is used to interact directly with DeckLink SDK for video capture.
42 // Implements the reference counted interface IUnknown. Has a weak reference to
43 // VideoCaptureDeviceDeckLinkMac for sending captured frames, error messages and
44 // logs.
45 class DeckLinkCaptureDelegate : public IDeckLinkInputCallback {
46 public:
47 DeckLinkCaptureDelegate(const media::VideoCaptureDevice::Name& device_name,
48 media::VideoCaptureDeviceDeckLinkMac* frame_receiver);
49 virtual ~DeckLinkCaptureDelegate();
50
51 void AllocateAndStart(const media::VideoCaptureParams& params);
52 void StopAndDeAllocate();
53
54 // Remove the VideoCaptureDeviceDeckLinkMac's weak reference.
55 void ResetVideoCaptureDeviceReference();
56
57 private:
58 // IDeckLinkInputCallback interface implementation.
59 virtual HRESULT VideoInputFormatChanged (
Robert Sesek 2014/09/24 17:06:14 nit: extra space before (
mcasas 2014/09/24 19:39:56 Done.
60 BMDVideoInputFormatChangedEvents notification_events,
61 IDeckLinkDisplayMode *new_display_mode,
62 BMDDetectedVideoInputFormatFlags detected_signal_flags) OVERRIDE;
63 virtual HRESULT VideoInputFrameArrived (
Robert Sesek 2014/09/24 17:06:14 same
mcasas 2014/09/24 19:39:56 Done.
64 IDeckLinkVideoInputFrame* video_frame,
65 IDeckLinkAudioInputPacket* audio_packet) OVERRIDE;
66
67 // IUnknown interface implementation.
68 virtual HRESULT QueryInterface (REFIID iid, void** ppv) OVERRIDE;
Robert Sesek 2014/09/24 17:06:14 same
mcasas 2014/09/24 19:39:57 Done.
69 virtual ULONG AddRef() OVERRIDE;
70 virtual ULONG Release() OVERRIDE;
71
72 // Forwarder to VideoCaptureDeviceDeckLinkMac::SendErrorString().
73 void SendErrorString(const std::string& reason);
74
75 // Forwarder to VideoCaptureDeviceDeckLinkMac::SendLogString().
76 void SendLogString(const std::string& message);
77
78 const media::VideoCaptureDevice::Name device_name_;
79
80 // Protects concurrent setting and using of |frame_receiver_|.
81 base::Lock lock_;
82 // Weak reference to the captured frames client, used also for error messages
83 // and logging. Initialized on construction and used until cleared by calling
84 // ResetVideoCaptureDeviceReference().
85 media::VideoCaptureDeviceDeckLinkMac* frame_receiver_;
86
87 // This is used to control the video capturing device input interface.
88 ScopedDeckLinkPtr<IDeckLinkInput> decklink_input_;
89 // |decklink_| represents a physical device attached to the host.
90 ScopedDeckLinkPtr<IDeckLink> decklink_;
91 // Internal counter for IUnknown interface implementation.
Robert Sesek 2014/09/24 17:06:14 Is it really necessary to roll your own reference
mcasas 2014/09/24 19:39:56 The DeckLink SDK forces whoever implements IDeckLi
Robert Sesek 2014/09/29 21:08:51 Can you really not find any other way to not use p
mcasas 2014/09/30 14:18:49 I made DeckLinkCaptureDelegate derive from base::R
92 int32_t ref_count_;
93
94 base::ThreadChecker thread_checker_;
95
96 friend class scoped_refptr<DeckLinkCaptureDelegate>;
97
98 DISALLOW_COPY_AND_ASSIGN(DeckLinkCaptureDelegate);
99 };
100
101 static float GetDisplayModeFrameRate(
102 const ScopedDeckLinkPtr<IDeckLinkDisplayMode>& display_mode) {
103 BMDTimeValue time_value, time_scale;
104 float display_mode_frame_rate = 0.0f;
105 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK &&
106 time_value > 0) {
107 display_mode_frame_rate = static_cast<float>(time_scale) / time_value;
108 }
109 // Interlaced formats are going to be marked as double the frame rate,
110 // which follows the general naming convention.
111 if (display_mode->GetFieldDominance() == bmdLowerFieldFirst ||
112 display_mode->GetFieldDominance() == bmdUpperFieldFirst) {
113 display_mode_frame_rate *= 2.0f;
114 }
115 return display_mode_frame_rate;
116 }
117
118 DeckLinkCaptureDelegate::DeckLinkCaptureDelegate(
119 const media::VideoCaptureDevice::Name& device_name,
120 media::VideoCaptureDeviceDeckLinkMac* frame_receiver)
121 : device_name_(device_name),
122 frame_receiver_(frame_receiver) {
123 }
124
125 DeckLinkCaptureDelegate::~DeckLinkCaptureDelegate() {}
126
127 void DeckLinkCaptureDelegate::AllocateAndStart(
128 const media::VideoCaptureParams& params) {
129 DCHECK(thread_checker_.CalledOnValidThread());
130 scoped_refptr<IDeckLinkIterator> decklink_iter(
131 CreateDeckLinkIteratorInstance());
132 DLOG_IF(ERROR, !decklink_iter.get()) << "Error creating DeckLink iterator";
133 if (!decklink_iter.get())
134 return;
135
136 while (decklink_iter->Next(decklink_.Receive()) == S_OK) {
137 CFStringRef device_model_name = NULL;
138 if ((decklink_->GetModelName(&device_model_name) == S_OK) ||
139 (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) {
140 break;
141 }
142 decklink_.Release();
143 }
144 if (!decklink_.get()) {
145 SendErrorString("Device id not found in the system");
146 return;
147 }
148
149 if (decklink_->QueryInterface(IID_IDeckLinkInput,
150 decklink_input_.ReceiveVoid()) != S_OK) {
151 SendErrorString("Error querying input interface.");
152 return;
153 }
154
155 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
156 if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) !=
157 S_OK) {
158 SendErrorString("Error creating Display Mode Iterator");
159 return;
160 }
161
162 ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode;
163 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
164 float min_diff = FLT_MAX;
165 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
166 const float diff = labs(display_mode->GetWidth() -
167 params.requested_format.frame_size.width()) +
168 labs(params.requested_format.frame_size.height() -
169 display_mode->GetHeight()) + fabs(params.requested_format.frame_rate -
170 GetDisplayModeFrameRate(display_mode));
171 if (diff < min_diff) {
172 chosen_display_mode = display_mode;
173 min_diff = diff;
174 }
175 display_mode.Release();
176 }
177 if (!chosen_display_mode.get()) {
178 SendErrorString("Could not find a display mode");
179 return;
180 }
181 #if !defined(NDEBUG)
182 DVLOG(1) << "Requested format: " << params.requested_format.ToString();
183 CFStringRef format_name = NULL;
184 if (chosen_display_mode->GetName(&format_name) == S_OK)
185 DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name);
186 #endif
187
188 // Enable video input. Configure for no input video format change detection,
189 // this in turn will disable calls to VideoInputFormatChanged().
190 if (decklink_input_->EnableVideoInput(chosen_display_mode->GetDisplayMode(),
191 bmdFormat8BitYUV, bmdVideoInputFlagDefault) != S_OK) {
192 SendErrorString("Could not select the video format we like.");
193 return;
194 }
195
196 decklink_input_->SetCallback(this);
197 if (decklink_input_->StartStreams() != S_OK)
198 SendErrorString("Could not start capturing");
199 }
200
201 void DeckLinkCaptureDelegate::StopAndDeAllocate() {
202 DCHECK(thread_checker_.CalledOnValidThread());
203 if (!decklink_input_.get())
204 return;
205 if (decklink_input_->StopStreams() != S_OK)
206 SendLogString("Problem stopping capture.");
207 decklink_input_->SetCallback(NULL);
208 decklink_input_->DisableVideoInput();
209 ResetVideoCaptureDeviceReference();
210 }
211
212 HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged (
Robert Sesek 2014/09/24 17:06:14 nit: space before (
mcasas 2014/09/24 19:39:56 Done.
213 BMDVideoInputFormatChangedEvents notification_events,
214 IDeckLinkDisplayMode *new_display_mode,
215 BMDDetectedVideoInputFormatFlags detected_signal_flags) {
216 DCHECK(thread_checker_.CalledOnValidThread());
217 return S_OK;
218 }
219
220 HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived (
Robert Sesek 2014/09/24 17:06:14 same
mcasas 2014/09/24 19:39:56 Done.
221 IDeckLinkVideoInputFrame* video_frame,
222 IDeckLinkAudioInputPacket* /* audio_packet */) {
223 // Capture frames are manipulated as an IDeckLinkVideoFrame.
224 uint8* video_data = NULL;
225 video_frame->GetBytes(reinterpret_cast<void**>(&video_data));
226
227 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN;
228 switch (video_frame->GetPixelFormat()) {
229 case bmdFormat8BitYUV: // A.k.a. '2vuy';
230 pixel_format = media::PIXEL_FORMAT_UYVY;
231 break;
232 case bmdFormat8BitARGB:
233 pixel_format = media::PIXEL_FORMAT_ARGB;
234 break;
235 default:
236 SendErrorString("Unsupported pixel format");
237 break;
238 }
239
240 const media::VideoCaptureFormat capture_format(
241 gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()),
242 0.0f, // Frame rate is not needed for captured data callback.
243 pixel_format);
244 base::AutoLock lock(lock_);
245 if (frame_receiver_) {
246 frame_receiver_->OnIncomingCapturedData(
247 video_data,
248 video_frame->GetRowBytes() * video_frame->GetHeight(),
249 capture_format,
250 0, // Rotation.
251 base::TimeTicks::Now());
252 }
253 return S_OK;
254 }
255
256 HRESULT DeckLinkCaptureDelegate::QueryInterface(REFIID iid, void** ppv) {
257 DCHECK(thread_checker_.CalledOnValidThread());
258 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
259 if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0 ||
260 memcmp(&iid, &IID_IDeckLinkInputCallback, sizeof(REFIID)) == 0) {
261 *ppv = static_cast<IDeckLinkInputCallback*>(this);
262 AddRef();
263 return S_OK;
264 }
265 return E_NOINTERFACE;
266 }
267
268 ULONG DeckLinkCaptureDelegate::AddRef(void) {
Robert Sesek 2014/09/24 17:06:14 no (void)
mcasas 2014/09/24 19:39:57 Done.
269 DCHECK(thread_checker_.CalledOnValidThread());
270 return OSAtomicIncrement32(&ref_count_);
271 }
272
273 ULONG DeckLinkCaptureDelegate::Release(void) {
Robert Sesek 2014/09/24 17:06:14 no (void)
mcasas 2014/09/24 19:39:56 Done.
274 DCHECK(thread_checker_.CalledOnValidThread());
275 int32_t newRefValue = OSAtomicDecrement32(&ref_count_);
Robert Sesek 2014/09/24 17:06:14 naming: under_score
mcasas 2014/09/24 19:39:56 Done.
276 if (newRefValue == 0)
277 delete this;
278 return newRefValue;
279 }
280
281 void DeckLinkCaptureDelegate::SendErrorString(const std::string& reason) {
282 base::AutoLock lock(lock_);
283 if (frame_receiver_)
284 frame_receiver_->SendErrorString(reason);
285 }
286
287 void DeckLinkCaptureDelegate::SendLogString(const std::string& message) {
288 base::AutoLock lock(lock_);
289 if (frame_receiver_)
290 frame_receiver_->SendLogString(message);
291 }
292
293 void DeckLinkCaptureDelegate::ResetVideoCaptureDeviceReference() {
294 DCHECK(thread_checker_.CalledOnValidThread());
295 base::AutoLock lock(lock_);
296 frame_receiver_ = NULL;
297 }
298
39 } // namespace 299 } // namespace
40 300
41 namespace media { 301 namespace media {
42 302
43 std::string JoinDeviceNameAndFormat(CFStringRef name, CFStringRef format) { 303 static std::string JoinDeviceNameAndFormat(CFStringRef name,
304 CFStringRef format) {
44 return base::SysCFStringRefToUTF8(name) + " - " + 305 return base::SysCFStringRefToUTF8(name) + " - " +
45 base::SysCFStringRefToUTF8(format); 306 base::SysCFStringRefToUTF8(format);
46 } 307 }
47 308
48 //static 309 //static
49 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( 310 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices(
50 VideoCaptureDevice::Names* device_names) { 311 VideoCaptureDevice::Names* device_names) {
51 scoped_refptr<IDeckLinkIterator> decklink_iter( 312 scoped_refptr<IDeckLinkIterator> decklink_iter(
52 CreateDeckLinkIteratorInstance()); 313 CreateDeckLinkIteratorInstance());
53 // At this point, not being able to create a DeckLink iterator means that 314 // At this point, not being able to create a DeckLink iterator means that
54 // there are no Blackmagic devices in the system but this isn't an error. 315 // there are no Blackmagic DeckLink devices in the system, don't print error.
55 DVLOG_IF(1, !decklink_iter.get()) << "Could not create DeckLink iterator"; 316 DVLOG_IF(1, !decklink_iter.get()) << "Could not create DeckLink iterator";
56 if (!decklink_iter.get()) 317 if (!decklink_iter.get())
57 return; 318 return;
58 319
59 ScopedDeckLinkPtr<IDeckLink> decklink; 320 ScopedDeckLinkPtr<IDeckLink> decklink;
60 while (decklink_iter->Next(decklink.Receive()) == S_OK) { 321 while (decklink_iter->Next(decklink.Receive()) == S_OK) {
61 ScopedDeckLinkPtr<IDeckLink> decklink_local; 322 ScopedDeckLinkPtr<IDeckLink> decklink_local;
62 decklink_local.swap(decklink); 323 decklink_local.swap(decklink);
63 324
64 CFStringRef device_model_name = NULL; 325 CFStringRef device_model_name = NULL;
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
139 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { 400 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
140 CFStringRef format_name = NULL; 401 CFStringRef format_name = NULL;
141 if (display_mode->GetName(&format_name) == S_OK && device.id() != 402 if (display_mode->GetName(&format_name) == S_OK && device.id() !=
142 JoinDeviceNameAndFormat(device_model_name, format_name)) { 403 JoinDeviceNameAndFormat(device_model_name, format_name)) {
143 display_mode.Release(); 404 display_mode.Release();
144 continue; 405 continue;
145 } 406 }
146 407
147 // IDeckLinkDisplayMode does not have information on pixel format, this 408 // IDeckLinkDisplayMode does not have information on pixel format, this
148 // is only available on capture. 409 // is only available on capture.
149 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; 410 const media::VideoCaptureFormat format(
150 BMDTimeValue time_value, time_scale;
151 float frame_rate = 0.0f;
152 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK &&
153 time_value > 0) {
154 frame_rate = static_cast<float>(time_scale) / time_value;
155 }
156 media::VideoCaptureFormat format(
157 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()), 411 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()),
158 frame_rate, 412 GetDisplayModeFrameRate(display_mode),
159 pixel_format); 413 PIXEL_FORMAT_UNKNOWN);
160 supported_formats->push_back(format); 414 supported_formats->push_back(format);
161 DVLOG(2) << device.name() << " " << format.ToString(); 415 DVLOG(2) << device.name() << " " << format.ToString();
162 display_mode.Release(); 416 display_mode.Release();
163 } 417 }
164 return; 418 return;
165 } 419 }
166 } 420 }
167 421
168 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( 422 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac(
169 const Name& device_name) {} 423 const Name& device_name)
424 : decklink_capture_delegate_(
425 new DeckLinkCaptureDelegate(device_name, this)) {
426 }
170 427
171 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} 428 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {
429 decklink_capture_delegate_->ResetVideoCaptureDeviceReference();
430 }
431
432 void VideoCaptureDeviceDeckLinkMac::OnIncomingCapturedData(
433 const uint8* data,
434 int length,
435 const VideoCaptureFormat& frame_format,
436 int rotation, // Clockwise.
437 base::TimeTicks timestamp) {
438 base::AutoLock lock(lock_);
439 if (client_) {
440 client_->OnIncomingCapturedData(data, length, frame_format, rotation,
441 timestamp);
442 }
443 }
444
445 void VideoCaptureDeviceDeckLinkMac::SendErrorString(const std::string& reason) {
446 DCHECK(thread_checker_.CalledOnValidThread());
447 base::AutoLock lock(lock_);
448 if (client_)
449 client_->OnError(reason);
450 }
451
452 void VideoCaptureDeviceDeckLinkMac::SendLogString(const std::string& message) {
453 DCHECK(thread_checker_.CalledOnValidThread());
454 base::AutoLock lock(lock_);
455 if (client_)
456 client_->OnLog(message);
457 }
172 458
173 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( 459 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart(
174 const VideoCaptureParams& params, 460 const VideoCaptureParams& params,
175 scoped_ptr<VideoCaptureDevice::Client> client) { 461 scoped_ptr<VideoCaptureDevice::Client> client) {
176 NOTIMPLEMENTED(); 462 DCHECK(thread_checker_.CalledOnValidThread());
463 client_ = client.Pass();
464 if (decklink_capture_delegate_.get())
465 decklink_capture_delegate_->AllocateAndStart(params);
177 } 466 }
178 467
179 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { 468 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() {
180 NOTIMPLEMENTED(); 469 if (decklink_capture_delegate_.get())
470 decklink_capture_delegate_->StopAndDeAllocate();
181 } 471 }
182 472
183 } // namespace media 473 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698