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

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: Public base::RefCountedThreadSafe<DeckLinkCaptureDelegate> inheritance. 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 while (decklink_iter->Next(decklink_.Receive()) == S_OK) {
140 CFStringRef device_model_name = NULL;
141 if ((decklink_->GetModelName(&device_model_name) == S_OK) ||
142 (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) {
143 break;
144 }
145 decklink_.Release();
146 }
147 if (!decklink_.get()) {
148 SendErrorString("Device id not found in the system");
149 return;
150 }
151
152 if (decklink_->QueryInterface(IID_IDeckLinkInput,
153 decklink_input_.ReceiveVoid()) != S_OK) {
154 SendErrorString("Error querying input interface.");
155 return;
tommi (sloooow) - chröme 2014/09/30 18:21:39 release decklink_?
mcasas 2014/10/01 16:08:55 See below.
156 }
157
158 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter;
159 if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) !=
160 S_OK) {
161 SendErrorString("Error creating Display Mode Iterator");
162 return;
tommi (sloooow) - chröme 2014/09/30 18:21:39 release decklink_ and decklink_input_?
mcasas 2014/10/01 16:08:55 See below.
163 }
164
165 ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode;
166 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode;
167 float min_diff = FLT_MAX;
168 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
169 const float diff = labs(display_mode->GetWidth() -
170 params.requested_format.frame_size.width()) +
171 labs(params.requested_format.frame_size.height() -
172 display_mode->GetHeight()) + fabs(params.requested_format.frame_rate -
173 GetDisplayModeFrameRate(display_mode));
174 if (diff < min_diff) {
175 chosen_display_mode = display_mode;
176 min_diff = diff;
177 }
178 display_mode.Release();
179 }
180 if (!chosen_display_mode.get()) {
181 SendErrorString("Could not find a display mode");
182 return;
tommi (sloooow) - chröme 2014/09/30 18:21:38 free so-far-obtained objects? same below What yo
mcasas 2014/10/01 16:08:55 Done.
183 }
184 #if !defined(NDEBUG)
185 DVLOG(1) << "Requested format: " << params.requested_format.ToString();
186 CFStringRef format_name = NULL;
187 if (chosen_display_mode->GetName(&format_name) == S_OK)
188 DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name);
189 #endif
190
191 // Enable video input. Configure for no input video format change detection,
192 // this in turn will disable calls to VideoInputFormatChanged().
193 if (decklink_input_->EnableVideoInput(chosen_display_mode->GetDisplayMode(),
194 bmdFormat8BitYUV, bmdVideoInputFlagDefault) != S_OK) {
195 SendErrorString("Could not select the video format we like.");
196 return;
197 }
198
199 decklink_input_->SetCallback(this);
200 if (decklink_input_->StartStreams() != S_OK)
201 SendErrorString("Could not start capturing");
202 }
203
204 void DeckLinkCaptureDelegate::StopAndDeAllocate() {
tommi (sloooow) - chröme 2014/09/30 18:21:38 Should this method also release all outstanding re
mcasas 2014/10/01 16:08:55 Done.
205 DCHECK(thread_checker_.CalledOnValidThread());
206 if (!decklink_input_.get())
207 return;
208 if (decklink_input_->StopStreams() != S_OK)
209 SendLogString("Problem stopping capture.");
210 decklink_input_->SetCallback(NULL);
211 decklink_input_->DisableVideoInput();
212 ResetVideoCaptureDeviceReference();
213 }
214
215 HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged(
216 BMDVideoInputFormatChangedEvents notification_events,
217 IDeckLinkDisplayMode *new_display_mode,
218 BMDDetectedVideoInputFormatFlags detected_signal_flags) {
219 DCHECK(thread_checker_.CalledOnValidThread());
220 return S_OK;
221 }
222
223 HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived(
224 IDeckLinkVideoInputFrame* video_frame,
225 IDeckLinkAudioInputPacket* /* audio_packet */) {
226 // Capture frames are manipulated as an IDeckLinkVideoFrame.
227 uint8* video_data = NULL;
228 video_frame->GetBytes(reinterpret_cast<void**>(&video_data));
229
230 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN;
231 switch (video_frame->GetPixelFormat()) {
232 case bmdFormat8BitYUV: // A.k.a. '2vuy';
233 pixel_format = media::PIXEL_FORMAT_UYVY;
234 break;
235 case bmdFormat8BitARGB:
236 pixel_format = media::PIXEL_FORMAT_ARGB;
237 break;
238 default:
239 SendErrorString("Unsupported pixel format");
240 break;
241 }
242
243 const media::VideoCaptureFormat capture_format(
244 gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()),
245 0.0f, // Frame rate is not needed for captured data callback.
246 pixel_format);
247 base::AutoLock lock(lock_);
248 if (frame_receiver_) {
249 frame_receiver_->OnIncomingCapturedData(
250 video_data,
251 video_frame->GetRowBytes() * video_frame->GetHeight(),
252 capture_format,
253 0, // Rotation.
254 base::TimeTicks::Now());
255 }
256 return S_OK;
257 }
258
259 HRESULT DeckLinkCaptureDelegate::QueryInterface(REFIID iid, void** ppv) {
260 DCHECK(thread_checker_.CalledOnValidThread());
261 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID);
262 if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0 ||
263 memcmp(&iid, &IID_IDeckLinkInputCallback, sizeof(REFIID)) == 0) {
264 *ppv = static_cast<IDeckLinkInputCallback*>(this);
265 AddRef();
266 return S_OK;
267 }
268 return E_NOINTERFACE;
269 }
270
271 ULONG DeckLinkCaptureDelegate::AddRef() {
272 DCHECK(thread_checker_.CalledOnValidThread());
273 base::RefCountedThreadSafe<DeckLinkCaptureDelegate>::AddRef();
274 return 1;
275 }
276
277 ULONG DeckLinkCaptureDelegate::Release() {
278 DCHECK(thread_checker_.CalledOnValidThread());
279 bool ret_value = !HasOneRef();
280 base::RefCountedThreadSafe<DeckLinkCaptureDelegate>::Release();
281 return ret_value;
282 }
283
284 void DeckLinkCaptureDelegate::SendErrorString(const std::string& reason) {
285 base::AutoLock lock(lock_);
286 if (frame_receiver_)
287 frame_receiver_->SendErrorString(reason);
288 }
289
290 void DeckLinkCaptureDelegate::SendLogString(const std::string& message) {
291 base::AutoLock lock(lock_);
292 if (frame_receiver_)
293 frame_receiver_->SendLogString(message);
294 }
295
296 void DeckLinkCaptureDelegate::ResetVideoCaptureDeviceReference() {
297 DCHECK(thread_checker_.CalledOnValidThread());
298 base::AutoLock lock(lock_);
299 frame_receiver_ = NULL;
300 }
301
39 } // namespace 302 } // namespace
40 303
41 namespace media { 304 namespace media {
42 305
43 std::string JoinDeviceNameAndFormat(CFStringRef name, CFStringRef format) { 306 static std::string JoinDeviceNameAndFormat(CFStringRef name,
307 CFStringRef format) {
44 return base::SysCFStringRefToUTF8(name) + " - " + 308 return base::SysCFStringRefToUTF8(name) + " - " +
45 base::SysCFStringRefToUTF8(format); 309 base::SysCFStringRefToUTF8(format);
46 } 310 }
47 311
48 //static 312 //static
49 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( 313 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices(
50 VideoCaptureDevice::Names* device_names) { 314 VideoCaptureDevice::Names* device_names) {
51 scoped_refptr<IDeckLinkIterator> decklink_iter( 315 scoped_refptr<IDeckLinkIterator> decklink_iter(
52 CreateDeckLinkIteratorInstance()); 316 CreateDeckLinkIteratorInstance());
53 // At this point, not being able to create a DeckLink iterator means that 317 // 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. 318 // 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"; 319 DVLOG_IF(1, !decklink_iter.get()) << "Could not create DeckLink iterator";
56 if (!decklink_iter.get()) 320 if (!decklink_iter.get())
57 return; 321 return;
58 322
59 ScopedDeckLinkPtr<IDeckLink> decklink; 323 ScopedDeckLinkPtr<IDeckLink> decklink;
60 while (decklink_iter->Next(decklink.Receive()) == S_OK) { 324 while (decklink_iter->Next(decklink.Receive()) == S_OK) {
61 ScopedDeckLinkPtr<IDeckLink> decklink_local; 325 ScopedDeckLinkPtr<IDeckLink> decklink_local;
62 decklink_local.swap(decklink); 326 decklink_local.swap(decklink);
63 327
64 CFStringRef device_model_name = NULL; 328 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) { 403 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) {
140 CFStringRef format_name = NULL; 404 CFStringRef format_name = NULL;
141 if (display_mode->GetName(&format_name) == S_OK && device.id() != 405 if (display_mode->GetName(&format_name) == S_OK && device.id() !=
142 JoinDeviceNameAndFormat(device_model_name, format_name)) { 406 JoinDeviceNameAndFormat(device_model_name, format_name)) {
143 display_mode.Release(); 407 display_mode.Release();
144 continue; 408 continue;
145 } 409 }
146 410
147 // IDeckLinkDisplayMode does not have information on pixel format, this 411 // IDeckLinkDisplayMode does not have information on pixel format, this
148 // is only available on capture. 412 // is only available on capture.
149 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; 413 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()), 414 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()),
158 frame_rate, 415 GetDisplayModeFrameRate(display_mode),
159 pixel_format); 416 PIXEL_FORMAT_UNKNOWN);
160 supported_formats->push_back(format); 417 supported_formats->push_back(format);
161 DVLOG(2) << device.name() << " " << format.ToString(); 418 DVLOG(2) << device.name() << " " << format.ToString();
162 display_mode.Release(); 419 display_mode.Release();
163 } 420 }
164 return; 421 return;
165 } 422 }
166 } 423 }
167 424
168 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( 425 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac(
169 const Name& device_name) {} 426 const Name& device_name)
427 : decklink_capture_delegate_(
428 new DeckLinkCaptureDelegate(device_name, this)) {
429 }
170 430
171 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} 431 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {
432 decklink_capture_delegate_->ResetVideoCaptureDeviceReference();
433 }
434
435 void VideoCaptureDeviceDeckLinkMac::OnIncomingCapturedData(
436 const uint8* data,
437 size_t length,
438 const VideoCaptureFormat& frame_format,
439 int rotation, // Clockwise.
440 base::TimeTicks timestamp) {
441 base::AutoLock lock(lock_);
442 if (client_) {
443 client_->OnIncomingCapturedData(data, length, frame_format, rotation,
444 timestamp);
445 }
446 }
447
448 void VideoCaptureDeviceDeckLinkMac::SendErrorString(const std::string& reason) {
449 DCHECK(thread_checker_.CalledOnValidThread());
450 base::AutoLock lock(lock_);
451 if (client_)
452 client_->OnError(reason);
453 }
454
455 void VideoCaptureDeviceDeckLinkMac::SendLogString(const std::string& message) {
456 DCHECK(thread_checker_.CalledOnValidThread());
457 base::AutoLock lock(lock_);
458 if (client_)
459 client_->OnLog(message);
460 }
172 461
173 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( 462 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart(
174 const VideoCaptureParams& params, 463 const VideoCaptureParams& params,
175 scoped_ptr<VideoCaptureDevice::Client> client) { 464 scoped_ptr<VideoCaptureDevice::Client> client) {
176 NOTIMPLEMENTED(); 465 DCHECK(thread_checker_.CalledOnValidThread());
466 client_ = client.Pass();
467 if (decklink_capture_delegate_.get())
468 decklink_capture_delegate_->AllocateAndStart(params);
177 } 469 }
178 470
179 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { 471 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() {
tommi (sloooow) - chröme 2014/09/30 18:21:38 thread check?
180 NOTIMPLEMENTED(); 472 if (decklink_capture_delegate_.get())
473 decklink_capture_delegate_->StopAndDeAllocate();
181 } 474 }
182 475
183 } // namespace media 476 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698