Browse Source

decklink: Add option to select channel format

Closes jp9000/obs-studio#867
mntone 8 years ago
parent
commit
c459ade360

+ 114 - 0
plugins/decklink/audio-repack.c

@@ -0,0 +1,114 @@
+#include "audio-repack.h"
+
+#include <emmintrin.h>
+
+int check_buffer(struct audio_repack *repack,
+		uint32_t frame_count)
+{
+	const uint32_t new_size = frame_count * repack->base_dst_size
+			+ repack->extra_dst_size;
+
+	if (repack->packet_size < new_size) {
+		repack->packet_buffer = brealloc(
+				repack->packet_buffer, new_size);
+		if (!repack->packet_buffer)
+			return -1;
+
+		repack->packet_size = new_size;
+	}
+
+	return 0;
+}
+
+/*
+	Swap channel between LFE and FC, and
+	squash data array
+
+	| FL | FR |LFE | FC | BL | BR |emp |emp |
+    |    |       x      |    |
+	| FL | FR | FC |LFE | BL | BR |
+ */
+int repack_8to6ch_swap23(struct audio_repack *repack,
+		const uint8_t *bsrc, uint32_t frame_count)
+{
+	if (check_buffer(repack, frame_count) < 0)
+		return -1;
+
+	const uint32_t size = frame_count * repack->base_src_size;
+
+	const __m128i *src = (__m128i *)bsrc;
+	const __m128i *esrc = src + frame_count;
+	uint32_t *dst = (uint32_t *)repack->packet_buffer;
+	while (src != esrc) {
+		__m128i target = _mm_load_si128(src++);
+		__m128i buf = _mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0));
+		_mm_storeu_si128((__m128i *)dst, buf);
+		dst += 3;
+	}
+
+	return 0;
+}
+
+/*
+	Swap channel between LFE and FC
+
+	| FL | FR |LFE | FC | BL | BR |SBL |SBR |
+	  |    |       x      |    |    |    |
+	| FL | FR | FC |LFE | BL | BR |SBL |SBR |
+ */
+int repack_8ch_swap23(struct audio_repack *repack,
+		const uint8_t *bsrc, uint32_t frame_count)
+{
+	if (check_buffer(repack, frame_count) < 0)
+		return -1;
+
+	const uint32_t size = frame_count * repack->base_src_size;
+
+	const __m128i *src = (__m128i *)bsrc;
+	const __m128i *esrc = src + frame_count;
+	__m128i *dst = (__m128i *)repack->packet_buffer;
+	while (src != esrc) {
+		__m128i target = _mm_load_si128(src++);
+		__m128i buf = _mm_shufflelo_epi16(target, _MM_SHUFFLE(2, 3, 1, 0));
+		_mm_store_si128(dst++, buf);
+	}
+
+	return 0;
+}
+
+int audio_repack_init(struct audio_repack *repack,
+		audio_repack_mode_t repack_mode, uint8_t sample_bit)
+{
+	memset(repack, 0, sizeof(*repack));
+
+	if (sample_bit != 16)
+		return -1;
+
+	switch (repack_mode) {
+	case repack_mode_8to6ch_swap23:
+		repack->base_src_size  = 8 * (16 / 8);
+		repack->base_dst_size  = 6 * (16 / 8);
+		repack->extra_dst_size = 2;
+		repack->repack_func    = &repack_8to6ch_swap23;
+		break;
+
+	case repack_mode_8ch_swap23:
+		repack->base_src_size  = 8 * (16 / 8);
+		repack->base_dst_size  = 8 * (16 / 8);
+		repack->extra_dst_size = 0;
+		repack->repack_func    = &repack_8ch_swap23;
+		break;
+
+	default: return -1;
+	}
+
+	return 0;
+}
+
+void audio_repack_free(struct audio_repack *repack)
+{
+	if (repack->packet_buffer)
+		bfree(repack->packet_buffer);
+
+	memset(repack, 0, sizeof(*repack));
+}

+ 41 - 0
plugins/decklink/audio-repack.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+#include <string.h>
+
+#include <obs.h>
+
+struct audio_repack;
+
+typedef int (*audio_repack_func_t)(struct audio_repack *,
+		const uint8_t *, uint32_t);
+
+struct audio_repack {
+	uint8_t             *packet_buffer;
+	uint32_t            packet_size;
+
+	uint32_t            base_src_size;
+	uint32_t            base_dst_size;
+	uint32_t            extra_dst_size;
+
+	audio_repack_func_t repack_func;
+};
+
+enum _audio_repack_mode {
+	repack_mode_8to6ch_swap23,
+	repack_mode_8ch_swap23,
+};
+
+typedef enum _audio_repack_mode audio_repack_mode_t;
+
+extern int audio_repack_init(struct audio_repack *repack,
+		audio_repack_mode_t repack_mode, uint8_t sample_bit);
+extern void audio_repack_free(struct audio_repack *repack);
+
+#ifdef __cplusplus
+}
+#endif

