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

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