From 8285141ba5e5687e7ba1b3c97f303583941cfcbf Mon Sep 17 00:00:00 2001 From: Colin Edwards Date: Wed, 16 Dec 2020 14:44:37 -0600 Subject: [PATCH] decklink: Fix automatic pixel format detection --- plugins/decklink/DecklinkInput.cpp | 2 +- plugins/decklink/DecklinkInput.hpp | 1 + plugins/decklink/OBSVideoFrame.cpp | 9 ++- plugins/decklink/OBSVideoFrame.h | 2 +- plugins/decklink/const.h | 2 + plugins/decklink/data/locale/en-US.ini | 1 + plugins/decklink/decklink-device-instance.cpp | 77 +++++++++++++------ plugins/decklink/decklink-device-instance.hpp | 3 +- plugins/decklink/decklink-source.cpp | 7 ++ 9 files changed, 76 insertions(+), 28 deletions(-) diff --git a/plugins/decklink/DecklinkInput.cpp b/plugins/decklink/DecklinkInput.cpp index 965f4dd15..db6a40761 100644 --- a/plugins/decklink/DecklinkInput.cpp +++ b/plugins/decklink/DecklinkInput.cpp @@ -100,7 +100,7 @@ bool DeckLinkInput::Activate(DeckLinkDevice *device, long long modeId, return false; } - if (!instance->StartCapture(mode, bmdVideoConnection, + if (!instance->StartCapture(mode, allow10Bit, bmdVideoConnection, bmdAudioConnection)) { instance = nullptr; return false; diff --git a/plugins/decklink/DecklinkInput.hpp b/plugins/decklink/DecklinkInput.hpp index 7ebd8948c..a030f7545 100644 --- a/plugins/decklink/DecklinkInput.hpp +++ b/plugins/decklink/DecklinkInput.hpp @@ -50,6 +50,7 @@ public: std::string hash; long long id; bool swap = false; + bool allow10Bit = false; BMDVideoConnection videoConnection; BMDAudioConnection audioConnection; }; diff --git a/plugins/decklink/OBSVideoFrame.cpp b/plugins/decklink/OBSVideoFrame.cpp index a06412616..81e68033e 100644 --- a/plugins/decklink/OBSVideoFrame.cpp +++ b/plugins/decklink/OBSVideoFrame.cpp @@ -1,11 +1,14 @@ #include "OBSVideoFrame.h" -OBSVideoFrame::OBSVideoFrame(long width, long height) +OBSVideoFrame::OBSVideoFrame(long width, long height, + BMDPixelFormat pixelFormat) { + int bpp = 2; this->width = width; this->height = height; - this->rowBytes = width * 2; - this->data = new unsigned char[width * height * 2 + 1]; + this->rowBytes = width * bpp; + this->data = new unsigned char[width * height * bpp + 1]; + this->pixelFormat = pixelFormat; } HRESULT OBSVideoFrame::SetFlags(BMDFrameFlags newFlags) diff --git a/plugins/decklink/OBSVideoFrame.h b/plugins/decklink/OBSVideoFrame.h index 934ecf046..9551f10ab 100644 --- a/plugins/decklink/OBSVideoFrame.h +++ b/plugins/decklink/OBSVideoFrame.h @@ -15,7 +15,7 @@ private: unsigned char *data; public: - OBSVideoFrame(long width, long height); + OBSVideoFrame(long width, long height, BMDPixelFormat pixelFormat); HRESULT STDMETHODCALLTYPE SetFlags(BMDFrameFlags newFlags) override; diff --git a/plugins/decklink/const.h b/plugins/decklink/const.h index bde439eeb..db97d0594 100644 --- a/plugins/decklink/const.h +++ b/plugins/decklink/const.h @@ -13,6 +13,7 @@ #define AUTO_START "auto_start" #define KEYER "keyer" #define SWAP "swap" +#define ALLOW_10_BIT "allow_10_bit" #define TEXT_DEVICE obs_module_text("Device") #define TEXT_VIDEO_CONNECTION obs_module_text("VideoConnection") @@ -39,3 +40,4 @@ #define TEXT_ENABLE_KEYER obs_module_text("Keyer") #define TEXT_SWAP obs_module_text("SwapFC-LFE") #define TEXT_SWAP_TOOLTIP obs_module_text("SwapFC-LFE.Tooltip") +#define TEXT_ALLOW_10_BIT obs_module_text("Allow10Bit") diff --git a/plugins/decklink/data/locale/en-US.ini b/plugins/decklink/data/locale/en-US.ini index a73f79d83..8c6554bbf 100644 --- a/plugins/decklink/data/locale/en-US.ini +++ b/plugins/decklink/data/locale/en-US.ini @@ -23,3 +23,4 @@ SwapFC-LFE="Swap FC and LFE" SwapFC-LFE.Tooltip="Swap Front Center Channel and LFE Channel" VideoConnection="Video Connection" AudioConnection="Audio Connection" +Allow10Bit="Allow 10 Bit (Required for SDI captions, may cause performance overhead)" \ No newline at end of file diff --git a/plugins/decklink/decklink-device-instance.cpp b/plugins/decklink/decklink-device-instance.cpp index 4adc7e01c..47d17a4a0 100644 --- a/plugins/decklink/decklink-device-instance.cpp +++ b/plugins/decklink/decklink-device-instance.cpp @@ -24,10 +24,10 @@ static inline enum video_format ConvertPixelFormat(BMDPixelFormat format) return VIDEO_FORMAT_BGRX; default: - case bmdFormat8BitYUV:; + case bmdFormat8BitYUV: + case bmdFormat10BitYUV:; + return VIDEO_FORMAT_UYVY; } - - return VIDEO_FORMAT_UYVY; } static inline int ConvertChannelFormat(speaker_layout format) @@ -168,21 +168,28 @@ void DeckLinkDeviceInstance::HandleVideoFrame( packets->Release(); } - IDeckLinkVideoConversion *frameConverter = - CreateVideoConversionInstance(); + IDeckLinkVideoFrame *frame; + if (videoFrame->GetPixelFormat() != convertFrame->GetPixelFormat()) { + IDeckLinkVideoConversion *frameConverter = + CreateVideoConversionInstance(); - frameConverter->ConvertFrame(videoFrame, convertFrame); + frameConverter->ConvertFrame(videoFrame, convertFrame); + + frame = convertFrame; + } else { + frame = videoFrame; + } void *bytes; - if (convertFrame->GetBytes(&bytes) != S_OK) { + if (frame->GetBytes(&bytes) != S_OK) { LOG(LOG_WARNING, "Failed to get video frame data"); return; } currentFrame.data[0] = (uint8_t *)bytes; - currentFrame.linesize[0] = (uint32_t)convertFrame->GetRowBytes(); - currentFrame.width = (uint32_t)convertFrame->GetWidth(); - currentFrame.height = (uint32_t)convertFrame->GetHeight(); + currentFrame.linesize[0] = (uint32_t)frame->GetRowBytes(); + currentFrame.width = (uint32_t)frame->GetWidth(); + currentFrame.height = (uint32_t)frame->GetHeight(); currentFrame.timestamp = timestamp; obs_source_output_video2( @@ -326,10 +333,22 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) currentFrame.color_range_min, currentFrame.color_range_max); - if (convertFrame) { - delete convertFrame; + delete convertFrame; + + BMDPixelFormat convertFormat; + switch (pixelFormat) { + case bmdFormat8BitBGRA: + convertFormat = bmdFormat8BitBGRA; + break; + default: + case bmdFormat10BitYUV: + case bmdFormat8BitYUV:; + convertFormat = bmdFormat8BitYUV; + break; } - convertFrame = new OBSVideoFrame(mode_->GetWidth(), mode_->GetHeight()); + + convertFrame = new OBSVideoFrame(mode_->GetWidth(), mode_->GetHeight(), + convertFormat); #ifdef LOG_SETUP_VIDEO_FORMAT LOG(LOG_INFO, "Setup video format: %s, %s, %s", @@ -340,6 +359,7 @@ void DeckLinkDeviceInstance::SetupVideoFormat(DeckLinkDeviceMode *mode_) } bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_, + bool allow10Bit_, BMDVideoConnection bmdVideoConnection, BMDAudioConnection bmdAudioConnection) { @@ -392,7 +412,11 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_, bool isauto = mode_->GetName() == "Auto"; if (isauto) { displayMode = bmdModeNTSC; - pixelFormat = bmdFormat10BitYUV; + if (allow10Bit) { + pixelFormat = bmdFormat10BitYUV; + } else { + pixelFormat = bmdFormat8BitYUV; + } flags = bmdVideoInputEnableFormatDetection; } else { displayMode = mode_->GetDisplayMode(); @@ -401,6 +425,8 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_, flags = bmdVideoInputFlagDefault; } + allow10Bit = allow10Bit_; + const HRESULT videoResult = input->EnableVideoInput(displayMode, pixelFormat, flags); if (videoResult != S_OK) { @@ -631,15 +657,22 @@ HRESULT STDMETHODCALLTYPE DeckLinkDeviceInstance::VideoInputFormatChanged( { if (events & bmdVideoInputColorspaceChanged) { - switch (detectedSignalFlags) { - case bmdDetectedVideoInputRGB444: + if (detectedSignalFlags & bmdDetectedVideoInputRGB444) { pixelFormat = bmdFormat8BitBGRA; - break; - - default: - case bmdDetectedVideoInputYCbCr422: - pixelFormat = bmdFormat10BitYUV; - break; + } + if (detectedSignalFlags & bmdDetectedVideoInputYCbCr422) { + if (detectedSignalFlags & + bmdDetectedVideoInput10BitDepth) { + if (allow10Bit) { + pixelFormat = bmdFormat10BitYUV; + } else { + pixelFormat = bmdFormat8BitYUV; + } + } + if (detectedSignalFlags & + bmdDetectedVideoInput8BitDepth) { + pixelFormat = bmdFormat8BitYUV; + } } } diff --git a/plugins/decklink/decklink-device-instance.hpp b/plugins/decklink/decklink-device-instance.hpp index e6c3d6920..9ca049c93 100644 --- a/plugins/decklink/decklink-device-instance.hpp +++ b/plugins/decklink/decklink-device-instance.hpp @@ -35,6 +35,7 @@ protected: AudioRepacker *audioRepacker = nullptr; speaker_layout channelFormat = SPEAKERS_STEREO; bool swap; + bool allow10Bit; OBSVideoFrame *convertFrame = nullptr; IDeckLinkMutableVideoFrame *decklinkOutputFrame = nullptr; @@ -85,7 +86,7 @@ public: inline DeckLinkDeviceMode *GetMode() const { return mode; } - bool StartCapture(DeckLinkDeviceMode *mode, + bool StartCapture(DeckLinkDeviceMode *mode, bool allow10Bit, BMDVideoConnection bmdVideoConnection, BMDAudioConnection bmdAudioConnection); bool StopCapture(void); diff --git a/plugins/decklink/decklink-source.cpp b/plugins/decklink/decklink-source.cpp index 1b3184476..99577e826 100644 --- a/plugins/decklink/decklink-source.cpp +++ b/plugins/decklink/decklink-source.cpp @@ -80,6 +80,7 @@ static void decklink_update(void *data, obs_data_t *settings) decklink->SetChannelFormat(channelFormat); decklink->hash = std::string(hash); decklink->swap = obs_data_get_bool(settings, SWAP); + decklink->allow10Bit = obs_data_get_bool(settings, ALLOW_10_BIT); decklink->Activate(device, id, videoConnection, audioConnection); } @@ -247,6 +248,9 @@ static bool mode_id_changed(obs_properties_t *props, obs_property_t *list, list = obs_properties_get(props, PIXEL_FORMAT); obs_property_set_visible(list, id != MODE_ID_AUTO); + auto allow10BitProp = obs_properties_get(props, ALLOW_10_BIT); + obs_property_set_visible(allow10BitProp, id == MODE_ID_AUTO); + return true; } @@ -277,6 +281,7 @@ static obs_properties_t *decklink_get_properties(void *data) OBS_COMBO_FORMAT_INT); obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV); + obs_property_list_add_int(list, "10-bit YUV", bmdFormat10BitYUV); obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA); list = obs_properties_add_list(props, COLOR_SPACE, TEXT_COLOR_SPACE, @@ -322,6 +327,8 @@ static obs_properties_t *decklink_get_properties(void *data) obs_properties_add_bool(props, DEACTIVATE_WNS, TEXT_DWNS); + obs_properties_add_bool(props, ALLOW_10_BIT, TEXT_ALLOW_10_BIT); + UNUSED_PARAMETER(data); return props; }