decklink: Add feature to detect resolution/format
Closes jp9000/obs-studio#879
This commit is contained in:
parent
1e7e50114e
commit
41c2f5e13b
@ -3,6 +3,12 @@ Device="Device"
|
||||
Mode="Mode"
|
||||
Buffering="Use Buffering"
|
||||
PixelFormat="Pixel Format"
|
||||
ColorSpace="YUV Color Space"
|
||||
ColorSpace.Default="Default"
|
||||
ColorRange="YUV Color Range"
|
||||
ColorRange.Default="Default"
|
||||
ColorRange.Partial="Partial"
|
||||
ColorRange.Full="Full"
|
||||
ChannelFormat="Channel"
|
||||
ChannelFormat.None="None"
|
||||
ChannelFormat.2_0ch="2ch"
|
||||
|
@ -118,16 +118,15 @@ void DeckLinkDeviceInstance::HandleVideoFrame(
|
||||
currentFrame.height = (uint32_t)videoFrame->GetHeight();
|
||||
currentFrame.timestamp = timestamp;
|
||||
|
||||
video_format_get_parameters(VIDEO_CS_601, VIDEO_RANGE_PARTIAL,
|
||||
currentFrame.color_matrix, currentFrame.color_range_min,
|
||||
currentFrame.color_range_max);
|
||||
|
||||
obs_source_output_video(decklink->GetSource(), ¤tFrame);
|
||||
}
|
||||
|
||||
void DeckLinkDeviceInstance::FinalizeStream()
|
||||
{
|
||||
input->SetCallback(nullptr);
|
||||
input->DisableVideoInput();
|
||||
if (channelFormat != SPEAKERS_UNKNOWN)
|
||||
input->DisableAudioInput();
|
||||
|
||||
if (audioRepacker != nullptr)
|
||||
{
|
||||
@ -138,6 +137,43 @@ void DeckLinkDeviceInstance::FinalizeStream()
|
||||
mode = nullptr;
|
||||
}
|
||||
|
||||
//#define LOG_SETUP_VIDEO_FORMAT 1
|
||||
|
||||
void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_)
|
||||
{
|
||||
if (mode_ == nullptr)
|
||||
return;
|
||||
|
||||
currentFrame.format = ConvertPixelFormat(pixelFormat);
|
||||
|
||||
colorSpace = decklink->GetColorSpace();
|
||||
if (colorSpace == VIDEO_CS_DEFAULT) {
|
||||
const BMDDisplayModeFlags flags = mode_->GetDisplayModeFlags();
|
||||
if (flags & bmdDisplayModeColorspaceRec709)
|
||||
activeColorSpace = VIDEO_CS_709;
|
||||
else if (flags & bmdDisplayModeColorspaceRec601)
|
||||
activeColorSpace = VIDEO_CS_601;
|
||||
else
|
||||
activeColorSpace = VIDEO_CS_DEFAULT;
|
||||
} else {
|
||||
activeColorSpace = colorSpace;
|
||||
}
|
||||
|
||||
colorRange = decklink->GetColorRange();
|
||||
currentFrame.full_range = colorRange == VIDEO_RANGE_FULL;
|
||||
|
||||
video_format_get_parameters(activeColorSpace, colorRange,
|
||||
currentFrame.color_matrix, currentFrame.color_range_min,
|
||||
currentFrame.color_range_max);
|
||||
|
||||
#ifdef LOG_SETUP_VIDEO_FORMAT
|
||||
LOG(LOG_INFO, "Setup video format: %s, %s, %s",
|
||||
pixelFormat == bmdFormat8BitYUV ? "YUV" : "RGB",
|
||||
activeColorSpace == VIDEO_CS_709 ? "BT.709" : "BT.601",
|
||||
colorRange == VIDEO_RANGE_FULL ? "full" : "limited");
|
||||
#endif
|
||||
}
|
||||
|
||||
bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
|
||||
{
|
||||
if (mode != nullptr)
|
||||
@ -150,19 +186,28 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
|
||||
if (!device->GetInput(&input))
|
||||
return false;
|
||||
|
||||
pixelFormat = decklink->GetPixelFormat();
|
||||
currentFrame.format = ConvertPixelFormat(pixelFormat);
|
||||
BMDVideoInputFlags flags;
|
||||
|
||||
const BMDDisplayMode displayMode = mode_->GetDisplayMode();
|
||||
bool isauto = mode_->GetName() == "Auto";
|
||||
if (isauto) {
|
||||
displayMode = bmdModeNTSC;
|
||||
pixelFormat = bmdFormat8BitYUV;
|
||||
flags = bmdVideoInputEnableFormatDetection;
|
||||
} else {
|
||||
displayMode = mode_->GetDisplayMode();
|
||||
pixelFormat = decklink->GetPixelFormat();
|
||||
flags = bmdVideoInputFlagDefault;
|
||||
}
|
||||
|
||||
const HRESULT videoResult = input->EnableVideoInput(displayMode,
|
||||
pixelFormat, bmdVideoInputFlagDefault);
|
||||
|
||||
pixelFormat, flags);
|
||||
if (videoResult != S_OK) {
|
||||
LOG(LOG_ERROR, "Failed to enable video input");
|
||||
return false;
|
||||
}
|
||||
|
||||
SetupVideoFormat(mode_);
|
||||
|
||||
channelFormat = decklink->GetChannelFormat();
|
||||
currentPacket.speakers = channelFormat;
|
||||
|
||||
@ -171,7 +216,6 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
|
||||
const HRESULT audioResult = input->EnableAudioInput(
|
||||
bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
|
||||
channel);
|
||||
|
||||
if (audioResult != S_OK)
|
||||
LOG(LOG_WARNING, "Failed to enable audio input; continuing...");
|
||||
|
||||
@ -257,12 +301,41 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFormatChanged(
|
||||
IDeckLinkDisplayMode *newMode,
|
||||
BMDDetectedVideoInputFormatFlags detectedSignalFlags)
|
||||
{
|
||||
UNUSED_PARAMETER(events);
|
||||
UNUSED_PARAMETER(newMode);
|
||||
UNUSED_PARAMETER(detectedSignalFlags);
|
||||
input->PauseStreams();
|
||||
|
||||
// There is no implementation for automatic format detection, so this
|
||||
// method goes unused.
|
||||
mode->SetMode(newMode);
|
||||
|
||||
if (events & bmdVideoInputDisplayModeChanged) {
|
||||
displayMode = mode->GetDisplayMode();
|
||||
}
|
||||
|
||||
if (events & bmdVideoInputColorspaceChanged) {
|
||||
switch (detectedSignalFlags) {
|
||||
case bmdDetectedVideoInputRGB444:
|
||||
pixelFormat = bmdFormat8BitBGRA;
|
||||
break;
|
||||
|
||||
default:
|
||||
case bmdDetectedVideoInputYCbCr422:
|
||||
pixelFormat = bmdFormat8BitYUV;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const HRESULT videoResult = input->EnableVideoInput(displayMode,
|
||||
pixelFormat, bmdVideoInputEnableFormatDetection);
|
||||
if (videoResult != S_OK) {
|
||||
LOG(LOG_ERROR, "Failed to enable video input");
|
||||
input->StopStreams();
|
||||
FinalizeStream();
|
||||
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
SetupVideoFormat(mode);
|
||||
|
||||
input->FlushStreams();
|
||||
input->StartStreams();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
@ -11,7 +11,11 @@ protected:
|
||||
DeckLink *decklink = nullptr;
|
||||
DeckLinkDevice *device = nullptr;
|
||||
DeckLinkDeviceMode *mode = nullptr;
|
||||
BMDDisplayMode displayMode = bmdModeNTSC;
|
||||
BMDPixelFormat pixelFormat = bmdFormat8BitYUV;
|
||||
video_colorspace colorSpace = VIDEO_CS_DEFAULT;
|
||||
video_colorspace activeColorSpace = VIDEO_CS_DEFAULT;
|
||||
video_range_type colorRange = VIDEO_RANGE_DEFAULT;
|
||||
ComPtr<IDeckLinkInput> input;
|
||||
volatile long refCount = 1;
|
||||
int64_t audioOffset = 0;
|
||||
@ -21,6 +25,7 @@ protected:
|
||||
speaker_layout channelFormat = SPEAKERS_STEREO;
|
||||
|
||||
void FinalizeStream();
|
||||
void SetupVideoFormat(DeckLinkDeviceMode *mode_);
|
||||
|
||||
void HandleAudioPacket(IDeckLinkAudioInputPacket *audioPacket,
|
||||
const uint64_t timestamp);
|
||||
@ -38,6 +43,8 @@ public:
|
||||
}
|
||||
|
||||
inline BMDPixelFormat GetActivePixelFormat() const {return pixelFormat;}
|
||||
inline video_colorspace GetActiveColorSpace() const {return colorSpace;}
|
||||
inline video_range_type GetActiveColorRange() const {return colorRange;}
|
||||
inline speaker_layout GetActiveChannelFormat() const {return channelFormat;}
|
||||
|
||||
inline DeckLinkDeviceMode *GetMode() const {return mode;}
|
||||
|
@ -32,6 +32,14 @@ BMDDisplayMode DeckLinkDeviceMode::GetDisplayMode(void) const
|
||||
return bmdModeUnknown;
|
||||
}
|
||||
|
||||
BMDDisplayModeFlags DeckLinkDeviceMode::GetDisplayModeFlags(void) const
|
||||
{
|
||||
if (mode != nullptr)
|
||||
return mode->GetFlags();
|
||||
|
||||
return (BMDDisplayModeFlags)0;
|
||||
}
|
||||
|
||||
long long DeckLinkDeviceMode::GetId(void) const
|
||||
{
|
||||
return id;
|
||||
@ -41,3 +49,14 @@ const std::string& DeckLinkDeviceMode::GetName(void) const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
void DeckLinkDeviceMode::SetMode(IDeckLinkDisplayMode *mode_)
|
||||
{
|
||||
IDeckLinkDisplayMode *old = mode;
|
||||
if (old != nullptr)
|
||||
old->Release();
|
||||
|
||||
mode = mode_;
|
||||
if (mode != nullptr)
|
||||
mode->AddRef();
|
||||
}
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
#define MODE_ID_AUTO -1
|
||||
|
||||
class DeckLinkDeviceMode {
|
||||
protected:
|
||||
long long id;
|
||||
@ -16,6 +18,9 @@ public:
|
||||
virtual ~DeckLinkDeviceMode(void);
|
||||
|
||||
BMDDisplayMode GetDisplayMode(void) const;
|
||||
BMDDisplayModeFlags GetDisplayModeFlags(void) const;
|
||||
long long GetId(void) const;
|
||||
const std::string& GetName(void) const;
|
||||
|
||||
void SetMode(IDeckLinkDisplayMode *mode);
|
||||
};
|
||||
|
@ -29,6 +29,21 @@ ULONG DeckLinkDevice::Release()
|
||||
|
||||
bool DeckLinkDevice::Init()
|
||||
{
|
||||
ComPtr<IDeckLinkAttributes> attributes;
|
||||
const HRESULT result = device->QueryInterface(IID_IDeckLinkAttributes,
|
||||
(void **)&attributes);
|
||||
|
||||
if (result == S_OK) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
ComPtr<IDeckLinkInput> input;
|
||||
if (device->QueryInterface(IID_IDeckLinkInput, (void**)&input) != S_OK)
|
||||
return false;
|
||||
@ -66,9 +81,6 @@ bool DeckLinkDevice::Init()
|
||||
|
||||
hash = displayName;
|
||||
|
||||
ComPtr<IDeckLinkAttributes> attributes;
|
||||
const HRESULT result = device->QueryInterface(IID_IDeckLinkAttributes,
|
||||
(void **)&attributes);
|
||||
if (result != S_OK)
|
||||
return true;
|
||||
|
||||
|
@ -66,6 +66,8 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId)
|
||||
return false;
|
||||
if (instance->GetActiveModeId() == modeId &&
|
||||
instance->GetActivePixelFormat() == pixelFormat &&
|
||||
instance->GetActiveColorSpace() == colorSpace &&
|
||||
instance->GetActiveColorRange() == colorRange &&
|
||||
instance->GetActiveChannelFormat() == channelFormat)
|
||||
return false;
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ protected:
|
||||
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();
|
||||
@ -42,6 +44,16 @@ public:
|
||||
{
|
||||
pixelFormat = format;
|
||||
}
|
||||
inline video_colorspace GetColorSpace() const {return colorSpace;}
|
||||
inline void SetColorSpace(video_colorspace format)
|
||||
{
|
||||
colorSpace = format;
|
||||
}
|
||||
inline video_range_type GetColorRange() const {return colorRange;}
|
||||
inline void SetColorRange(video_range_type format)
|
||||
{
|
||||
colorRange = format;
|
||||
}
|
||||
inline speaker_layout GetChannelFormat() const {return channelFormat;}
|
||||
inline void SetChannelFormat(speaker_layout format)
|
||||
{
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#if defined(_WIN32)
|
||||
#include <DeckLinkAPI.h>
|
||||
typedef BOOL decklink_bool_t;
|
||||
typedef BSTR decklink_string_t;
|
||||
IDeckLinkDiscovery *CreateDeckLinkDiscoveryInstance(void);
|
||||
#define IUnknownUUID IID_IUnknown
|
||||
@ -10,9 +11,11 @@ typedef REFIID CFUUIDBytes;
|
||||
#elif defined(__APPLE__)
|
||||
#include "mac/decklink-sdk/DeckLinkAPI.h"
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
typedef bool decklink_bool_t;
|
||||
typedef CFStringRef decklink_string_t;
|
||||
#elif defined(__linux__)
|
||||
#include "linux/decklink-sdk/DeckLinkAPI.h"
|
||||
typedef bool decklink_bool_t;
|
||||
typedef const char *decklink_string_t;
|
||||
#endif
|
||||
|
||||
|
@ -13,11 +13,19 @@ OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US")
|
||||
#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 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")
|
||||
@ -58,6 +66,10 @@ static void decklink_update(void *data, obs_data_t *settings)
|
||||
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);
|
||||
speaker_layout channelFormat = (speaker_layout)obs_data_get_int(settings,
|
||||
CHANNEL_FORMAT);
|
||||
|
||||
@ -68,6 +80,8 @@ static void decklink_update(void *data, obs_data_t *settings)
|
||||
device.Set(deviceEnum->FindByHash(hash));
|
||||
|
||||
decklink->SetPixelFormat(pixelFormat);
|
||||
decklink->SetColorSpace(colorSpace);
|
||||
decklink->SetColorRange(colorRange);
|
||||
decklink->SetChannelFormat(channelFormat);
|
||||
decklink->Activate(device, id);
|
||||
}
|
||||
@ -76,6 +90,8 @@ static void decklink_get_defaults(obs_data_t *settings)
|
||||
{
|
||||
obs_data_set_default_bool(settings, BUFFERING, true);
|
||||
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);
|
||||
}
|
||||
|
||||
@ -162,6 +178,38 @@ static void fill_out_devices(obs_property_t *list)
|
||||
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();
|
||||
@ -174,13 +222,28 @@ static obs_properties_t *decklink_get_properties(void *data)
|
||||
|
||||
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user