+ 25 - 0
plugins/decklink/audio-repack.hpp

@@ -0,0 +1,25 @@
+#pragma once
+
+#include "audio-repack.h"
+
+class AudioRepacker {
+	struct audio_repack arepack;
+
+public:
+	inline AudioRepacker(audio_repack_mode_t repack_mode)
+	{
+		audio_repack_init(&arepack, repack_mode, 16);
+	}
+	inline ~AudioRepacker()
+	{
+		audio_repack_free(&arepack);
+	}
+
+	inline int repack(const uint8_t *src, uint32_t frame_size)
+	{
+		return (*arepack.repack_func)(&arepack, src, frame_size);
+	}
+
+	inline operator struct audio_repack*() {return &arepack;}
+	inline struct audio_repack *operator->() {return &arepack;}
+};

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

@@ -3,3 +3,9 @@ Device="Device"
 Mode="Mode"
 Buffering="Use Buffering"
 PixelFormat="Pixel Format"
+ChannelFormat="Channel"
+ChannelFormat.None="None"
+ChannelFormat.2_0ch="2ch"
+ChannelFormat.5_1ch="5.1ch"
+ChannelFormat.5_1chBack="5.1ch (Back)"
+ChannelFormat.7_1ch="7.1ch"

+ 84 - 19
plugins/decklink/decklink-device-instance.cpp

@@ -1,4 +1,5 @@
 #include "decklink-device-instance.hpp"
+#include "audio-repack.hpp"
 
 #include <util/platform.h>
 #include <util/threading.h>
@@ -8,6 +9,8 @@
 #define LOG(level, message, ...) blog(level, "%s: " message, \
 		obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__)
 
+#define ISSTEREO(flag) ((flag) == SPEAKERS_STEREO)
+
 static inline enum video_format ConvertPixelFormat(BMDPixelFormat format)
 {
 	switch (format) {
@@ -20,6 +23,36 @@ static inline enum video_format ConvertPixelFormat(BMDPixelFormat format)
 	return VIDEO_FORMAT_UYVY;
 }
 
+static inline int ConvertChannelFormat(speaker_layout format)
+{
+	switch (format) {
+	case SPEAKERS_5POINT1:
+	case SPEAKERS_5POINT1_SURROUND:
+	case SPEAKERS_7POINT1:
+		return 8;
+
+	default:
+	case SPEAKERS_STEREO:
+		return 2;
+	}
+}
+
+static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format)
+{
+	switch (format) {
+	case SPEAKERS_5POINT1:
+	case SPEAKERS_5POINT1_SURROUND:
+		return repack_mode_8to6ch_swap23;
+
+	case SPEAKERS_7POINT1:
+		return repack_mode_8ch_swap23;
+
+	default:
+		assert(false && "No repack requested");
+		return (audio_repack_mode_t)-1;
+	}
+}
+
 DeckLinkDeviceInstance::DeckLinkDeviceInstance(DeckLink *decklink_,
 		DeckLinkDevice *device_) :
 	currentFrame(), currentPacket(), decklink(decklink_), device(device_)
@@ -46,9 +79,20 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
 		return;
 	}
 
-	currentPacket.data[0]   = (uint8_t *)bytes;
-	currentPacket.frames    = (uint32_t)audioPacket->GetSampleFrameCount();
-	currentPacket.timestamp = timestamp;
+	const uint32_t frameCount = (uint32_t)audioPacket->GetSampleFrameCount();
+	currentPacket.frames      = frameCount;
+	currentPacket.timestamp   = timestamp;
+
+	if (!ISSTEREO(channelFormat)) {
+		if (audioRepacker->repack((uint8_t *)bytes, frameCount) < 0) {
+			LOG(LOG_ERROR, "Failed to convert audio packet data");
+			return;
+		}
+
+		currentPacket.data[0]   = (*audioRepacker)->packet_buffer;
+	} else {
+		currentPacket.data[0]   = (uint8_t *)bytes;
+	}
 
 	obs_source_output_audio(decklink->GetSource(), &currentPacket);
 }
@@ -78,6 +122,19 @@ void DeckLinkDeviceInstance::HandleVideoFrame(
 	obs_source_output_video(decklink->GetSource(), &currentFrame);
 }
 
