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