libobs: Add surround sound audio support

(This commit also modifies the following modules: UI,
deps/media-playback, coreaudio-encoder, decklink, linux-alsa,
linux-pulseaudio, mac-capture, obs-ffmpeg, obs-filters, obs-libfdk,
obs-outputs, win-dshow, and win-wasapi)

Adds surround sound audio support to the core, core plugins, and user
interface.

Compatible streaming services: Twitch, FB 360 live
Compatible protocols: rtmp / mpeg-ts tcp udp
Compatible file formats: mkv mp4 ts  (others untested)
Compatible codecs: ffmpeg aac, fdk_aac, CoreAudio aac,
		   opus, vorbis, pcm (others untested).
Tested streaming servers: wowza, nginx
	 HLS, mpeg-dash : surround passthrough
Html5 players tested with live surround:
	 videojs, mediaelement, viblast (hls+dash), hls.js
Decklink: on win32, swap channels order for 5.1 7.1
         (due to different channel mapping on wav, mpeg, ffmpeg)
Audio filters: surround working.
Monitoring: surround working (win macOs linux (pulse-audio)).
VST:	 stereo plugins keep in general only the first two channels.
	 surround plugins should work (e.g. mcfx does).
OS: win, macOs, linux (alsa, pulse-audio).
Misc: larger audio bitrates unlocked to accommodate more channels
NB: mf-aac only supports mono and stereo + 5.1 on win 10
         (not implemented due to lack of usefulness)

Closes jp9000/obs-studio#968
This commit is contained in:
pkviet 2017-05-27 02:15:54 +02:00 committed by jp9000
parent 54ecfc8fe7
commit bbac3280c1
30 changed files with 628 additions and 116 deletions

View file

