Chromium Code Reviews

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: tommi@s comments Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff |
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 : private IDeckLinkInputCallback {
tommi (sloooow) - chröme 2014/09/23 12:59:54 nit: it's very unusual to have a private inheritan
mcasas 2014/09/23 15:03:47 Changed to public inheritance.
46 public:
47 DeckLinkCaptureDelegate(const media::VideoCaptureDevice::Name& device_name,
48 media::VideoCaptureDeviceDeckLinkMac* frame_receiver);
49 virtual ~DeckLinkCaptureDelegate();
50
51 // IUnknown interface implementation.
52 virtual HRESULT QueryInterface (REFIID iid, LPVOID *ppv) OVERRIDE;
tommi (sloooow) - chröme 2014/09/23 12:59:54 no space after QI LPVOID *ppv -> void** ppv
mcasas 2014/09/23 15:03:46 Done.
53 virtual ULONG AddRef() OVERRIDE;
54 virtual ULONG Release() OVERRIDE;
55
56 void AllocateAndStart(const media::VideoCaptureParams& params);
57 void StopAndDeAllocate();
58
59 // Remove the VideoCaptureDeviceDeckLinkMac's weak reference.
60 void ResetVideoCaptureDeviceReference();
61
62 private:
63 // IDeckLinkInputCallback interface implementation.
64 virtual HRESULT VideoInputFormatChanged (
65 BMDVideoInputFormatChangedEvents notification_events,
66 IDeckLinkDisplayMode *new_display_mode,
67 BMDDetectedVideoInputFormatFlags detected_signal_flags) OVERRIDE;
68 virtual HRESULT VideoInputFrameArrived (
69 IDeckLinkVideoInputFrame* video_frame,
70 IDeckLinkAudioInputPacket* audio_packet) 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.
92 int32_t ref_count_;
93
94 base::ThreadChecker thread_checker_;
95
96 DISALLOW_COPY_AND_ASSIGN(DeckLinkCaptureDelegate);
97 };
98
99 static float GetDisplayModeFrameRate(
100 const ScopedDeckLinkPtr<IDeckLinkDisplayMode>& display_mode) {
101 BMDTimeValue time_value, time_scale;
102 float display_mode_frame_rate = 0.0f;
103 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK &&
104 time_value > 0) {
105 display_mode_frame_rate = static_cast<float>(time_scale) / time_value;
106 }
107 // Interlaced formats are going to be marked as double the frame rate,
108 // which follows the general naming convention.
109 if (display_mode->GetFieldDominance() == bmdLowerFieldFirst ||
110 display_mode->GetFieldDominance() == bmdUpperFieldFirst) {
111 display_mode_frame_rate *= 2.0f;
112 }
113 return display_mode_frame_rate;
114 }
115
116 DeckLinkCaptureDelegate::DeckLinkCaptureDelegate(
117 const media::VideoCaptureDevice::Name& device_name,
118 media::VideoCaptureDeviceDeckLinkMac* frame_receiver)
119 : device_name_(device_name),
120 frame_receiver_(frame_receiver) {
121 }
122
123 DeckLinkCaptureDelegate::~DeckLinkCaptureDelegate() {}
124
125 void DeckLinkCaptureDelegate::AllocateAndStart(
126 const media::VideoCaptureParams& params) {
127 DCHECK(thread_checker_.CalledOnValidThread());
128 scoped_refptr<IDeckLinkIterator> decklink_iter(
129 CreateDeckLinkIteratorInstance());
130 DLOG_IF(ERROR, !decklink_iter.get()) << "Error creating DeckLink iterator";
131 if (!decklink_iter.get())
132 return;
133
134 while (decklink_iter->Next(decklink_.Receive()) == S_OK) {
135 CFStringRef device_model_name = NULL;
136 if ((decklink_->GetModelName(&device_model_name) == S_OK) ||
137 (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) {
138 break;
139 }
140 decklink_.Release();
141 }
142 if (!decklink_.get()) {
143 SendErrorString("Device id not found in the system");
144 return;
145 }
146
147 if (decklink_->QueryInterface(IID_IDeckLinkInput,
148 decklink_input_.ReceiveVoid()) != S_OK) {
149 SendErrorString("Error querying input interface.");
150 return;
151 }
152
153 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
154 if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) !=
155 S_OK) {
156 SendErrorString("Error creating Display Mode Iterator");
157 return;
158 }
159
160 ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode;
161 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
162 float min_diff = FLT_MAX;
163 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
164 const float diff = abs(display_mode->GetWidth() -
165 params.requested_format.frame_size.width()) +
166 abs(params.requested_format.frame_size.height() -
167 display_mode->GetHeight()) + fabs(params.requested_format.frame_rate -
168 GetDisplayModeFrameRate(display_mode));
169 if (diff < min_diff) {
170 chosen_display_mode = display_mode;
171 min_diff = diff;
172 }
173 display_mode.Release();
174 }
175 if (!chosen_display_mode.get()) {
176 SendErrorString("Could not find a display mode");
177 return;
178 }
179 #if !defined(NDEBUG)
180 DVLOG(1) << "Requested format: " << params.requested_format.ToString();
181 CFStringRef format_name = NULL;
182 if (chosen_display_mode->GetName(&format_name) == S_OK)
183 DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name);
184 #endif
185
186 // Enable video input. Configure for no input video format change detection,
187 // this in turn will disable calls to VideoInputFormatChanged().
188 if (decklink_input_->EnableVideoInput(chosen_display_mode->GetDisplayMode(),
189 bmdFormat8BitYUV, bmdVideoInputFlagDefault) != S_OK) {
190 SendErrorString("Could not select the video format we like.");
191 return;
192 }
193
194 decklink_input_->SetCallback(this);
195 if (decklink_input_->StartStreams() != S_OK)
196 SendErrorString("Could not start capturing");
197 }
198
199 void DeckLinkCaptureDelegate::StopAndDeAllocate() {
200 DCHECK(thread_checker_.CalledOnValidThread());
201 if (!decklink_input_.get())
202 return;
203 if (decklink_input_->StopStreams() != S_OK)
204 SendLogString("Problem stopping capture.");
205 decklink_input_->SetCallback(NULL);
206 decklink_input_->DisableVideoInput();
207 ResetVideoCaptureDeviceReference();
208 }
209
210 HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged (
211 BMDVideoInputFormatChangedEvents notification_events,
212 IDeckLinkDisplayMode *new_display_mode,
213 BMDDetectedVideoInputFormatFlags detected_signal_flags) {
214 DCHECK(thread_checker_.CalledOnValidThread());
215 return S_OK;
216 }
217
218 HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived (
219 IDeckLinkVideoInputFrame* video_frame,
220 IDeckLinkAudioInputPacket* /* audio_packet */) {
221 // Capture frames are manipulated as an IDeckLinkVideoFrame.
222 uint8* video_data = NULL;
223 video_frame->GetBytes(reinterpret_cast<void**>(&video_data));
224
225 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN;
226 switch (video_frame->GetPixelFormat()) {
227 case bmdFormat8BitYUV: // A.k.a. '2vuy';
228 pixel_format = media::PIXEL_FORMAT_UYVY;
229 break;
230 case bmdFormat8BitARGB:
231 pixel_format = media::PIXEL_FORMAT_ARGB;
232 break;
233 default:
234 SendErrorString("Unsupported pixel format");
235 break;
236 }
237
238 const media::VideoCaptureFormat capture_format(
239 gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()),
240 0.0f, // Frame rate is not needed for captured data callback.
241 pixel_format);
242 base::AutoLock lock(lock_);
243 if (frame_receiver_) {
244 frame_receiver_->OnIncomingCapturedData(
245 video_data,
246 video_frame->GetRowBytes() * video_frame->GetHeight(),
247 capture_format,
248 0, // Rotation.
249 base::TimeTicks::Now());
250 }
251 return S_OK;
252 }
253
254 HRESULT DeckLinkCaptureDelegate::QueryInterface(REFIID iid, LPVOID *ppv) {
255 DCHECK(thread_checker_.CalledOnValidThread());
256 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
257 if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0 ||
258 memcmp(&iid, &IID_IDeckLinkNotificationCallback, sizeof(REFIID)) == 0) {
259 *ppv = reinterpret_cast<IDeckLinkNotificationCallback*>(this);
260 AddRef();
261 return S_OK;
262 }
263 return E_NOINTERFACE;
264 }
265
266 ULONG DeckLinkCaptureDelegate::AddRef(void) {
267 DCHECK(thread_checker_.CalledOnValidThread());
268 return OSAtomicIncrement32(&ref_count_);
269 }
270
271 ULONG DeckLinkCaptureDelegate::Release(void) {
272 DCHECK(thread_checker_.CalledOnValidThread());
273 int32_t newRefValue = OSAtomicDecrement32(&ref_count_);
274 if (newRefValue == 0)
275 delete this;
276 return newRefValue;
277 }
278
279 void DeckLinkCaptureDelegate::SendErrorString(const std::string& reason) {
280 base::AutoLock lock(lock_);
281 if (frame_receiver_)
282 frame_receiver_->SendErrorString(reason);
283 }
284
285 void DeckLinkCaptureDelegate::SendLogString(const std::string& message) {
286 base::AutoLock lock(lock_);
287 if (frame_receiver_)
288 frame_receiver_->SendLogString(message);
289 }
290
291 void DeckLinkCaptureDelegate::ResetVideoCaptureDeviceReference() {
292 DCHECK(thread_checker_.CalledOnValidThread());
293 base::AutoLock lock(lock_);
294 frame_receiver_ = NULL;
295 }
296
39 } // namespace 297 } // namespace
40 298
41 namespace media { 299 namespace media {
42 300
43 std::string JoinDeviceNameAndFormat(CFStringRef name, CFStringRef format) { 301 static std::string JoinDeviceNameAndFormat(CFStringRef name,
302 CFStringRef format) {
44 return base::SysCFStringRefToUTF8(name) + " - " + 303 return base::SysCFStringRefToUTF8(name) + " - " +
45 base::SysCFStringRefToUTF8(format); 304 base::SysCFStringRefToUTF8(format);
46 } 305 }
47 306
48 //static 307 //static
49 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( 308 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices(
50 VideoCaptureDevice::Names* device_names) { 309 VideoCaptureDevice::Names* device_names) {
51 scoped_refptr<IDeckLinkIterator> decklink_iter( 310 scoped_refptr<IDeckLinkIterator> decklink_iter(
52 CreateDeckLinkIteratorInstance()); 311 CreateDeckLinkIteratorInstance());
53 // At this point, not being able to create a DeckLink iterator means that 312 // 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. 313 // 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"; 314 DVLOG_IF(1, !decklink_iter.get()) << "Could not create DeckLink iterator";
56 if (!decklink_iter.get()) 315 if (!decklink_iter.get())
57 return; 316 return;
58 317
59 ScopedDeckLinkPtr<IDeckLink> decklink; 318 ScopedDeckLinkPtr<IDeckLink> decklink;
60 while (decklink_iter->Next(decklink.Receive()) == S_OK) { 319 while (decklink_iter->Next(decklink.Receive()) == S_OK) {
61 ScopedDeckLinkPtr<IDeckLink> decklink_local; 320 ScopedDeckLinkPtr<IDeckLink> decklink_local;
62 decklink_local.swap(decklink); 321 decklink_local.swap(decklink);
63 322
64 CFStringRef device_model_name = NULL; 323 CFStringRef device_model_name = NULL;
(...skipping 74 matching lines...)
139 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { 398 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
140 CFStringRef format_name = NULL; 399 CFStringRef format_name = NULL;
141 if (display_mode->GetName(&format_name) == S_OK && device.id() != 400 if (display_mode->GetName(&format_name) == S_OK && device.id() !=
142 JoinDeviceNameAndFormat(device_model_name, format_name)) { 401 JoinDeviceNameAndFormat(device_model_name, format_name)) {
143 display_mode.Release(); 402 display_mode.Release();
144 continue; 403 continue;
145 } 404 }
146 405
147 // IDeckLinkDisplayMode does not have information on pixel format, this 406 // IDeckLinkDisplayMode does not have information on pixel format, this
148 // is only available on capture. 407 // is only available on capture.
149 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; 408 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()), 409 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()),
158 frame_rate, 410 GetDisplayModeFrameRate(display_mode),
159 pixel_format); 411 PIXEL_FORMAT_UNKNOWN);
160 supported_formats->push_back(format); 412 supported_formats->push_back(format);
161 DVLOG(2) << device.name() << " " << format.ToString(); 413 DVLOG(2) << device.name() << " " << format.ToString();
162 display_mode.Release(); 414 display_mode.Release();
163 } 415 }
164 return; 416 return;
165 } 417 }
166 } 418 }
167 419
168 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( 420 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac(
169 const Name& device_name) {} 421 const Name& device_name)
422 : decklink_capture_delegate_(
423 new DeckLinkCaptureDelegate(device_name, this)) {
424 }
170 425
171 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} 426 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {
427 decklink_capture_delegate_->ResetVideoCaptureDeviceReference();
428 }
429
430 void VideoCaptureDeviceDeckLinkMac::OnIncomingCapturedData(
431 const uint8* data,
432 int length,
433 const VideoCaptureFormat& frame_format,
434 int rotation, // Clockwise.
435 base::TimeTicks timestamp) {
436 base::AutoLock lock(lock_);
437 if (client_) {
438 client_->OnIncomingCapturedData(data, length, frame_format, rotation,
439 timestamp);
440 }
441 }
442
443 void VideoCaptureDeviceDeckLinkMac::SendErrorString(const std::string& reason) {
444 DCHECK(thread_checker_.CalledOnValidThread());
445 base::AutoLock lock(lock_);
446 if (client_)
447 client_->OnError(reason);
448 }
449
450 void VideoCaptureDeviceDeckLinkMac::SendLogString(const std::string& message) {
451 DCHECK(thread_checker_.CalledOnValidThread());
452 base::AutoLock lock(lock_);
453 if (client_)
454 client_->OnLog(message);
455 }
172 456
173 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( 457 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart(
174 const VideoCaptureParams& params, 458 const VideoCaptureParams& params,
175 scoped_ptr<VideoCaptureDevice::Client> client) { 459 scoped_ptr<VideoCaptureDevice::Client> client) {
176 NOTIMPLEMENTED(); 460 DCHECK(thread_checker_.CalledOnValidThread());
461 client_ = client.Pass();
462 if (decklink_capture_delegate_.get())
463 decklink_capture_delegate_->AllocateAndStart(params);
177 } 464 }
178 465
179 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { 466 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() {
180 NOTIMPLEMENTED(); 467 if (decklink_capture_delegate_.get())
468 decklink_capture_delegate_->StopAndDeAllocate();
181 } 469 }
182 470
183 } // namespace media 471 } // namespace media
OLDNEW

Powered by Google App Engine