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> { | |
tommi (sloooow) - chröme
2014/09/05 09:24:22
if you create a special subclass that implements t
mcasas
2014/09/09 11:05:01
Acknowledged.
| |
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 |
13 // static | |
14 float GetDisplayModeFrameRate( | |
15 const ScopedDeckLinkPtr<IDeckLinkDisplayMode>& display_mode) { | |
16 BMDTimeValue time_value, time_scale; | |
17 float display_mode_frame_rate = 0.0f; | |
18 if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK && | |
19 time_value > 0) { | |
20 display_mode_frame_rate = static_cast<float>(time_scale) / time_value; | |
21 } | |
22 // Interlaced formats are going to be marked as double the frame rate, | |
23 // which follows the general naming conventions. | |
24 if (display_mode->GetFieldDominance() == bmdLowerFieldFirst || | |
25 display_mode->GetFieldDominance() == bmdUpperFieldFirst) | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
{}
mcasas
2014/09/09 11:05:00
Done.
| |
26 display_mode_frame_rate *= 2.0f; | |
27 return display_mode_frame_rate; | |
28 } | |
29 | |
43 //static | 30 //static |
44 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( | 31 void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( |
45 VideoCaptureDevice::Names* device_names) { | 32 VideoCaptureDevice::Names* device_names) { |
46 scoped_refptr<IDeckLinkIterator> decklink_iter( | 33 scoped_refptr<IDeckLinkIterator> decklink_iter( |
47 CreateDeckLinkIteratorInstance()); | 34 CreateDeckLinkIteratorInstance()); |
48 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; | 35 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; |
49 if (!decklink_iter) | 36 if (!decklink_iter) |
50 return; | 37 return; |
51 | 38 |
52 ScopedDeckLinkPtr<IDeckLink> decklink; | 39 ScopedDeckLinkPtr<IDeckLink> decklink; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
103 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; | 90 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; |
104 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) != | 91 if (decklink_input->GetDisplayModeIterator(display_mode_iter.Receive()) != |
105 S_OK) { | 92 S_OK) { |
106 continue; | 93 continue; |
107 } | 94 } |
108 | 95 |
109 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; | 96 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; |
110 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { | 97 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { |
111 // IDeckLinkDisplayMode does not have information on pixel format, it | 98 // IDeckLinkDisplayMode does not have information on pixel format, it |
112 // is only available on capture. | 99 // is only available on capture. |
113 media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; | 100 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()), | 101 gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()), |
122 frame_rate, | 102 GetDisplayModeFrameRate(display_mode), |
123 pixel_format); | 103 PIXEL_FORMAT_UNKNOWN); |
124 supported_formats->push_back(format); | 104 supported_formats->push_back(format); |
125 DVLOG(2) << device.name() << " resolution: " | 105 DVLOG(2) << format.ToString(); |
126 << format.frame_size.ToString() << "@: " << format.frame_rate | |
127 << ", pixel format: " << format.pixel_format; | |
128 display_mode.Release(); | 106 display_mode.Release(); |
129 } | 107 } |
130 return; | 108 return; |
131 } | 109 } |
132 } | 110 } |
133 | 111 |
134 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( | 112 VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( |
135 const Name& device_name) {} | 113 const Name& device_name) |
114 : device_name_(device_name) { | |
115 } | |
136 | 116 |
137 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} | 117 VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} |
138 | 118 |
139 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( | 119 void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( |
tommi (sloooow) - chröme
2014/09/05 09:24:21
add thread check
mcasas
2014/09/09 11:05:01
Done.
| |
140 const VideoCaptureParams& params, | 120 const VideoCaptureParams& params, |
141 scoped_ptr<VideoCaptureDevice::Client> client) { | 121 scoped_ptr<VideoCaptureDevice::Client> client) { |
142 NOTIMPLEMENTED(); | 122 scoped_refptr<IDeckLinkIterator> decklink_iter( |
123 CreateDeckLinkIteratorInstance()); | |
124 DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; | |
125 if (!decklink_iter) | |
126 return; | |
127 | |
128 while (decklink_iter->Next(decklink_.Receive()) == S_OK) { | |
129 CFStringRef device_model_name = NULL; | |
130 if ((decklink_->GetModelName(&device_model_name) == S_OK) || | |
131 (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) { | |
132 break; | |
133 } | |
134 decklink_.Release(); | |
135 } | |
136 if (!decklink_) { | |
137 SetErrorState("Device id not found in the system"); | |
138 return; | |
139 } | |
140 | |
141 if (decklink_->QueryInterface(IID_IDeckLinkInput, | |
142 decklink_input_.ReceiveVoid()) != S_OK) { | |
143 SetErrorState("Error querying input interface."); | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
should we release declink_ here in this case?
mcasas
2014/09/09 11:05:01
SetErrorState() will ping VCController->VideoCaptu
| |
144 return; | |
145 } | |
146 | |
147 ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; | |
148 if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) != | |
149 S_OK) { | |
150 SetErrorState("Error creating Display Mode Iterator"); | |
151 return; | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
same here for decklink_ and decklink_input_.
(same
mcasas
2014/09/09 11:05:01
See above.
| |
152 } | |
153 | |
154 ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode; | |
155 ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; | |
156 int min_diff = INT_MAX; | |
157 while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { | |
158 const int diff = abs(display_mode->GetWidth() - | |
159 params.requested_format.frame_size.width()) + | |
160 abs(params.requested_format.frame_size.height() - | |
161 display_mode->GetHeight()) + fabs(params.requested_format.frame_rate - | |
162 GetDisplayModeFrameRate(display_mode)); | |
163 if (diff < min_diff) { | |
164 chosen_display_mode = display_mode; | |
165 min_diff = diff; | |
166 } | |
167 display_mode.Release(); | |
168 } | |
169 if (!chosen_display_mode) { | |
170 SetErrorState("Could not find a display mode"); | |
171 return; | |
172 } | |
173 #if !defined(NDEBUG) | |
174 CFStringRef format_name = NULL; | |
175 if (chosen_display_mode->GetName(&format_name) == S_OK) | |
176 DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name); | |
177 #endif | |
178 | |
179 // Enable video input. Configure for no input video format change detection, | |
180 // this in turn will disable calls to VideoInputFormatChanged(). | |
181 if (decklink_input_->EnableVideoInput(chosen_display_mode->GetDisplayMode(), | |
182 bmdFormat8BitYUV, bmdVideoInputFlagDefault) != S_OK) { | |
183 SetErrorState("Could not select the video format we like."); | |
184 return; | |
185 } | |
186 | |
187 client_ = client.Pass(); | |
188 decklink_input_->SetCallback(this); | |
189 if (decklink_input_->StartStreams() != S_OK) { | |
190 SetErrorState("Could not start capturing"); | |
191 return; | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
nit: return statement not necessary - but we shoul
mcasas
2014/09/09 11:05:01
See above.
| |
192 } | |
143 } | 193 } |
144 | 194 |
145 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { | 195 void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { |
146 NOTIMPLEMENTED(); | 196 if (decklink_input_->StopStreams() != S_OK) |
tommi (sloooow) - chröme
2014/09/05 09:24:21
add thread check first and also check these pointe
mcasas
2014/09/09 11:05:01
Done.
| |
197 LogMessage("Problem stopping capture."); | |
198 decklink_input_->SetCallback(NULL); | |
199 decklink_input_->DisableVideoInput(); | |
200 } | |
201 | |
202 HRESULT VideoCaptureDeviceDeckLinkMac::VideoInputFormatChanged ( | |
203 BMDVideoInputFormatChangedEvents notification_events, | |
204 IDeckLinkDisplayMode *new_display_mode, | |
205 BMDDetectedVideoInputFormatFlags detected_signal_flags) { | |
206 DVLOG(1) << __FUNCTION__; | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
thread check?
mcasas
2014/09/09 11:05:00
Done.
| |
207 return S_OK; | |
208 } | |
209 | |
210 HRESULT VideoCaptureDeviceDeckLinkMac::VideoInputFrameArrived ( | |
211 IDeckLinkVideoInputFrame* video_frame, | |
212 IDeckLinkAudioInputPacket* /* audio_packet */) { | |
213 // Capture frames are manipulated as an IDeckLinkVideoFrame. | |
tommi (sloooow) - chröme
2014/09/05 09:24:22
thread check?
mcasas
2014/09/09 11:05:01
Done.
| |
214 void *videoData; | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
void* video_data = NULL;
mcasas
2014/09/09 11:05:01
Done.
| |
215 video_frame->GetBytes(&videoData); | |
216 | |
217 VideoPixelFormat pixel_format = PIXEL_FORMAT_UNKNOWN; | |
218 switch (video_frame->GetPixelFormat()) { | |
219 case bmdFormat8BitYUV: // A.k.a. '2vuy'; | |
220 pixel_format = PIXEL_FORMAT_UYVY; | |
221 break; | |
222 case bmdFormat8BitARGB: | |
223 pixel_format = PIXEL_FORMAT_ARGB; | |
224 break; | |
225 default: | |
226 SetErrorState("Unsupported pixel format"); | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
add break; for posterity
mcasas
2014/09/09 11:05:01
Done.
| |
227 } | |
228 | |
229 const VideoCaptureFormat captureFormat( | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
why captureFormat here and not capture_format? I
mcasas
2014/09/09 11:05:01
No, you're all right. The sample code(s) I followe
| |
230 gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()), | |
231 0.0f, // Frame rate is not needed for captured data callback. | |
232 pixel_format); | |
233 client_->OnIncomingCapturedData( | |
234 static_cast<uint8*>(videoData), | |
235 video_frame->GetRowBytes() * video_frame->GetHeight(), | |
236 captureFormat, | |
237 0, // Rotation. | |
238 base::TimeTicks::Now()); | |
239 | |
240 return S_OK; | |
241 } | |
242 | |
243 HRESULT VideoCaptureDeviceDeckLinkMac::QueryInterface(REFIID iid, LPVOID *ppv) { | |
244 HRESULT result = E_NOINTERFACE; | |
245 *ppv = NULL; | |
tommi (sloooow) - chröme
2014/09/05 09:24:22
no need to do this
mcasas
2014/09/09 11:05:01
Acknowledged.
| |
246 // Obtain the IUnknown interface and compare it the provided REFIID | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
this isn't obtaining the interface itself, it's fe
mcasas
2014/09/09 11:05:00
Acknowledged.
| |
247 CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID); | |
248 if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0) { | |
249 *ppv = this; | |
250 AddRef(); | |
251 result = S_OK; | |
252 } else if (memcmp(&iid, &IID_IDeckLinkNotificationCallback, sizeof(REFIID)) | |
253 == 0) { | |
254 *ppv = (IDeckLinkNotificationCallback*)this; | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
static_cast
mcasas
2014/09/09 11:05:00
Acknowledged.
| |
255 AddRef(); | |
256 result = S_OK; | |
257 } | |
258 return result; | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
this implementation can be simplified:
if (memcmp
mcasas
2014/09/09 11:05:01
Done.
| |
259 } | |
260 | |
261 ULONG VideoCaptureDeviceDeckLinkMac::AddRef(void) { | |
262 return OSAtomicIncrement32(&ref_count_); | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
why atomic? Do we expect multithreaded access? (i
mcasas
2014/09/09 11:05:00
Reference/Example implementation says so :)
Added
| |
263 } | |
264 | |
265 ULONG VideoCaptureDeviceDeckLinkMac::Release(void) { | |
266 int32_t newRefValue = OSAtomicDecrement32(&ref_count_); | |
267 if (newRefValue == 0) { | |
268 delete this; | |
269 return 0; | |
tommi (sloooow) - chröme
2014/09/05 09:24:21
nit: this return statement isn't necessary
mcasas
2014/09/09 11:05:01
Done.
| |
270 } | |
271 return newRefValue; | |
272 } | |
273 | |
274 | |
275 void VideoCaptureDeviceDeckLinkMac::SetErrorState(const std::string& reason) { | |
276 if (client_) | |
277 client_->OnError(reason); | |
278 } | |
279 | |
280 void VideoCaptureDeviceDeckLinkMac::LogMessage(const std::string& message) { | |
281 if (client_) | |
282 client_->OnLog(message); | |
147 } | 283 } |
148 | 284 |
149 } // namespace media | 285 } // namespace media |
OLD | NEW |