@ -148,6 +148,10 @@ static void PopulateBitrateMap()
{
call_once(populateBitrateMap, []()
{
struct obs_audio_info aoi;
obs_get_audio_info(&aoi);
uint32_t output_channels = get_audio_channels(aoi.speakers);
HandleEncoderProperties(fallbackEncoder.c_str());
const char *id = nullptr;
@ -174,6 +178,10 @@ static void PopulateBitrateMap()
if (aac_ != GetCodec(encoder.c_str()))
continue;
// disable mf_aac if audio ouput is not stereo nor mono
if ((output_channels >= 3) && (encoder == "mf_aac"))
continue;
HandleEncoderProperties(encoder.c_str());
}

View file

@ -683,6 +683,10 @@ Basic.Settings.Video.DownscaleFilter.Lanczos="Lanczos (Sharpened scaling, 32 sam
Basic.Settings.Audio="Audio"
Basic.Settings.Audio.SampleRate="Sample Rate"
Basic.Settings.Audio.Channels="Channels"
Basic.Settings.Audio.MultiChannelWarning.Enabled="WARNING: Surround sound audio is enabled."
Basic.Settings.Audio.MultichannelWarning="If streaming, check to see if your streaming service supports both surround sound ingest and surround sound playback. Twitch, Facebook 360 Live, Mixer RTMP, Smashcast are examples where surround sound is fully supported. Although Facebook Live and YouTube Live both accept surround ingest, Facebook Live downmixes to stereo, and YouTube Live plays only two channels.\n\nOBS audio filters are compatible with surround sound, though VST plugin support isn't guaranteed."
Basic.Settings.Audio.MultichannelWarning.Title="Enable surround sound audio?"
Basic.Settings.Audio.MultichannelWarning.Confirm="Are you sure you want to enable surround sound audio?"
Basic.Settings.Audio.DesktopDevice="Desktop Audio Device"
Basic.Settings.Audio.DesktopDevice2="Desktop Audio Device 2"
Basic.Settings.Audio.AuxDevice="Mic/Auxiliary Audio Device"

View file

@ -3180,6 +3180,31 @@
<string>Stereo</string>
</property>
</item>
<item>
<property name="text">
<string>2.1</string>
</property>
</item>
<item>
<property name="text">
<string>4.0 Quad</string>
</property>
</item>
<item>
<property name="text">
<string>4.1</string>
</property>
</item>
<item>
<property name="text">
<string>5.1</string>
</property>
</item>
<item>
<property name="text">
<string>7.1</string>
</property>
</item>
</widget>
</item>
<item row="2" column="0">
@ -3309,6 +3334,19 @@
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QLabel" name="audioMsg_2">
<property name="text">
<string notr="true"/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="themeID" stdset="0">
<string notr="true">warning</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="videoPage">
@ -3613,8 +3651,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>98</width>
<height>28</height>
<width>818</width>
<height>697</height>
</rect>
</property>
<layout class="QFormLayout" name="hotkeyLayout">
@ -3659,7 +3697,7 @@
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<y>-62</y>
<width>803</width>
<height>761</height>
</rect>

View file

@ -3039,6 +3039,16 @@ bool OBSBasic::ResetAudio()
if (strcmp(channelSetupStr, "Mono") == 0)
ai.speakers = SPEAKERS_MONO;
else if (strcmp(channelSetupStr, "2.1") == 0)
ai.speakers = SPEAKERS_2POINT1;
else if (strcmp(channelSetupStr, "4.0 Quad") == 0)
ai.speakers = SPEAKERS_QUAD;
else if (strcmp(channelSetupStr, "4.1") == 0)
ai.speakers = SPEAKERS_4POINT1;
else if (strcmp(channelSetupStr, "5.1") == 0)
ai.speakers = SPEAKERS_5POINT1;
else if (strcmp(channelSetupStr, "7.1") == 0)
ai.speakers = SPEAKERS_7POINT1;
else
ai.speakers = SPEAKERS_STEREO;

View file

@ -243,6 +243,9 @@ static void PopulateAACBitrates(initializer_list<QComboBox*> boxes)
}
}
void RestrictResetBitrates(initializer_list<QComboBox*> boxes,
int maxbitrate);
void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
const char *slot)
{
@ -256,6 +259,7 @@ void OBSBasicSettings::HookWidget(QWidget *widget, const char *signal,
#define CHECK_CHANGED SIGNAL(clicked(bool))
#define SCROLL_CHANGED SIGNAL(valueChanged(int))
#define DSCROLL_CHANGED SIGNAL(valueChanged(double))
#define TOGGLE_CHANGED SIGNAL(toggled(bool))
#define GENERAL_CHANGED SLOT(GeneralChanged())
#define STREAM1_CHANGED SLOT(Stream1Changed())
@ -587,6 +591,10 @@ OBSBasicSettings::OBSBasicSettings(QWidget *parent)
FillAudioMonitoringDevices();
#endif
connect(ui->channelSetup, SIGNAL(currentIndexChanged(int)),
this, SLOT(SurroundWarning(int)));
connect(ui->channelSetup, SIGNAL(currentIndexChanged(int)),
this, SLOT(SpeakerLayoutChanged(int)));
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)),
this, SLOT(SimpleRecordingQualityChanged()));
connect(ui->simpleOutRecQuality, SIGNAL(currentIndexChanged(int)),
@ -1368,6 +1376,30 @@ void OBSBasicSettings::LoadVideoSettings()
loading = false;
}
static inline bool IsSurround(const char *speakers)
{
static const char *surroundLayouts[] = {
"2.1",
"4.0 Quad",
"4.1",
"5.1",
"7.1",
nullptr
};
if (!speakers || !*speakers)
return false;
const char **curLayout = surroundLayouts;
for (; *curLayout; ++curLayout) {
if (strcmp(*curLayout, speakers) == 0) {
return true;
}
}
return false;
}
void OBSBasicSettings::LoadSimpleOutputSettings()
{
const char *path = config_get_string(main->Config(), "SimpleOutput",
@ -1423,6 +1455,13 @@ void OBSBasicSettings::LoadSimpleOutputSettings()
int idx = ui->simpleOutRecFormat->findText(format);
ui->simpleOutRecFormat->setCurrentIndex(idx);
const char *speakers = config_get_string(main->Config(), "Audio",
"ChannelSetup");
// restrict list of bitrates when multichannel is OFF
if (!IsSurround(speakers))
RestrictResetBitrates({ui->simpleOutputABitrate}, 320);
SetComboByName(ui->simpleOutputABitrate,
std::to_string(audioBitrate).c_str());
@ -1741,6 +1780,20 @@ void OBSBasicSettings::LoadAdvOutputAudioSettings()
track5Bitrate = FindClosestAvailableAACBitrate(track5Bitrate);
track6Bitrate = FindClosestAvailableAACBitrate(track6Bitrate);
// restrict list of bitrates when multichannel is OFF
const char *speakers = config_get_string(main->Config(), "Audio",
"ChannelSetup");
// restrict list of bitrates when multichannel is OFF
if (!IsSurround(speakers)) {
RestrictResetBitrates({ui->advOutTrack1Bitrate,
ui->advOutTrack2Bitrate,
ui->advOutTrack3Bitrate,
ui->advOutTrack4Bitrate,
ui->advOutTrack5Bitrate,
ui->advOutTrack6Bitrate}, 320);
}
SetComboByName(ui->advOutTrack1Bitrate,
std::to_string(track1Bitrate).c_str());
SetComboByName(ui->advOutTrack2Bitrate,
@ -2044,6 +2097,16 @@ void OBSBasicSettings::LoadAudioSettings()
if (strcmp(speakers, "Mono") == 0)
ui->channelSetup->setCurrentIndex(0);
else if (strcmp(speakers, "2.1") == 0)
ui->channelSetup->setCurrentIndex(2);
else if (strcmp(speakers, "4.0 Quad") == 0)
ui->channelSetup->setCurrentIndex(3);
else if (strcmp(speakers, "4.1") == 0)
ui->channelSetup->setCurrentIndex(4);
else if (strcmp(speakers, "5.1") == 0)
ui->channelSetup->setCurrentIndex(5);
else if (strcmp(speakers, "7.1") == 0)
ui->channelSetup->setCurrentIndex(6);
else
ui->channelSetup->setCurrentIndex(1);
@ -2918,7 +2981,34 @@ void OBSBasicSettings::SaveAudioSettings()
QString sampleRateStr = ui->sampleRate->currentText();
int channelSetupIdx = ui->channelSetup->currentIndex();
const char *channelSetup = (channelSetupIdx == 0) ? "Mono" : "Stereo";
const char *channelSetup;
switch (channelSetupIdx) {
case 0:
channelSetup = "Mono";
break;
case 1:
channelSetup = "Stereo";
break;
case 2:
channelSetup = "2.1";
break;
case 3:
channelSetup = "4.0 Quad";
break;
case 4:
channelSetup = "4.1";
break;
case 5:
channelSetup = "5.1";
break;
case 6:
channelSetup = "7.1";
break;
default:
channelSetup = "Stereo";
break;
}
int sampleRate = 44100;
if (sampleRateStr == "48khz")
@ -3436,6 +3526,85 @@ void OBSBasicSettings::ReloadAudioSources()
LoadAudioSources();
}
#define MULTI_CHANNEL_WARNING "Basic.Settings.Audio.MultichannelWarning"
void OBSBasicSettings::SpeakerLayoutChanged(int idx)
{
QString speakerLayoutQstr = ui->channelSetup->itemText(idx);
std::string speakerLayout = QT_TO_UTF8(speakerLayoutQstr);
bool surround = IsSurround(speakerLayout.c_str());
if (surround) {
QString warning =
QTStr(MULTI_CHANNEL_WARNING ".Enabled") +
QStringLiteral("\n\n") +
QTStr(MULTI_CHANNEL_WARNING);
/*
* Display all bitrates
*/
ui->audioMsg_2->setText(warning);
PopulateAACBitrates({ui->simpleOutputABitrate,
ui->advOutTrack1Bitrate,
ui->advOutTrack2Bitrate,
ui->advOutTrack3Bitrate,
ui->advOutTrack4Bitrate,
ui->advOutTrack5Bitrate,
ui->advOutTrack6Bitrate});
} else {
/*
* Reset audio bitrate for simple and adv mode, update list of
* bitrates and save setting.
*/
ui->audioMsg_2->setText(QString());
RestrictResetBitrates({ui->simpleOutputABitrate,
ui->advOutTrack1Bitrate,
ui->advOutTrack2Bitrate,
ui->advOutTrack3Bitrate,
ui->advOutTrack4Bitrate,
ui->advOutTrack5Bitrate,
ui->advOutTrack6Bitrate}, 320);
SaveCombo(ui->simpleOutputABitrate, "SimpleOutput", "ABitrate");
SaveCombo(ui->advOutTrack1Bitrate, "AdvOut", "Track1Bitrate");
SaveCombo(ui->advOutTrack2Bitrate, "AdvOut", "Track2Bitrate");
SaveCombo(ui->advOutTrack3Bitrate, "AdvOut", "Track3Bitrate");
SaveCombo(ui->advOutTrack4Bitrate, "AdvOut", "Track4Bitrate");
SaveCombo(ui->advOutTrack5Bitrate, "AdvOut", "Track5Bitrate");
SaveCombo(ui->advOutTrack6Bitrate, "AdvOut", "Track6Bitrate");
}
}
/*
* resets current bitrate if too large and restricts the number of bitrates
* displayed when multichannel OFF
*/
void RestrictResetBitrates(initializer_list<QComboBox*> boxes, int maxbitrate)
{
for (auto box : boxes) {
int idx = box->currentIndex();
int max_bitrate = FindClosestAvailableAACBitrate(maxbitrate);
int count = box->count();
int max_idx = box->findText(QT_UTF8(std::to_string
(max_bitrate).c_str()));
for (int i = (count - 1); i > max_idx; i--)
box->removeItem(i);
if (idx > max_idx) {
int default_bitrate = FindClosestAvailableAACBitrate(
maxbitrate / 2);
int default_idx = box->findText(QT_UTF8(std::to_string
(default_bitrate).c_str()));
box->setCurrentIndex(default_idx);
box->setProperty("changed", QVariant(true));
} else {
box->setCurrentIndex(idx);
}
}
}
void OBSBasicSettings::VideoChangedRestart()
{
if (!loading) {
@ -4007,6 +4176,47 @@ void OBSBasicSettings::SimpleRecordingEncoderChanged()
ui->simpleOutInfoLayout->addWidget(simpleOutRecWarning);
}
void OBSBasicSettings::SurroundWarning(int idx)
{
if (idx == lastChannelSetupIdx || idx == -1)
return;
if (loading) {
lastChannelSetupIdx = idx;
return;
}
QString speakerLayoutQstr = ui->channelSetup->itemText(idx);
bool surround = IsSurround(QT_TO_UTF8(speakerLayoutQstr));
QString lastQstr = ui->channelSetup->itemText(lastChannelSetupIdx);
bool wasSurround = IsSurround(QT_TO_UTF8(lastQstr));
if (surround && !wasSurround) {
QMessageBox::StandardButton button;
QString warningString =
QTStr("Basic.Settings.ProgramRestart") +
QStringLiteral("\n\n") +
QTStr(MULTI_CHANNEL_WARNING) +
QStringLiteral("\n\n") +
QTStr(MULTI_CHANNEL_WARNING ".Confirm");
button = OBSMessageBox::question(this,
QTStr(MULTI_CHANNEL_WARNING ".Title"),
warningString);
if (button == QMessageBox::No) {
QMetaObject::invokeMethod(ui->channelSetup,
"setCurrentIndex", Qt::QueuedConnection,
Q_ARG(int, lastChannelSetupIdx));
return;
}
}
lastChannelSetupIdx = idx;
}
void OBSBasicSettings::SimpleRecordingQualityLosslessWarning(int idx)
{
if (idx == lastSimpleRecQualityIdx || idx == -1)

View file

@ -98,6 +98,7 @@ private:
std::string savedTheme;
int lastSimpleRecQualityIdx = 0;
int lastChannelSetupIdx = 0;
OBSFFFormatDesc formats;
@ -274,6 +275,8 @@ private slots:
void AudioChanged();
void AudioChangedRestart();
void ReloadAudioSources();
void SurroundWarning(int idx);
void SpeakerLayoutChanged(int idx);
void OutputsChanged();
void Stream1Changed();
void VideoChanged();

View file

@ -61,6 +61,21 @@ static inline enum audio_format convert_sample_format(int f)
return AUDIO_FORMAT_UNKNOWN;
}
static inline enum speaker_layout convert_speaker_layout(uint8_t channels)
{
switch (channels) {
case 0: return SPEAKERS_UNKNOWN;
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_QUAD;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
default: return SPEAKERS_UNKNOWN;
}
}
static inline enum video_colorspace convert_color_space(enum AVColorSpace s)
{
return s == AVCOL_SPC_BT709 ? VIDEO_CS_709 : VIDEO_CS_DEFAULT;
@ -257,7 +272,7 @@ static void mp_media_next_audio(mp_media_t *m)
audio.data[i] = f->data[i];
audio.samples_per_sec = f->sample_rate;
audio.speakers = (enum speaker_layout)f->channels;
audio.speakers = convert_speaker_layout(f->channels);
audio.format = convert_sample_format(f->format);
audio.frames = f->nb_samples;
audio.timestamp = m->base_ts + d->frame_pts - m->start_ts +

View file

@ -31,9 +31,17 @@ struct audio_monitor {
static enum speaker_layout pulseaudio_channels_to_obs_speakers(
uint_fast32_t channels)
{
if ((channels >= 1 && channels <= 6) || channels == 8)
return (enum speaker_layout) channels;
return SPEAKERS_UNKNOWN;
switch (channels) {
case 0: return SPEAKERS_UNKNOWN;
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_QUAD;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
default: return SPEAKERS_UNKNOWN;
}
}
static enum audio_format pulseaudio_to_obs_audio_format(

View file

@ -2,8 +2,11 @@
#include <mmdeviceapi.h>
#include <audioclient.h>
#define KSAUDIO_SPEAKER_4POINT1 (KSAUDIO_SPEAKER_QUAD|SPEAKER_LOW_FREQUENCY)
#define KSAUDIO_SPEAKER_2POINT1 (KSAUDIO_SPEAKER_STEREO|SPEAKER_LOW_FREQUENCY)
#define KSAUDIO_SPEAKER_SURROUND_AVUTIL \
(KSAUDIO_SPEAKER_STEREO|SPEAKER_FRONT_CENTER)
#define KSAUDIO_SPEAKER_4POINT1 (KSAUDIO_SPEAKER_SURROUND|SPEAKER_LOW_FREQUENCY)
#define safe_release(ptr) \
do { \

View file

@ -26,7 +26,7 @@ extern "C" {
#endif
#define MAX_AUDIO_MIXES 6
#define MAX_AUDIO_CHANNELS 2
#define MAX_AUDIO_CHANNELS 8
#define AUDIO_OUTPUT_FRAMES 1024
/*
@ -101,13 +101,13 @@ static inline uint32_t get_audio_channels(enum speaker_layout speakers)
switch (speakers) {
case SPEAKERS_MONO: return 1;
case SPEAKERS_STEREO: return 2;
case SPEAKERS_2POINT1: return 3;
case SPEAKERS_SURROUND:
case SPEAKERS_2POINT1:
case SPEAKERS_SURROUND: return 3;
case SPEAKERS_QUAD: return 4;
case SPEAKERS_4POINT1: return 5;
case SPEAKERS_5POINT1:
case SPEAKERS_5POINT1_SURROUND: return 6;
case SPEAKERS_7POINT1: return 8;
case SPEAKERS_7POINT1:
case SPEAKERS_7POINT1_SURROUND: return 8;
case SPEAKERS_UNKNOWN: return 0;
}

View file

@ -601,6 +601,29 @@ static void *aac_create(obs_data_t *settings, obs_encoder_t *encoder)
kAudioConverterCurrentOutputStreamDescription,
&size, &out));
/*
* Fix channel map differences between CoreAudio AAC, FFmpeg, Wav
* New channel mappings below assume 4.1, 5.1, 7.1 resp.
*/
if (ca->channels == 5) {
SInt32 channelMap5[5] = {2, 0, 1, 3, 4};
AudioConverterSetProperty(ca->converter,
kAudioConverterChannelMap,
sizeof(channelMap5), channelMap5);
} else if (ca->channels == 6) {
SInt32 channelMap6[6] = {2, 0, 1, 4, 5, 3};
AudioConverterSetProperty(ca->converter,
kAudioConverterChannelMap,
sizeof(channelMap6), channelMap6);
} else if (ca->channels == 8) {
SInt32 channelMap8[8] = {2, 0, 1, 6, 7, 4, 5, 3};
AudioConverterSetProperty(ca->converter,
kAudioConverterChannelMap,
sizeof(channelMap8), channelMap8);
}
ca->in_frame_size = in.mBytesPerFrame;
ca->in_packets = out.mFramesPerPacket / in.mFramesPerPacket;
ca->in_bytes_required = ca->in_packets * ca->in_frame_size;

View file

@ -21,52 +21,48 @@ int check_buffer(struct audio_repack *repack,
}
/*
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 |
* Swap channel 3 & 4, 5 & 7, 6 & 8 and squash arrays
* 4.0 (quad):
*
* | FL | FR | BR | BL | emp | emp |emp |emp |
* | | x |
* | FL | FR | BL | BC |
*
* 4.1:
*
* | FL | FR |LFE | FC | BC | emp |emp |emp |
* | | x | |
* | FL | FR | FC |LFE | BC |
*
* 5.1:
*
* | FL | FR |LFE | FC |(emp|emp)|(BL|BR)|
* | | x x
* | FL | FR | FC |LFE | BL | BR |
*
* 7.1:
*
* | FL | FR |LFE | FC |( SL | SR )|(BL |BR )|
* | | x X
* | FL | FR | FC |LFE |( BL | BR )|(SL |SR )|
*/
int repack_8to6ch_swap23(struct audio_repack *repack,
int repack_squash_swap(struct audio_repack *repack,
const uint8_t *bsrc, uint32_t frame_count)
{
if (check_buffer(repack, frame_count) < 0)
return -1;
int squash = repack->extra_dst_size;
const __m128i *src = (__m128i *)bsrc;
const __m128i *esrc = src + frame_count;
uint32_t *dst = (uint32_t *)repack->packet_buffer;
uint16_t *dst = (uint16_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 __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);
__m128i buf = _mm_shufflelo_epi16(target,_MM_SHUFFLE(2, 3, 1, 0));
__m128i buf2 = _mm_shufflehi_epi16(buf, _MM_SHUFFLE(1, 0, 3, 2));
_mm_storeu_si128((__m128i *)dst, buf2);
dst += 8 - squash;
}
return 0;
@ -81,18 +77,32 @@ int audio_repack_init(struct audio_repack *repack,
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;
case repack_mode_8to4ch_swap23:
repack->base_src_size = 8 * (16 / 8);
repack->base_dst_size = 4 * (16 / 8);
repack->extra_dst_size = 4;
repack->repack_func = &repack_squash_swap;
break;
case repack_mode_8ch_swap23:
repack->base_src_size = 8 * (16 / 8);
repack->base_dst_size = 8 * (16 / 8);
case repack_mode_8to5ch_swap23:
repack->base_src_size = 8 * (16 / 8);
repack->base_dst_size = 5 * (16 / 8);
repack->extra_dst_size = 3;
repack->repack_func = &repack_squash_swap;
break;
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_squash_swap;
break;
case repack_mode_8ch_swap23_swap46_swap57:
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;
repack->repack_func = &repack_squash_swap;
break;
default: return -1;

View file

@ -26,8 +26,10 @@ struct audio_repack {
};
enum _audio_repack_mode {
repack_mode_8to4ch_swap23,
repack_mode_8to5ch_swap23,
repack_mode_8to6ch_swap23,
repack_mode_8ch_swap23,
repack_mode_8ch_swap23_swap46_swap57,
};
typedef enum _audio_repack_mode audio_repack_mode_t;

View file

@ -12,6 +12,13 @@ ColorRange.Full="Full"
ChannelFormat="Channel"
ChannelFormat.None="None"
ChannelFormat.2_0ch="2ch"
ChannelFormat.2_1ch="2.1"
ChannelFormat.3_1ch="3.1"
ChannelFormat.4_0ch="4ch"
ChannelFormat.4_1ch="4.1ch"
ChannelFormat.5_1ch="5.1ch"
ChannelFormat.5_1chBack="5.1ch (Back)"
ChannelFormat.5_1chBack="5.1ch Back"
ChannelFormat.7_1ch="7.1ch"
ChannelFormat.7_1chBack="7.1ch Back"
ChannelFormat.8_0ch="8.0ch"
ChannelFormat.16_0ch="16.0ch"

View file

@ -9,7 +9,11 @@
#define LOG(level, message, ...) blog(level, "%s: " message, \
obs_source_get_name(this->decklink->GetSource()), ##__VA_ARGS__)
#define ISSTEREO(flag) ((flag) == SPEAKERS_STEREO)
#ifdef _WIN32
#define IS_WIN 1
#else
#define IS_WIN 0
#endif
static inline enum video_format ConvertPixelFormat(BMDPixelFormat format)
{
@ -26,9 +30,13 @@ static inline enum video_format ConvertPixelFormat(BMDPixelFormat format)
static inline int ConvertChannelFormat(speaker_layout format)
{
switch (format) {
case SPEAKERS_2POINT1:
case SPEAKERS_QUAD:
case SPEAKERS_4POINT1:
case SPEAKERS_5POINT1:
case SPEAKERS_5POINT1_SURROUND:
case SPEAKERS_7POINT1:
case SPEAKERS_7POINT1_SURROUND:
return 8;
default:
@ -40,13 +48,16 @@ static inline int ConvertChannelFormat(speaker_layout format)
static inline audio_repack_mode_t ConvertRepackFormat(speaker_layout format)
{
switch (format) {
case SPEAKERS_QUAD:
return repack_mode_8to4ch_swap23;
case SPEAKERS_4POINT1:
return repack_mode_8to5ch_swap23;
case SPEAKERS_5POINT1:
case SPEAKERS_5POINT1_SURROUND:
return repack_mode_8to6ch_swap23;
case SPEAKERS_7POINT1:
return repack_mode_8ch_swap23;
case SPEAKERS_7POINT1_SURROUND:
return repack_mode_8ch_swap23_swap46_swap57;
default:
assert(false && "No repack requested");
return (audio_repack_mode_t)-1;
@ -90,15 +101,23 @@ void DeckLinkDeviceInstance::HandleAudioPacket(
(uint64_t)currentPacket.samples_per_sec;
}
if (!ISSTEREO(channelFormat)) {
int maxdevicechannel = device->GetMaxChannel();
bool isWin = IS_WIN;
if (channelFormat != SPEAKERS_UNKNOWN &&
channelFormat != SPEAKERS_MONO &&
channelFormat != SPEAKERS_STEREO &&
channelFormat != SPEAKERS_2POINT1 &&
maxdevicechannel >= 8 &&
isWin) {
if (audioRepacker->repack((uint8_t *)bytes, frameCount) < 0) {
LOG(LOG_ERROR, "Failed to convert audio packet data");
return;
}
currentPacket.data[0] = (*audioRepacker)->packet_buffer;
currentPacket.data[0] = (*audioRepacker)->packet_buffer;
} else {
currentPacket.data[0] = (uint8_t *)bytes;
currentPacket.data[0] = (uint8_t *)bytes;
}
nextAudioTS = timestamp +
@ -218,6 +237,9 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
channelFormat = decklink->GetChannelFormat();
currentPacket.speakers = channelFormat;
int maxdevicechannel = device->GetMaxChannel();
bool isWin = IS_WIN;
if (channelFormat != SPEAKERS_UNKNOWN) {
const int channel = ConvertChannelFormat(channelFormat);
const HRESULT audioResult = input->EnableAudioInput(
@ -226,8 +248,15 @@ bool DeckLinkDeviceInstance::StartCapture(DeckLinkDeviceMode *mode_)
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);
if (channelFormat != SPEAKERS_UNKNOWN &&
channelFormat != SPEAKERS_MONO &&
channelFormat != SPEAKERS_STEREO &&
channelFormat != SPEAKERS_2POINT1 &&
maxdevicechannel >= 8 &&
isWin) {
const audio_repack_mode_t repack_mode = ConvertRepackFormat
(channelFormat);
audioRepacker = new AudioRepacker(repack_mode);
}
}

View file

@ -29,8 +29,12 @@ OBS_MODULE_USE_DEFAULT_LOCALE("decklink", "en-US")
#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_5_1CH_BACK obs_module_text("ChannelFormat.5_1chBack")
#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")
@ -154,10 +158,12 @@ static bool decklink_device_changed(obs_properties_t *props,
}
if (device->GetMaxChannel() >= 8) {
obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_4_0CH,
SPEAKERS_QUAD);
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_5_1CH_BACK, SPEAKERS_5POINT1_SURROUND);
obs_property_list_add_int(channelList, TEXT_CHANNEL_FORMAT_7_1CH,
SPEAKERS_7POINT1);
}
@ -253,6 +259,18 @@ static obs_properties_t *decklink_get_properties(void *data)
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_QUAD);
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_5_1CH_BACK,
SPEAKERS_5POINT1_SURROUND);
obs_property_list_add_int(list, TEXT_CHANNEL_FORMAT_7_1CH,
SPEAKERS_7POINT1);
obs_properties_add_bool(props, BUFFERING, TEXT_BUFFERING);

View file

@ -622,13 +622,13 @@ enum audio_format _alsa_to_obs_audio_format(snd_pcm_format_t format)
enum speaker_layout _alsa_channels_to_obs_speakers(unsigned int channels)
{
switch(channels) {
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_SURROUND;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_QUAD;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
}
return SPEAKERS_UNKNOWN;

View file

@ -81,13 +81,13 @@ static enum speaker_layout pulse_channels_to_obs_speakers(
uint_fast32_t channels)
{
switch(channels) {
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_SURROUND;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_QUAD;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
}
return SPEAKERS_UNKNOWN;

View file

@ -187,9 +187,15 @@ static inline enum audio_format convert_ca_format(UInt32 format_flags,
static inline enum speaker_layout convert_ca_speaker_layout(UInt32 channels)
{
/* directly map channel count to enum values */
if (channels >= 1 && channels <= 8 && channels != 7)
return (enum speaker_layout)channels;
switch (channels) {
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_QUAD;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
}
return SPEAKERS_UNKNOWN;
}

View file

@ -352,7 +352,12 @@ static void create_audio_stream(struct ffmpeg_mux *ffm, int idx)
context->extradata_size = ffm->audio_header[idx].size;
context->channel_layout =
av_get_default_channel_layout(context->channels);
//AVlib default channel layout for 4 channels is 4.0 ; fix for quad
if (context->channels == 4)
context->channel_layout = av_get_channel_layout("quad");
//AVlib default channel layout for 5 channels is 5.0 ; fix for 4.1
if (context->channels == 5)
context->channel_layout = av_get_channel_layout("4.1");
if (ffm->output->oformat->flags & AVFMT_GLOBALHEADER)
context->flags |= CODEC_FLAG_GLOBAL_H;

View file

@ -56,6 +56,45 @@ struct enc_encoder {
int frame_size_bytes;
};
static inline uint64_t convert_speaker_layout(enum speaker_layout layout)
{
switch (layout) {
case SPEAKERS_UNKNOWN: return 0;
case SPEAKERS_MONO: return AV_CH_LAYOUT_MONO;
case SPEAKERS_STEREO: return AV_CH_LAYOUT_STEREO;
case SPEAKERS_2POINT1: return AV_CH_LAYOUT_2_1;
case SPEAKERS_QUAD: return AV_CH_LAYOUT_QUAD;
case SPEAKERS_4POINT1: return AV_CH_LAYOUT_4POINT1;
case SPEAKERS_5POINT1: return AV_CH_LAYOUT_5POINT1;
case SPEAKERS_5POINT1_SURROUND: return AV_CH_LAYOUT_5POINT1_BACK;
case SPEAKERS_7POINT1: return AV_CH_LAYOUT_7POINT1;
case SPEAKERS_7POINT1_SURROUND: return AV_CH_LAYOUT_7POINT1_WIDE_BACK;
case SPEAKERS_SURROUND: return AV_CH_LAYOUT_SURROUND;
}
/* shouldn't get here */
return 0;
}
static inline enum speaker_layout convert_ff_channel_layout(uint64_t channel_layout)
{
switch (channel_layout) {
case AV_CH_LAYOUT_MONO: return SPEAKERS_MONO;
case AV_CH_LAYOUT_STEREO: return SPEAKERS_STEREO;
case AV_CH_LAYOUT_2_1: return SPEAKERS_2POINT1;
case AV_CH_LAYOUT_QUAD: return SPEAKERS_QUAD;
case AV_CH_LAYOUT_4POINT1: return SPEAKERS_4POINT1;
case AV_CH_LAYOUT_5POINT1: return SPEAKERS_5POINT1;
case AV_CH_LAYOUT_5POINT1_BACK: return SPEAKERS_5POINT1_SURROUND;
case AV_CH_LAYOUT_7POINT1: return SPEAKERS_7POINT1;
case AV_CH_LAYOUT_7POINT1_WIDE_BACK: return SPEAKERS_7POINT1_SURROUND;
case AV_CH_LAYOUT_SURROUND: return SPEAKERS_SURROUND;
}
/* shouldn't get here */
return SPEAKERS_UNKNOWN;
}
static const char *aac_getname(void *unused)
{
UNUSED_PARAMETER(unused);
@ -169,7 +208,10 @@ static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder,
}
enc->context->bit_rate = bitrate * 1000;
const struct audio_output_info *aoi;
aoi = audio_output_get_info(audio);
enc->context->channels = (int)audio_output_get_channels(audio);
enc->context->channel_layout = convert_speaker_layout(aoi->speakers);
enc->context->sample_rate = audio_output_get_sample_rate(audio);
enc->context->sample_fmt = enc->codec->sample_fmts ?
enc->codec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;
@ -206,8 +248,9 @@ static void *enc_create(obs_data_t *settings, obs_encoder_t *encoder,
enc->context->cutoff = cutoff;
}
info("bitrate: %" PRId64 ", channels: %d",
enc->context->bit_rate / 1000, enc->context->channels);
info("bitrate: %" PRId64 ", channels: %d, channel_layout: %x\n",
enc->context->bit_rate / 1000, enc->context->channels,
enc->context->channel_layout);
init_sizes(enc, audio);
@ -303,9 +346,8 @@ static obs_properties_t *enc_properties(void *unused)
UNUSED_PARAMETER(unused);
obs_properties_t *props = obs_properties_create();
obs_properties_add_int(props, "bitrate",
obs_module_text("Bitrate"), 64, 320, 32);
obs_module_text("Bitrate"), 64, 1024, 32);
return props;
}
@ -323,6 +365,7 @@ static void enc_audio_info(void *data, struct audio_convert_info *info)
struct enc_encoder *enc = data;
info->format = convert_ffmpeg_sample_format(enc->context->sample_fmt);
info->samples_per_sec = (uint32_t)enc->context->sample_rate;
info->speakers = convert_ff_channel_layout(enc->context->channel_layout);
}
static size_t enc_frame_size(void *data)

View file

@ -337,6 +337,15 @@ static bool create_audio_stream(struct ffmpeg_data *data)
context->sample_rate = aoi.samples_per_sec;
context->channel_layout =
av_get_default_channel_layout(context->channels);
//AVlib default channel layout for 4 channels is 4.0 ; fix for quad
if (aoi.speakers == SPEAKERS_QUAD)
context->channel_layout = av_get_channel_layout("quad");
//AVlib default channel layout for 5 channels is 5.0 ; fix for 4.1
if (aoi.speakers == SPEAKERS_4POINT1)
context->channel_layout = av_get_channel_layout("4.1");
context->sample_fmt = data->acodec->sample_fmts ?
data->acodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP;

View file

@ -16,6 +16,7 @@
struct gain_data {
obs_source_t *context;
size_t channels;
float multiple;
};
@ -35,7 +36,7 @@ static void gain_update(void *data, obs_data_t *s)
{
struct gain_data *gf = data;
double val = obs_data_get_double(s, S_GAIN_DB);
gf->channels = audio_output_get_channels(obs_get_audio());
gf->multiple = db_to_mul((float)val);
}
@ -51,11 +52,11 @@ static struct obs_audio_data *gain_filter_audio(void *data,
struct obs_audio_data *audio)
{
struct gain_data *gf = data;
float *adata[2] = {(float*)audio->data[0], (float*)audio->data[1]};
const size_t channels = gf->channels;
float **adata = (float**)audio->data;
const float multiple = gf->multiple;
for (size_t c = 0; c < 2; c++) {
for (size_t c = 0; c < channels; c++) {
if (audio->data[c]) {
for (size_t i = 0; i < audio->frames; i++) {
adata[c][i] *= multiple;

View file

@ -109,7 +109,7 @@ static struct obs_audio_data *noise_gate_filter_audio(void *data,
{
struct noise_gate_data *ng = data;
float *adata[2] = {(float*)audio->data[0], (float*)audio->data[1]};
float **adata = (float**)audio->data;
const float close_threshold = ng->close_threshold;
const float open_threshold = ng->open_threshold;
const float sample_rate_i = ng->sample_rate_i;
@ -120,9 +120,10 @@ static struct obs_audio_data *noise_gate_filter_audio(void *data,
const size_t channels = ng->channels;
for (size_t i = 0; i < audio->frames; i++) {
float cur_level = (channels == 2)
? fmaxf(fabsf(adata[0][i]), fabsf(adata[1][i]))
: fabsf(adata[0][i]);
float cur_level = fabsf(adata[0][i]);
for (size_t j = 0; j < channels; j++) {
cur_level = fmaxf(cur_level, fabsf(adata[j][i]));
}
if (cur_level > open_threshold && !ng->is_open) {
ng->is_open = true;

View file

@ -27,7 +27,7 @@
#define MT_ obs_module_text
#define TEXT_SUPPRESS_LEVEL MT_("NoiseSuppress.SuppressLevel")
#define MAX_PREPROC_CHANNELS 2
#define MAX_PREPROC_CHANNELS 8
/* -------------------------------------------------------- */
@ -120,12 +120,12 @@ static void noise_suppress_update(void *data, obs_data_t *s)
/* One speex state for each channel (limit 2) */
ng->copy_buffers[0] = bmalloc(frames * channels * sizeof(float));
ng->segment_buffers[0] = bmalloc(frames * channels * sizeof(spx_int16_t));
if (channels == 2) {
ng->copy_buffers[1] = ng->copy_buffers[0] + frames;
ng->segment_buffers[1] = ng->segment_buffers[0] + frames;
for (size_t c = 1; c < channels; ++c) {
ng->copy_buffers[c] = ng->copy_buffers[c-1] + frames;
ng->segment_buffers[c] = ng->segment_buffers[c-1] + frames;
}
for (size_t i = 0; i < channels; i++)
alloc_channel(ng, sample_rate, i, frames);
}

View file

@ -131,6 +131,16 @@ static void *libfdk_create(obs_data_t *settings, obs_encoder_t *encoder)
case 6:
mode = MODE_1_2_2_1;
break;
/* lib_fdk-aac > 1.3 required for 7.1 surround;
* uncomment if available on linux build
*/
#ifndef __linux__
case 8:
mode = MODE_7_1_REAR_SURROUND;
break;
#endif
default:
blog(LOG_ERROR, "Invalid channel count");
goto fail;

View file

@ -102,6 +102,22 @@ static bool build_flv_meta_data(obs_output_t *context,
enc_bool_val(&enc, end, "stereo",
audio_output_get_channels(audio) == 2);
enc_bool_val(&enc, end, "2.1",
audio_output_get_channels(audio) == 3);
enc_bool_val(&enc, end, "3.1",
audio_output_get_channels(audio) == 4);
enc_bool_val(&enc, end, "4.0 Quad",
audio_output_get_channels(audio) == 4);
enc_bool_val(&enc, end, "4.1",
audio_output_get_channels(audio) == 5);
enc_bool_val(&enc, end, "5.1",
audio_output_get_channels(audio) == 6);
enc_bool_val(&enc, end, "5.1surround",
audio_output_get_channels(audio) == 6);
enc_bool_val(&enc, end, "7.1",
audio_output_get_channels(audio) == 8);
enc_bool_val(&enc, end, "7.1surround",
audio_output_get_channels(audio) == 8);
dstr_printf(&encoder_name, "%s (libobs version ",
MODULE_NAME);

View file

@ -94,6 +94,21 @@ static inline enum audio_format convert_sample_format(int f)
return AUDIO_FORMAT_UNKNOWN;
}
static inline enum speaker_layout convert_speaker_layout(uint8_t channels)
{
switch (channels) {
case 0: return SPEAKERS_UNKNOWN;
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_QUAD;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
default: return SPEAKERS_UNKNOWN;
}
}
static inline void copy_data(struct ffmpeg_decode *decode, uint8_t *data,
size_t size)
{
@ -142,7 +157,7 @@ int ffmpeg_decode_audio(struct ffmpeg_decode *decode,
audio->data[i] = decode->frame->data[i];
audio->samples_per_sec = decode->frame->sample_rate;
audio->speakers = (enum speaker_layout)decode->decoder->channels;
audio->speakers = convert_speaker_layout(decode->decoder->channels);
audio->format = convert_sample_format(decode->frame->format);
audio->frames = decode->frame->nb_samples;

View file

@ -403,6 +403,21 @@ static inline audio_format ConvertAudioFormat(AudioFormat format)
}
}
static inline enum speaker_layout convert_speaker_layout(uint8_t channels)
{
switch (channels) {
case 0: return SPEAKERS_UNKNOWN;
case 1: return SPEAKERS_MONO;
case 2: return SPEAKERS_STEREO;
case 3: return SPEAKERS_2POINT1;
case 4: return SPEAKERS_QUAD;
case 5: return SPEAKERS_4POINT1;
case 6: return SPEAKERS_5POINT1;
case 8: return SPEAKERS_7POINT1;
default: return SPEAKERS_UNKNOWN;
}
}
//#define LOG_ENCODED_VIDEO_TS 1
//#define LOG_ENCODED_AUDIO_TS 1
@ -559,7 +574,7 @@ void DShowInput::OnAudioData(const AudioConfig &config,
return;
}
audio.speakers = (enum speaker_layout)config.channels;
audio.speakers = convert_speaker_layout(config.channels);
audio.format = ConvertAudioFormat(config.format);
audio.samples_per_sec = (uint32_t)config.sampleRate;
audio.data[0] = data;

View file

@ -15,8 +15,11 @@ using namespace std;
static void GetWASAPIDefaults(obs_data_t *settings);
#define KSAUDIO_SPEAKER_4POINT1 (KSAUDIO_SPEAKER_QUAD|SPEAKER_LOW_FREQUENCY)
// Fix inconsistent defs of speaker_surround between avutil & wasapi
#define KSAUDIO_SPEAKER_2POINT1 (KSAUDIO_SPEAKER_STEREO|SPEAKER_LOW_FREQUENCY)
#define KSAUDIO_SPEAKER_SURROUND_AVUTIL \
(KSAUDIO_SPEAKER_STEREO|SPEAKER_FRONT_CENTER)
#define KSAUDIO_SPEAKER_4POINT1 (KSAUDIO_SPEAKER_QUAD|SPEAKER_LOW_FREQUENCY)
class WASAPISource {
ComPtr<IMMDevice> device;
@ -244,11 +247,11 @@ static speaker_layout ConvertSpeakerLayout(DWORD layout, WORD channels)
case KSAUDIO_SPEAKER_QUAD: return SPEAKERS_QUAD;
case KSAUDIO_SPEAKER_2POINT1: return SPEAKERS_2POINT1;
case KSAUDIO_SPEAKER_4POINT1: return SPEAKERS_4POINT1;
case KSAUDIO_SPEAKER_SURROUND: return SPEAKERS_SURROUND;
case KSAUDIO_SPEAKER_5POINT1: return SPEAKERS_5POINT1;
case KSAUDIO_SPEAKER_5POINT1_SURROUND: return SPEAKERS_5POINT1_SURROUND;
case KSAUDIO_SPEAKER_7POINT1: return SPEAKERS_7POINT1;
case KSAUDIO_SPEAKER_7POINT1_SURROUND: return SPEAKERS_7POINT1_SURROUND;
case KSAUDIO_SPEAKER_SURROUND_AVUTIL: return SPEAKERS_SURROUND;
case KSAUDIO_SPEAKER_5POINT1: return SPEAKERS_5POINT1_SURROUND;
case KSAUDIO_SPEAKER_5POINT1_SURROUND: return SPEAKERS_5POINT1;
case KSAUDIO_SPEAKER_7POINT1: return SPEAKERS_7POINT1_SURROUND;
case KSAUDIO_SPEAKER_7POINT1_SURROUND: return SPEAKERS_7POINT1;
}
return (speaker_layout)channels;