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:
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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"
|
||||
|
@@ -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);
|
||||
}
|
||||
}
|
||||
|
@@ -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);
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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)
|
||||
|
@@ -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;
|
||||
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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;
|
||||
|
@@ -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);
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user