diff --git a/plugins/decklink/DecklinkOutput.cpp b/plugins/decklink/DecklinkOutput.cpp
index d9b430473..8398fd67f 100644
--- a/plugins/decklink/DecklinkOutput.cpp
+++ b/plugins/decklink/DecklinkOutput.cpp
@@ -82,9 +82,9 @@ obs_output_t *DeckLinkOutput::GetOutput(void) const
return output;
}
-void DeckLinkOutput::DisplayVideoFrame(video_data *frame)
+void DeckLinkOutput::UpdateVideoFrame(video_data *frame)
{
- instance->DisplayVideoFrame(frame);
+ instance->UpdateVideoFrame(frame);
}
void DeckLinkOutput::WriteAudio(audio_data *frames)
diff --git a/plugins/decklink/DecklinkOutput.hpp b/plugins/decklink/DecklinkOutput.hpp
index 3b0cfc536..efc50be14 100644
--- a/plugins/decklink/DecklinkOutput.hpp
+++ b/plugins/decklink/DecklinkOutput.hpp
@@ -28,7 +28,7 @@ public:
obs_output_t *GetOutput(void) const;
bool Activate(DeckLinkDevice *device, long long modeId) override;
void Deactivate() override;
- void DisplayVideoFrame(video_data *pData);
+ void UpdateVideoFrame(video_data *pData);
void WriteAudio(audio_data *frames);
void SetSize(int width, int height);
int GetWidth();
diff --git a/plugins/decklink/decklink-device-instance.cpp b/plugins/decklink/decklink-device-instance.cpp
index c0b33ff0e..71a8ec10c 100644
--- a/plugins/decklink/decklink-device-instance.cpp
+++ b/plugins/decklink/decklink-device-instance.cpp
@@ -17,6 +17,50 @@
#include
#include
+template RenderDelegate::RenderDelegate(T *pOwner)
+{
+ m_pOwner = pOwner;
+}
+
+template RenderDelegate::~RenderDelegate() {}
+
+template
+HRESULT RenderDelegate::QueryInterface(REFIID, LPVOID *ppv)
+{
+ *ppv = NULL;
+ return E_NOINTERFACE;
+}
+
+template ULONG RenderDelegate::AddRef()
+{
+ return ++m_refCount;
+}
+
+template ULONG RenderDelegate::Release()
+{
+ const ULONG newRefValue = --m_refCount;
+ if (newRefValue == 0) {
+ delete this;
+ return 0;
+ }
+
+ return newRefValue;
+}
+
+template
+HRESULT
+RenderDelegate::ScheduledFrameCompleted(IDeckLinkVideoFrame *completedFrame,
+ BMDOutputFrameCompletionResult)
+{
+ m_pOwner->ScheduleVideoFrame(completedFrame);
+ return S_OK;
+}
+
+template HRESULT RenderDelegate::ScheduledPlaybackHasStopped()
+{
+ return S_OK;
+}
+
static inline enum video_format ConvertPixelFormat(BMDPixelFormat format)
{
switch (format) {
@@ -528,19 +572,24 @@ bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_)
if (mode_ == nullptr)
return false;
- LOG(LOG_INFO, "Starting output...");
-
- if (!device->GetOutput(&output))
+ auto decklinkOutput = dynamic_cast(decklink);
+ if (decklinkOutput == nullptr)
return false;
- const HRESULT videoResult = output->EnableVideoOutput(
+ LOG(LOG_INFO, "Starting output...");
+
+ ComPtr output_;
+ if (!device->GetOutput(&output_))
+ return false;
+
+ const HRESULT videoResult = output_->EnableVideoOutput(
mode_->GetDisplayMode(), bmdVideoOutputFlagDefault);
if (videoResult != S_OK) {
LOG(LOG_ERROR, "Failed to enable video output");
return false;
}
- const HRESULT audioResult = output->EnableAudioOutput(
+ const HRESULT audioResult = output_->EnableAudioOutput(
bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger, 2,
bmdAudioOutputStreamTimestamped);
if (audioResult != S_OK) {
@@ -548,7 +597,10 @@ bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_)
return false;
}
- mode = mode_;
+ if (!mode_->GetFrameRate(&frameDuration, &frameTimescale)) {
+ LOG(LOG_ERROR, "Failed to get frame rate");
+ return false;
+ }
ComPtr deckLinkKeyer;
if (device->GetKeyer(&deckLinkKeyer)) {
@@ -561,19 +613,37 @@ bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_)
}
}
- auto decklinkOutput = dynamic_cast(decklink);
- if (decklinkOutput == nullptr)
- return false;
+ frameData.clear();
+ size_t i = 0;
+ for (; i < 3; ++i) {
+ ComPtr decklinkOutputFrame;
+ HRESULT result = output_->CreateVideoFrame(
+ decklinkOutput->GetWidth(), decklinkOutput->GetHeight(),
+ decklinkOutput->GetWidth() * 4, bmdFormat8BitBGRA,
+ bmdFrameFlagDefault, &decklinkOutputFrame);
+ if (result != S_OK) {
+ blog(LOG_ERROR, "failed to create video frame 0x%X",
+ result);
+ return false;
+ }
- HRESULT result;
- result = output->CreateVideoFrame(
- decklinkOutput->GetWidth(), decklinkOutput->GetHeight(),
- decklinkOutput->GetWidth() * 4, bmdFormat8BitBGRA,
- bmdFrameFlagDefault, &decklinkOutputFrame);
- if (result != S_OK) {
- blog(LOG_ERROR, "failed to make frame 0x%X", result);
- return false;
+ const long size = decklinkOutputFrame->GetRowBytes() *
+ decklinkOutputFrame->GetHeight();
+ frameData.resize(size);
+ output_->ScheduleVideoFrame(decklinkOutputFrame,
+ (i * frameDuration), frameDuration,
+ frameTimescale);
}
+ totalFramesScheduled = i;
+
+ *renderDelegate.Assign() =
+ new RenderDelegate(this);
+ output_->SetScheduledFrameCompletionCallback(renderDelegate);
+
+ output_->StartScheduledPlayback(0, 100, 1.0);
+
+ mode = mode_;
+ output = std::move(output_);
return true;
}
@@ -586,31 +656,43 @@ bool DeckLinkDeviceInstance::StopOutput()
LOG(LOG_INFO, "Stopping output of '%s'...",
GetDevice()->GetDisplayName().c_str());
+ output->SetScheduledFrameCompletionCallback(NULL);
output->DisableVideoOutput();
output->DisableAudioOutput();
-
- decklinkOutputFrame.Clear();
+ output.Clear();
+ renderDelegate.Clear();
return true;
}
-void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame)
+void DeckLinkDeviceInstance::UpdateVideoFrame(video_data *frame)
{
auto decklinkOutput = dynamic_cast(decklink);
if (decklinkOutput == nullptr)
return;
- uint8_t *destData;
- decklinkOutputFrame->GetBytes((void **)&destData);
+ std::lock_guard lock(frameDataMutex);
+ const uint8_t *const outData = frame->data[0];
+ frameData.assign(outData,
+ outData + decklinkOutput->GetWidth() *
+ decklinkOutput->GetHeight() * 4);
+}
- uint8_t *outData = frame->data[0];
+void DeckLinkDeviceInstance::ScheduleVideoFrame(IDeckLinkVideoFrame *frame)
+{
+ void *bytes;
+ if (SUCCEEDED(frame->GetBytes(&bytes))) {
+ {
+ std::lock_guard lock(frameDataMutex);
+ memcpy(bytes, frameData.data(),
+ frame->GetRowBytes() * frame->GetHeight());
+ }
- std::copy(outData,
- outData + (decklinkOutput->GetWidth() *
- decklinkOutput->GetHeight() * 4),
- destData);
-
- output->DisplayVideoFrameSync(decklinkOutputFrame);
+ output->ScheduleVideoFrame(
+ frame, (totalFramesScheduled * frameDuration),
+ frameDuration, frameTimescale);
+ ++totalFramesScheduled;
+ }
}
void DeckLinkDeviceInstance::WriteAudio(audio_data *frames)
diff --git a/plugins/decklink/decklink-device-instance.hpp b/plugins/decklink/decklink-device-instance.hpp
index 4fed306a0..aaad97972 100644
--- a/plugins/decklink/decklink-device-instance.hpp
+++ b/plugins/decklink/decklink-device-instance.hpp
@@ -7,10 +7,37 @@
#include
#include "decklink-device.hpp"
#include "OBSVideoFrame.h"
+#include
+#include
+#include
class AudioRepacker;
class DecklinkBase;
+template
+class RenderDelegate : public IDeckLinkVideoOutputCallback {
+private:
+ std::atomic m_refCount = 1;
+ T *m_pOwner;
+
+ ~RenderDelegate();
+
+public:
+ RenderDelegate(T *pOwner);
+
+ // IUnknown
+ virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid,
+ LPVOID *ppv);
+ virtual ULONG STDMETHODCALLTYPE AddRef();
+ virtual ULONG STDMETHODCALLTYPE Release();
+
+ // IDeckLinkVideoOutputCallback
+ virtual HRESULT STDMETHODCALLTYPE
+ ScheduledFrameCompleted(IDeckLinkVideoFrame *completedFrame,
+ BMDOutputFrameCompletionResult result);
+ virtual HRESULT STDMETHODCALLTYPE ScheduledPlaybackHasStopped();
+};
+
class DeckLinkDeviceInstance : public IDeckLinkInputCallback {
protected:
ComPtr deckLinkConfiguration;
@@ -39,7 +66,12 @@ protected:
bool allow10Bit;
OBSVideoFrame *convertFrame = nullptr;
- ComPtr decklinkOutputFrame;
+ std::mutex frameDataMutex;
+ std::vector frameData;
+ BMDTimeValue frameDuration;
+ BMDTimeScale frameTimescale;
+ size_t totalFramesScheduled;
+ ComPtr> renderDelegate;
void FinalizeStream();
void SetupVideoFormat(DeckLinkDeviceMode *mode_);
@@ -107,7 +139,8 @@ public:
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv);
ULONG STDMETHODCALLTYPE Release(void);
- void DisplayVideoFrame(video_data *frame);
+ void UpdateVideoFrame(video_data *frame);
+ void ScheduleVideoFrame(IDeckLinkVideoFrame *frame);
void WriteAudio(audio_data *frames);
void HandleCaptionPacket(IDeckLinkAncillaryPacket *packet,
const uint64_t timestamp);
diff --git a/plugins/decklink/decklink-device-mode.cpp b/plugins/decklink/decklink-device-mode.cpp
index 4f5882e68..435b57b74 100644
--- a/plugins/decklink/decklink-device-mode.cpp
+++ b/plugins/decklink/decklink-device-mode.cpp
@@ -42,6 +42,15 @@ int DeckLinkDeviceMode::GetHeight()
return 0;
}
+bool DeckLinkDeviceMode::GetFrameRate(BMDTimeValue *frameDuration,
+ BMDTimeScale *timeScale)
+{
+ if (mode != nullptr)
+ return SUCCEEDED(mode->GetFrameRate(frameDuration, timeScale));
+
+ return false;
+}
+
BMDDisplayModeFlags DeckLinkDeviceMode::GetDisplayModeFlags(void) const
{
if (mode != nullptr)
diff --git a/plugins/decklink/decklink-device-mode.hpp b/plugins/decklink/decklink-device-mode.hpp
index e5780577f..0bb568e2c 100644
--- a/plugins/decklink/decklink-device-mode.hpp
+++ b/plugins/decklink/decklink-device-mode.hpp
@@ -27,4 +27,5 @@ public:
int GetWidth();
int GetHeight();
+ bool GetFrameRate(BMDTimeValue *frameDuration, BMDTimeScale *timeScale);
};
diff --git a/plugins/decklink/decklink-output.cpp b/plugins/decklink/decklink-output.cpp
index 2526b902a..f78b42707 100644
--- a/plugins/decklink/decklink-output.cpp
+++ b/plugins/decklink/decklink-output.cpp
@@ -130,7 +130,7 @@ static void decklink_output_raw_video(void *data, struct video_data *frame)
if (!decklink->start_timestamp)
decklink->start_timestamp = frame->timestamp;
- decklink->DisplayVideoFrame(frame);
+ decklink->UpdateVideoFrame(frame);
}
static bool prepare_audio(DeckLinkOutput *decklink,