Decklink: add output support
parent
f8e628ac36
commit
21b67cff64
|
@ -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()
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
#include <obs-module.h>
|
||||||
|
|
||||||
|
#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<DeckLinkDeviceInstance> 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;
|
||||||
|
};
|
|
@ -1,30 +1,23 @@
|
||||||
#include "decklink.hpp"
|
#include "DecklinkInput.hpp"
|
||||||
#include "decklink-device-discovery.hpp"
|
|
||||||
#include "decklink-device-instance.hpp"
|
|
||||||
#include "decklink-device-mode.hpp"
|
|
||||||
|
|
||||||
#include <util/threading.h>
|
#include <util/threading.h>
|
||||||
|
|
||||||
DeckLink::DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_) :
|
DeckLinkInput::DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery_)
|
||||||
discovery(discovery_), source(source)
|
: 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();
|
Deactivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckLinkDevice *DeckLink::GetDevice() const
|
void DeckLinkInput::DevicesChanged(void *param, DeckLinkDevice *device, bool added)
|
||||||
{
|
{
|
||||||
return instance ? instance->GetDevice() : nullptr;
|
DeckLinkInput *decklink = reinterpret_cast<DeckLinkInput*>(param);
|
||||||
}
|
|
||||||
|
|
||||||
void DeckLink::DevicesChanged(void *param, DeckLinkDevice *device, bool added)
|
|
||||||
{
|
|
||||||
DeckLink *decklink = reinterpret_cast<DeckLink*>(param);
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(decklink->deviceMutex);
|
std::lock_guard<std::recursive_mutex> lock(decklink->deviceMutex);
|
||||||
|
|
||||||
obs_source_update_properties(decklink->source);
|
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<std::recursive_mutex> lock(deviceMutex);
|
std::lock_guard<std::recursive_mutex> lock(deviceMutex);
|
||||||
DeckLinkDevice *curDevice = GetDevice();
|
DeckLinkDevice *curDevice = GetDevice();
|
||||||
|
@ -82,7 +75,12 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId)
|
||||||
if (instance == nullptr)
|
if (instance == nullptr)
|
||||||
return false;
|
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) {
|
if (mode == nullptr) {
|
||||||
instance = nullptr;
|
instance = nullptr;
|
||||||
return false;
|
return false;
|
||||||
|
@ -100,7 +98,7 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeckLink::Deactivate(void)
|
void DeckLinkInput::Deactivate(void)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(deviceMutex);
|
std::lock_guard<std::recursive_mutex> lock(deviceMutex);
|
||||||
if (instance)
|
if (instance)
|
||||||
|
@ -111,12 +109,12 @@ void DeckLink::Deactivate(void)
|
||||||
os_atomic_dec_long(&activateRefs);
|
os_atomic_dec_long(&activateRefs);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DeckLink::Capturing(void)
|
bool DeckLinkInput::Capturing(void)
|
||||||
{
|
{
|
||||||
return isCapturing;
|
return isCapturing;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeckLink::SaveSettings()
|
void DeckLinkInput::SaveSettings()
|
||||||
{
|
{
|
||||||
if (!instance)
|
if (!instance)
|
||||||
return;
|
return;
|
||||||
|
@ -136,7 +134,7 @@ void DeckLink::SaveSettings()
|
||||||
obs_data_release(settings);
|
obs_data_release(settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
obs_source_t *DeckLink::GetSource(void) const
|
obs_source_t *DeckLinkInput::GetSource(void) const
|
||||||
{
|
{
|
||||||
return source;
|
return source;
|
||||||
}
|
}
|
|
@ -1,40 +1,19 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "platform.hpp"
|
#include "DecklinkBase.h"
|
||||||
|
|
||||||
#include <obs-module.h>
|
class DeckLinkInput : public DecklinkBase {
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
class DeckLinkDeviceDiscovery;
|
|
||||||
class DeckLinkDeviceInstance;
|
|
||||||
class DeckLinkDevice;
|
|
||||||
class DeckLinkDeviceMode;
|
|
||||||
|
|
||||||
class DeckLink {
|
|
||||||
protected:
|
protected:
|
||||||
ComPtr<DeckLinkDeviceInstance> instance;
|
|
||||||
DeckLinkDeviceDiscovery *discovery;
|
|
||||||
bool isCapturing = false;
|
bool isCapturing = false;
|
||||||
obs_source_t *source;
|
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();
|
void SaveSettings();
|
||||||
static void DevicesChanged(void *param, DeckLinkDevice *device,
|
static void DevicesChanged(void *param, DeckLinkDevice *device,
|
||||||
bool added);
|
bool added);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DeckLink(obs_source_t *source, DeckLinkDeviceDiscovery *discovery);
|
DeckLinkInput(obs_source_t *source, DeckLinkDeviceDiscovery *discovery);
|
||||||
virtual ~DeckLink(void);
|
virtual ~DeckLinkInput(void);
|
||||||
|
|
||||||
DeckLinkDevice *GetDevice() const;
|
|
||||||
|
|
||||||
long long GetActiveModeId(void) const;
|
long long GetActiveModeId(void) const;
|
||||||
obs_source_t *GetSource(void) const;
|
obs_source_t *GetSource(void) const;
|
|
@ -0,0 +1,110 @@
|
||||||
|
#include "DecklinkOutput.hpp"
|
||||||
|
|
||||||
|
#include <util/threading.h>
|
||||||
|
|
||||||
|
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<DeckLinkOutput*>(param);
|
||||||
|
std::lock_guard<std::recursive_mutex> lock(decklink->deviceMutex);
|
||||||
|
|
||||||
|
blog(LOG_DEBUG, "%s", device->GetHash().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DeckLinkOutput::Activate(DeckLinkDevice *device, long long modeId)
|
||||||
|
{
|
||||||
|
std::lock_guard<std::recursive_mutex> 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<std::recursive_mutex> 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;
|
||||||
|
}
|
|
@ -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();
|
||||||
|
};
|
|
@ -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")
|
|
@ -18,3 +18,4 @@ ChannelFormat.4_1ch="4.1ch"
|
||||||
ChannelFormat.5_1ch="5.1ch"
|
ChannelFormat.5_1ch="5.1ch"
|
||||||
ChannelFormat.7_1ch="7.1ch"
|
ChannelFormat.7_1ch="7.1ch"
|
||||||
DeactivateWhenNotShowing="Deactivate when not showing"
|
DeactivateWhenNotShowing="Deactivate when not showing"
|
||||||
|
AutoStart="Auto start on launch"
|
||||||
|
|
|
@ -1,10 +1,11 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <obs-module.h>
|
||||||
|
#include "platform.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#include "decklink.hpp"
|
|
||||||
|
|
||||||
class DeckLinkDevice;
|
class DeckLinkDevice;
|
||||||
|
|
||||||
typedef void (*DeviceChangeCallback)(void *param, DeckLinkDevice *device,
|
typedef void (*DeviceChangeCallback)(void *param, DeckLinkDevice *device,
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
#include "decklink-device-instance.hpp"
|
#include "decklink-device-instance.hpp"
|
||||||
#include "audio-repack.hpp"
|
#include "audio-repack.hpp"
|
||||||
|
|
||||||
|
#include "DecklinkInput.hpp"
|
||||||
|
#include "DecklinkOutput.hpp"
|
||||||
|
|
||||||
#include <util/platform.h>
|
#include <util/platform.h>
|
||||||
#include <util/threading.h>
|
#include <util/threading.h>
|
||||||
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <algorithm>
|
||||||
#define LOG(level, message, ...) blog(level, "%s: " message, \
|
|
||||||
obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__)
|
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#define IS_WIN 1
|
#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_) :
|
DeckLinkDevice *device_) :
|
||||||
currentFrame(), currentPacket(), decklink(decklink_), device(device_)
|
currentFrame(), currentPacket(), decklink(decklink_), device(device_)
|
||||||
{
|
{
|
||||||
|
@ -92,7 +93,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
|
||||||
currentPacket.frames = frameCount;
|
currentPacket.frames = frameCount;
|
||||||
currentPacket.timestamp = timestamp;
|
currentPacket.timestamp = timestamp;
|
||||||
|
|
||||||
if (decklink && !decklink->buffering) {
|
if (decklink && !static_cast<DeckLinkInput*>(decklink)->buffering) {
|
||||||
currentPacket.timestamp = os_gettime_ns();
|
currentPacket.timestamp = os_gettime_ns();
|
||||||
currentPacket.timestamp -=
|
currentPacket.timestamp -=
|
||||||
(uint64_t)frameCount * 1000000000ULL /
|
(uint64_t)frameCount * 1000000000ULL /
|
||||||
|
@ -120,7 +121,7 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
|
||||||
nextAudioTS = timestamp +
|
nextAudioTS = timestamp +
|
||||||
((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1;
|
((uint64_t)frameCount * 1000000000ULL / 48000ULL) + 1;
|
||||||
|
|
||||||
obs_source_output_audio(decklink->GetSource(), ¤tPacket);
|
obs_source_output_audio(static_cast<DeckLinkInput*>(decklink)->GetSource(), ¤tPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeckLinkDeviceInstance::HandleVideoFrame(
|
void DeckLinkDeviceInstance::HandleVideoFrame(
|
||||||
|
@ -141,7 +142,7 @@ void DeckLinkDeviceInstance::HandleVideoFrame(
|
||||||
currentFrame.height = (uint32_t)videoFrame->GetHeight();
|
currentFrame.height = (uint32_t)videoFrame->GetHeight();
|
||||||
currentFrame.timestamp = timestamp;
|
currentFrame.timestamp = timestamp;
|
||||||
|
|
||||||
obs_source_output_video(decklink->GetSource(), ¤tFrame);
|
obs_source_output_video(static_cast<DeckLinkInput*>(decklink)->GetSource(), ¤tFrame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeckLinkDeviceInstance::FinalizeStream()
|
void DeckLinkDeviceInstance::FinalizeStream()
|
||||||
|
@ -169,7 +170,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_)
|
||||||
|
|
||||||
currentFrame.format = ConvertPixelFormat(pixelFormat);
|
currentFrame.format = ConvertPixelFormat(pixelFormat);
|
||||||
|
|
||||||
colorSpace = decklink->GetColorSpace();
|
colorSpace = static_cast<DeckLinkInput*>(decklink)->GetColorSpace();
|
||||||
if (colorSpace == VIDEO_CS_DEFAULT) {
|
if (colorSpace == VIDEO_CS_DEFAULT) {
|
||||||
const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags();
|
const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags();
|
||||||
if (flags & bmdDisplayModeColorspaceRec709)
|
if (flags & bmdDisplayModeColorspaceRec709)
|
||||||
|
@ -182,7 +183,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_)
|
||||||
activeColorSpace = colorSpace;
|
activeColorSpace = colorSpace;
|
||||||
}
|
}
|
||||||
|
|
||||||
colorRange = decklink->GetColorRange();
|
colorRange = static_cast<DeckLinkInput*>(decklink)->GetColorRange();
|
||||||
currentFrame.full_range = colorRange == VIDEO_RANGE_FULL;
|
currentFrame.full_range = colorRange == VIDEO_RANGE_FULL;
|
||||||
|
|
||||||
video_format_get_parameters(activeColorSpace, colorRange,
|
video_format_get_parameters(activeColorSpace, colorRange,
|
||||||
|
@ -218,7 +219,7 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
|
||||||
flags = bmdVideoInputEnableFormatDetection;
|
flags = bmdVideoInputEnableFormatDetection;
|
||||||
} else {
|
} else {
|
||||||
displayMode = mode_->GetDisplayMode();
|
displayMode = mode_->GetDisplayMode();
|
||||||
pixelFormat = decklink->GetPixelFormat();
|
pixelFormat = static_cast<DeckLinkInput*>(decklink)->GetPixelFormat();
|
||||||
flags = bmdVideoInputFlagDefault;
|
flags = bmdVideoInputFlagDefault;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -231,7 +232,7 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
|
||||||
|
|
||||||
SetupVideoFormat(mode_);
|
SetupVideoFormat(mode_);
|
||||||
|
|
||||||
channelFormat = decklink->GetChannelFormat();
|
channelFormat = static_cast<DeckLinkInput*>(decklink)->GetChannelFormat();
|
||||||
currentPacket.speakers = channelFormat;
|
currentPacket.speakers = channelFormat;
|
||||||
|
|
||||||
int maxdevicechannel = device->GetMaxChannel();
|
int maxdevicechannel = device->GetMaxChannel();
|
||||||
|
@ -288,6 +289,101 @@ bool DeckLinkDeviceInstance::StopCapture(void)
|
||||||
return true;
|
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<DeckLinkOutput*>(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<DeckLinkOutput*>(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
|
#define TIME_BASE 1000000000
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived(
|
HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFrameArrived(
|
||||||
|
|
|
@ -1,14 +1,19 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define LOG(level, message, ...) blog(level, "%s: " message, "decklink", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
#include <obs-module.h>
|
||||||
#include "decklink-device.hpp"
|
#include "decklink-device.hpp"
|
||||||
|
#include "../../libobs/media-io/video-scaler.h"
|
||||||
|
|
||||||
class AudioRepacker;
|
class AudioRepacker;
|
||||||
|
class DecklinkBase;
|
||||||
|
|
||||||
class DeckLinkDeviceInstance : public IDeckLinkInputCallback {
|
class DeckLinkDeviceInstance : public IDeckLinkInputCallback {
|
||||||
protected:
|
protected:
|
||||||
struct obs_source_frame currentFrame;
|
struct obs_source_frame currentFrame;
|
||||||
struct obs_source_audio currentPacket;
|
struct obs_source_audio currentPacket;
|
||||||
DeckLink *decklink = nullptr;
|
DecklinkBase *decklink = nullptr;
|
||||||
DeckLinkDevice *device = nullptr;
|
DeckLinkDevice *device = nullptr;
|
||||||
DeckLinkDeviceMode *mode = nullptr;
|
DeckLinkDeviceMode *mode = nullptr;
|
||||||
BMDDisplayMode displayMode = bmdModeNTSC;
|
BMDDisplayMode displayMode = bmdModeNTSC;
|
||||||
|
@ -17,6 +22,7 @@ protected:
|
||||||
video_colorspace activeColorSpace = VIDEO_CS_DEFAULT;
|
video_colorspace activeColorSpace = VIDEO_CS_DEFAULT;
|
||||||
video_range_type colorRange = VIDEO_RANGE_DEFAULT;
|
video_range_type colorRange = VIDEO_RANGE_DEFAULT;
|
||||||
ComPtr<IDeckLinkInput> input;
|
ComPtr<IDeckLinkInput> input;
|
||||||
|
ComPtr<IDeckLinkOutput> output;
|
||||||
volatile long refCount = 1;
|
volatile long refCount = 1;
|
||||||
int64_t audioOffset = 0;
|
int64_t audioOffset = 0;
|
||||||
uint64_t nextAudioTS = 0;
|
uint64_t nextAudioTS = 0;
|
||||||
|
@ -24,6 +30,8 @@ protected:
|
||||||
AudioRepacker *audioRepacker = nullptr;
|
AudioRepacker *audioRepacker = nullptr;
|
||||||
speaker_layout channelFormat = SPEAKERS_STEREO;
|
speaker_layout channelFormat = SPEAKERS_STEREO;
|
||||||
|
|
||||||
|
IDeckLinkMutableVideoFrame *decklinkOutputFrame;
|
||||||
|
|
||||||
void FinalizeStream();
|
void FinalizeStream();
|
||||||
void SetupVideoFormat(DeckLinkDeviceMode *mode_);
|
void SetupVideoFormat(DeckLinkDeviceMode *mode_);
|
||||||
|
|
||||||
|
@ -33,7 +41,7 @@ protected:
|
||||||
const uint64_t timestamp);
|
const uint64_t timestamp);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
DeckLinkDeviceInstance(DeckLink *decklink, DeckLinkDevice *device);
|
DeckLinkDeviceInstance(DecklinkBase *decklink, DeckLinkDevice *device);
|
||||||
virtual ~DeckLinkDeviceInstance();
|
virtual ~DeckLinkDeviceInstance();
|
||||||
|
|
||||||
inline DeckLinkDevice *GetDevice() const {return device;}
|
inline DeckLinkDevice *GetDevice() const {return device;}
|
||||||
|
@ -52,6 +60,9 @@ public:
|
||||||
bool StartCapture(DeckLinkDeviceMode *mode);
|
bool StartCapture(DeckLinkDeviceMode *mode);
|
||||||
bool StopCapture(void);
|
bool StopCapture(void);
|
||||||
|
|
||||||
|
bool StartOutput(DeckLinkDeviceMode *mode_);
|
||||||
|
bool StopOutput(void);
|
||||||
|
|
||||||
HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(
|
HRESULT STDMETHODCALLTYPE VideoInputFrameArrived(
|
||||||
IDeckLinkVideoInputFrame *videoFrame,
|
IDeckLinkVideoInputFrame *videoFrame,
|
||||||
IDeckLinkAudioInputPacket *audioPacket);
|
IDeckLinkAudioInputPacket *audioPacket);
|
||||||
|
@ -63,4 +74,7 @@ public:
|
||||||
ULONG STDMETHODCALLTYPE AddRef(void);
|
ULONG STDMETHODCALLTYPE AddRef(void);
|
||||||
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv);
|
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID iid, LPVOID *ppv);
|
||||||
ULONG STDMETHODCALLTYPE Release(void);
|
ULONG STDMETHODCALLTYPE Release(void);
|
||||||
|
|
||||||
|
void DisplayVideoFrame(video_data *frame);
|
||||||
|
void WriteAudio(audio_data *frames);
|
||||||
};
|
};
|
||||||
|
|
|
@ -32,6 +32,22 @@ BMDDisplayMode DeckLinkDeviceMode::GetDisplayMode(void) const
|
||||||
return bmdModeUnknown;
|
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
|
BMDDisplayModeFlags DeckLinkDeviceMode::GetDisplayModeFlags(void) const
|
||||||
{
|
{
|
||||||
if (mode != nullptr)
|
if (mode != nullptr)
|
||||||
|
|
|
@ -23,4 +23,7 @@ public:
|
||||||
const std::string& GetName(void) const;
|
const std::string& GetName(void) const;
|
||||||
|
|
||||||
void SetMode(IDeckLinkDisplayMode *mode);
|
void SetMode(IDeckLinkDisplayMode *mode);
|
||||||
|
|
||||||
|
int GetWidth();
|
||||||
|
int GetHeight();
|
||||||
};
|
};
|
||||||
|
|
|
@ -10,7 +10,10 @@ DeckLinkDevice::DeckLinkDevice(IDeckLink *device_) : device(device_)
|
||||||
|
|
||||||
DeckLinkDevice::~DeckLinkDevice(void)
|
DeckLinkDevice::~DeckLinkDevice(void)
|
||||||
{
|
{
|
||||||
for (DeckLinkDeviceMode *mode : modes)
|
for (DeckLinkDeviceMode *mode : inputModes)
|
||||||
|
delete mode;
|
||||||
|
|
||||||
|
for (DeckLinkDeviceMode *mode : outputModes)
|
||||||
delete mode;
|
delete mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,17 +40,17 @@ bool DeckLinkDevice::Init()
|
||||||
decklink_bool_t detectable = false;
|
decklink_bool_t detectable = false;
|
||||||
if (attributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection,
|
if (attributes->GetFlag(BMDDeckLinkSupportsInputFormatDetection,
|
||||||
&detectable) == S_OK && !!detectable) {
|
&detectable) == S_OK && !!detectable) {
|
||||||
DeckLinkDeviceMode *mode =
|
DeckLinkDeviceMode *mode = new DeckLinkDeviceMode(
|
||||||
new DeckLinkDeviceMode("Auto", MODE_ID_AUTO);
|
"Auto",
|
||||||
modes.push_back(mode);
|
MODE_ID_AUTO);
|
||||||
modeIdMap[MODE_ID_AUTO] = mode;
|
inputModes.push_back(mode);
|
||||||
|
inputModeIdMap[MODE_ID_AUTO] = mode;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Find input modes
|
||||||
ComPtr<IDeckLinkInput> input;
|
ComPtr<IDeckLinkInput> input;
|
||||||
if (device->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK)
|
if (device->QueryInterface(IID_IDeckLinkInput, (void **) &input) == S_OK) {
|
||||||
return false;
|
|
||||||
|
|
||||||
IDeckLinkDisplayModeIterator *modeIterator;
|
IDeckLinkDisplayModeIterator *modeIterator;
|
||||||
if (input->GetDisplayModeIterator(&modeIterator) == S_OK) {
|
if (input->GetDisplayModeIterator(&modeIterator) == S_OK) {
|
||||||
IDeckLinkDisplayMode *displayMode;
|
IDeckLinkDisplayMode *displayMode;
|
||||||
|
@ -59,14 +62,40 @@ bool DeckLinkDevice::Init()
|
||||||
|
|
||||||
DeckLinkDeviceMode *mode =
|
DeckLinkDeviceMode *mode =
|
||||||
new DeckLinkDeviceMode(displayMode, modeId);
|
new DeckLinkDeviceMode(displayMode, modeId);
|
||||||
modes.push_back(mode);
|
inputModes.push_back(mode);
|
||||||
modeIdMap[modeId] = mode;
|
inputModeIdMap[modeId] = mode;
|
||||||
displayMode->Release();
|
displayMode->Release();
|
||||||
++modeId;
|
++modeId;
|
||||||
}
|
}
|
||||||
|
|
||||||
modeIterator->Release();
|
modeIterator->Release();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find output modes
|
||||||
|
ComPtr<IDeckLinkOutput> output;
|
||||||
|
if (device->QueryInterface(IID_IDeckLinkOutput, (void **) &output) == S_OK) {
|
||||||
|
|
||||||
|
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;
|
decklink_string_t decklinkModelName;
|
||||||
decklink_string_t decklinkDisplayName;
|
decklink_string_t decklinkDisplayName;
|
||||||
|
@ -115,9 +144,22 @@ bool DeckLinkDevice::GetInput(IDeckLinkInput **input)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
DeckLinkDeviceMode *DeckLinkDevice::FindMode(long long id)
|
bool DeckLinkDevice::GetOutput(IDeckLinkOutput **output)
|
||||||
{
|
{
|
||||||
return modeIdMap[id];
|
if (device->QueryInterface(IID_IDeckLinkOutput, (void**)output) != S_OK)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
DeckLinkDeviceMode *DeckLinkDevice::FindInputMode(long long id)
|
||||||
|
{
|
||||||
|
return inputModeIdMap[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
DeckLinkDeviceMode *DeckLinkDevice::FindOutputMode(long long id)
|
||||||
|
{
|
||||||
|
return outputModeIdMap[id];
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& DeckLinkDevice::GetDisplayName(void)
|
const std::string& DeckLinkDevice::GetDisplayName(void)
|
||||||
|
@ -130,9 +172,14 @@ const std::string& DeckLinkDevice::GetHash(void) const
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<DeckLinkDeviceMode *>& DeckLinkDevice::GetModes(void) const
|
const std::vector<DeckLinkDeviceMode *>& DeckLinkDevice::GetInputModes(void) const
|
||||||
{
|
{
|
||||||
return modes;
|
return inputModes;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<DeckLinkDeviceMode *>& DeckLinkDevice::GetOutputModes(void) const
|
||||||
|
{
|
||||||
|
return outputModes;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::string& DeckLinkDevice::GetName(void) const
|
const std::string& DeckLinkDevice::GetName(void) const
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "decklink.hpp"
|
|
||||||
#include "decklink-device-mode.hpp"
|
#include "decklink-device-mode.hpp"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
@ -9,8 +8,10 @@
|
||||||
|
|
||||||
class DeckLinkDevice {
|
class DeckLinkDevice {
|
||||||
ComPtr<IDeckLink> device;
|
ComPtr<IDeckLink> device;
|
||||||
std::map<long long, DeckLinkDeviceMode *> modeIdMap;
|
std::map<long long, DeckLinkDeviceMode *> inputModeIdMap;
|
||||||
std::vector<DeckLinkDeviceMode *> modes;
|
std::vector<DeckLinkDeviceMode *> inputModes;
|
||||||
|
std::map<long long, DeckLinkDeviceMode *> outputModeIdMap;
|
||||||
|
std::vector<DeckLinkDeviceMode *> outputModes;
|
||||||
std::string name;
|
std::string name;
|
||||||
std::string displayName;
|
std::string displayName;
|
||||||
std::string hash;
|
std::string hash;
|
||||||
|
@ -26,14 +27,17 @@ public:
|
||||||
|
|
||||||
bool Init();
|
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& GetDisplayName(void);
|
||||||
const std::string& GetHash(void) const;
|
const std::string& GetHash(void) const;
|
||||||
const std::vector<DeckLinkDeviceMode *>& GetModes(void) const;
|
const std::vector<DeckLinkDeviceMode *>& GetInputModes(void) const;
|
||||||
|
const std::vector<DeckLinkDeviceMode *>& GetOutputModes(void) const;
|
||||||
const std::string& GetName(void) const;
|
const std::string& GetName(void) const;
|
||||||
int32_t GetMaxChannel(void) const;
|
int32_t GetMaxChannel(void) const;
|
||||||
|
|
||||||
bool GetInput(IDeckLinkInput **input);
|
bool GetInput(IDeckLinkInput **input);
|
||||||
|
bool GetOutput(IDeckLinkOutput **output);
|
||||||
|
|
||||||
inline bool IsDevice(IDeckLink *device_)
|
inline bool IsDevice(IDeckLink *device_)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#include "decklink-devices.hpp"
|
||||||
|
|
||||||
|
DeckLinkDeviceDiscovery *deviceEnum = nullptr;
|
||||||
|
|
||||||
|
void fill_out_devices(obs_property_t *list)
|
||||||
|
{
|
||||||
|
deviceEnum->Lock();
|
||||||
|
|
||||||
|
const std::vector<DeckLinkDevice*> &devices = deviceEnum->GetDevices();
|
||||||
|
for (DeckLinkDevice *device : devices) {
|
||||||
|
obs_property_list_add_string(list,
|
||||||
|
device->GetDisplayName().c_str(),
|
||||||
|
device->GetHash().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
deviceEnum->Unlock();
|
||||||
|
}
|
|
@ -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);
|
|
@ -0,0 +1,239 @@
|
||||||
|
#include <obs-module.h>
|
||||||
|
#include <obs-avc.h>
|
||||||
|
|
||||||
|
#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<DeckLinkDevice> 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<DeckLinkDevice> 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<DeckLinkDevice> 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<DeckLinkDeviceMode*> &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;
|
||||||
|
}
|
|
@ -0,0 +1,288 @@
|
||||||
|
#include <obs-module.h>
|
||||||
|
|
||||||
|
#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<DeckLinkDevice> 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<DeckLinkDevice> 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<DeckLinkDevice> 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<DeckLinkDeviceMode*> &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;
|
||||||
|
}
|
|
@ -21,8 +21,12 @@ set(linux-decklink-sdk_SOURCES
|
||||||
)
|
)
|
||||||
|
|
||||||
set(linux-decklink_HEADERS
|
set(linux-decklink_HEADERS
|
||||||
|
../decklink-devices.hpp
|
||||||
|
../const.h
|
||||||
|
../DecklinkOutput.hpp
|
||||||
../platform.hpp
|
../platform.hpp
|
||||||
../decklink.hpp
|
../DecklinkInput.hpp
|
||||||
|
../DecklinkBase.h
|
||||||
../decklink-device-instance.hpp
|
../decklink-device-instance.hpp
|
||||||
../decklink-device-discovery.hpp
|
../decklink-device-discovery.hpp
|
||||||
../decklink-device.hpp
|
../decklink-device.hpp
|
||||||
|
@ -33,21 +37,29 @@ set(linux-decklink_HEADERS
|
||||||
|
|
||||||
set(linux-decklink_SOURCES
|
set(linux-decklink_SOURCES
|
||||||
../plugin-main.cpp
|
../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-instance.cpp
|
||||||
../decklink-device-discovery.cpp
|
../decklink-device-discovery.cpp
|
||||||
../decklink-device.cpp
|
../decklink-device.cpp
|
||||||
../decklink-device-mode.cpp
|
../decklink-device-mode.cpp
|
||||||
../audio-repack.c
|
../audio-repack.c
|
||||||
platform.cpp)
|
platform.cpp
|
||||||
|
)
|
||||||
|
|
||||||
add_library(linux-decklink MODULE
|
add_library(linux-decklink MODULE
|
||||||
${linux-decklink_SOURCES}
|
${linux-decklink_SOURCES}
|
||||||
${linux-decklink_HEADERS}
|
${linux-decklink_HEADERS}
|
||||||
${linux-decklink-sdk_HEADERS}
|
${linux-decklink-sdk_HEADERS}
|
||||||
${linux-decklink-sdk_SOURCES})
|
${linux-decklink-sdk_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(linux-decklink
|
target_link_libraries(linux-decklink
|
||||||
libobs)
|
libobs
|
||||||
|
)
|
||||||
|
|
||||||
install_obs_plugin_with_data(linux-decklink ../data)
|
install_obs_plugin_with_data(linux-decklink ../data)
|
||||||
|
|
|
@ -5,9 +5,9 @@ if(DISABLE_DECKLINK)
|
||||||
return()
|
return()
|
||||||
endif()
|
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
|
set(mac-decklink-sdk_HEADERS
|
||||||
decklink-sdk/DeckLinkAPI.h
|
decklink-sdk/DeckLinkAPI.h
|
||||||
|
@ -17,16 +17,19 @@ set(mac-decklink-sdk_HEADERS
|
||||||
decklink-sdk/DeckLinkAPIModes.h
|
decklink-sdk/DeckLinkAPIModes.h
|
||||||
decklink-sdk/DeckLinkAPIStreaming.h
|
decklink-sdk/DeckLinkAPIStreaming.h
|
||||||
decklink-sdk/DeckLinkAPITypes.h
|
decklink-sdk/DeckLinkAPITypes.h
|
||||||
decklink-sdk/DeckLinkAPIVersion.h
|
decklink-sdk/DeckLinkAPIVersion.h)
|
||||||
)
|
|
||||||
|
|
||||||
set(mac-decklink-sdk_SOURCES
|
set(mac-decklink-sdk_SOURCES
|
||||||
decklink-sdk/DeckLinkAPIDispatch.cpp
|
decklink-sdk/DeckLinkAPIDispatch.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
set(mac-decklink_HEADERS
|
set(mac-decklink_HEADERS
|
||||||
|
../decklink-devices.hpp
|
||||||
|
../const.h
|
||||||
|
../DecklinkOutput.hpp
|
||||||
../platform.hpp
|
../platform.hpp
|
||||||
../decklink.hpp
|
../DecklinkInput.hpp
|
||||||
|
../DecklinkBase.h
|
||||||
../decklink-device-instance.hpp
|
../decklink-device-instance.hpp
|
||||||
../decklink-device-discovery.hpp
|
../decklink-device-discovery.hpp
|
||||||
../decklink-device.hpp
|
../decklink-device.hpp
|
||||||
|
@ -37,19 +40,34 @@ set(mac-decklink_HEADERS
|
||||||
|
|
||||||
set(mac-decklink_SOURCES
|
set(mac-decklink_SOURCES
|
||||||
../plugin-main.cpp
|
../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-instance.cpp
|
||||||
../decklink-device-discovery.cpp
|
../decklink-device-discovery.cpp
|
||||||
../decklink-device.cpp
|
../decklink-device.cpp
|
||||||
../decklink-device-mode.cpp
|
../decklink-device-mode.cpp
|
||||||
../audio-repack.c
|
../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
|
add_library(mac-decklink MODULE
|
||||||
${mac-decklink_SOURCES}
|
${mac-decklink_SOURCES}
|
||||||
${mac-decklink_HEADERS}
|
${mac-decklink_HEADERS}
|
||||||
${mac-decklink-sdk_HEADERS}
|
${mac-decklink-sdk_HEADERS}
|
||||||
${mac-decklink-sdk_SOURCES})
|
${mac-decklink-sdk_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(mac-decklink
|
target_link_libraries(mac-decklink
|
||||||
libobs
|
libobs
|
||||||
|
|
|
@ -1,323 +1,14 @@
|
||||||
#include "decklink.hpp"
|
|
||||||
#include "decklink-device.hpp"
|
|
||||||
#include "decklink-device-discovery.hpp"
|
|
||||||
|
|
||||||
#include <obs-module.h>
|
#include <obs-module.h>
|
||||||
|
#include "decklink-devices.hpp"
|
||||||
|
|
||||||
OBS_DECLARE_MODULE()
|
OBS_DECLARE_MODULE()
|
||||||
OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US")
|
OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US")
|
||||||
|
|
||||||
#define DEVICE_HASH "device_hash"
|
extern struct obs_source_info create_decklink_source_info();
|
||||||
#define DEVICE_NAME "device_name"
|
struct obs_source_info decklink_source_info;
|
||||||
#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")
|
extern struct obs_output_info create_decklink_output_info();
|
||||||
#define TEXT_MODE obs_module_text("Mode")
|
struct obs_output_info decklink_output_info;
|
||||||
#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<DeckLinkDevice> 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<DeckLinkDevice> 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;
|
|
||||||
|
|
||||||
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<DeckLinkDevice> 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<DeckLinkDeviceMode*> &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<DeckLinkDevice*> &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;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool obs_module_load(void)
|
bool obs_module_load(void)
|
||||||
{
|
{
|
||||||
|
@ -325,21 +16,11 @@ bool obs_module_load(void)
|
||||||
if (!deviceEnum->Init())
|
if (!deviceEnum->Init())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
struct obs_source_info info = {};
|
decklink_source_info = create_decklink_source_info();
|
||||||
info.id = "decklink-input";
|
obs_register_source(&decklink_source_info);
|
||||||
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;
|
|
||||||
|
|
||||||
obs_register_source(&info);
|
decklink_output_info = create_decklink_output_info();
|
||||||
|
obs_register_output(&decklink_output_info);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,12 @@ set(win-decklink-sdk_HEADERS
|
||||||
)
|
)
|
||||||
|
|
||||||
set(win-decklink_HEADERS
|
set(win-decklink_HEADERS
|
||||||
|
../decklink-devices.hpp
|
||||||
|
../DecklinkOutput.hpp
|
||||||
|
../const.h
|
||||||
../platform.hpp
|
../platform.hpp
|
||||||
../decklink.hpp
|
../DecklinkInput.hpp
|
||||||
|
../DecklinkBase.h
|
||||||
../decklink-device-instance.hpp
|
../decklink-device-instance.hpp
|
||||||
../decklink-device-discovery.hpp
|
../decklink-device-discovery.hpp
|
||||||
../decklink-device.hpp
|
../decklink-device.hpp
|
||||||
|
@ -28,25 +32,34 @@ set(win-decklink_HEADERS
|
||||||
|
|
||||||
set(win-decklink_SOURCES
|
set(win-decklink_SOURCES
|
||||||
../plugin-main.cpp
|
../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-instance.cpp
|
||||||
../decklink-device-discovery.cpp
|
../decklink-device-discovery.cpp
|
||||||
../decklink-device.cpp
|
../decklink-device.cpp
|
||||||
../decklink-device-mode.cpp
|
../decklink-device-mode.cpp
|
||||||
../audio-repack.c
|
../audio-repack.c
|
||||||
platform.cpp)
|
platform.cpp
|
||||||
|
)
|
||||||
|
|
||||||
add_idl_files(win-decklink-sdk_GENERATED_FILES
|
add_idl_files(win-decklink-sdk_GENERATED_FILES
|
||||||
${win-decklink-sdk_IDLS})
|
${win-decklink-sdk_IDLS}
|
||||||
|
)
|
||||||
|
|
||||||
include_directories(
|
include_directories(
|
||||||
${CMAKE_CURRENT_BINARY_DIR})
|
${CMAKE_CURRENT_BINARY_DIR}
|
||||||
|
)
|
||||||
|
|
||||||
add_library(win-decklink MODULE
|
add_library(win-decklink MODULE
|
||||||
${win-decklink_SOURCES}
|
${win-decklink_SOURCES}
|
||||||
${win-decklink_HEADERS}
|
${win-decklink_HEADERS}
|
||||||
${win-decklink-sdk_HEADERS}
|
${win-decklink-sdk_HEADERS}
|
||||||
${win-decklink-sdk_GENERATED_FILES})
|
${win-decklink-sdk_GENERATED_FILES}
|
||||||
|
)
|
||||||
|
|
||||||
target_link_libraries(win-decklink
|
target_link_libraries(win-decklink
|
||||||
libobs)
|
libobs)
|
||||||
|
|
Loading…
Reference in New Issue