Răsfoiți Sursa

decklink: Add feature to detect resolution/format

Closes jp9000/obs-studio#879
mntone 8 ani în urmă
părinte
comite
41c2f5e13b

+ 6 - 0
plugins/decklink/data/locale/en-US.ini

@@ -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"

+ 88 - 15
plugins/decklink/decklink-device-instance.cpp

@@ -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(), &currentFrame);
 }
 
 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();
+
+	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);
 
-	// There is no implementation for automatic format detection, so this
-	// method goes unused.
+	input->FlushStreams();
+	input->StartStreams();
 
 	return S_OK;
 }

+ 7 - 0
plugins/decklink/decklink-device-instance.hpp

@@ -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;}

+ 19 - 0
plugins/decklink/decklink-device-mode.cpp

@@ -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();
+}

+ 5 - 0
plugins/decklink/decklink-device-mode.hpp

@@ -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);
 };

+ 15 - 3
plugins/decklink/decklink-device.cpp

@@ -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;
 

+ 2 - 0
plugins/decklink/decklink.cpp

@@ -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;
 	}

+ 12 - 0
plugins/decklink/decklink.hpp

@@ -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)
 	{

+ 3 - 0
plugins/decklink/platform.hpp

@@ -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
 

+ 63 - 0
plugins/decklink/plugin-main.cpp

@@ -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);