From c232ebde1537101c8bd81068c4383ad0087fb865 Mon Sep 17 00:00:00 2001 From: jp9000 Date: Sun, 23 Feb 2014 16:27:19 -0700 Subject: [PATCH] Implement a few more audio options/functions Implement a few audio options in to the user interface as well as a few inline audio functions in audio-io.h. Make it so ffmpeg plugin automatically converts to the desired format. Use regular interleaved float internally for audio instead of planar float. --- libobs/media-io/audio-io.c | 8 ++ libobs/media-io/audio-io.h | 12 ++- obs/forms/NameDialog.ui | 12 +-- obs/forms/OBSBasicSettings.ui | 107 +++++++++++++++++--- obs/obs-app.cpp | 5 + obs/window-basic-main.cpp | 22 ++-- obs/window-basic-settings.cpp | 133 +++++++++++++++++++++++-- obs/window-basic-settings.hpp | 14 ++- plugins/obs-ffmpeg/obs-ffmpeg-output.c | 50 +++++++--- test/test-input/test-sinewave.c | 2 +- 10 files changed, 311 insertions(+), 54 deletions(-) diff --git a/libobs/media-io/audio-io.c b/libobs/media-io/audio-io.c index 617af18fc..06be02375 100644 --- a/libobs/media-io/audio-io.c +++ b/libobs/media-io/audio-io.c @@ -519,6 +519,14 @@ bool audio_output_connect(audio_t audio, audio->info.samples_per_sec; } + if (input.conversion.format == AUDIO_FORMAT_UNKNOWN) + input.conversion.format = audio->info.format; + if (input.conversion.speakers == SPEAKERS_UNKNOWN) + input.conversion.speakers = audio->info.speakers; + if (input.conversion.samples_per_sec == 0) + input.conversion.samples_per_sec = + audio->info.samples_per_sec; + success = audio_input_init(&input, audio); if (success) da_push_back(audio->inputs, &input); diff --git a/libobs/media-io/audio-io.h b/libobs/media-io/audio-io.h index cd7d0633f..8b81c91a6 100644 --- a/libobs/media-io/audio-io.h +++ b/libobs/media-io/audio-io.h @@ -127,7 +127,7 @@ static inline size_t get_audio_bytes_per_channel(enum audio_format type) return 0; } -static inline size_t is_audio_planar(enum audio_format type) +static inline bool is_audio_planar(enum audio_format type) { switch (type) { case AUDIO_FORMAT_U8BIT: @@ -149,10 +149,18 @@ static inline size_t is_audio_planar(enum audio_format type) return false; } +static inline size_t get_audio_planes(enum audio_format type, + enum speaker_layout speakers) +{ + return (is_audio_planar(type) ? get_audio_channels(speakers) : 1); +} + static inline size_t get_audio_size(enum audio_format type, enum speaker_layout speakers, uint32_t frames) { - return get_audio_channels(speakers) * + bool planar = is_audio_planar(type); + + return (planar ? 1 : get_audio_channels(speakers)) * get_audio_bytes_per_channel(type) * frames; } diff --git a/obs/forms/NameDialog.ui b/obs/forms/NameDialog.ui index 838554553..cdd244bf4 100644 --- a/obs/forms/NameDialog.ui +++ b/obs/forms/NameDialog.ui @@ -2,6 +2,9 @@ NameDialog + + Qt::WindowModal + 0 @@ -10,12 +13,6 @@ 102 - - - 0 - 0 - - Dialog @@ -31,6 +28,9 @@ TextLabel + + true + diff --git a/obs/forms/OBSBasicSettings.ui b/obs/forms/OBSBasicSettings.ui index bb48005e1..26dbd26ed 100644 --- a/obs/forms/OBSBasicSettings.ui +++ b/obs/forms/OBSBasicSettings.ui @@ -77,7 +77,7 @@ - 3 + 2 @@ -127,14 +127,66 @@ Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - false + + + + Sample Rate: - + + + + 44.1khz + + + 1 + + + + 22.05khz + + + + + 44.1khz + + + + + 48khz + + + + + + + + Channels: + + + + + + + Stereo + + + 1 + + + + Mono + + + + + Stereo + + + + + @@ -150,7 +202,14 @@ - + + + + false + + + + Desktop Audio Device 2: @@ -160,55 +219,75 @@ - + false - + Mic/Aux Audio Device 1: - + false - + Mic/Aux Audio Device 2: - + false - + Mic/Aux Audio Device 3: - + false + + + + 60 + + + 20000 + + + 700 + + + + + + + Audio Buffering Time (milliseconds): + + + diff --git a/obs/obs-app.cpp b/obs/obs-app.cpp index ab9691a93..52bfca9f0 100644 --- a/obs/obs-app.cpp +++ b/obs/obs-app.cpp @@ -96,6 +96,11 @@ bool OBSApp::InitGlobalConfigDefaults() config_set_default_uint(globalConfig, "Video", "FPSDen", 1); config_set_default_uint(globalConfig, "Video", "FPSNS", 33333333); + config_set_default_uint(globalConfig, "Audio", "SampleRate", 44100); + config_set_default_string(globalConfig, "Audio", "ChannelSetup", + "Stereo"); + config_set_default_uint(globalConfig, "Audio", "BufferingTime", 700); + return true; } diff --git a/obs/window-basic-main.cpp b/obs/window-basic-main.cpp index d9ba9229c..5245d9e6b 100644 --- a/obs/window-basic-main.cpp +++ b/obs/window-basic-main.cpp @@ -313,13 +313,23 @@ bool OBSBasic::ResetVideo() bool OBSBasic::ResetAudio() { - /* TODO: load audio settings from config */ struct audio_output_info ai; - ai.name = "test"; - ai.samples_per_sec = 44100; - ai.format = AUDIO_FORMAT_FLOAT_PLANAR; - ai.speakers = SPEAKERS_STEREO; - ai.buffer_ms = 700; + ai.name = "Main Audio Track"; + ai.format = AUDIO_FORMAT_FLOAT; + + ai.samples_per_sec = config_get_uint(GetGlobalConfig(), "Audio", + "SampleRate"); + + const char *channelSetupStr = config_get_string(GetGlobalConfig(), + "Audio", "ChannelSetup"); + + if (strcmp(channelSetupStr, "Mono") == 0) + ai.speakers = SPEAKERS_MONO; + else + ai.speakers = SPEAKERS_STEREO; + + ai.buffer_ms = config_get_uint(GetGlobalConfig(), "Audio", + "BufferingTime"); return obs_reset_audio(&ai); } diff --git a/obs/window-basic-settings.cpp b/obs/window-basic-settings.cpp index 5b8b98126..52dc20cd4 100644 --- a/obs/window-basic-settings.cpp +++ b/obs/window-basic-settings.cpp @@ -178,6 +178,8 @@ static const size_t numVals = sizeof(vals)/sizeof(double); void OBSBasicSettings::ResetDownscales(uint32_t cx, uint32_t cy) { + ui->outputResolution->clear(); + for (size_t idx = 0; idx < numVals; idx++) { uint32_t downscaleCX = uint32_t(double(cx) / vals[idx]); uint32_t downscaleCY = uint32_t(double(cy) / vals[idx]); @@ -196,7 +198,6 @@ void OBSBasicSettings::LoadResolutionLists() vector monitors; ui->baseResolution->clear(); - ui->outputResolution->clear(); GetMonitors(monitors); @@ -270,14 +271,47 @@ void OBSBasicSettings::LoadVideoSettings() loading = false; } +void OBSBasicSettings::LoadAudioSettings() +{ + uint32_t sampleRate = config_get_uint(GetGlobalConfig(), "Audio", + "SampleRate"); + const char *speakers = config_get_string(GetGlobalConfig(), "Audio", + "ChannelSetup"); + uint32_t bufferingTime = config_get_uint(GetGlobalConfig(), "Audio", + "BufferingTime"); + + loading = true; + + const char *str; + if (sampleRate == 22050) + str = "22.05khz"; + else if (sampleRate == 48000) + str = "48khz"; + else + str = "44.1khz"; + + int sampleRateIdx = ui->sampleRate->findText(str); + if (sampleRateIdx != -1) + ui->sampleRate->setCurrentIndex(sampleRateIdx); + + if (strcmp(speakers, "Mono") == 0) + ui->channelSetup->setCurrentIndex(0); + else + ui->channelSetup->setCurrentIndex(1); + + ui->audioBufferingTime->setValue(bufferingTime); + + loading = false; +} + void OBSBasicSettings::LoadSettings(bool changedOnly) { if (!changedOnly || generalChanged) LoadGeneralSettings(); //if (!changedOnly || outputChanged) // LoadOutputSettings(); - //if (!changedOnly || audioChanged) - // LoadOutputSettings(); + if (!changedOnly || audioChanged) + LoadAudioSettings(); if (!changedOnly || videoChanged) LoadVideoSettings(); } @@ -331,14 +365,39 @@ void OBSBasicSettings::SaveVideoSettings() window->ResetVideo(); } +void OBSBasicSettings::SaveAudioSettings() +{ + QString sampleRateStr = ui->sampleRate->currentText(); + int channelSetupIdx = ui->channelSetup->currentIndex(); + int bufferingTime = ui->audioBufferingTime->value(); + + const char *channelSetup; + if (channelSetupIdx == 0) + channelSetup = "Mono"; + else + channelSetup = "Stereo"; + + int sampleRate = 44100; + if (sampleRateStr == "22.05khz") + sampleRate = 22050; + else if (sampleRateStr == "48khz") + sampleRate = 48000; + + config_set_uint(GetGlobalConfig(), "Audio", "SampleRate", sampleRate); + config_set_string(GetGlobalConfig(), "Audio", "ChannelSetup", + channelSetup); + config_set_uint(GetGlobalConfig(), "Audio", "BufferingTime", + bufferingTime); +} + void OBSBasicSettings::SaveSettings() { if (generalChanged) SaveGeneralSettings(); //if (outputChanged) // SaveOutputSettings(); - //if (audioChanged) - // SaveAudioSettings(); + if (audioChanged) + SaveAudioSettings(); if (videoChanged) SaveVideoSettings(); @@ -430,6 +489,30 @@ void OBSBasicSettings::on_language_currentIndexChanged(int index) UNUSED_PARAMETER(index); } +void OBSBasicSettings::on_sampleRate_currentIndexChanged(int index) +{ + if (!loading) + audioChanged = true; + + UNUSED_PARAMETER(index); +} + +void OBSBasicSettings::on_channelSetup_currentIndexChanged(int index) +{ + if (!loading) + audioChanged = true; + + UNUSED_PARAMETER(index); +} + +void OBSBasicSettings::on_audioBufferingTime_valueChanged(int value) +{ + if (!loading) + audioChanged = true; + + UNUSED_PARAMETER(value); +} + void OBSBasicSettings::on_renderer_currentIndexChanged(int index) { if (!loading) { @@ -450,8 +533,14 @@ void OBSBasicSettings::on_fpsType_currentIndexChanged(int index) void OBSBasicSettings::on_baseResolution_editTextChanged(const QString &text) { - if (!loading && ValidResolutions(ui.get())) + if (!loading && ValidResolutions(ui.get())) { + QString baseResolution = ui->baseResolution->currentText(); + uint32_t cx, cy; + + ConvertResText(QT_TO_UTF8(baseResolution), cx, cy); + ResetDownscales(cx, cy); videoChanged = true; + } UNUSED_PARAMETER(text); } @@ -463,3 +552,35 @@ void OBSBasicSettings::on_outputResolution_editTextChanged(const QString &text) UNUSED_PARAMETER(text); } + +void OBSBasicSettings::on_fpsCommon_currentIndexChanged(int index) +{ + if (!loading) + videoChanged = true; + + UNUSED_PARAMETER(index); +} + +void OBSBasicSettings::on_fpsInteger_valueChanged(int value) +{ + if (!loading) + videoChanged = true; + + UNUSED_PARAMETER(value); +} + +void OBSBasicSettings::on_fpsNumerator_valueChanged(int value) +{ + if (!loading) + videoChanged = true; + + UNUSED_PARAMETER(value); +} + +void OBSBasicSettings::on_fpsDenominator_valueChanged(int value) +{ + if (!loading) + videoChanged = true; + + UNUSED_PARAMETER(value); +} diff --git a/obs/window-basic-settings.hpp b/obs/window-basic-settings.hpp index 0a1e347d2..4e9dd8f6d 100644 --- a/obs/window-basic-settings.hpp +++ b/obs/window-basic-settings.hpp @@ -56,8 +56,9 @@ private: void LoadGeneralSettings(); //void LoadOutputSettings(); - //void LoadAudioSettings(); + void LoadAudioSettings(); void LoadVideoSettings(); + void LoadSettings(bool changedOnly); /* general */ void LoadLanguageList(); @@ -67,11 +68,10 @@ private: void ResetDownscales(uint32_t cx, uint32_t cy); void LoadResolutionLists(); void LoadFPSData(); - void LoadSettings(bool changedOnly); void SaveGeneralSettings(); //void SaveOutputSettings(); - //void SaveAudioSettings(); + void SaveAudioSettings(); void SaveVideoSettings(); void SaveSettings(); @@ -81,10 +81,18 @@ private slots: void on_language_currentIndexChanged(int index); + void on_sampleRate_currentIndexChanged(int index); + void on_channelSetup_currentIndexChanged(int index); + void on_audioBufferingTime_valueChanged(int index); + void on_renderer_currentIndexChanged(int index); void on_fpsType_currentIndexChanged(int index); void on_baseResolution_editTextChanged(const QString &text); void on_outputResolution_editTextChanged(const QString &text); + void on_fpsCommon_currentIndexChanged(int index); + void on_fpsInteger_valueChanged(int value); + void on_fpsNumerator_valueChanged(int value); + void on_fpsDenominator_valueChanged(int value); protected: virtual void closeEvent(QCloseEvent *event); diff --git a/plugins/obs-ffmpeg/obs-ffmpeg-output.c b/plugins/obs-ffmpeg/obs-ffmpeg-output.c index 3ab0e4121..aed91162a 100644 --- a/plugins/obs-ffmpeg/obs-ffmpeg-output.c +++ b/plugins/obs-ffmpeg/obs-ffmpeg-output.c @@ -21,6 +21,9 @@ #include #include +/* NOTE: much of this stuff is test stuff that was more or less copied from + * the muxing.c ffmpeg example */ + struct ffmpeg_data { AVStream *video; AVStream *audio; @@ -34,6 +37,9 @@ struct ffmpeg_data { int frame_size; int total_frames; + enum audio_format audio_format; + size_t audio_planes; + size_t audio_size; struct circlebuf excess_frames[MAX_AV_PLANES]; uint8_t *samples[MAX_AV_PLANES]; AVFrame *aframe; @@ -52,12 +58,6 @@ struct ffmpeg_output { /* ------------------------------------------------------------------------- */ -/* TODO: remove these later */ -#define SPS_TODO 44100 - -/* NOTE: much of this stuff is test stuff that was more or less copied from - * the muxing.c ffmpeg example */ - static inline enum AVPixelFormat obs_to_ffmpeg_video_format( enum video_format format) { @@ -76,6 +76,24 @@ static inline enum AVPixelFormat obs_to_ffmpeg_video_format( return AV_PIX_FMT_NONE; } +static inline enum audio_format convert_ffmpeg_sample_format( + enum AVSampleFormat format) +{ + switch ((uint32_t)format) { + case AV_SAMPLE_FMT_U8: return AUDIO_FORMAT_U8BIT; + case AV_SAMPLE_FMT_S16: return AUDIO_FORMAT_16BIT; + case AV_SAMPLE_FMT_S32: return AUDIO_FORMAT_32BIT; + case AV_SAMPLE_FMT_FLT: return AUDIO_FORMAT_FLOAT; + case AV_SAMPLE_FMT_U8P: return AUDIO_FORMAT_U8BIT_PLANAR; + case AV_SAMPLE_FMT_S16P: return AUDIO_FORMAT_16BIT_PLANAR; + case AV_SAMPLE_FMT_S32P: return AUDIO_FORMAT_32BIT_PLANAR; + case AV_SAMPLE_FMT_FLTP: return AUDIO_FORMAT_FLOAT_PLANAR; + } + + /* shouldn't get here */ + return AUDIO_FORMAT_16BIT; +} + static bool new_stream(struct ffmpeg_data *data, AVStream **stream, AVCodec **codec, enum AVCodecID id) { @@ -237,6 +255,10 @@ static bool create_audio_stream(struct ffmpeg_data *data) context->sample_fmt = data->acodec->sample_fmts ? data->acodec->sample_fmts[0] : AV_SAMPLE_FMT_FLTP; + data->audio_format = convert_ffmpeg_sample_format(context->sample_fmt); + data->audio_planes = get_audio_planes(data->audio_format, aoi.speakers); + data->audio_size = get_audio_size(data->audio_format, aoi.speakers, 1); + if (data->output->oformat->flags & AVFMT_GLOBALHEADER) context->flags |= CODEC_FLAG_GLOBAL_HEADER; @@ -532,21 +554,19 @@ static void receive_audio(void *param, const struct audio_data *frame) struct ffmpeg_data *data = &output->ff_data; AVCodecContext *context = data->audio->codec; - size_t planes = audio_output_planes(obs_audio()); - size_t block_size = audio_output_blocksize(obs_audio()); - size_t frame_size_bytes = (size_t)data->frame_size * block_size; + size_t frame_size_bytes = (size_t)data->frame_size * data->audio_size; - for (size_t i = 0; i < planes; i++) + for (size_t i = 0; i < data->audio_planes; i++) circlebuf_push_back(&data->excess_frames[i], frame->data[0], - frame->frames * block_size); + frame->frames * data->audio_size); while (data->excess_frames[0].size >= frame_size_bytes) { - for (size_t i = 0; i < planes; i++) + for (size_t i = 0; i < data->audio_planes; i++) circlebuf_pop_front(&data->excess_frames[i], data->samples[i], frame_size_bytes); - encode_audio(data, context, block_size); + encode_audio(data, context, data->audio_size); } } @@ -575,9 +595,7 @@ static bool ffmpeg_output_start(void *data) return false; struct audio_convert_info aci = { - .samples_per_sec = SPS_TODO, - .format = AUDIO_FORMAT_FLOAT_PLANAR, - .speakers = SPEAKERS_STEREO + .format = output->ff_data.audio_format }; struct video_scale_info vsi = { diff --git a/test/test-input/test-sinewave.c b/test/test-input/test-sinewave.c index af9441d70..998714e29 100644 --- a/test/test-input/test-sinewave.c +++ b/test/test-input/test-sinewave.c @@ -37,7 +37,7 @@ static void *sinewave_thread(void *pdata) if (cos_val > M_PI_X2) cos_val -= M_PI_X2; - double wave = cos(cos_val); + double wave = cos(cos_val) * 0.5; bytes[i] = (uint8_t)((wave+1.0)*0.5 * 255.0); }