Chromium Code Reviews| 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 |