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 57c2c020ef0c0646240b0a3009ddf22de58b31a9..c5dd6470711c9510e4f7e236ea64c6401b9b21c8 100644 |
--- a/media/video/capture/mac/video_capture_device_decklink_mac.mm |
+++ b/media/video/capture/mac/video_capture_device_decklink_mac.mm |
@@ -6,6 +6,7 @@ |
#include "base/logging.h" |
#include "base/memory/ref_counted.h" |
+#include "base/synchronization/lock.h" |
#include "base/strings/sys_string_conversions.h" |
#include "third_party/decklink/mac/include/DeckLinkAPI.h" |
@@ -36,11 +37,266 @@ class ScopedDeckLinkPtr : public scoped_refptr<T> { |
} |
}; |
+// This class is used to interact directly with DeckLink SDK for video capture. |
+// Implements the reference counted interface IUnknown. Has a weak reference to |
+// VideoCaptureDeviceDeckLinkMac for sending captured frames, error messages and |
+// logs. |
+class DeckLinkCaptureDelegate : private IDeckLinkInputCallback { |
+ public: |
+ DeckLinkCaptureDelegate(const media::VideoCaptureDevice::Name& device_name, |
+ media::VideoCaptureDeviceDeckLinkMac* frame_receiver); |
+ virtual ~DeckLinkCaptureDelegate(); |
+ |
+ // IDeckLinkInputCallback interface implementation. |
+ virtual HRESULT VideoInputFormatChanged ( |
tommi (sloooow) - chröme
2014/09/22 20:26:38
make all of these private?
mcasas
2014/09/23 08:54:54
Made IDeckLinkInputCallback interface methods priv
tommi (sloooow) - chröme
2014/09/23 12:59:53
ah, makes sense.
(ultra nit could we add scoped_r
mcasas
2014/09/23 15:03:46
Done.
|
+ BMDVideoInputFormatChangedEvents notification_events, |
+ IDeckLinkDisplayMode *new_display_mode, |
+ BMDDetectedVideoInputFormatFlags detected_signal_flags) OVERRIDE; |
+ virtual HRESULT VideoInputFrameArrived ( |
+ IDeckLinkVideoInputFrame* video_frame, |
+ IDeckLinkAudioInputPacket* audio_packet) OVERRIDE; |
+ |
+ // IUnknown interface implementation. |
+ virtual HRESULT QueryInterface (REFIID iid, LPVOID *ppv) OVERRIDE; |
+ virtual ULONG AddRef() OVERRIDE; |
+ virtual ULONG Release() OVERRIDE; |
+ |
+ void AllocateAndStart(const media::VideoCaptureParams& params); |
+ void StopAndDeAllocate(); |
+ |
+ // Remove the VideoCaptureDeviceDeckLinkMac's weak reference. |
+ void ResetVideoCaptureDeviceReference(); |
+ |
+ private: |
+ // Forwarder to VideoCaptureDeviceDeckLinkMac::sendErrorString(). |
+ void sendErrorString(const std::string& reason); |
tommi (sloooow) - chröme
2014/09/22 20:26:38
why the lower case 's'?
mcasas
2014/09/23 08:54:54
My bad, that would be Obj-C.
|
+ |
+ // Forwarder to VideoCaptureDeviceDeckLinkMac::sendLogString(). |
+ void sendLogString(const std::string& message); |
+ |
+ const media::VideoCaptureDevice::Name device_name_; |
+ |
+ // Protects concurrent setting and using of |frame_receiver_|. |
+ base::Lock lock_; |
+ media::VideoCaptureDeviceDeckLinkMac* frame_receiver_; // Weak. |
tommi (sloooow) - chröme
2014/09/22 20:26:38
explain lifetime?
mcasas
2014/09/23 08:54:54
Done.
|
+ |
+ // This is used to control the video capturing device input interface. |
+ ScopedDeckLinkPtr<IDeckLinkInput> decklink_input_; |
+ // |decklink_| represents a physical device attached to the host. |
+ ScopedDeckLinkPtr<IDeckLink> decklink_; |
+ // Internal counter for IUnknown interface implementation. |
+ int32_t ref_count_; |
+ |
+ base::ThreadChecker thread_checker_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(DeckLinkCaptureDelegate); |
+}; |
+ |
+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; |
+ } |
+ // Interlaced formats are going to be marked as double the frame rate, |
+ // which follows the general naming convention. |
+ if (display_mode->GetFieldDominance() == bmdLowerFieldFirst || |
+ display_mode->GetFieldDominance() == bmdUpperFieldFirst) { |
+ display_mode_frame_rate *= 2.0f; |
+ } |
+ return display_mode_frame_rate; |
+} |
+ |
+DeckLinkCaptureDelegate::DeckLinkCaptureDelegate( |
+ const media::VideoCaptureDevice::Name& device_name, |
+ media::VideoCaptureDeviceDeckLinkMac* frame_receiver) |
+ : device_name_(device_name), |
+ frame_receiver_(frame_receiver) { |
+} |
+ |
+DeckLinkCaptureDelegate::~DeckLinkCaptureDelegate() {} |
+ |
+void DeckLinkCaptureDelegate::AllocateAndStart( |
+ const media::VideoCaptureParams& params) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ scoped_refptr<IDeckLinkIterator> decklink_iter( |
+ CreateDeckLinkIteratorInstance()); |
+ DLOG_IF(ERROR, !decklink_iter.get()) << "Error creating DeckLink iterator"; |
+ if (!decklink_iter.get()) |
+ 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_.get()) { |
+ sendErrorString("Device id not found in the system"); |
+ return; |
+ } |
+ |
+ if (decklink_->QueryInterface(IID_IDeckLinkInput, |
+ decklink_input_.ReceiveVoid()) != S_OK) { |
tommi (sloooow) - chröme
2014/09/22 20:26:38
indent
mcasas
2014/09/23 08:54:54
Done.
|
+ sendErrorString("Error querying input interface."); |
+ return; |
+ } |
+ |
+ ScopedDeckLinkPtr<IDeckLinkDisplayModeIterator> display_mode_iter; |
+ if (decklink_input_->GetDisplayModeIterator(display_mode_iter.Receive()) != |
+ S_OK) { |
+ sendErrorString("Error creating Display Mode Iterator"); |
+ return; |
+ } |
+ |
+ ScopedDeckLinkPtr<IDeckLinkDisplayMode> chosen_display_mode; |
+ ScopedDeckLinkPtr<IDeckLinkDisplayMode> display_mode; |
+ float min_diff = FLT_MAX; |
+ while (display_mode_iter->Next(display_mode.Receive()) == S_OK) { |
+ const float 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.get()) { |
+ sendErrorString("Could not find a display mode"); |
+ return; |
+ } |
+#if !defined(NDEBUG) |
+ DVLOG(1) << "Requested format: " << params.requested_format.ToString(); |
+ 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) { |
tommi (sloooow) - chröme
2014/09/22 20:26:38
indent
mcasas
2014/09/23 08:54:54
Done.
|
+ sendErrorString("Could not select the video format we like."); |
+ return; |
+ } |
+ |
+ decklink_input_->SetCallback(this); |
+ if (decklink_input_->StartStreams() != S_OK) |
+ sendErrorString("Could not start capturing"); |
+} |
+ |
+void DeckLinkCaptureDelegate::StopAndDeAllocate() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ if (!decklink_input_.get()) { |
tommi (sloooow) - chröme
2014/09/22 20:26:38
no {} (see next 'if')
mcasas
2014/09/23 08:54:54
Done.
|
+ return; |
+ } |
+ if (decklink_input_->StopStreams() != S_OK) |
+ sendLogString("Problem stopping capture."); |
+ decklink_input_->SetCallback(NULL); |
+ decklink_input_->DisableVideoInput(); |
+ ResetVideoCaptureDeviceReference(); |
+} |
+ |
+HRESULT DeckLinkCaptureDelegate::VideoInputFormatChanged ( |
+ BMDVideoInputFormatChangedEvents notification_events, |
+ IDeckLinkDisplayMode *new_display_mode, |
+ BMDDetectedVideoInputFormatFlags detected_signal_flags) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ return S_OK; |
+} |
+ |
+HRESULT DeckLinkCaptureDelegate::VideoInputFrameArrived ( |
+ IDeckLinkVideoInputFrame* video_frame, |
+ IDeckLinkAudioInputPacket* /* audio_packet */) { |
+ // Capture frames are manipulated as an IDeckLinkVideoFrame. |
+ void *video_data = NULL; |
tommi (sloooow) - chröme
2014/09/22 20:26:38
void* video_data = NULL;
should this perhaps be u
mcasas
2014/09/23 08:54:54
Done.
|
+ video_frame->GetBytes(&video_data); |
+ |
+ media::VideoPixelFormat pixel_format = media::PIXEL_FORMAT_UNKNOWN; |
+ switch (video_frame->GetPixelFormat()) { |
+ case bmdFormat8BitYUV: // A.k.a. '2vuy'; |
+ pixel_format = media::PIXEL_FORMAT_UYVY; |
+ break; |
+ case bmdFormat8BitARGB: |
+ pixel_format = media::PIXEL_FORMAT_ARGB; |
+ break; |
+ default: |
+ sendErrorString("Unsupported pixel format"); |
+ break; |
+ } |
+ |
+ const media::VideoCaptureFormat capture_format( |
+ gfx::Size(video_frame->GetWidth(), video_frame->GetHeight()), |
+ 0.0f, // Frame rate is not needed for captured data callback. |
+ pixel_format); |
+ base::AutoLock lock(lock_); |
+ if (frame_receiver_) { |
+ frame_receiver_->OnIncomingCapturedData( |
+ static_cast<uint8*>(video_data), |
+ video_frame->GetRowBytes() * video_frame->GetHeight(), |
+ capture_format, |
+ 0, // Rotation. |
+ base::TimeTicks::Now()); |
+ } |
+ return S_OK; |
+} |
+ |
+HRESULT DeckLinkCaptureDelegate::QueryInterface(REFIID iid, LPVOID *ppv) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ CFUUIDBytes iunknown = CFUUIDGetUUIDBytes(IUnknownUUID); |
+ if (memcmp(&iid, &iunknown, sizeof(REFIID)) == 0 || |
+ memcmp(&iid, &IID_IDeckLinkNotificationCallback, sizeof(REFIID)) == 0) { |
+ *ppv = reinterpret_cast<IDeckLinkNotificationCallback*>(this); |
tommi (sloooow) - chröme
2014/09/22 20:26:38
static_cast
mcasas
2014/09/23 08:54:54
According to clang,
static_cast from 'DeckLinkCap
tommi (sloooow) - chröme
2014/09/23 12:59:53
yeah, that's because the inheritance was set to 'p
mcasas
2014/09/23 15:03:46
Actually, thanks for insisting here. The code is w
tommi (sloooow) - chröme
2014/09/23 17:15:28
Actually, we still need the cast in there to be sa
|
+ AddRef(); |
+ return S_OK; |
+ } |
+ return E_NOINTERFACE; |
+} |
+ |
+ULONG DeckLinkCaptureDelegate::AddRef(void) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ return OSAtomicIncrement32(&ref_count_); |
+} |
+ |
+ULONG DeckLinkCaptureDelegate::Release(void) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ int32_t newRefValue = OSAtomicDecrement32(&ref_count_); |
+ if (newRefValue == 0) |
+ delete this; |
+ return newRefValue; |
+} |
+ |
+void DeckLinkCaptureDelegate::sendErrorString(const std::string& reason) { |
+ base::AutoLock lock(lock_); |
+ if (frame_receiver_) |
+ frame_receiver_->sendErrorString(reason); |
+} |
+ |
+void DeckLinkCaptureDelegate::sendLogString(const std::string& message) { |
+ base::AutoLock lock(lock_); |
+ if (frame_receiver_) |
+ frame_receiver_->sendLogString(message); |
+} |
+ |
+void DeckLinkCaptureDelegate::ResetVideoCaptureDeviceReference() { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ base::AutoLock lock(lock_); |
+ frame_receiver_ = NULL; |
+} |
+ |
} // namespace |
namespace media { |
-std::string JoinDeviceNameAndFormat(CFStringRef name, CFStringRef format) { |
+static std::string JoinDeviceNameAndFormat(CFStringRef name, |
+ CFStringRef format) { |
return base::SysCFStringRefToUTF8(name) + " - " + |
base::SysCFStringRefToUTF8(format); |
} |
@@ -51,7 +307,7 @@ void VideoCaptureDeviceDeckLinkMac::EnumerateDevices( |
scoped_refptr<IDeckLinkIterator> decklink_iter( |
CreateDeckLinkIteratorInstance()); |
// At this point, not being able to create a DeckLink iterator means that |
- // there are no Blackmagic devices in the system but this isn't an error. |
+ // there are no Blackmagic DeckLink devices in the system, don't print error. |
DVLOG_IF(1, !decklink_iter.get()) << "Could not create DeckLink iterator"; |
if (!decklink_iter.get()) |
return; |
@@ -120,7 +376,7 @@ void VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities( |
ScopedDeckLinkPtr<IDeckLinkInput> decklink_input; |
if (decklink_local->QueryInterface(IID_IDeckLinkInput, |
- decklink_input.ReceiveVoid()) != S_OK) { |
+ decklink_input.ReceiveVoid()) != S_OK) { |
DLOG(ERROR) << "Error Blackmagic querying input interface."; |
return; |
} |
@@ -146,17 +402,10 @@ void VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities( |
// IDeckLinkDisplayMode does not have information on pixel format, this |
// 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() << " " << format.ToString(); |
display_mode.Release(); |
@@ -166,18 +415,54 @@ void VideoCaptureDeviceDeckLinkMac::EnumerateDeviceCapabilities( |
} |
VideoCaptureDeviceDeckLinkMac::VideoCaptureDeviceDeckLinkMac( |
- const Name& device_name) {} |
+ const Name& device_name) |
+ : decklink_capture_delegate_( |
+ new DeckLinkCaptureDelegate(device_name, this)) { |
+} |
+ |
+VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() { |
+ decklink_capture_delegate_->ResetVideoCaptureDeviceReference(); |
+} |
+ |
+void VideoCaptureDeviceDeckLinkMac::OnIncomingCapturedData( |
+ const uint8* data, |
+ int length, |
+ const VideoCaptureFormat& frame_format, |
+ int rotation, // Clockwise. |
+ base::TimeTicks timestamp) { |
+ base::AutoLock lock(lock_); |
+ if (client_) { |
+ client_->OnIncomingCapturedData(data, length, frame_format, rotation, |
+ timestamp); |
+ } |
+} |
-VideoCaptureDeviceDeckLinkMac::~VideoCaptureDeviceDeckLinkMac() {} |
+void VideoCaptureDeviceDeckLinkMac::sendErrorString(const std::string& reason) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ base::AutoLock lock(lock_); |
+ if (client_) |
+ client_->OnError(reason); |
+} |
+ |
+void VideoCaptureDeviceDeckLinkMac::sendLogString(const std::string& message) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ base::AutoLock lock(lock_); |
+ if (client_) |
+ client_->OnLog(message); |
+} |
void VideoCaptureDeviceDeckLinkMac::AllocateAndStart( |
- const VideoCaptureParams& params, |
- scoped_ptr<VideoCaptureDevice::Client> client) { |
- NOTIMPLEMENTED(); |
+ const VideoCaptureParams& params, |
+ scoped_ptr<VideoCaptureDevice::Client> client) { |
+ DCHECK(thread_checker_.CalledOnValidThread()); |
+ client_ = client.Pass(); |
+ if (decklink_capture_delegate_.get()) |
+ decklink_capture_delegate_->AllocateAndStart(params); |
} |
void VideoCaptureDeviceDeckLinkMac::StopAndDeAllocate() { |
- NOTIMPLEMENTED(); |
+ if (decklink_capture_delegate_.get()) |
+ decklink_capture_delegate_->StopAndDeAllocate(); |
} |
} // namespace media |