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/strings/sys_string_conversions.h" | 9 #include "base/strings/sys_string_conversions.h" |
10 #include "third_party/decklink/mac/include/DeckLinkAPI.h" | |
11 | |
12 namespace { | |
13 | |
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 | |
16 // parts of base::win::ScopedComPtr<> for ref counting. | |
17 template <class T> | |
18 class ScopedDeckLinkPtr : public scoped_refptr<T> { | |
19 public: | |
20 using scoped_refptr<T>::ptr_; | |
21 | |
22 T** Receive() { | |
23 DCHECK(!ptr_) << "Object leak. Pointer must be NULL"; | |
24 return &ptr_; | |
25 } | |
26 | |
27 void** ReceiveVoid() { | |
28 return reinterpret_cast<void**>(Receive()); | |
29 } | |
30 | |
31 void Release() { | |
32 if (ptr_ != NULL) { | |
33 ptr_->Release(); | |
34 ptr_ = NULL; | |
35 } | |
36 } | |
37 }; | |
38 | |
39 } // namespace | |
40 | 10 |
41 namespace media { | 11 namespace media { |
42 | 12 |
43 //static | 13 //static |
44 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( | 14 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( |
45 VideoCaptureDevice::Names* device_names) { | 15 VideoCaptureDevice::Names* device_names) { |
46 scoped_refptr<IDeckLinkIterator> decklink_iter( | 16 scoped_refptr<IDeckLinkIterator> decklink_iter( |
47 CreateDeckLinkIteratorInstance()); | 17 CreateDeckLinkIteratorInstance()); |
48 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; | 18 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; |
49 if (!decklink_iter) | 19 if (!decklink_iter) |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
101 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; | 71 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; |
102 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) != | 72 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) != |
103 S_OK) { | 73 S_OK) { |
104 continue; | 74 continue; |
105 } | 75 } |
106 | 76 |
107 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; | 77 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; |
108 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { | 78 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { |
109 // IDeckLinkDisplayMode does not have information on pixel format, it | 79 // IDeckLinkDisplayMode does not have information on pixel format, it |
110 // is only available on capture. | 80 // is only available on capture. |
111 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; | 81 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; |
magjed_chromium
2014/09/04 13:39:18
Why do you need to declare this variable? Why not
mcasas
2014/09/05 09:00:40
Done.
| |
112 BMDTimeValue time_value, time_scale; | 82 BMDTimeValue time_value, time_scale; |
113 float frame_rate = 0.0f; | 83 float frame_rate = 0.0f; |
114 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK && | 84 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK && |
115 time_value > 0) { | 85 time_value > 0) { |
116 frame_rate = static_cast<float>(time_scale) / time_value; | 86 frame_rate = static_cast<float>(time_scale) / time_value; |
117 } | 87 } |
88 // Interlaced formats are going to be marked as double the frame rate, | |
89 // which follows the general naming conventions. | |
90 if (display_mode->GetFieldDominance() == bmdLowerFieldFirst || | |
91 display_mode->GetFieldDominance() == bmdUpperFieldFirst) | |
92 frame_rate *= 2.0f; | |
118 media::VideoCaptureFormat format( | 93 media::VideoCaptureFormat format( |
magjed_chromium
2014/09/04 13:39:18
format can be made const
mcasas
2014/09/05 09:00:40
Done.
| |
119 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()), | 94 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()), |
120 frame_rate, | 95 frame_rate, |
121 pixel_format); | 96 pixel_format); |
122 supported_formats->push_back(format); | 97 supported_formats->push_back(format); |
123 DVLOG(2) << device.name() << " resolution: " | 98 DVLOG(2) << device.name() << " resolution: " |
124 << format.frame_size.ToString() << "@: " << format.frame_rate | 99 << format.frame_size.ToString() << "@: " << format.frame_rate |
125 << ", pixel format: " << format.pixel_format; | 100 << ", pixel format: " << format.pixel_format; |
126 display_mode.Release(); | 101 display_mode.Release(); |
127 } | 102 } |
128 return; | 103 return; |
129 } | 104 } |
130 } | 105 } |
131 | 106 |
132 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( | 107 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( |
133 const Name& device_name) {} | 108 const Name& device_name) |
109 : device_name_(device_name) { | |
110 } | |
134 | 111 |
135 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} | 112 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} |
136 | 113 |
137 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( | 114 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( |
138 const VideoCaptureParams& params, | 115 const VideoCaptureParams& params, |
139 scoped_ptr<VideoCaptureDevice::Client> client) { | 116 scoped_ptr<VideoCaptureDevice::Client> client) { |
140 NOTIMPLEMENTED(); | 117 scoped_refptr<IDeckLinkIterator> decklink_iter( |
118 CreateDeckLinkIteratorInstance()); | |
119 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; | |
120 if (!decklink_iter) | |
121 return; | |
122 | |
123 while (decklink_iter->Next(decklink_.Receive()) == S_OK) { | |
124 CFStringRef device_model_name = NULL; | |
125 if ((decklink_->GetModelName(&device_model_name) == S_OK) || | |
126 (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) { | |
127 break; | |
128 } | |
129 decklink_.Release(); | |
130 } | |
131 if (!decklink_) { | |
132 SetErrorState("Device id not found in the system"); | |
133 return; | |
134 } | |
135 | |
136 if (decklink_->QueryInterface(IID_IDeckLinkInput, | |
137 decklink_input_.ReceiveVoid()) != S_OK) { | |
138 SetErrorState("Error querying input interface."); | |
139 return; | |
140 } | |
141 | |
142 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; | |
143 if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) != | |
144 S_OK) { | |
145 SetErrorState("Error creating Display Mode Iterator"); | |
146 return; | |
147 } | |
148 | |
149 ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode; | |
150 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; | |
151 int min_diff = INT_MAX; | |
152 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { | |
153 BMDTimeValue time_value, time_scale; | |
154 float display_mode_frame_rate = 0.0f; | |
155 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK) | |
156 display_mode_frame_rate = static_cast<float>(time_scale) / time_value; | |
magjed_chromium
2014/09/04 13:39:18
Don't you need to check time_value > 0 here? Anywa
mcasas
2014/09/05 09:00:40
Done.
| |
157 // Interlaced formats are going to be marked as double the frame rate, | |
158 // which follows the general naming conventions. | |
159 if (display_mode->GetFieldDominance() == bmdLowerFieldFirst || | |
160 display_mode->GetFieldDominance() == bmdUpperFieldFirst) | |
161 display_mode_frame_rate *= 2.0f; | |
162 | |
163 int diff = abs(display_mode->GetWidth() - | |
magjed_chromium
2014/09/04 13:39:18
diff can be made const
mcasas
2014/09/05 09:00:40
Done.
| |
164 params.requested_format.frame_size.width()) + | |
165 abs(params.requested_format.frame_size.height() - | |
166 display_mode->GetHeight()) + fabs(params.requested_format.frame_rate - | |
167 display_mode_frame_rate); | |
168 if (diff < min_diff) { | |
169 chosen_display_mode = display_mode; | |
170 min_diff = diff; | |
171 } | |
172 display_mode.Release(); | |
173 } | |
174 if (!chosen_display_mode) { | |
175 SetErrorState("Could not find a display mode"); | |
176 return; | |
177 } | |
178 #if !defined(NDEBUG) | |
179 CFStringRef format_name = NULL; | |
180 if (chosen_display_mode->GetName(&format_name) == S_OK) | |
181 DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name); | |
182 #endif | |
183 | |
184 // Enable video input. Configure for no input video format change detection, | |
185 // this in turn will disable calls to VideoInputFormatChanged(). | |
186 if (decklink_input_->EnableVideoInput(chosen_display_mode->GetDisplayMode(), | |
187 bmdFormat8BitYUV, bmdVideoInputFlagDefault) != S_OK) { | |
188 SetErrorState("Could not select the video format we like."); | |
189 return; | |
190 } | |
191 | |
192 client_ = client.Pass(); | |
193 decklink_input_->SetCallback(this); | |
194 if (decklink_input_->StartStreams() != S_OK) { | |
195 SetErrorState("Could not start capturing"); | |
196 return; | |
197 } | |
141 } | 198 } |
142 | 199 |
143 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { | 200 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { |
144 NOTIMPLEMENTED(); | 201 if (decklink_input_->StopStreams() != S_OK) |
202 LogMessage("Problem stopping capture."); | |
203 decklink_input_->SetCallback(NULL); | |
204 decklink_input_->DisableVideoInput(); | |
205 } | |
206 | |
207 HRESULT VideoCaptureDeviceDeckLinkMac::VideoInputFormatChanged ( | |
208 BMDVideoInputFormatChangedEvents notification_events, | |
209 IDeckLinkDisplayMode *new_display_mode, | |
210 BMDDetectedVideoInputFormatFlags detected_signal_flags) { | |
211 DVLOG(1) << __FUNCTION__; | |
212 return S_OK; | |
213 } | |
214 | |
215 HRESULT VideoCaptureDeviceDeckLinkMac::VideoInputFrameArrived ( | |
216 IDeckLinkVideoInputFrame* video_frame, | |
217 IDeckLinkAudioInputPacket* /* audio_packet */) { | |
218 // Capture frames are manipulated as an IDeckLinkVideoFrame. | |
219 void *videoData; | |
220 video_frame->GetBytes(&videoData); | |
221 | |
222 VideoPixelFormat pixel_format = PIXEL_FORMAT_UNKNOWN; | |
223 switch (video_frame->GetPixelFormat()) { | |
224 case bmdFormat8BitYUV: // A.k.a. '2vuy'; | |
225 pixel_format = PIXEL_FORMAT_UYVY; | |
226 break; | |
227 case bmdFormat8BitARGB: | |
228 pixel_format = PIXEL_FORMAT_ARGB; | |
229 break; | |
230 case bmdFormat8BitBGRA: | |
magjed_chromium
2014/09/04 13:39:18
Why have you included this case if you don't do an
mcasas
2014/09/05 09:00:40
I guess eventually I wanted to do something but th
| |
231 default: // All others are 10-bit pixel formats and we don't support them. | |
232 SetErrorState("Unsupported pixel format"); | |
233 } | |
234 | |
235 media::VideoCaptureFormat captureFormat( | |
magjed_chromium
2014/09/04 13:39:18
captureFormat can be made const
mcasas
2014/09/05 09:00:40
Done.
| |
236 gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()), | |
237 0.0f, // Frame rate is not needed for captured data callback. | |
238 pixel_format); | |
239 client_->OnIncomingCapturedData( | |
240 static_cast<uint8*>(videoData), | |
241 video_frame->GetRowBytes() * video_frame->GetHeight(), | |
242 captureFormat, | |
243 0, // Rotation. | |
244 base::TimeTicks::Now()); | |
245 | |
246 return S_OK; | |
247 } | |
248 | |
249 HRESULT VideoCaptureDeviceDeckLinkMac::QueryInterface(REFIID iid, LPVOID *ppv) { | |
250 CFUUIDBytes iunknown; | |
251 HRESULT result = E_NOINTERFACE; | |
252 *ppv = NULL; | |
253 // Obtain the IUnknown interface and compare it the provided REFIID | |
254 iunknown = CFUUIDGetUUIDBytes(IUnknownUUID); | |
255 if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0) { | |
256 *ppv = this; | |
257 AddRef(); | |
258 result = S_OK; | |
259 } else if (memcmp(&iid, &IID_IDeckLinkNotificationCallback, sizeof(REFIID)) | |
260 == 0) { | |
magjed_chromium
2014/09/04 13:39:18
you should align == 0 to memcmp
mcasas
2014/09/05 09:00:40
Done.
| |
261 *ppv = (IDeckLinkNotificationCallback*)this; | |
262 AddRef(); | |
263 result = S_OK; | |
264 } | |
265 return result; | |
266 } | |
267 | |
268 ULONG VideoCaptureDeviceDeckLinkMac::AddRef(void) { | |
269 return OSAtomicIncrement32(&ref_count_); | |
270 } | |
271 | |
272 ULONG VideoCaptureDeviceDeckLinkMac::Release(void) { | |
273 int32_t newRefValue; | |
274 | |
275 newRefValue = OSAtomicDecrement32(&ref_count_); | |
276 if (newRefValue == 0) { | |
277 delete this; | |
278 return 0; | |
279 } | |
280 | |
281 return newRefValue; | |
282 } | |
283 | |
284 | |
285 void VideoCaptureDeviceDeckLinkMac::SetErrorState(const std::string& reason) { | |
286 if (client_) | |
287 client_->OnError(reason); | |
288 } | |
289 | |
290 void VideoCaptureDeviceDeckLinkMac::LogMessage(const std::string& message) { | |
291 if (client_) | |
292 client_->OnLog(message); | |
145 } | 293 } |
146 | 294 |
147 } // namespace media | 295 } // namespace media |
OLD | NEW |