Index: media/video/capture/mac/video_capture_device_decklink_mac.mm |
diff --git a/media/video/capture/mac/video_capture_device_decklink_mac.mm b/media/video/capture/mac/video_capture_device_decklink_mac.mm |
index 2842543862c7f0d0f5281a8c6b0b5c3fab8f9527..f8c95bb68ac9a5e8071625a614a68cb1cc16cef9 100644 |
--- a/media/video/capture/mac/video_capture_device_decklink_mac.mm |
+++ b/media/video/capture/mac/video_capture_device_decklink_mac.mm |
@@ -7,38 +7,25 @@ |
#include "base/logging.h" |
#include "base/memory/ref_counted.h" |
#include "base/strings/sys_string_conversions.h" |
-#include "third_party/decklink/mac/include/DeckLinkAPI.h" |
-namespace { |
- |
-// DeckLink SDK uses ScopedComPtr-style APIs. Chrome ScopedComPtr is only |
-// available for Windows builds. This is a verbatim knock-off of the needed |
-// parts of base::win::ScopedComPtr<> for ref counting. |
-template <class T> |
-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.
|
- public: |
- using scoped_refptr<T>::ptr_; |
- |
- T** Receive() { |
- DCHECK(!ptr_) << "Object leak. Pointer must be NULL"; |
- return &ptr_; |
- } |
- |
- void** ReceiveVoid() { |
- return reinterpret_cast<void**>(Receive()); |
- } |
+namespace media { |
- void Release() { |
- if (ptr_ != NULL) { |
- ptr_->Release(); |
- ptr_ = NULL; |
- } |
+// static |
+float GetDisplayModeFrameRate( |
+ const ScopedDeckLinkPtr<IDeckLinkDisplayMode>& display_mode) { |
+ BMDTimeValue time_value, time_scale; |
+ float display_mode_frame_rate = 0.0f; |
+ if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK && |
+ time_value > 0) { |
+ display_mode_frame_rate = static_cast<float>(time_scale) / time_value; |
} |
-}; |
- |
-} // namespace |
- |
-namespace media { |
+ // Interlaced formats are going to be marked as double the frame rate, |
+ // which follows the general naming conventions. |
+ if (display_mode->GetFieldDominance() == bmdLowerFieldFirst || |
+ display_mode->GetFieldDominance() == bmdUpperFieldFirst) |
tommi (sloooow) - chröme
2014/09/05 09:24:21
{}
mcasas
2014/09/09 11:05:00
Done.
|
+ display_mode_frame_rate *= 2.0f; |
+ return display_mode_frame_rate; |
+} |
//static |
void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( |
@@ -110,21 +97,12 @@ void VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities( |
while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { |
// IDeckLinkDisplayMode does not have information on pixel format, it |
// is only available on capture. |
- media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; |
- BMDTimeValue time_value, time_scale; |
- float frame_rate = 0.0f; |
- if (display_mode->GetFrameRate(&time_value, &time_scale) == S_OK && |
- time_value > 0) { |
- frame_rate = static_cast<float>(time_scale) / time_value; |
- } |
- media::VideoCaptureFormat format( |
+ const media::VideoCaptureFormat format( |
gfx::Size(display_mode->GetWidth(), display_mode->GetHeight()), |
- frame_rate, |
- pixel_format); |
+ GetDisplayModeFrameRate(display_mode), |
+ PIXEL_FORMAT_UNKNOWN); |
supported_formats->push_back(format); |
- DVLOG(2) << device.name() << " resolution: " |
- << format.frame_size.ToString() << "@: " << format.frame_rate |
- << ", pixel format: " << format.pixel_format; |
+ DVLOG(2) << format.ToString(); |
display_mode.Release(); |
} |
return; |
@@ -132,18 +110,176 @@ void VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities( |
} |
VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( |
- const Name& device_name) {} |
+ const Name& device_name) |
+ : device_name_(device_name) { |
+} |
VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} |
void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( |
tommi (sloooow) - chröme
2014/09/05 09:24:21
add thread check
mcasas
2014/09/09 11:05:01
Done.
|
const VideoCaptureParams& params, |
scoped_ptr<VideoCaptureDevice::Client> client) { |
- NOTIMPLEMENTED(); |
+ scoped_refptr<IDeckLinkIterator> decklink_iter( |
+ CreateDeckLinkIteratorInstance()); |
+ DLOG_IF(ERROR, !decklink_iter) << "Error creating DeckLink iterator"; |
+ if (!decklink_iter) |
+ return; |
+ |
+ while (decklink_iter->Next(decklink_.Receive()) == S_OK) { |
+ CFStringRef device_model_name = NULL; |
+ if ((decklink_->GetModelName(&device_model_name) == S_OK) || |
+ (device_name_.id() == base::SysCFStringRefToUTF8(device_model_name))) { |
+ break; |
+ } |
+ decklink_.Release(); |
+ } |
+ if (!decklink_) { |
+ SetErrorState("Device id not found in the system"); |
+ return; |
+ } |
+ |
+ if (decklink_->QueryInterface(IID_IDeckLinkInput, |
+ decklink_input_.ReceiveVoid()) != S_OK) { |
+ 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
|
+ return; |
+ } |
+ |
+ ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; |
+ if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) != |
+ S_OK) { |
+ SetErrorState("Error creating Display Mode Iterator"); |
+ 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.
|
+ } |
+ |
+ ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode; |
+ ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; |
+ int min_diff = INT_MAX; |
+ while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { |
+ const int diff = abs(display_mode->GetWidth() - |
+ params.requested_format.frame_size.width()) + |
+ abs(params.requested_format.frame_size.height() - |
+ display_mode->GetHeight()) + fabs(params.requested_format.frame_rate - |
+ GetDisplayModeFrameRate(display_mode)); |
+ if (diff < min_diff) { |
+ chosen_display_mode = display_mode; |
+ min_diff = diff; |
+ } |
+ display_mode.Release(); |
+ } |
+ if (!chosen_display_mode) { |
+ SetErrorState("Could not find a display mode"); |
+ return; |
+ } |
+#if !defined(NDEBUG) |
+ CFStringRef format_name = NULL; |
+ if (chosen_display_mode->GetName(&format_name) == S_OK) |
+ DVLOG(1) << "Chosen format: " << base::SysCFStringRefToUTF8(format_name); |
+#endif |
+ |
+ // Enable video input. Configure for no input video format change detection, |
+ // this in turn will disable calls to VideoInputFormatChanged(). |
+ if (decklink_input_->EnableVideoInput(chosen_display_mode->GetDisplayMode(), |
+ bmdFormat8BitYUV, bmdVideoInputFlagDefault) != S_OK) { |
+ SetErrorState("Could not select the video format we like."); |
+ return; |
+ } |
+ |
+ client_ = client.Pass(); |
+ decklink_input_->SetCallback(this); |
+ if (decklink_input_->StartStreams() != S_OK) { |
+ SetErrorState("Could not start capturing"); |
+ 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.
|
+ } |
} |
void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { |
- NOTIMPLEMENTED(); |
+ 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.
|
+ LogMessage("Problem stopping capture."); |
+ decklink_input_->SetCallback(NULL); |
+ decklink_input_->DisableVideoInput(); |
+} |
+ |
+HRESULT VideoCaptureDeviceDeckLinkMac::VideoInputFormatChanged ( |
+ BMDVideoInputFormatChangedEvents notification_events, |
+ IDeckLinkDisplayMode *new_display_mode, |
+ BMDDetectedVideoInputFormatFlags detected_signal_flags) { |
+ DVLOG(1) << __FUNCTION__; |
tommi (sloooow) - chröme
2014/09/05 09:24:21
thread check?
mcasas
2014/09/09 11:05:00
Done.
|
+ return S_OK; |
+} |
+ |
+HRESULT VideoCaptureDeviceDeckLinkMac::VideoInputFrameArrived ( |
+ IDeckLinkVideoInputFrame* video_frame, |
+ IDeckLinkAudioInputPacket* /* audio_packet */) { |
+ // 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.
|
+ void *videoData; |
tommi (sloooow) - chröme
2014/09/05 09:24:21
void* video_data = NULL;
mcasas
2014/09/09 11:05:01
Done.
|
+ video_frame->GetBytes(&videoData); |
+ |
+ VideoPixelFormat pixel_format = PIXEL_FORMAT_UNKNOWN; |
+ switch (video_frame->GetPixelFormat()) { |
+ case bmdFormat8BitYUV: // A.k.a. '2vuy'; |
+ pixel_format = PIXEL_FORMAT_UYVY; |
+ break; |
+ case bmdFormat8BitARGB: |
+ pixel_format = PIXEL_FORMAT_ARGB; |
+ break; |
+ default: |
+ 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.
|
+ } |
+ |
+ 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
|
+ gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()), |
+ 0.0f, // Frame rate is not needed for captured data callback. |
+ pixel_format); |
+ client_->OnIncomingCapturedData( |
+ static_cast<uint8*>(videoData), |
+ video_frame->GetRowBytes() * video_frame->GetHeight(), |
+ captureFormat, |
+ 0, // Rotation. |
+ base::TimeTicks::Now()); |
+ |
+ return S_OK; |
+} |
+ |
+HRESULT VideoCaptureDeviceDeckLinkMac::QueryInterface(REFIID iid, LPVOID *ppv) { |
+ HRESULT result = E_NOINTERFACE; |
+ *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.
|
+ // 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.
|
+ CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID); |
+ if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0) { |
+ *ppv = this; |
+ AddRef(); |
+ result = S_OK; |
+ } else if (memcmp(&iid, &IID_IDeckLinkNotificationCallback, sizeof(REFIID)) |
+ == 0) { |
+ *ppv = (IDeckLinkNotificationCallback*)this; |
tommi (sloooow) - chröme
2014/09/05 09:24:21
static_cast
mcasas
2014/09/09 11:05:00
Acknowledged.
|
+ AddRef(); |
+ result = S_OK; |
+ } |
+ 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.
|
+} |
+ |
+ULONG VideoCaptureDeviceDeckLinkMac::AddRef(void) { |
+ 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
|
+} |
+ |
+ULONG VideoCaptureDeviceDeckLinkMac::Release(void) { |
+ int32_t newRefValue = OSAtomicDecrement32(&ref_count_); |
+ if (newRefValue == 0) { |
+ delete this; |
+ 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.
|
+ } |
+ return newRefValue; |
+} |
+ |
+ |
+void VideoCaptureDeviceDeckLinkMac::SetErrorState(const std::string& reason) { |
+ if (client_) |
+ client_->OnError(reason); |
+} |
+ |
+void VideoCaptureDeviceDeckLinkMac::LogMessage(const std::string& message) { |
+ if (client_) |
+ client_->OnLog(message); |
} |
} // namespace media |