diff --git a/plugins/decklink/DecklinkBase.cpp b/plugins/decklink/DecklinkBase.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e916a243c2a6309e44f20ba3fdec996c151c9465 --- /dev/null +++ b/plugins/decklink/DecklinkBase.cpp @@ -0,0 +1,20 @@ +#include "DecklinkBase.h" + +DecklinkBase::DecklinkBase(DeckLinkDeviceDiscovery *discovery_) + : discovery(discovery_) +{ +} + +DeckLinkDevice *DecklinkBase::GetDevice() const +{ + return instance ? instance->GetDevice() : nullptr; +} + +bool DecklinkBase::Activate(DeckLinkDevice*, long long) +{ + return false; +} + +void DecklinkBase::Deactivate() +{ +} diff --git a/plugins/decklink/DecklinkBase.h b/plugins/decklink/DecklinkBase.h new file mode 100644 index 0000000000000000000000000000000000000000..a337092e9a3e83905077f2cc452ef62392dfb621 --- /dev/null +++ b/plugins/decklink/DecklinkBase.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include +#include + +#include + +#include "platform.hpp" + +#include "decklink-device-discovery.hpp" +#include "decklink-device-instance.hpp" +#include "decklink-device-mode.hpp" + +class DecklinkBase { + +protected: + DecklinkBase(DeckLinkDeviceDiscovery *discovery_); + ComPtr instance; + DeckLinkDeviceDiscovery *discovery; + std::recursive_mutex deviceMutex; + volatile long activateRefs = 0; + BMDPixelFormat pixelFormat = bmdFormat8BitYUV; + video_colorspace colorSpace = VIDEO_CS_DEFAULT; + video_range_type colorRange = VIDEO_RANGE_DEFAULT; + speaker_layout channelFormat = SPEAKERS_STEREO; + +public: + virtual bool Activate(DeckLinkDevice *device, long long modeId); + virtual void Deactivate(); + + DeckLinkDevice *GetDevice() const; +}; diff --git a/plugins/decklink/decklink.cpp b/plugins/decklink/DecklinkInput.cpp similarity index 73% rename from plugins/decklink/decklink.cpp rename to plugins/decklink/DecklinkInput.cpp index eeac2b65bbefcdc3681f9a8acbc718ab53e2af6b..58687a5c6656db1558c0e62b3bf8a7afc6b26af3 100644 --- a/plugins/decklink/decklink.cpp +++ b/plugins/decklink/DecklinkInput.cpp @@ -1,30 +1,23 @@ -#include "decklink.hpp" -#include "decklink-device-discovery.hpp" -#include "decklink-device-instance.hpp" -#include "decklink-device-mode.hpp" +#include "DecklinkInput.hpp" #include -DeckLink::DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_) : - discovery(discovery_), source(source) +DeckLinkInput::DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_) + : DecklinkBase(discovery_), + source(source) { - discovery->AddCallback(DeckLink::DevicesChanged, this); + discovery->AddCallback(DeckLinkInput::DevicesChanged, this); } -DeckLink::~DeckLink(void) +DeckLinkInput::~DeckLinkInput(void) { - discovery->RemoveCallback(DeckLink::DevicesChanged, this); + discovery->RemoveCallback(DeckLinkInput::DevicesChanged, this); Deactivate(); } -DeckLinkDevice *DeckLink::GetDevice() const +void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device, bool added) { - return instance ? instance->GetDevice() : nullptr; -} - -void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added) -{ - DeckLink *decklink = reinterpret_cast(param); + DeckLinkInput *decklink = reinterpret_cast(param); std::lock_guard lock(decklink->deviceMutex); obs_source_update_properties(decklink->source); @@ -54,7 +47,7 @@ void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added) } } -bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) +bool DeckLinkInput::Activate(DeckLinkDevice *device, long long modeId) { std::lock_guard lock(deviceMutex); DeckLinkDevice *curDevice = GetDevice(); @@ -82,7 +75,12 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) if (instance == nullptr) return false; - DeckLinkDeviceMode *mode = GetDevice()->FindMode(modeId); + if (GetDevice() == nullptr) { + LOG(LOG_ERROR, "Tried to activate an input with nullptr device."); + return false; + } + + DeckLinkDeviceMode *mode = GetDevice()->FindInputMode(modeId); if (mode == nullptr) { instance = nullptr; return false; @@ -100,7 +98,7 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId) return true; } -void DeckLink::Deactivate(void) +void DeckLinkInput::Deactivate(void) { std::lock_guard lock(deviceMutex); if (instance) @@ -111,12 +109,12 @@ void DeckLink::Deactivate(void) os_atomic_dec_long(&activateRefs); } -bool DeckLink::Capturing(void) +bool DeckLinkInput::Capturing(void) { return isCapturing; } -void DeckLink::SaveSettings() +void DeckLinkInput::SaveSettings() { if (!instance) return; @@ -136,7 +134,7 @@ void DeckLink::SaveSettings() obs_data_release(settings); } -obs_source_t *DeckLink::GetSource(void) const +obs_source_t *DeckLinkInput::GetSource(void) const { return source; } diff --git a/plugins/decklink/decklink.hpp b/plugins/decklink/DecklinkInput.hpp similarity index 56% rename from plugins/decklink/decklink.hpp rename to plugins/decklink/DecklinkInput.hpp index 4227144e56a9f1b8e0a9bd208b0aba8a94dd5363..4111fa9f947503ea8b6861c7a4892aa33653c6e5 100644 --- a/plugins/decklink/decklink.hpp +++ b/plugins/decklink/DecklinkInput.hpp @@ -1,40 +1,19 @@ #pragma once -#include "platform.hpp" +#include "DecklinkBase.h" -#include - -#include -#include -#include - -class DeckLinkDeviceDiscovery; -class DeckLinkDeviceInstance; -class DeckLinkDevice; -class DeckLinkDeviceMode; - -class DeckLink { +class DeckLinkInput : public DecklinkBase { protected: - ComPtr instance; - DeckLinkDeviceDiscovery *discovery; bool isCapturing = false; obs_source_t *source; - volatile long activateRefs = 0; - std::recursive_mutex deviceMutex; - BMDPixelFormat pixelFormat = bmdFormat8BitYUV; - video_colorspace colorSpace = VIDEO_CS_DEFAULT; - video_range_type colorRange = VIDEO_RANGE_DEFAULT; - speaker_layout channelFormat = SPEAKERS_STEREO; void SaveSettings(); static void DevicesChanged(void *param, DeckLinkDevice *device, bool added); public: - DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery); - virtual ~DeckLink(void); - - DeckLinkDevice *GetDevice() const; + DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery); + virtual ~DeckLinkInput(void); long long GetActiveModeId(void) const; obs_source_t *GetSource(void) const; diff --git a/plugins/decklink/DecklinkOutput.cpp b/plugins/decklink/DecklinkOutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..40eaf337718af9a88c13aa9465dc23fb6108375a --- /dev/null +++ b/plugins/decklink/DecklinkOutput.cpp @@ -0,0 +1,110 @@ +#include "DecklinkOutput.hpp" + +#include + +DeckLinkOutput::DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery_) + : DecklinkBase(discovery_), + output(output) +{ + discovery->AddCallback(DeckLinkOutput::DevicesChanged, this); +} + +DeckLinkOutput::~DeckLinkOutput(void) +{ + discovery->RemoveCallback(DeckLinkOutput::DevicesChanged, this); + Deactivate(); +} + +void DeckLinkOutput::DevicesChanged(void *param, DeckLinkDevice *device, bool) +{ + auto *decklink = reinterpret_cast(param); + std::lock_guard lock(decklink->deviceMutex); + + blog(LOG_DEBUG, "%s", device->GetHash().c_str()); +} + +bool DeckLinkOutput::Activate(DeckLinkDevice *device, long long modeId) +{ + std::lock_guard lock(deviceMutex); + DeckLinkDevice *curDevice = GetDevice(); + const bool same = device == curDevice; + const bool isActive = instance != nullptr; + + if (same) { + if (!isActive) + return false; + + if (instance->GetActiveModeId() == modeId && + instance->GetActivePixelFormat() == pixelFormat && + instance->GetActiveColorSpace() == colorSpace && + instance->GetActiveColorRange() == colorRange && + instance->GetActiveChannelFormat() == channelFormat) + return false; + } + + if (isActive) + instance->StopOutput(); + + if (!same) + instance.Set(new DeckLinkDeviceInstance(this, device)); + + if (instance == nullptr) + return false; + + DeckLinkDeviceMode *mode = GetDevice()->FindOutputMode(modeId); + if (mode == nullptr) { + instance = nullptr; + return false; + } + + + if (!instance->StartOutput(mode)) { + instance = nullptr; + return false; + } + + os_atomic_inc_long(&activateRefs); + return true; +} + +void DeckLinkOutput::Deactivate(void) +{ + std::lock_guard lock(deviceMutex); + if (instance) + instance->StopOutput(); + + instance = nullptr; + + os_atomic_dec_long(&activateRefs); +} + +obs_output_t *DeckLinkOutput::GetOutput(void) const +{ + return output; +} + +void DeckLinkOutput::DisplayVideoFrame(video_data *frame) +{ + instance->DisplayVideoFrame(frame); +} + +void DeckLinkOutput::WriteAudio(audio_data *frames) +{ + instance->WriteAudio(frames); +} + +void DeckLinkOutput::SetSize(int width, int height) +{ + this->width = width; + this->height = height; +} + +int DeckLinkOutput::GetWidth() +{ + return width; +} + +int DeckLinkOutput::GetHeight() +{ + return height; +} diff --git a/plugins/decklink/DecklinkOutput.hpp b/plugins/decklink/DecklinkOutput.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bdd873bca18878056b1a90f0475973ea4533575e --- /dev/null +++ b/plugins/decklink/DecklinkOutput.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "DecklinkBase.h" + +#include "../../libobs/media-io/video-scaler.h" + +class DeckLinkOutput : public DecklinkBase { +protected: + obs_output_t *output; + int width; + int height; + + static void DevicesChanged(void *param, DeckLinkDevice *device, bool added); + +public: + const char *deviceHash; + long long modeID; + uint64_t start_timestamp; + uint32_t audio_samplerate; + size_t audio_planes; + size_t audio_size; + + DeckLinkOutput(obs_output_t *output, DeckLinkDeviceDiscovery *discovery); + virtual ~DeckLinkOutput(void); + obs_output_t *GetOutput(void) const; + bool Activate(DeckLinkDevice *device, long long modeId) override; + void Deactivate() override; + void DisplayVideoFrame(video_data *pData); + void WriteAudio(audio_data *frames); + void SetSize(int width, int height); + int GetWidth(); + int GetHeight(); +}; diff --git a/plugins/decklink/const.h b/plugins/decklink/const.h new file mode 100644 index 0000000000000000000000000000000000000000..166777f20f757cd07eb5f50f34b6892e05c4af7e --- /dev/null +++ b/plugins/decklink/const.h @@ -0,0 +1,32 @@ +#define DEVICE_HASH "device_hash" +#define DEVICE_NAME "device_name" +#define MODE_ID "mode_id" +#define MODE_NAME "mode_name" +#define CHANNEL_FORMAT "channel_format" +#define PIXEL_FORMAT "pixel_format" +#define COLOR_SPACE "color_space" +#define COLOR_RANGE "color_range" +#define BUFFERING "buffering" +#define DEACTIVATE_WNS "deactivate_when_not_showing" +#define AUTO_START "auto_start" + +#define TEXT_DEVICE obs_module_text("Device") +#define TEXT_MODE obs_module_text("Mode") +#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat") +#define TEXT_COLOR_SPACE obs_module_text("ColorSpace") +#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default") +#define TEXT_COLOR_RANGE obs_module_text("ColorRange") +#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default") +#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial") +#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full") +#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat") +#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None") +#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch") +#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch") +#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch") +#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch") +#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch") +#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch") +#define TEXT_BUFFERING obs_module_text("Buffering") +#define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing") +#define TEXT_AUTO_START obs_module_text("AutoStart") diff --git a/plugins/decklink/data/locale/en-US.ini b/plugins/decklink/data/locale/en-US.ini index 701a8d0174f40f370f5b812819ed09f3670ba2b4..7fc9453a4e9c2989d9b02c2c97ec79b8c2532435 100644 --- a/plugins/decklink/data/locale/en-US.ini +++ b/plugins/decklink/data/locale/en-US.ini @@ -18,3 +18,4 @@ ChannelFormat.4_1ch="4.1ch" ChannelFormat.5_1ch="5.1ch" ChannelFormat.7_1ch="7.1ch" DeactivateWhenNotShowing="Deactivate when not showing" +AutoStart="Auto start on launch" diff --git a/plugins/decklink/decklink-device-discovery.hpp b/plugins/decklink/decklink-device-discovery.hpp index 3bb6e3cd837b8bb9881157f433805d41969e12ba..9f2842cec8fe344aa6dc868c1fa72bee2f781092 100644 --- a/plugins/decklink/decklink-device-discovery.hpp +++ b/plugins/decklink/decklink-device-discovery.hpp @@ -1,10 +1,11 @@ #pragma once +#include +#include "platform.hpp" + #include #include -#include "decklink.hpp" - class DeckLinkDevice; typedef void (*DeviceChangeCallback)(void *param, DeckLinkDevice *device, diff --git a/plugins/decklink/decklink-device-instance.cpp b/plugins/decklink/decklink-device-instance.cpp index 5dc674e5eb71018dfd7c4cd01fe43e39e19c54ea..03fb70d62c376aaa7ca03bb223a9cb519ab7a5e8 100644 --- a/plugins/decklink/decklink-device-instance.cpp +++ b/plugins/decklink/decklink-device-instance.cpp @@ -1,13 +1,14 @@ #include "decklink-device-instance.hpp" #include "audio-repack.hpp" +#include "DecklinkInput.hpp" +#include "DecklinkOutput.hpp" + #include #include #include - -#define LOG(level, message, ...) blog(level, "%s: " message, \ - obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__) +#include #ifdef _WIN32 #define IS_WIN 1 @@ -62,7 +63,7 @@ static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format) } } -DeckLinkDeviceInstance::DeckLinkDeviceInstance(DeckLink *decklink_, +DeckLinkDeviceInstance::DeckLinkDeviceInstance(DecklinkBase *decklink_, DeckLinkDevice *device_) : currentFrame(), currentPacket(), decklink(decklink_), device(device_) { @@ -92,7 +93,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket( currentPacket.frames = frameCount; currentPacket.timestamp = timestamp; - if (decklink && !decklink->buffering) { + if (decklink && !static_cast(decklink)->buffering) { currentPacket.timestamp = os_gettime_ns(); currentPacket.timestamp -= (uint64_t)frameCount * 1000000000ULL / @@ -120,7 +121,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket( nextAudioTS = timestamp + ((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1; - obs_source_output_audio(decklink->GetSource(), ¤tPacket); + obs_source_output_audio(static_cast(decklink)->GetSource(), ¤tPacket); } void DeckLinkDeviceInstance::HandleVideoFrame( @@ -141,7 +142,7 @@ void DeckLinkDeviceInstance::HandleVideoFrame( currentFrame.height = (uint32_t)videoFrame->GetHeight(); currentFrame.timestamp = timestamp; - obs_source_output_video(decklink->GetSource(), ¤tFrame); + obs_source_output_video(static_cast(decklink)->GetSource(), ¤tFrame); } void DeckLinkDeviceInstance::FinalizeStream() @@ -169,7 +170,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) currentFrame.format = ConvertPixelFormat(pixelFormat); - colorSpace = decklink->GetColorSpace(); + colorSpace = static_cast(decklink)->GetColorSpace(); if (colorSpace == VIDEO_CS_DEFAULT) { const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags(); if (flags & bmdDisplayModeColorspaceRec709) @@ -182,7 +183,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) activeColorSpace = colorSpace; } - colorRange = decklink->GetColorRange(); + colorRange = static_cast(decklink)->GetColorRange(); currentFrame.full_range = colorRange == VIDEO_RANGE_FULL; video_format_get_parameters(activeColorSpace, colorRange, @@ -218,7 +219,7 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) flags = bmdVideoInputEnableFormatDetection; } else { displayMode = mode_->GetDisplayMode(); - pixelFormat = decklink->GetPixelFormat(); + pixelFormat = static_cast(decklink)->GetPixelFormat(); flags = bmdVideoInputFlagDefault; } @@ -231,7 +232,7 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_) SetupVideoFormat(mode_); - channelFormat = decklink->GetChannelFormat(); + channelFormat = static_cast(decklink)->GetChannelFormat(); currentPacket.speakers = channelFormat; int maxdevicechannel = device->GetMaxChannel(); @@ -288,6 +289,101 @@ bool DeckLinkDeviceInstance::StopCapture(void) return true; } +bool DeckLinkDeviceInstance::StartOutput(DeckLinkDeviceMode *mode_) +{ + if (mode != nullptr) + return false; + if (mode_ == nullptr) + return false; + + LOG(LOG_INFO, "Starting 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( + bmdAudioSampleRate48kHz, + bmdAudioSampleType16bitInteger, + 2, + bmdAudioOutputStreamTimestamped); + if (audioResult != S_OK) { + LOG(LOG_ERROR, "Failed to enable audio output"); + return false; + } + + mode = mode_; + + auto decklinkOutput = dynamic_cast(decklink); + if (decklinkOutput == nullptr) + return false; + + HRESULT result; + result = output->CreateVideoFrame(decklinkOutput->GetWidth(), + decklinkOutput->GetHeight(), + decklinkOutput->GetWidth() * 2, + bmdFormat8BitYUV, + bmdFrameFlagDefault, + &decklinkOutputFrame); + if (result != S_OK) { + blog(LOG_ERROR ,"failed to make frame 0x%X", result); + return false; + } + + return true; +} + +bool DeckLinkDeviceInstance::StopOutput() +{ + if (mode == nullptr || output == nullptr) + return false; + + LOG(LOG_INFO, "Stopping output of '%s'...", + GetDevice()->GetDisplayName().c_str()); + + output->DisableVideoOutput(); + output->DisableAudioOutput(); + + if (decklinkOutputFrame != nullptr) { + decklinkOutputFrame->Release(); + decklinkOutputFrame = nullptr; + } + + return true; +} + +void DeckLinkDeviceInstance::DisplayVideoFrame(video_data *frame) +{ + auto decklinkOutput = dynamic_cast(decklink); + if (decklinkOutput == nullptr) + return; + + uint8_t *destData; + decklinkOutputFrame->GetBytes((void**)&destData); + + uint8_t *outData = frame->data[0]; + + std::copy(outData, outData + (decklinkOutput->GetWidth() * + decklinkOutput->GetHeight() * 2), destData); + + output->DisplayVideoFrameSync(decklinkOutputFrame); +} + +void DeckLinkDeviceInstance::WriteAudio(audio_data *frames) +{ + uint32_t sampleFramesWritten; + output->WriteAudioSamplesSync(frames->data[0], + frames->frames, + &sampleFramesWritten); +} + #define TIME_BASE 1000000000 HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived( diff --git a/plugins/decklink/decklink-device-instance.hpp b/plugins/decklink/decklink-device-instance.hpp index ffbed0782476578afd42101a74d5f4e5ca2678c7..3d3251340ff007010d745046383030aba1687bef 100644 --- a/plugins/decklink/decklink-device-instance.hpp +++ b/plugins/decklink/decklink-device-instance.hpp @@ -1,14 +1,19 @@ #pragma once +#define LOG(level, message, ...) blog(level, "%s: " message, "decklink", ##__VA_ARGS__) + +#include #include "decklink-device.hpp" +#include "../../libobs/media-io/video-scaler.h" class AudioRepacker; +class DecklinkBase; class DeckLinkDeviceInstance : public IDeckLinkInputCallback { protected: struct obs_source_frame currentFrame; struct obs_source_audio currentPacket; - DeckLink *decklink = nullptr; + DecklinkBase *decklink = nullptr; DeckLinkDevice *device = nullptr; DeckLinkDeviceMode *mode = nullptr; BMDDisplayMode displayMode = bmdModeNTSC; @@ -17,6 +22,7 @@ protected: video_colorspace activeColorSpace = VIDEO_CS_DEFAULT; video_range_type colorRange = VIDEO_RANGE_DEFAULT; ComPtr input; + ComPtr output; volatile long refCount = 1; int64_t audioOffset = 0; uint64_t nextAudioTS = 0; @@ -24,6 +30,8 @@ protected: AudioRepacker *audioRepacker = nullptr; speaker_layout channelFormat = SPEAKERS_STEREO; + IDeckLinkMutableVideoFrame *decklinkOutputFrame; + void FinalizeStream(); void SetupVideoFormat(DeckLinkDeviceMode *mode_); @@ -33,7 +41,7 @@ protected: const uint64_t timestamp); public: - DeckLinkDeviceInstance(DeckLink *decklink, DeckLinkDevice *device); + DeckLinkDeviceInstance(DecklinkBase *decklink, DeckLinkDevice *device); virtual ~DeckLinkDeviceInstance(); inline DeckLinkDevice *GetDevice() const {return device;} @@ -52,6 +60,9 @@ public: bool StartCapture(DeckLinkDeviceMode *mode); bool StopCapture(void); + bool StartOutput(DeckLinkDeviceMode *mode_); + bool StopOutput(void); + HRESULT STDMETHODCALLTYPE VideoInputFrameArrived( IDeckLinkVideoInputFrame *videoFrame, IDeckLinkAudioInputPacket *audioPacket); @@ -63,4 +74,7 @@ public: ULONG STDMETHODCALLTYPE AddRef(void); HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv); ULONG STDMETHODCALLTYPE Release(void); + + void DisplayVideoFrame(video_data *frame); + void WriteAudio(audio_data *frames); }; diff --git a/plugins/decklink/decklink-device-mode.cpp b/plugins/decklink/decklink-device-mode.cpp index 48d6eac5dbfff5e5c0e1815d5c72e09b379c3391..67a052bd3f4ab81216dc8725801c74ef848cd3ac 100644 --- a/plugins/decklink/decklink-device-mode.cpp +++ b/plugins/decklink/decklink-device-mode.cpp @@ -32,6 +32,22 @@ BMDDisplayMode DeckLinkDeviceMode::GetDisplayMode(void) const return bmdModeUnknown; } +int DeckLinkDeviceMode::GetWidth() +{ + if (mode != nullptr) + return mode->GetWidth(); + + return 0; +} + +int DeckLinkDeviceMode::GetHeight() +{ + if (mode != nullptr) + return mode->GetHeight(); + + return 0; +} + 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 8ff642da7d5c94f0c8e490e8c0c20e89ed177589..ae001532ba818f0616c893ba5abeaf7bd317e808 100644 --- a/plugins/decklink/decklink-device-mode.hpp +++ b/plugins/decklink/decklink-device-mode.hpp @@ -23,4 +23,7 @@ public: const std::string& GetName(void) const; void SetMode(IDeckLinkDisplayMode *mode); + + int GetWidth(); + int GetHeight(); }; diff --git a/plugins/decklink/decklink-device.cpp b/plugins/decklink/decklink-device.cpp index d173053afa5291d7b7d3f418373aec48a0cb2c65..9d0f35c6531fe6280ef7ac05d3c17eb7ad5b6612 100644 --- a/plugins/decklink/decklink-device.cpp +++ b/plugins/decklink/decklink-device.cpp @@ -10,7 +10,10 @@ DeckLinkDevice::DeckLinkDevice(IDeckLink *device_) : device(device_) DeckLinkDevice::~DeckLinkDevice(void) { - for (DeckLinkDeviceMode *mode : modes) + for (DeckLinkDeviceMode *mode : inputModes) + delete mode; + + for (DeckLinkDeviceMode *mode : outputModes) delete mode; } @@ -37,35 +40,61 @@ bool DeckLinkDevice::Init() decklink_bool_t detectable = false; if (attributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection, &detectable) == S_OK && !!detectable) { - DeckLinkDeviceMode *mode = - new DeckLinkDeviceMode("Auto", MODE_ID_AUTO); - modes.push_back(mode); - modeIdMap[MODE_ID_AUTO] = mode; + DeckLinkDeviceMode *mode = new DeckLinkDeviceMode( + "Auto", + MODE_ID_AUTO); + inputModes.push_back(mode); + inputModeIdMap[MODE_ID_AUTO] = mode; } } + // Find input modes ComPtr input; - if (device->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK) - return false; - - IDeckLinkDisplayModeIterator *modeIterator; - if (input->GetDisplayModeIterator(&modeIterator) == S_OK) { - IDeckLinkDisplayMode *displayMode; - long long modeId = 1; - - while (modeIterator->Next(&displayMode) == S_OK) { - if (displayMode == nullptr) - continue; - - DeckLinkDeviceMode *mode = - new DeckLinkDeviceMode(displayMode, modeId); - modes.push_back(mode); - modeIdMap[modeId] = mode; - displayMode->Release(); - ++modeId; + if (device->QueryInterface(IID_IDeckLinkInput, (void **) &input) == S_OK) { + IDeckLinkDisplayModeIterator *modeIterator; + if (input->GetDisplayModeIterator(&modeIterator) == S_OK) { + IDeckLinkDisplayMode *displayMode; + long long modeId = 1; + + while (modeIterator->Next(&displayMode) == S_OK) { + if (displayMode == nullptr) + continue; + + DeckLinkDeviceMode *mode = + new DeckLinkDeviceMode(displayMode, modeId); + inputModes.push_back(mode); + inputModeIdMap[modeId] = mode; + displayMode->Release(); + ++modeId; + } + + modeIterator->Release(); } + } + + // find output modes + ComPtr output; + if (device->QueryInterface(IID_IDeckLinkOutput, (void **) &output) == S_OK) { - modeIterator->Release(); + IDeckLinkDisplayModeIterator *modeIterator; + if (output->GetDisplayModeIterator(&modeIterator) == S_OK) { + IDeckLinkDisplayMode *displayMode; + long long modeId = 1; + + while (modeIterator->Next(&displayMode) == S_OK) { + if (displayMode == nullptr) + continue; + + DeckLinkDeviceMode *mode = + new DeckLinkDeviceMode(displayMode, modeId); + outputModes.push_back(mode); + outputModeIdMap[modeId] = mode; + displayMode->Release(); + ++modeId; + } + + modeIterator->Release(); + } } decklink_string_t decklinkModelName; @@ -115,9 +144,22 @@ bool DeckLinkDevice::GetInput(IDeckLinkInput **input) return true; } -DeckLinkDeviceMode *DeckLinkDevice::FindMode(long long id) +bool DeckLinkDevice::GetOutput(IDeckLinkOutput **output) +{ + if (device->QueryInterface(IID_IDeckLinkOutput, (void**)output) != S_OK) + return false; + + return true; +} + +DeckLinkDeviceMode *DeckLinkDevice::FindInputMode(long long id) { - return modeIdMap[id]; + return inputModeIdMap[id]; +} + +DeckLinkDeviceMode *DeckLinkDevice::FindOutputMode(long long id) +{ + return outputModeIdMap[id]; } const std::string& DeckLinkDevice::GetDisplayName(void) @@ -130,9 +172,14 @@ const std::string& DeckLinkDevice::GetHash(void) const return hash; } -const std::vector& DeckLinkDevice::GetModes(void) const +const std::vector& DeckLinkDevice::GetInputModes(void) const +{ + return inputModes; +} + +const std::vector& DeckLinkDevice::GetOutputModes(void) const { - return modes; + return outputModes; } const std::string& DeckLinkDevice::GetName(void) const diff --git a/plugins/decklink/decklink-device.hpp b/plugins/decklink/decklink-device.hpp index 9825ad8dfce7df2769b7240656c3907b77240640..723bce0a04f863829447c737a43b79327e184773 100644 --- a/plugins/decklink/decklink-device.hpp +++ b/plugins/decklink/decklink-device.hpp @@ -1,6 +1,5 @@ #pragma once -#include "decklink.hpp" #include "decklink-device-mode.hpp" #include @@ -9,8 +8,10 @@ class DeckLinkDevice { ComPtr device; - std::map modeIdMap; - std::vector modes; + std::map inputModeIdMap; + std::vector inputModes; + std::map outputModeIdMap; + std::vector outputModes; std::string name; std::string displayName; std::string hash; @@ -26,14 +27,17 @@ public: bool Init(); - DeckLinkDeviceMode *FindMode(long long id); + DeckLinkDeviceMode *FindInputMode(long long id); + DeckLinkDeviceMode *FindOutputMode(long long id); const std::string& GetDisplayName(void); const std::string& GetHash(void) const; - const std::vector& GetModes(void) const; + const std::vector& GetInputModes(void) const; + const std::vector& GetOutputModes(void) const; const std::string& GetName(void) const; int32_t GetMaxChannel(void) const; bool GetInput(IDeckLinkInput **input); + bool GetOutput(IDeckLinkOutput **output); inline bool IsDevice(IDeckLink *device_) { diff --git a/plugins/decklink/decklink-devices.cpp b/plugins/decklink/decklink-devices.cpp new file mode 100644 index 0000000000000000000000000000000000000000..28607ef73d44efce74b69c60a3d997c77ad659f8 --- /dev/null +++ b/plugins/decklink/decklink-devices.cpp @@ -0,0 +1,17 @@ +#include "decklink-devices.hpp" + +DeckLinkDeviceDiscovery *deviceEnum = nullptr; + +void fill_out_devices(obs_property_t *list) +{ + deviceEnum->Lock(); + + const std::vector &devices = deviceEnum->GetDevices(); + for (DeckLinkDevice *device : devices) { + obs_property_list_add_string(list, + device->GetDisplayName().c_str(), + device->GetHash().c_str()); + } + + deviceEnum->Unlock(); +} diff --git a/plugins/decklink/decklink-devices.hpp b/plugins/decklink/decklink-devices.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fa1d24cad971e7ab3db28da4094dac4b5e133992 --- /dev/null +++ b/plugins/decklink/decklink-devices.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" + +extern DeckLinkDeviceDiscovery *deviceEnum; + +void fill_out_devices(obs_property_t *list); diff --git a/plugins/decklink/decklink-output.cpp b/plugins/decklink/decklink-output.cpp new file mode 100644 index 0000000000000000000000000000000000000000..19edc3e27f50d1110f33f9295659562c5f015ec7 --- /dev/null +++ b/plugins/decklink/decklink-output.cpp @@ -0,0 +1,239 @@ +#include +#include + +#include "const.h" + +#include "DecklinkOutput.hpp" +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" +#include "decklink-devices.hpp" + +#include "../../libobs/media-io/video-scaler.h" + +static void decklink_output_destroy(void *data) +{ + auto *decklink = (DeckLinkOutput *)data; + delete decklink; +} + +static void *decklink_output_create(obs_data_t *settings, obs_output_t *output) +{ + auto *decklinkOutput = new DeckLinkOutput(output, deviceEnum); + + decklinkOutput->deviceHash = obs_data_get_string(settings, DEVICE_HASH); + decklinkOutput->modeID = obs_data_get_int(settings, MODE_ID); + + return decklinkOutput; +} + +static void decklink_output_update(void *data, obs_data_t *settings) +{ + auto *decklink = (DeckLinkOutput *)data; + + decklink->deviceHash = obs_data_get_string(settings, DEVICE_HASH); + decklink->modeID = obs_data_get_int(settings, MODE_ID); +} + +static bool decklink_output_start(void *data) +{ + auto *decklink = (DeckLinkOutput *)data; + struct obs_audio_info aoi; + + if (!obs_get_audio_info(&aoi)) { + blog(LOG_WARNING, "No active audio"); + return false; + } + + decklink->audio_samplerate = aoi.samples_per_sec; + decklink->audio_planes = 2; + decklink->audio_size = get_audio_size(AUDIO_FORMAT_16BIT, aoi.speakers, 1); + + decklink->start_timestamp = 0; + + ComPtr device; + + device.Set(deviceEnum->FindByHash(decklink->deviceHash)); + + DeckLinkDeviceMode *mode = device->FindOutputMode(decklink->modeID); + + decklink->SetSize(mode->GetWidth(), mode->GetHeight()); + + struct video_scale_info to = {}; + to.format = VIDEO_FORMAT_UYVY; + to.width = mode->GetWidth(); + to.height = mode->GetHeight(); + + obs_output_set_video_conversion(decklink->GetOutput(), &to); + + decklink->Activate(device, decklink->modeID); + + struct audio_convert_info conversion = {}; + conversion.format = AUDIO_FORMAT_16BIT; + conversion.speakers = SPEAKERS_STEREO; + conversion.samples_per_sec = 48000; // Only format the decklink supports + + obs_output_set_audio_conversion(decklink->GetOutput(), &conversion); + + if (!obs_output_begin_data_capture(decklink->GetOutput(), 0)) + return false; + + return true; +} + +static void decklink_output_stop(void *data, uint64_t) +{ + auto *decklink = (DeckLinkOutput *)data; + + obs_output_end_data_capture(decklink->GetOutput()); + + ComPtr device; + + device.Set(deviceEnum->FindByHash(decklink->deviceHash)); + + decklink->Deactivate(); +} + +static void decklink_output_raw_video(void *data, struct video_data *frame) +{ + auto *decklink = (DeckLinkOutput *)data; + + if (!decklink->start_timestamp) + decklink->start_timestamp = frame->timestamp; + + decklink->DisplayVideoFrame(frame); +} + +static bool prepare_audio(DeckLinkOutput *decklink, + const struct audio_data *frame, + struct audio_data *output) +{ + *output = *frame; + + if (frame->timestamp < decklink->start_timestamp) { + uint64_t duration = (uint64_t)frame->frames * 1000000000 / + (uint64_t)decklink->audio_samplerate; + uint64_t end_ts = frame->timestamp + duration; + uint64_t cutoff; + + if (end_ts <= decklink->start_timestamp) + return false; + + cutoff = decklink->start_timestamp - frame->timestamp; + output->timestamp += cutoff; + + cutoff *= (uint64_t)decklink->audio_samplerate / 1000000000; + + for (size_t i = 0; i < decklink->audio_planes; i++) + output->data[i] += decklink->audio_size * + (uint32_t)cutoff; + + output->frames -= (uint32_t)cutoff; + } + + return true; +} + +static void decklink_output_raw_audio(void *data, struct audio_data *frames) +{ + auto *decklink = (DeckLinkOutput *)data; + struct audio_data in; + + if (!decklink->start_timestamp) + return; + + if (!prepare_audio(decklink, frames, &in)) + return; + + decklink->WriteAudio(&in); +} + +static bool decklink_output_device_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + const char *name = obs_data_get_string(settings, DEVICE_NAME); + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + const char *mode = obs_data_get_string(settings, MODE_NAME); + long long modeId = obs_data_get_int(settings, MODE_ID); + + size_t itemCount = obs_property_list_item_count(list); + bool itemFound = false; + + for (size_t i = 0; i < itemCount; i++) { + const char *curHash = obs_property_list_item_string(list, i); + if (strcmp(hash, curHash) == 0) { + itemFound = true; + break; + } + } + + if (!itemFound) { + obs_property_list_insert_string(list, 0, name, hash); + obs_property_list_item_disable(list, 0, true); + } + + obs_property_t *modeList = obs_properties_get(props, MODE_ID); + + obs_property_list_clear(modeList); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + if (!device) { + obs_property_list_add_int(modeList, mode, modeId); + obs_property_list_item_disable(modeList, 0, true); + } else { + const std::vector &modes = + device->GetOutputModes(); + + for (DeckLinkDeviceMode *mode : modes) { + obs_property_list_add_int(modeList, + mode->GetName().c_str(), + mode->GetId()); + } + } + + return true; +} + +static obs_properties_t *decklink_output_properties(void *unused) +{ + UNUSED_PARAMETER(unused); + obs_properties_t *props = obs_properties_create(); + + obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, + TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_set_modified_callback(list, decklink_output_device_changed); + + fill_out_devices(list); + + obs_properties_add_list(props, + MODE_ID, TEXT_MODE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + + obs_properties_add_bool(props, AUTO_START, TEXT_AUTO_START); + + return props; +} + +static const char *decklink_output_get_name(void*) +{ + return obs_module_text("BlackmagicDevice"); +} + +struct obs_output_info create_decklink_output_info() +{ + struct obs_output_info decklink_output_info = {}; + + decklink_output_info.id = "decklink_output"; + decklink_output_info.flags = OBS_OUTPUT_AV; + decklink_output_info.get_name = decklink_output_get_name; + decklink_output_info.create = decklink_output_create; + decklink_output_info.destroy = decklink_output_destroy; + decklink_output_info.start = decklink_output_start; + decklink_output_info.stop = decklink_output_stop; + decklink_output_info.get_properties = decklink_output_properties; + decklink_output_info.raw_video = decklink_output_raw_video; + decklink_output_info.raw_audio = decklink_output_raw_audio; + decklink_output_info.update = decklink_output_update; + + return decklink_output_info; +} diff --git a/plugins/decklink/decklink-source.cpp b/plugins/decklink/decklink-source.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9e448c8f1c56eff3f3f9be4cef6baaa9a5bd8001 --- /dev/null +++ b/plugins/decklink/decklink-source.cpp @@ -0,0 +1,288 @@ +#include + +#include "const.h" + +#include "DecklinkInput.hpp" +#include "decklink-device.hpp" +#include "decklink-device-discovery.hpp" +#include "decklink-devices.hpp" + +static void decklink_enable_buffering(DeckLinkInput *decklink, bool enabled) +{ + obs_source_t *source = decklink->GetSource(); + obs_source_set_async_unbuffered(source, !enabled); + decklink->buffering = enabled; +} + +static void decklink_deactivate_when_not_showing(DeckLinkInput *decklink, bool dwns) +{ + decklink->dwns = dwns; +} + +static void *decklink_create(obs_data_t *settings, obs_source_t *source) +{ + DeckLinkInput *decklink = new DeckLinkInput(source, deviceEnum); + + obs_source_set_async_decoupled(source, true); + decklink_enable_buffering(decklink, + obs_data_get_bool(settings, BUFFERING)); + + obs_source_update(source, settings); + return decklink; +} + +static void decklink_destroy(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + delete decklink; +} + +static void decklink_update(void *data, obs_data_t *settings) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + long long id = obs_data_get_int(settings, MODE_ID); + BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, + PIXEL_FORMAT); + video_colorspace colorSpace = (video_colorspace)obs_data_get_int(settings, + COLOR_SPACE); + video_range_type colorRange = (video_range_type)obs_data_get_int(settings, + COLOR_RANGE); + int chFmtInt = (int)obs_data_get_int(settings, CHANNEL_FORMAT); + + if (chFmtInt == 7) + chFmtInt = SPEAKERS_5POINT1; + else if (chFmtInt < SPEAKERS_UNKNOWN || chFmtInt > SPEAKERS_7POINT1) + chFmtInt = 2; + + speaker_layout channelFormat = (speaker_layout)chFmtInt; + + decklink_enable_buffering(decklink, + obs_data_get_bool(settings, BUFFERING)); + + decklink_deactivate_when_not_showing(decklink, + obs_data_get_bool(settings, DEACTIVATE_WNS)); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + decklink->SetPixelFormat(pixelFormat); + decklink->SetColorSpace(colorSpace); + decklink->SetColorRange(colorRange); + decklink->SetChannelFormat(channelFormat); + decklink->Activate(device, id); + decklink->hash = std::string(hash); +} + +static void decklink_show(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + obs_source_t *source = decklink->GetSource(); + bool showing = obs_source_showing(source); + if (decklink->dwns && showing && !decklink->Capturing()) { + ComPtr device; + device.Set(deviceEnum->FindByHash(decklink->hash.c_str())); + decklink->Activate(device, decklink->id); + } +} +static void decklink_hide(void *data) +{ + DeckLinkInput *decklink = (DeckLinkInput *)data; + obs_source_t *source = decklink->GetSource(); + bool showing = obs_source_showing(source); + if (decklink->dwns && showing) + decklink->Deactivate(); +} + +static void decklink_get_defaults(obs_data_t *settings) +{ + obs_data_set_default_bool(settings, BUFFERING, false); + obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV); + obs_data_set_default_int(settings, COLOR_SPACE, VIDEO_CS_DEFAULT); + obs_data_set_default_int(settings, COLOR_RANGE, VIDEO_RANGE_DEFAULT); + obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO); +} + +static const char *decklink_get_name(void*) +{ + return obs_module_text("BlackmagicDevice"); +} + +static bool decklink_device_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + const char *name = obs_data_get_string(settings, DEVICE_NAME); + const char *hash = obs_data_get_string(settings, DEVICE_HASH); + const char *mode = obs_data_get_string(settings, MODE_NAME); + long long modeId = obs_data_get_int(settings, MODE_ID); + + size_t itemCount = obs_property_list_item_count(list); + bool itemFound = false; + + for (size_t i = 0; i < itemCount; i++) { + const char *curHash = obs_property_list_item_string(list, i); + if (strcmp(hash, curHash) == 0) { + itemFound = true; + break; + } + } + + if (!itemFound) { + obs_property_list_insert_string(list, 0, name, hash); + obs_property_list_item_disable(list, 0, true); + } + + obs_property_t *modeList = obs_properties_get(props, MODE_ID); + obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); + + obs_property_list_clear(modeList); + + obs_property_list_clear(channelList); + obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_NONE, + SPEAKERS_UNKNOWN); + obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_0CH, + SPEAKERS_STEREO); + + ComPtr device; + device.Set(deviceEnum->FindByHash(hash)); + + if (!device) { + obs_property_list_add_int(modeList, mode, modeId); + obs_property_list_item_disable(modeList, 0, true); + } else { + const std::vector &modes = + device->GetInputModes(); + + for (DeckLinkDeviceMode *mode : modes) { + obs_property_list_add_int(modeList, + mode->GetName().c_str(), + mode->GetId()); + } + + if (device->GetMaxChannel() >= 8) { + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_2_1CH, SPEAKERS_2POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_4_0CH, SPEAKERS_4POINT0); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_4_1CH, SPEAKERS_4POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_5_1CH, SPEAKERS_5POINT1); + obs_property_list_add_int(channelList, + TEXT_CHANNEL_FORMAT_7_1CH, SPEAKERS_7POINT1); + } + } + + return true; +} + +static bool color_format_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings); + +static bool mode_id_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + long long id = obs_data_get_int(settings, MODE_ID); + + list = obs_properties_get(props, PIXEL_FORMAT); + obs_property_set_visible(list, id != MODE_ID_AUTO); + + return color_format_changed(props, nullptr, settings); +} + +static bool color_format_changed(obs_properties_t *props, + obs_property_t *list, obs_data_t *settings) +{ + long long id = obs_data_get_int(settings, MODE_ID); + BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, + PIXEL_FORMAT); + + list = obs_properties_get(props, COLOR_SPACE); + obs_property_set_visible(list, + id != MODE_ID_AUTO && pixelFormat == bmdFormat8BitYUV); + + list = obs_properties_get(props, COLOR_RANGE); + obs_property_set_visible(list, + id == MODE_ID_AUTO || pixelFormat == bmdFormat8BitYUV); + + return true; +} + +static obs_properties_t *decklink_get_properties(void *data) +{ + obs_properties_t *props = obs_properties_create(); + + obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, + TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); + obs_property_set_modified_callback(list, decklink_device_changed); + + fill_out_devices(list); + + list = obs_properties_add_list(props, MODE_ID, TEXT_MODE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_set_modified_callback(list, mode_id_changed); + + list = obs_properties_add_list(props, PIXEL_FORMAT, + TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_set_modified_callback(list, color_format_changed); + + obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); + obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); + + list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT, VIDEO_CS_DEFAULT); + obs_property_list_add_int(list, "BT.601", VIDEO_CS_601); + obs_property_list_add_int(list, "BT.709", VIDEO_CS_709); + + list = obs_properties_add_list(props, COLOR_RANGE, TEXT_COLOR_RANGE, + OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT, VIDEO_RANGE_DEFAULT); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL, VIDEO_RANGE_PARTIAL); + obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL, VIDEO_RANGE_FULL); + + list = obs_properties_add_list(props, CHANNEL_FORMAT, + TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST, + OBS_COMBO_FORMAT_INT); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE, + SPEAKERS_UNKNOWN); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH, + SPEAKERS_STEREO); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_1CH, + SPEAKERS_2POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_0CH, + SPEAKERS_4POINT0); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_1CH, + SPEAKERS_4POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_5_1CH, + SPEAKERS_5POINT1); + obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH, + SPEAKERS_7POINT1); + + obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); + + obs_properties_add_bool(props, DEACTIVATE_WNS, TEXT_DWNS); + + UNUSED_PARAMETER(data); + return props; +} + + +struct obs_source_info create_decklink_source_info() +{ + struct obs_source_info decklink_source_info = {}; + decklink_source_info.id = "decklink-input"; + decklink_source_info.type = OBS_SOURCE_TYPE_INPUT; + decklink_source_info.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | OBS_SOURCE_DO_NOT_DUPLICATE; + decklink_source_info.create = decklink_create; + decklink_source_info.destroy = decklink_destroy; + decklink_source_info.get_defaults = decklink_get_defaults; + decklink_source_info.get_name = decklink_get_name; + decklink_source_info.get_properties = decklink_get_properties; + decklink_source_info.update = decklink_update; + decklink_source_info.show = decklink_show; + decklink_source_info.hide = decklink_hide; + + return decklink_source_info; +} diff --git a/plugins/decklink/linux/CMakeLists.txt b/plugins/decklink/linux/CMakeLists.txt index 1ed5ae46dd04f5b08eae28ddd6fc727f9ca6387f..67da0210acc603f1b5ccedd0f1cd9d8943c32167 100644 --- a/plugins/decklink/linux/CMakeLists.txt +++ b/plugins/decklink/linux/CMakeLists.txt @@ -21,8 +21,12 @@ set(linux-decklink-sdk_SOURCES ) set(linux-decklink_HEADERS + ../decklink-devices.hpp + ../const.h + ../DecklinkOutput.hpp ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp @@ -33,21 +37,29 @@ set(linux-decklink_HEADERS set(linux-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../decklink-source.cpp + ../decklink-output.cpp + ../DecklinkOutput.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ) add_library(linux-decklink MODULE ${linux-decklink_SOURCES} ${linux-decklink_HEADERS} ${linux-decklink-sdk_HEADERS} - ${linux-decklink-sdk_SOURCES}) + ${linux-decklink-sdk_SOURCES} + ) target_link_libraries(linux-decklink - libobs) + libobs + ) install_obs_plugin_with_data(linux-decklink ../data) diff --git a/plugins/decklink/mac/CMakeLists.txt b/plugins/decklink/mac/CMakeLists.txt index d016470cd6fb22ba77dba6d2080540ecd2182c4c..2a2ed216f607273325f29606067db6ed941a8331 100644 --- a/plugins/decklink/mac/CMakeLists.txt +++ b/plugins/decklink/mac/CMakeLists.txt @@ -5,9 +5,9 @@ if(DISABLE_DECKLINK) return() endif() -find_library(COREFOUNDATION CoreFoundation) +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}") -include_directories(${COREFOUNDATION}) +find_library(COREFOUNDATION CoreFoundation) set(mac-decklink-sdk_HEADERS decklink-sdk/DeckLinkAPI.h @@ -17,16 +17,19 @@ set(mac-decklink-sdk_HEADERS decklink-sdk/DeckLinkAPIModes.h decklink-sdk/DeckLinkAPIStreaming.h decklink-sdk/DeckLinkAPITypes.h - decklink-sdk/DeckLinkAPIVersion.h - ) + decklink-sdk/DeckLinkAPIVersion.h) set(mac-decklink-sdk_SOURCES decklink-sdk/DeckLinkAPIDispatch.cpp ) set(mac-decklink_HEADERS + ../decklink-devices.hpp + ../const.h + ../DecklinkOutput.hpp ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp @@ -37,19 +40,34 @@ set(mac-decklink_HEADERS set(mac-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../decklink-output.cpp + ../decklink-source.cpp + ../DecklinkOutput.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ) + +list(APPEND decklink_HEADERS ${decklink_UI_HEADERS}) + +include_directories( + ${COREFOUNDATION} + "${CMAKE_SOURCE_DIR}/UI/obs-frontend-api") + +list(APPEND mac-decklink_HEADERS ${decklink_UI_HEADERS}) add_library(mac-decklink MODULE ${mac-decklink_SOURCES} ${mac-decklink_HEADERS} ${mac-decklink-sdk_HEADERS} - ${mac-decklink-sdk_SOURCES}) + ${mac-decklink-sdk_SOURCES} + ) target_link_libraries(mac-decklink libobs diff --git a/plugins/decklink/plugin-main.cpp b/plugins/decklink/plugin-main.cpp index 46ab8551c49075697f6c1103107d987ce17681fa..f37965008ea57bac2e539d07bf16e363e13355a0 100644 --- a/plugins/decklink/plugin-main.cpp +++ b/plugins/decklink/plugin-main.cpp @@ -1,323 +1,14 @@ -#include "decklink.hpp" -#include "decklink-device.hpp" -#include "decklink-device-discovery.hpp" - #include +#include "decklink-devices.hpp" OBS_DECLARE_MODULE() OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US") -#define DEVICE_HASH "device_hash" -#define DEVICE_NAME "device_name" -#define MODE_ID "mode_id" -#define MODE_NAME "mode_name" -#define CHANNEL_FORMAT "channel_format" -#define PIXEL_FORMAT "pixel_format" -#define COLOR_SPACE "color_space" -#define COLOR_RANGE "color_range" -#define BUFFERING "buffering" -#define DEACTIVATE_WNS "deactivate_when_not_showing" - -#define TEXT_DEVICE obs_module_text("Device") -#define TEXT_MODE obs_module_text("Mode") -#define TEXT_PIXEL_FORMAT obs_module_text("PixelFormat") -#define TEXT_COLOR_SPACE obs_module_text("ColorSpace") -#define TEXT_COLOR_SPACE_DEFAULT obs_module_text("ColorSpace.Default") -#define TEXT_COLOR_RANGE obs_module_text("ColorRange") -#define TEXT_COLOR_RANGE_DEFAULT obs_module_text("ColorRange.Default") -#define TEXT_COLOR_RANGE_PARTIAL obs_module_text("ColorRange.Partial") -#define TEXT_COLOR_RANGE_FULL obs_module_text("ColorRange.Full") -#define TEXT_CHANNEL_FORMAT obs_module_text("ChannelFormat") -#define TEXT_CHANNEL_FORMAT_NONE obs_module_text("ChannelFormat.None") -#define TEXT_CHANNEL_FORMAT_2_0CH obs_module_text("ChannelFormat.2_0ch") -#define TEXT_CHANNEL_FORMAT_2_1CH obs_module_text("ChannelFormat.2_1ch") -#define TEXT_CHANNEL_FORMAT_4_0CH obs_module_text("ChannelFormat.4_0ch") -#define TEXT_CHANNEL_FORMAT_4_1CH obs_module_text("ChannelFormat.4_1ch") -#define TEXT_CHANNEL_FORMAT_5_1CH obs_module_text("ChannelFormat.5_1ch") -#define TEXT_CHANNEL_FORMAT_7_1CH obs_module_text("ChannelFormat.7_1ch") -#define TEXT_BUFFERING obs_module_text("Buffering") -#define TEXT_DWNS obs_module_text("DeactivateWhenNotShowing") - -static DeckLinkDeviceDiscovery *deviceEnum = nullptr; - -static void decklink_enable_buffering(DeckLink *decklink, bool enabled) -{ - obs_source_t *source = decklink->GetSource(); - obs_source_set_async_unbuffered(source, !enabled); - decklink->buffering = enabled; -} - -static void decklink_deactivate_when_not_showing(DeckLink *decklink, bool dwns) -{ - decklink->dwns = dwns; -} - -static void *decklink_create(obs_data_t *settings, obs_source_t *source) -{ - DeckLink *decklink = new DeckLink(source, deviceEnum); - - obs_source_set_async_decoupled(source, true); - decklink_enable_buffering(decklink, - obs_data_get_bool(settings, BUFFERING)); - - obs_source_update(source, settings); - return decklink; -} - -static void decklink_destroy(void *data) -{ - DeckLink *decklink = (DeckLink *)data; - delete decklink; -} - -static void decklink_update(void *data, obs_data_t *settings) -{ - DeckLink *decklink = (DeckLink *)data; - const char *hash = obs_data_get_string(settings, DEVICE_HASH); - long long id = obs_data_get_int(settings, MODE_ID); - BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, - PIXEL_FORMAT); - video_colorspace colorSpace = (video_colorspace)obs_data_get_int(settings, - COLOR_SPACE); - video_range_type colorRange = (video_range_type)obs_data_get_int(settings, - COLOR_RANGE); - int chFmtInt = (int)obs_data_get_int(settings, CHANNEL_FORMAT); - - if (chFmtInt == 7) { - chFmtInt = SPEAKERS_5POINT1; - } else if (chFmtInt < SPEAKERS_UNKNOWN || chFmtInt > SPEAKERS_7POINT1) { - chFmtInt = 2; - } - - speaker_layout channelFormat = (speaker_layout)chFmtInt; - - decklink_enable_buffering(decklink, - obs_data_get_bool(settings, BUFFERING)); - - decklink_deactivate_when_not_showing(decklink, - obs_data_get_bool(settings, DEACTIVATE_WNS)); - - ComPtr device; - device.Set(deviceEnum->FindByHash(hash)); - - decklink->SetPixelFormat(pixelFormat); - decklink->SetColorSpace(colorSpace); - decklink->SetColorRange(colorRange); - decklink->SetChannelFormat(channelFormat); - decklink->Activate(device, id); - decklink->hash = std::string(hash); -} - -static void decklink_show(void *data) -{ - DeckLink *decklink = (DeckLink *)data; - obs_source_t *source = decklink->GetSource(); - bool showing = obs_source_showing(source); - if (decklink->dwns && showing && !decklink->Capturing()) { - ComPtr device; - device.Set(deviceEnum->FindByHash(decklink->hash.c_str())); - - decklink->Activate(device, decklink->id); - } -} - -static void decklink_hide(void *data) -{ - DeckLink *decklink = (DeckLink *)data; - obs_source_t *source = decklink->GetSource(); - bool showing = obs_source_showing(source); - if (decklink->dwns && showing) - decklink->Deactivate(); - -} - -static void decklink_get_defaults(obs_data_t *settings) -{ - obs_data_set_default_bool(settings, BUFFERING, false); - obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV); - obs_data_set_default_int(settings, COLOR_SPACE, VIDEO_CS_DEFAULT); - obs_data_set_default_int(settings, COLOR_RANGE, VIDEO_RANGE_DEFAULT); - obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO); -} - -static const char *decklink_get_name(void*) -{ - return obs_module_text("BlackmagicDevice"); -} - -static bool decklink_device_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - const char *name = obs_data_get_string(settings, DEVICE_NAME); - const char *hash = obs_data_get_string(settings, DEVICE_HASH); - const char *mode = obs_data_get_string(settings, MODE_NAME); - long long modeId = obs_data_get_int(settings, MODE_ID); - - size_t itemCount = obs_property_list_item_count(list); - bool itemFound = false; +extern struct obs_source_info create_decklink_source_info(); +struct obs_source_info decklink_source_info; - for (size_t i = 0; i < itemCount; i++) { - const char *curHash = obs_property_list_item_string(list, i); - if (strcmp(hash, curHash) == 0) { - itemFound = true; - break; - } - } - - if (!itemFound) { - obs_property_list_insert_string(list, 0, name, hash); - obs_property_list_item_disable(list, 0, true); - } - - obs_property_t *modeList = obs_properties_get(props, MODE_ID); - obs_property_t *channelList = obs_properties_get(props, CHANNEL_FORMAT); - - obs_property_list_clear(modeList); - - obs_property_list_clear(channelList); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_NONE, - SPEAKERS_UNKNOWN); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_0CH, - SPEAKERS_STEREO); - - ComPtr device; - device.Set(deviceEnum->FindByHash(hash)); - - if (!device) { - obs_property_list_add_int(modeList, mode, modeId); - obs_property_list_item_disable(modeList, 0, true); - } else { - const std::vector &modes = - device->GetModes(); - - for (DeckLinkDeviceMode *mode : modes) { - obs_property_list_add_int(modeList, - mode->GetName().c_str(), - mode->GetId()); - } - - if (device->GetMaxChannel() >= 8) { - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_2_1CH, - SPEAKERS_2POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_4_0CH, - SPEAKERS_4POINT0); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_4_1CH, - SPEAKERS_4POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_5_1CH, - SPEAKERS_5POINT1); - obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_7_1CH, - SPEAKERS_7POINT1); - } - } - - return true; -} - -static void fill_out_devices(obs_property_t *list) -{ - deviceEnum->Lock(); - - const std::vector &devices = deviceEnum->GetDevices(); - for (DeckLinkDevice *device : devices) { - obs_property_list_add_string(list, - device->GetDisplayName().c_str(), - device->GetHash().c_str()); - } - - deviceEnum->Unlock(); -} - -static bool color_format_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings); - -static bool mode_id_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - long long id = obs_data_get_int(settings, MODE_ID); - - list = obs_properties_get(props, PIXEL_FORMAT); - obs_property_set_visible(list, id != MODE_ID_AUTO); - - return color_format_changed(props, nullptr, settings); -} - -static bool color_format_changed(obs_properties_t *props, - obs_property_t *list, obs_data_t *settings) -{ - long long id = obs_data_get_int(settings, MODE_ID); - BMDPixelFormat pixelFormat = (BMDPixelFormat)obs_data_get_int(settings, - PIXEL_FORMAT); - - list = obs_properties_get(props, COLOR_SPACE); - obs_property_set_visible(list, - id != MODE_ID_AUTO && pixelFormat == bmdFormat8BitYUV); - - list = obs_properties_get(props, COLOR_RANGE); - obs_property_set_visible(list, - id == MODE_ID_AUTO || pixelFormat == bmdFormat8BitYUV); - - return true; -} - -static obs_properties_t *decklink_get_properties(void *data) -{ - obs_properties_t *props = obs_properties_create(); - - obs_property_t *list = obs_properties_add_list(props, DEVICE_HASH, - TEXT_DEVICE, OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_STRING); - obs_property_set_modified_callback(list, decklink_device_changed); - - fill_out_devices(list); - - list = obs_properties_add_list(props, MODE_ID, TEXT_MODE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_set_modified_callback(list, mode_id_changed); - - list = obs_properties_add_list(props, PIXEL_FORMAT, - TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_set_modified_callback(list, color_format_changed); - - obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); - obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); - - list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_COLOR_SPACE_DEFAULT, VIDEO_CS_DEFAULT); - obs_property_list_add_int(list, "BT.601", VIDEO_CS_601); - obs_property_list_add_int(list, "BT.709", VIDEO_CS_709); - - list = obs_properties_add_list(props, COLOR_RANGE, TEXT_COLOR_RANGE, - OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_DEFAULT, VIDEO_RANGE_DEFAULT); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_PARTIAL, VIDEO_RANGE_PARTIAL); - obs_property_list_add_int(list, TEXT_COLOR_RANGE_FULL, VIDEO_RANGE_FULL); - - list = obs_properties_add_list(props, CHANNEL_FORMAT, - TEXT_CHANNEL_FORMAT, OBS_COMBO_TYPE_LIST, - OBS_COMBO_FORMAT_INT); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_NONE, - SPEAKERS_UNKNOWN); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_0CH, - SPEAKERS_STEREO); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_2_1CH, - SPEAKERS_2POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_0CH, - SPEAKERS_4POINT0); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_4_1CH, - SPEAKERS_4POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_5_1CH, - SPEAKERS_5POINT1); - obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH, - SPEAKERS_7POINT1); - - obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING); - - obs_properties_add_bool(props, DEACTIVATE_WNS, TEXT_DWNS); - - UNUSED_PARAMETER(data); - return props; -} +extern struct obs_output_info create_decklink_output_info(); +struct obs_output_info decklink_output_info; bool obs_module_load(void) { @@ -325,21 +16,11 @@ bool obs_module_load(void) if (!deviceEnum->Init()) return true; - struct obs_source_info info = {}; - info.id = "decklink-input"; - info.type = OBS_SOURCE_TYPE_INPUT; - info.output_flags = OBS_SOURCE_ASYNC_VIDEO | OBS_SOURCE_AUDIO | - OBS_SOURCE_DO_NOT_DUPLICATE; - info.create = decklink_create; - info.destroy = decklink_destroy; - info.get_defaults = decklink_get_defaults; - info.get_name = decklink_get_name; - info.get_properties = decklink_get_properties; - info.update = decklink_update; - info.show = decklink_show; - info.hide = decklink_hide; + decklink_source_info = create_decklink_source_info(); + obs_register_source(&decklink_source_info); - obs_register_source(&info); + decklink_output_info = create_decklink_output_info(); + obs_register_output(&decklink_output_info); return true; } diff --git a/plugins/decklink/win/CMakeLists.txt b/plugins/decklink/win/CMakeLists.txt index af9464510d290a0090fb7e362b26fdcdb39dbaff..19ecaaeb3c3ebbbdba24c4e447c7d88c9689c907 100644 --- a/plugins/decklink/win/CMakeLists.txt +++ b/plugins/decklink/win/CMakeLists.txt @@ -16,8 +16,12 @@ set(win-decklink-sdk_HEADERS ) set(win-decklink_HEADERS + ../decklink-devices.hpp + ../DecklinkOutput.hpp + ../const.h ../platform.hpp - ../decklink.hpp + ../DecklinkInput.hpp + ../DecklinkBase.h ../decklink-device-instance.hpp ../decklink-device-discovery.hpp ../decklink-device.hpp @@ -28,25 +32,34 @@ set(win-decklink_HEADERS set(win-decklink_SOURCES ../plugin-main.cpp - ../decklink.cpp + ../decklink-devices.cpp + ../DecklinkOutput.cpp + ../decklink-source.cpp + ../decklink-output.cpp + ../DecklinkInput.cpp + ../DecklinkBase.cpp ../decklink-device-instance.cpp ../decklink-device-discovery.cpp ../decklink-device.cpp ../decklink-device-mode.cpp ../audio-repack.c - platform.cpp) + platform.cpp + ) add_idl_files(win-decklink-sdk_GENERATED_FILES - ${win-decklink-sdk_IDLS}) + ${win-decklink-sdk_IDLS} + ) include_directories( - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_BINARY_DIR} +) add_library(win-decklink MODULE ${win-decklink_SOURCES} ${win-decklink_HEADERS} ${win-decklink-sdk_HEADERS} - ${win-decklink-sdk_GENERATED_FILES}) + ${win-decklink-sdk_GENERATED_FILES} + ) target_link_libraries(win-decklink libobs)