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

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. Extracted DeckLinkCaptureDelegate. Created 6 years, 3 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 11
12 namespace { 12 namespace {
13 13
14 // DeckLink SDK uses ScopedComPtr-style APIs. Chrome ScopedComPtr is only 14 // DeckLink SDK uses ScopedComPtr-style APIs. Chrome ScopedComPtr is only
15 // available for Windows builds. This is a verbatim knock-off of the needed 15 // available for Windows builds. This is a verbatim knock-off of the needed
16 // parts of base::win::ScopedComPtr<> for ref counting. 16 // parts of base::win::ScopedComPtr<> for ref counting.
17 template <class T> 17 template <class T>
18 class ScopedDeckLinkPtr : public scoped_refptr<T> { 18 class ScopedDeckLinkPtr : public scoped_refptr<T> {
19 public: 19 public:
20 using scoped_refptr<T>::ptr_; 20 using scoped_refptr<T>::ptr_;
21 21
22 T** Receive() { 22 T** Receive() {
23 DCHECK(!ptr_) << "Object leak. Pointer must be NULL"; 23 DCHECK(!ptr_) << "Object leak. Pointer must be NULL";
24 return &ptr_; 24 return &ptr_;
25 } 25 }
26 26
27 void** ReceiveVoid() { 27 void** ReceiveVoid() {
28 return reinterpret_cast<void**>(Receive()); 28 return reinterpret_cast<void**>(Receive());
29 } 29 }
30 30
31 void Release() { 31 void Release() {
32 if (ptr_ != NULL) { 32 if (ptr_ != NULL) {
33 ptr_->Release(); 33 ptr_->Release();
34 ptr_ = NULL; 34 ptr_ = NULL;
35 } 35 }
36 } 36 }
37 }; 37 };
38 38
39 // This class is used to interact directly with DeckLink SDK for video capture.
40 // Implements the reference counted interface IUnknown. Has a weak reference to
41 // VideoCaptureDeviceDeckLinkMac for sending captured frames, error messages and
42 // logs.
43 class DeckLinkCaptureDelegate : private IDeckLinkInputCallback {
44 public:
45 DeckLinkCaptureDelegate(const media::VideoCaptureDevice::Name& device_name,
46 media::VideoCaptureDeviceDeckLinkMac* frame_receiver);
47 virtual ~DeckLinkCaptureDelegate();
48
49 // IDeckLinkInputCallback interface implementation.
50 virtual HRESULT VideoInputFormatChanged (
51 BMDVideoInputFormatChangedEvents notification_events,
52 IDeckLinkDisplayMode *new_display_mode,
53 BMDDetectedVideoInputFormatFlags detected_signal_flags) OVERRIDE;
54 virtual HRESULT VideoInputFrameArrived (
55 IDeckLinkVideoInputFrame* video_frame,
56 IDeckLinkAudioInputPacket* audio_packet) OVERRIDE;
57
58 // IUnknown interface implementation.
59 virtual HRESULT QueryInterface (REFIID iid, LPVOID *ppv) OVERRIDE;
60 virtual ULONG AddRef() OVERRIDE;
61 virtual ULONG Release() OVERRIDE;
62
63 void AllocateAndStart(const media::VideoCaptureParams& params);
64 void StopAndDeAllocate();
65
66 // Remove the VideoCaptureDeviceDeckLinkMac's weak reference.
67 void ResetVideoCaptureDeviceReference();
68
69 private:
70 // Forwarder to VideoCaptureDeviceDeckLinkMac::sendErrorString().
71 void sendErrorString(const std::string& reason);
72
73 // Forwarder to VideoCaptureDeviceDeckLinkMac::sendLogString().
74 void sendLogString(const std::string& message);
75
76 const media::VideoCaptureDevice::Name device_name_;
77
78 // Protects concurrent setting and using of |frame_receiver_|.
79 base::Lock lock_;
80 media::VideoCaptureDeviceDeckLinkMac* frame_receiver_; // Weak.
81
82 // This is used to control the video capturing device input interface.
83 ScopedDeckLinkPtr<IDeckLinkInput> decklink_input_;
84 // |decklink_| represents a physical device attached to the host.
85 ScopedDeckLinkPtr<IDeckLink> decklink_;
86 // Internal counter for IUnknown interface implementation.
87 int32_t ref_count_;
88
89 base::ThreadChecker thread_checker_;
90
91 DISALLOW_COPY_AND_ASSIGN(DeckLinkCaptureDelegate);
92 };
93
94 // static
95 float GetDisplayModeFrameRate(
96 const ScopedDeckLinkPtr<IDeckLinkDisplayMode>& display_mode) {
97 BMDTimeValue time_value, time_scale;
98 float display_mode_frame_rate = 0.0f;
99 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK &&
100 time_value > 0) {
101 display_mode_frame_rate = static_cast<float>(time_scale) / time_value;
102 }
103 // Interlaced formats are going to be marked as double the frame rate,
104 // which follows the general naming convention.
105 if (display_mode->GetFieldDominance() == bmdLowerFieldFirst ||
106 display_mode->GetFieldDominance() == bmdUpperFieldFirst) {
107 display_mode_frame_rate *= 2.0f;
108 }
109 return display_mode_frame_rate;
110 }
111
112 DeckLinkCaptureDelegate::DeckLinkCaptureDelegate(
113 const media::VideoCaptureDevice::Name& device_name,
114 media::VideoCaptureDeviceDeckLinkMac* frame_receiver)
115 : device_name_(device_name),
116 frame_receiver_(frame_receiver) {
117 }
118
119 DeckLinkCaptureDelegate::~DeckLinkCaptureDelegate() {}
120
121 void DeckLinkCaptureDelegate::AllocateAndStart(
122 const media::VideoCaptureParams& params) {
123 DCHECK(thread_checker_.CalledOnValidThread());
124 scoped_refptr<IDeckLinkIterator> decklink_iter(
125 CreateDeckLinkIteratorInstance());
126 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator";
127 if (!decklink_iter)
128 return;
129
130 while (decklink_iter->Next(decklink_.Receive()) == S_OK) {
131 CFStringRef device_model_name = NULL;
132 if ((decklink_->GetModelName(&device_model_name) == S_OK) ||
133 (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) {
134 break;
135 }
136 decklink_.Release();
137 }
138 if (!decklink_) {
139 sendErrorString("Device id not found in the system");
140 return;
141 }
142
143 if (decklink_->QueryInterface(IID_IDeckLinkInput,
144 decklink_input_.ReceiveVoid()) != S_OK) {
145 sendErrorString("Error querying input interface.");
146 return;
147 }
148
149 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
150 if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) !=
151 S_OK) {
152 sendErrorString("Error creating Display Mode Iterator");
153 return;
154 }
155
156 ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode;
157 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
158 int min_diff = INT_MAX;
159 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
magjed_chromium 2014/09/09 14:36:24 This code for finding the most closely matched for
mcasas 2014/09/22 14:42:36 Acknowledged.
160 const int diff = abs(display_mode->GetWidth() -
161 params.requested_format.frame_size.width()) +
162 abs(params.requested_format.frame_size.height() -
163 display_mode->GetHeight()) + fabs(params.requested_format.frame_rate -
164 GetDisplayModeFrameRate(display_mode));
165 if (diff < min_diff) {
166 chosen_display_mode = display_mode;
167 min_diff = diff;
168 }
169 display_mode.Release();
170 }
171 if (!chosen_display_mode) {
172 sendErrorString("Could not find a display mode");
173 return;
174 }
175 #if !defined(NDEBUG)
176 CFStringRef format_name = NULL;
177 if (chosen_display_mode->GetName(&format_name) == S_OK)
178 DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name);
179 #endif
180
181 // Enable video input. Configure for no input video format change detection,
182 // this in turn will disable calls to VideoInputFormatChanged().
183 if (decklink_input_->EnableVideoInput(chosen_display_mode->GetDisplayMode(),
184 bmdFormat8BitYUV, bmdVideoInputFlagDefault) != S_OK) {
185 sendErrorString("Could not select the video format we like.");
186 return;
187 }
188
189 decklink_input_->SetCallback(this);
190 if (decklink_input_->StartStreams() != S_OK)
191 sendErrorString("Could not start capturing");
192 }
193
194 void DeckLinkCaptureDelegate::StopAndDeAllocate() {
195 DCHECK(thread_checker_.CalledOnValidThread());
196 if (!decklink_input_) {
197 return;
198 }
199 if (decklink_input_->StopStreams() != S_OK)
200 sendLogString("Problem stopping capture.");
201 decklink_input_->SetCallback(NULL);
202 decklink_input_->DisableVideoInput();
203 ResetVideoCaptureDeviceReference();
204 }
205
206 HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged (
207 BMDVideoInputFormatChangedEvents notification_events,
208 IDeckLinkDisplayMode *new_display_mode,
209 BMDDetectedVideoInputFormatFlags detected_signal_flags) {
210 DCHECK(thread_checker_.CalledOnValidThread());
211 return S_OK;
212 }
213
214 HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived (
215 IDeckLinkVideoInputFrame* video_frame,
216 IDeckLinkAudioInputPacket* /* audio_packet */) {
217 // Capture frames are manipulated as an IDeckLinkVideoFrame.
218 void *video_data = NULL;
219 video_frame->GetBytes(&video_data);
220
221 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN;
222 switch (video_frame->GetPixelFormat()) {
223 case bmdFormat8BitYUV: // A.k.a. '2vuy';
224 pixel_format = media::PIXEL_FORMAT_UYVY;
225 break;
226 case bmdFormat8BitARGB:
227 pixel_format = media::PIXEL_FORMAT_ARGB;
228 break;
229 default:
230 sendErrorString("Unsupported pixel format");
231 break;
232 }
233
234 const media::VideoCaptureFormat capture_format(
235 gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()),
236 0.0f, // Frame rate is not needed for captured data callback.
237 pixel_format);
238 base::AutoLock lock(lock_);
239 if (frame_receiver_) {
240 frame_receiver_->OnIncomingCapturedData(
241 static_cast<uint8*>(video_data),
242 video_frame->GetRowBytes() * video_frame->GetHeight(),
243 capture_format,
244 0, // Rotation.
245 base::TimeTicks::Now());
246 }
247 return S_OK;
248 }
249
250 HRESULT DeckLinkCaptureDelegate::QueryInterface(REFIID iid, LPVOID *ppv) {
251 DCHECK(thread_checker_.CalledOnValidThread());
252 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
253 if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0 ||
254 memcmp(&iid, &IID_IDeckLinkNotificationCallback, sizeof(REFIID)) == 0) {
255 *ppv = reinterpret_cast<IDeckLinkNotificationCallback*>(this);
256 AddRef();
257 return S_OK;
258 }
259 return E_NOINTERFACE;
260 }
261
262 ULONG DeckLinkCaptureDelegate::AddRef(void) {
263 DCHECK(thread_checker_.CalledOnValidThread());
264 return OSAtomicIncrement32(&ref_count_);
265 }
266
267 ULONG DeckLinkCaptureDelegate::Release(void) {
268 DCHECK(thread_checker_.CalledOnValidThread());
269 int32_t newRefValue = OSAtomicDecrement32(&ref_count_);
270 if (newRefValue == 0)
271 delete this;
272 return newRefValue;
273 }
274
275 void DeckLinkCaptureDelegate::sendErrorString(const std::string& reason) {
276 base::AutoLock lock(lock_);
277 if (frame_receiver_)
278 frame_receiver_->sendErrorString(reason);
279 }
280
281 void DeckLinkCaptureDelegate::sendLogString(const std::string& message) {
282 base::AutoLock lock(lock_);
283 if (frame_receiver_)
284 frame_receiver_->sendLogString(message);
285 }
286
287 void DeckLinkCaptureDelegate::ResetVideoCaptureDeviceReference() {
288 DCHECK(thread_checker_.CalledOnValidThread());
289 base::AutoLock lock(lock_);
290 frame_receiver_ = NULL;
291 }
292
39 } // namespace 293 } // namespace
40 294
41 namespace media { 295 namespace media {
42 296
43 //static 297 //static
44 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( 298 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices(
45 VideoCaptureDevice::Names* device_names) { 299 VideoCaptureDevice::Names* device_names) {
46 scoped_refptr<IDeckLinkIterator> decklink_iter( 300 scoped_refptr<IDeckLinkIterator> decklink_iter(
47 CreateDeckLinkIteratorInstance()); 301 CreateDeckLinkIteratorInstance());
48 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; 302 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator";
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
103 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; 357 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
104 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) != 358 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) !=
105 S_OK) { 359 S_OK) {
106 continue; 360 continue;
107 } 361 }
108 362
109 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; 363 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
110 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { 364 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
111 // IDeckLinkDisplayMode does not have information on pixel format, it 365 // IDeckLinkDisplayMode does not have information on pixel format, it
112 // is only available on capture. 366 // is only available on capture.
113 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; 367 const media::VideoCaptureFormat format(
114 BMDTimeValue time_value, time_scale;
115 float frame_rate = 0.0f;
116 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK &&
117 time_value > 0) {
118 frame_rate = static_cast<float>(time_scale) / time_value;
119 }
120 media::VideoCaptureFormat format(
121 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()), 368 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()),
122 frame_rate, 369 GetDisplayModeFrameRate(display_mode),
123 pixel_format); 370 PIXEL_FORMAT_UNKNOWN);
124 supported_formats->push_back(format); 371 supported_formats->push_back(format);
125 DVLOG(2) << device.name() << " resolution: " 372 DVLOG(2) << format.ToString();
126 << format.frame_size.ToString() << "@: " << format.frame_rate
127 << ", pixel format: " << format.pixel_format;
128 display_mode.Release(); 373 display_mode.Release();
129 } 374 }
130 return; 375 return;
131 } 376 }
132 } 377 }
133 378
134 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( 379 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac(
135 const Name& device_name) {} 380 const Name& device_name)
381 : decklink_capture_delegate_(
382 new DeckLinkCaptureDelegate(device_name, this)) {
383 }
136 384
137 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} 385 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {
386 decklink_capture_delegate_->ResetVideoCaptureDeviceReference();
387 }
388
389 void VideoCaptureDeviceDeckLinkMac::OnIncomingCapturedData(
390 const uint8* data,
391 int length,
392 const VideoCaptureFormat& frame_format,
393 int rotation, // Clockwise.
394 base::TimeTicks timestamp) {
395 base::AutoLock lock(lock_);
396 if (client_) {
397 client_->OnIncomingCapturedData(data, length, frame_format, rotation,
398 timestamp);
399 }
400 }
401
402 void VideoCaptureDeviceDeckLinkMac::sendErrorString(const std::string& reason) {
403 DCHECK(thread_checker_.CalledOnValidThread());
404 base::AutoLock lock(lock_);
405 if (client_)
406 client_->OnError(reason);
407 }
408
409 void VideoCaptureDeviceDeckLinkMac::sendLogString(const std::string& message) {
410 DCHECK(thread_checker_.CalledOnValidThread());
411 base::AutoLock lock(lock_);
412 if (client_)
413 client_->OnLog(message);
414 }
138 415
139 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( 416 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart(
140 const VideoCaptureParams& params, 417 const VideoCaptureParams& params,
141 scoped_ptr<VideoCaptureDevice::Client> client) { 418 scoped_ptr<VideoCaptureDevice::Client> client) {
142 NOTIMPLEMENTED(); 419 DCHECK(thread_checker_.CalledOnValidThread());
420 client_ = client.Pass();
421 if (decklink_capture_delegate_)
422 decklink_capture_delegate_->AllocateAndStart(params);
143 } 423 }
144 424
145 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { 425 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() {
146 NOTIMPLEMENTED(); 426 if (decklink_capture_delegate_)
427 decklink_capture_delegate_->StopAndDeAllocate();
147 } 428 }
148 429
149 } // namespace media 430 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698