+void DeckLinkDeviceInstance::FinalizeStream()
+{
+	input->SetCallback(nullptr);
+
+	if (audioRepacker != nullptr)
+	{
+		delete audioRepacker;
+		audioRepacker = nullptr;
+	}
+
+	mode = nullptr;
+}
+
 bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
 {
 	if (mode != nullptr)
@@ -93,8 +150,6 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
 	pixelFormat = decklink->GetPixelFormat();
 	currentFrame.format = ConvertPixelFormat(pixelFormat);
 
-	input->SetCallback(this);
-
 	const BMDDisplayMode displayMode = mode_->GetDisplayMode();
 
 	const HRESULT videoResult = input->EnableVideoInput(displayMode,
@@ -102,22 +157,36 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
 
 	if (videoResult != S_OK) {
 		LOG(LOG_ERROR, "Failed to enable video input");
-		input->SetCallback(nullptr);
 		return false;
 	}
 
-	const HRESULT audioResult = input->EnableAudioInput(
-			bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
-			2);
+	channelFormat = decklink->GetChannelFormat();
+	currentPacket.speakers = channelFormat;
 
-	if (audioResult != S_OK)
-		LOG(LOG_WARNING, "Failed to enable audio input; continuing...");
+	if (channelFormat != SPEAKERS_UNKNOWN) {
+		const int channel = ConvertChannelFormat(channelFormat);
+		const HRESULT audioResult = input->EnableAudioInput(
+				bmdAudioSampleRate48kHz, bmdAudioSampleType16bitInteger,
+				channel);
+
+		if (audioResult != S_OK)
+			LOG(LOG_WARNING, "Failed to enable audio input; continuing...");
+
+		if (!ISSTEREO(channelFormat)) {
+			const audio_repack_mode_t repack_mode = ConvertRepackFormat(channelFormat);
+			audioRepacker = new AudioRepacker(repack_mode);
+		}
+	}
+
+	if (input->SetCallback(this) != S_OK) {
+		LOG(LOG_ERROR, "Failed to set callback");
+		FinalizeStream();
+		return false;
+	}
 
 	if (input->StartStreams() != S_OK) {
 		LOG(LOG_ERROR, "Failed to start streams");
-		input->SetCallback(nullptr);
-		input->DisableVideoInput();
-		input->DisableAudioInput();
+		FinalizeStream();
 		return false;
 	}
 
@@ -135,11 +204,7 @@ bool DeckLinkDeviceInstance::StopCapture(void)
 			GetDevice()->GetDisplayName().c_str());
 
 	input->StopStreams();
-	input->SetCallback(nullptr);
-	input->DisableVideoInput();
-	input->DisableAudioInput();
-
-	mode = nullptr;
+	FinalizeStream();
 
 	return true;
 }

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

@@ -2,6 +2,8 @@
 
 #include "decklink-device.hpp"
 
+class AudioRepacker;
+
 class DeckLinkDeviceInstance : public IDeckLinkInputCallback {
 protected:
 	struct obs_source_frame currentFrame;
@@ -13,6 +15,11 @@ protected:
 	ComPtr<IDeckLinkInput>  input;
 	volatile long           refCount = 1;
 
+	AudioRepacker           *audioRepacker = nullptr;
+	speaker_layout          channelFormat = SPEAKERS_STEREO;
+
+	void FinalizeStream();
+
 	void HandleAudioPacket(IDeckLinkAudioInputPacket *audioPacket,
 			const uint64_t timestamp);
 	void HandleVideoFrame(IDeckLinkVideoInputFrame *videoFrame,
@@ -29,6 +36,7 @@ public:
 	}
 
 	inline BMDPixelFormat GetActivePixelFormat() const {return pixelFormat;}
+	inline speaker_layout GetActiveChannelFormat() const {return channelFormat;}
 
 	inline DeckLinkDeviceMode *GetMode() const {return mode;}
 

+ 14 - 0
plugins/decklink/decklink-device.cpp

@@ -72,6 +72,15 @@ bool DeckLinkDevice::Init()
 	if (result != S_OK)
 		return true;
 
+	int64_t channels;
+	/* Intensity Shuttle for Thunderbolt return 2; however, it supports 8 channels */
+	if (name == "Intensity Shuttle Thunderbolt")
+		maxChannel = 8;
+	else if (attributes->GetInt(BMDDeckLinkMaximumAudioChannels, &channels) == S_OK)
+		maxChannel = (int32_t)channels;
+	else
+		maxChannel = 2;
+
 	/* http://forum.blackmagicdesign.com/viewtopic.php?f=12&t=33967
 	 * BMDDeckLinkTopologicalID for older devices
 	 * BMDDeckLinkPersistentID for newer ones */
@@ -118,3 +127,8 @@ const std::string& DeckLinkDevice::GetName(void) const
 {
 	return name;
 }
+
+const int32_t DeckLinkDevice::GetMaxChannel(void) const
+{
+	return maxChannel;
+}

+ 2 - 0
plugins/decklink/decklink-device.hpp

@@ -14,6 +14,7 @@ class DeckLinkDevice {
 	std::string                               name;
 	std::string                               displayName;
 	std::string                               hash;
+	int32_t                                   maxChannel;
 	volatile long                             refCount = 1;
 
 public:
@@ -30,6 +31,7 @@ public:
 	const std::string& GetHash(void) const;
 	const std::vector<DeckLinkDeviceMode *>& GetModes(void) const;
 	const std::string& GetName(void) const;
+	const int32_t GetMaxChannel(void) const;
 
 	bool GetInput(IDeckLinkInput **input);
 

+ 2 - 1
plugins/decklink/decklink.cpp

@@ -65,7 +65,8 @@ bool DeckLink::Activate(DeckLinkDevice *device, long long modeId)
 		if (!isActive)
 			return false;
 		if (instance->GetActiveModeId() == modeId &&
-		    instance->GetActivePixelFormat() == pixelFormat)
+		    instance->GetActivePixelFormat() == pixelFormat &&
+		    instance->GetActiveChannelFormat() == channelFormat)
 			return false;
 	}
 

