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

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

Powered by Google App Engine
This is Rietveld 408576698