Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(690)

Unified Diff: media/video/capture/mac/video_capture_device_decklink_mac.mm

Issue 535983002: Mac Video Capture: Support for Blackmagic DeckLink SDK video capture. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@crbug408493__1__Enumerate_blackmagic_devices__2__branched_from_master
Patch Set: Rebased against master Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698