+ 6 - 0
plugins/decklink/decklink.hpp

@@ -22,6 +22,7 @@ protected:
 	volatile long                         activateRefs = 0;
 	std::recursive_mutex                  deviceMutex;
 	BMDPixelFormat                        pixelFormat = bmdFormat8BitYUV;
+	speaker_layout                        channelFormat = SPEAKERS_STEREO;
 
 	void SaveSettings();
 	static void DevicesChanged(void *param, DeckLinkDevice *device,
@@ -41,6 +42,11 @@ public:
 	{
 		pixelFormat = format;
 	}
+	inline speaker_layout GetChannelFormat() const {return channelFormat;}
+	inline void SetChannelFormat(speaker_layout format)
+	{
+		channelFormat = format;
+	}
 
 	bool Activate(DeckLinkDevice *device, long long modeId);
 	void Deactivate();

+ 3 - 0
plugins/decklink/linux/CMakeLists.txt

@@ -22,6 +22,8 @@ set(linux-decklink_HEADERS
 	../decklink-device-discovery.hpp
 	../decklink-device.hpp
 	../decklink-device-mode.hpp
+	../audio-repack.h
+	../audio-repack.hpp
 	)
 
 set(linux-decklink_SOURCES
@@ -31,6 +33,7 @@ set(linux-decklink_SOURCES
 	../decklink-device-discovery.cpp
 	../decklink-device.cpp
 	../decklink-device-mode.cpp
+	../audio-repack.c
 	platform.cpp)
 
 add_library(linux-decklink MODULE

+ 3 - 0
plugins/decklink/mac/CMakeLists.txt

@@ -26,6 +26,8 @@ set(mac-decklink_HEADERS
 	../decklink-device-discovery.hpp
 	../decklink-device.hpp
 	../decklink-device-mode.hpp
+	../audio-repack.h
+	../audio-repack.hpp
 	)
 
 set(mac-decklink_SOURCES
@@ -35,6 +37,7 @@ set(mac-decklink_SOURCES
 	../decklink-device-discovery.cpp
 	../decklink-device.cpp
 	../decklink-device-mode.cpp
+	../audio-repack.c
 	platform.cpp)
 
 add_library(mac-decklink MODULE

+ 72 - 29
plugins/decklink/plugin-main.cpp

@@ -7,6 +7,25 @@
 OBS_DECLARE_MODULE()
 OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US")
 
+#define DEVICE_HASH     "device_hash"
+#define DEVICE_NAME     "device_name"
+#define MODE_ID         "mode_id"
+#define MODE_NAME       "mode_name"
+#define CHANNEL_FORMAT  "channel_format"
+#define PIXEL_FORMAT    "pixel_format"
+#define 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_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_5_1CH       obs_module_text("ChannelFormat.5_1ch")
+#define TEXT_CHANNEL_FORMAT_5_1CH_BACK  obs_module_text("ChannelFormat.5_1chBack")
+#define TEXT_CHANNEL_FORMAT_7_1CH       obs_module_text("ChannelFormat.7_1ch")
+#define TEXT_BUFFERING                  obs_module_text("Buffering")
+
 static DeckLinkDeviceDiscovery *deviceEnum = nullptr;
 
 static void decklink_enable_buffering(DeckLink *decklink, bool enabled)
@@ -25,7 +44,7 @@ static void *decklink_create(obs_data_t *settings, obs_source_t *source)
 	DeckLink *decklink = new DeckLink(source, deviceEnum);
 
 	decklink_enable_buffering(decklink,
-			obs_data_get_bool(settings, "buffering"));
+			obs_data_get_bool(settings, BUFFERING));
 
 	obs_source_update(source, settings);
 	return decklink;
@@ -40,25 +59,29 @@ static void decklink_destroy(void *data)
 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 format = (BMDPixelFormat)obs_data_get_int(settings,
-			"pixel_format");
+	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);
+	speaker_layout channelFormat = (speaker_layout)obs_data_get_int(settings,
+			CHANNEL_FORMAT);
 
 	decklink_enable_buffering(decklink,
-			obs_data_get_bool(settings, "buffering"));
+			obs_data_get_bool(settings, BUFFERING));
 
 	ComPtr<DeckLinkDevice> device;
 	device.Set(deviceEnum->FindByHash(hash));
 
-	decklink->SetPixelFormat(format);
+	decklink->SetPixelFormat(pixelFormat);
+	decklink->SetChannelFormat(channelFormat);
 	decklink->Activate(device, id);
 }
 
 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_bool(settings, BUFFERING, true);
+	obs_data_set_default_int(settings, PIXEL_FORMAT, bmdFormat8BitYUV);
+	obs_data_set_default_int(settings, CHANNEL_FORMAT, SPEAKERS_STEREO);
 }
 
 static const char *decklink_get_name(void*)
