OLD | NEW |
---|---|
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 Loading... | |
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 |
OLD | NEW |