diff --git a/libobs/media-io/audio-resampler-ffmpeg.c b/libobs/media-io/audio-resampler-ffmpeg.c index eb5be4f7435e2e..6ef65867857b13 100644 --- a/libobs/media-io/audio-resampler-ffmpeg.c +++ b/libobs/media-io/audio-resampler-ffmpeg.c @@ -216,3 +216,27 @@ bool audio_resampler_resample(audio_resampler_t *rs, uint8_t *output[], uint32_t *out_frames = (uint32_t)ret; return true; } + +// Allows passing of a matrix for channel re-ordering and custom weights. +bool audio_resampler_set_matrix(audio_resampler_t *rs, double *matrix, int stride) +{ + if (!rs) { + return false; + } + + swr_close(rs->context); + + if (swr_set_matrix(rs->context, matrix, stride) < 0) { + blog(LOG_DEBUG, "swr_set_matrix failed"); + audio_resampler_destroy(rs); + return false; + } + + int errcode = swr_init(rs->context); + if (errcode != 0) { + blog(LOG_ERROR, "swr_init failed: error code %d", errcode); + audio_resampler_destroy(rs); + return false; + } + return true; +} diff --git a/libobs/media-io/audio-resampler.h b/libobs/media-io/audio-resampler.h index ca1e7b08d58000..25f9b2872513d7 100644 --- a/libobs/media-io/audio-resampler.h +++ b/libobs/media-io/audio-resampler.h @@ -39,6 +39,8 @@ EXPORT void audio_resampler_destroy(audio_resampler_t *resampler); EXPORT bool audio_resampler_resample(audio_resampler_t *resampler, uint8_t *output[], uint32_t *out_frames, uint64_t *ts_offset, const uint8_t *const input[], uint32_t in_frames); +EXPORT bool audio_resampler_set_matrix(audio_resampler_t *rs, double *matrix, int stride); + #ifdef __cplusplus } #endif diff --git a/plugins/vlc-video/vlc-video-source.c b/plugins/vlc-video/vlc-video-source.c index 0be3ced5365d6a..c568e445b32581 100644 --- a/plugins/vlc-video/vlc-video-source.c +++ b/plugins/vlc-video/vlc-video-source.c @@ -1,4 +1,6 @@ #include "vlc-video-plugin.h" + +#include #include #include #include @@ -74,6 +76,8 @@ struct vlc_source { obs_hotkey_id stop_hotkey; obs_hotkey_id playlist_next_hotkey; obs_hotkey_id playlist_prev_hotkey; + + audio_resampler_t *resampler; }; static libvlc_media_t *get_media(media_file_array_t *files, const char *path) @@ -349,6 +353,10 @@ static void vlcs_destroy(void *data) free_files(&c->files); pthread_mutex_destroy(&c->mutex); + + if (c->resampler) + audio_resampler_destroy(c->resampler); + bfree(c); } @@ -471,10 +479,19 @@ static void vlcs_audio_play(void *data, const void *samples, unsigned count, int c->audio_capacity = count; } - memcpy((void *)c->audio.data[0], samples, size); c->audio.timestamp = (uint64_t)pts * 1000ULL - time_start; c->audio.frames = count; + if (c->resampler) { + uint8_t *output[MAX_AUDIO_CHANNELS]; + uint32_t out_frames; + uint64_t ts_offset; + audio_resampler_resample(c->resampler, (uint8_t **)output, &out_frames, &ts_offset, + (const uint8_t **)&samples, (uint32_t)count); + memcpy((void *)c->audio.data[0], output[0], size); + } else { + memcpy((void *)c->audio.data[0], samples, size); + } obs_source_output_audio(c->source, &c->audio); } @@ -490,6 +507,52 @@ static int vlcs_audio_setup(void **p_data, char *format, unsigned *rate, unsigne if (*channels > out_channels) *channels = out_channels; + bool need_resampling = (*channels == 6 && out_channels == 6) || (*channels == 8 && out_channels == 8); + + /* Setup swresampler for 5.1 & 7.1 audio: + * VLC orders 5.1 as FL FR BL BR FC LFE ==> switches to FL FR FC LFE BL BR; + * VLC orders 7.1 as FL FR SL SR BL BR FC LFE ==> switches to FL FR FC LFE BL BR SL SR */ + if (need_resampling) { + struct resample_info src, dst; + src.samples_per_sec = *rate; + src.format = new_audio_format; + src.speakers = (enum speaker_layout) * channels; + + dst.samples_per_sec = *rate; + dst.format = new_audio_format; + dst.speakers = (enum speaker_layout) * channels; + + c->resampler = audio_resampler_create(&dst, &src); + /* clang-format off */ + /* Each line in the matrices corresponds to an outgoing channel (1st line is ch1, etc.). + * The weight of incoming channels into an outgoing channel is in each row. + */ + double matrix6[36] = { + 1, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, + 0, 0, 1, 0, 0, 0, + 0, 0, 0, 1, 0, 0 + }; + double matrix8[64] = { + 1, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 0, 0, 1, + 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 1, 0, 0, + 0, 0, 1, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, + }; + /* clang-format on */ + bool is_5_1 = out_channels == 6; + if (c->resampler) { + if (!audio_resampler_set_matrix(c->resampler, is_5_1 ? matrix6 : matrix8, is_5_1 ? 6 : 8)) + c->resampler = NULL; + } + } + /* don't free audio data if the data is the same format */ if (c->audio.format == new_audio_format && c->audio.samples_per_sec == *rate && c->audio.speakers == (enum speaker_layout) * channels) @@ -931,6 +994,8 @@ static void *vlcs_create(obs_data_t *settings, obs_source_t *source) c->playlist_prev_hotkey = obs_hotkey_register_source( source, "VLCSource.PlaylistPrev", obs_module_text("PlaylistPrev"), vlcs_playlist_prev_hotkey, c); + c->resampler = NULL; + pthread_mutex_init_value(&c->mutex); if (pthread_mutex_init(&c->mutex, NULL) != 0) goto error;