@@ -69,10 +92,10 @@ static const char *decklink_get_name(void*)
 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");
+	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;
@@ -90,25 +113,41 @@ static bool decklink_device_changed(obs_properties_t *props,
 		obs_property_list_item_disable(list, 0, true);
 	}
 
-	list = obs_properties_get(props, "mode_id");
+	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(list);
+	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(list, mode, modeId);
-		obs_property_list_item_disable(list, 0, true);
+		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(list,
+			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_5_1CH,
+					SPEAKERS_5POINT1);
+			obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_5_1CH_BACK,
+					SPEAKERS_5POINT1_SURROUND);
+			obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_7_1CH,
+					SPEAKERS_7POINT1);
+		}
 	}
 
 	return true;
@@ -132,26 +171,30 @@ 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",
-			obs_module_text("Device"), OBS_COMBO_TYPE_LIST,
-			OBS_COMBO_FORMAT_STRING);
+	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",
-			obs_module_text("Mode"), OBS_COMBO_TYPE_LIST,
-			OBS_COMBO_FORMAT_INT);
+	list = obs_properties_add_list(props, MODE_ID, TEXT_MODE,
+			OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
 
-	list = obs_properties_add_list(props, "pixel_format",
-			obs_module_text("PixelFormat"), OBS_COMBO_TYPE_LIST,
+	list = obs_properties_add_list(props, PIXEL_FORMAT,
+			TEXT_PIXEL_FORMAT, OBS_COMBO_TYPE_LIST,
 			OBS_COMBO_FORMAT_INT);
-
 	obs_property_list_add_int(list, "8-bit YUV", bmdFormat8BitYUV);
 	obs_property_list_add_int(list, "8-bit BGRA", bmdFormat8BitBGRA);
 
-	obs_properties_add_bool(props, "buffering",
-			obs_module_text("Buffering"));
+	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_properties_add_bool(props, BUFFERING, TEXT_BUFFERING);
 
 	UNUSED_PARAMETER(data);
 	return props;

+ 3 - 0
plugins/decklink/win/CMakeLists.txt

@@ -17,6 +17,8 @@ set(win-decklink_HEADERS
 	../decklink-device-discovery.hpp
 	../decklink-device.hpp
 	../decklink-device-mode.hpp
+	../audio-repack.h
+	../audio-repack.hpp
 	)
 
 set(win-decklink_SOURCES
@@ -26,6 +28,7 @@ set(win-decklink_SOURCES
 	../decklink-device-discovery.cpp
 	../decklink-device.cpp
 	../decklink-device-mode.cpp
+	../audio-repack.c
 	platform.cpp)
 
 add_idl_files(win-decklink-sdk_GENERATED_FILES