Browse Source

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.
jp9000 11 years ago
parent
commit
c232ebde15

+ 8 - 0
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);

+ 10 - 2
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;
 }

+ 6 - 6
obs/forms/NameDialog.ui

@@ -2,6 +2,9 @@
 <ui version="4.0">
  <class>NameDialog</class>
  <widget class="QDialog" name="NameDialog">
+  <property name="windowModality">
+   <enum>Qt::WindowModal</enum>
+  </property>
   <property name="geometry">
    <rect>
     <x>0</x>
@@ -10,12 +13,6 @@
     <height>102</height>
    </rect>
   </property>
-  <property name="sizePolicy">
-   <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
-    <horstretch>0</horstretch>
-    <verstretch>0</verstretch>
-   </sizepolicy>
-  </property>
   <property name="windowTitle">
    <string notr="true">Dialog</string>
   </property>
@@ -31,6 +28,9 @@
      <property name="text">
       <string notr="true">TextLabel</string>
      </property>
+     <property name="wordWrap">
+      <bool>true</bool>
+     </property>
     </widget>
    </item>
    <item>

+ 92 - 13
obs/forms/OBSBasicSettings.ui

@@ -77,7 +77,7 @@
      <item>
       <widget class="QStackedWidget" name="settingsPages">
        <property name="currentIndex">
-        <number>3</number>
+        <number>2</number>
        </property>
        <widget class="QWidget" name="generalPage">
         <layout class="QFormLayout" name="formLayout_2">
@@ -127,14 +127,66 @@
          <property name="labelAlignment">
           <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
          </property>
+         <item row="0" column="0">
+          <widget class="QLabel" name="label_14">
+           <property name="text">
+            <string>Sample Rate:</string>
+           </property>
+          </widget>
+         </item>
          <item row="0" column="1">
-          <widget class="QComboBox" name="desktopAudioDevice1">
-           <property name="enabled">
-            <bool>false</bool>
+          <widget class="QComboBox" name="sampleRate">
+           <property name="currentText">
+            <string notr="true">44.1khz</string>
+           </property>
+           <property name="currentIndex">
+            <number>1</number>
+           </property>
+           <item>
+            <property name="text">
+             <string>22.05khz</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>44.1khz</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>48khz</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="label_15">
+           <property name="text">
+            <string>Channels:</string>
            </property>
           </widget>
          </item>
-         <item row="0" column="0">
+         <item row="1" column="1">
+          <widget class="QComboBox" name="channelSetup">
+           <property name="currentText">
+            <string>Stereo</string>
+           </property>
+           <property name="currentIndex">
+            <number>1</number>
+           </property>
+           <item>
+            <property name="text">
+             <string>Mono</string>
+            </property>
+           </item>
+           <item>
+            <property name="text">
+             <string>Stereo</string>
+            </property>
+           </item>
+          </widget>
+         </item>
+         <item row="2" column="0">
           <widget class="QLabel" name="label_2">
            <property name="minimumSize">
             <size>
@@ -150,7 +202,14 @@
            </property>
           </widget>
          </item>
-         <item row="1" column="0">
+         <item row="2" column="1">
+          <widget class="QComboBox" name="desktopAudioDevice1">
+           <property name="enabled">
+            <bool>false</bool>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="0">
           <widget class="QLabel" name="label_3">
            <property name="text">
             <string>Desktop Audio Device 2:</string>
@@ -160,55 +219,75 @@
            </property>
           </widget>
          </item>
-         <item row="1" column="1">
+         <item row="3" column="1">
           <widget class="QComboBox" name="desktopAudioDevice2">
            <property name="enabled">
             <bool>false</bool>
            </property>
           </widget>
          </item>
-         <item row="2" column="0">
+         <item row="4" column="0">
           <widget class="QLabel" name="label_4">
            <property name="text">
             <string>Mic/Aux Audio Device 1:</string>
            </property>
           </widget>
          </item>
-         <item row="2" column="1">
+         <item row="4" column="1">
           <widget class="QComboBox" name="auxAudioDevice1">
            <property name="enabled">
             <bool>false</bool>
            </property>
           </widget>
          </item>
-         <item row="3" column="0">
+         <item row="5" column="0">
           <widget class="QLabel" name="label_5">
            <property name="text">
             <string>Mic/Aux Audio Device 2:</string>
            </property>
           </widget>
          </item>
-         <item row="3" column="1">
+         <item row="5" column="1">
           <widget class="QComboBox" name="auxAudioDevice2">
            <property name="enabled">
             <bool>false</bool>
            </property>
           </widget>
          </item>
-         <item row="4" column="0">
+         <item row="6" column="0">
           <widget class="QLabel" name="label_6">
            <property name="text">
             <string>Mic/Aux Audio Device 3:</string>
            </property>
           </widget>
          </item>
-         <item row="4" column="1">
+         <item row="6" column="1">
           <widget class="QComboBox" name="auxAudioDevice3">
            <property name="enabled">
             <bool>false</bool>
            </property>
           </widget>
          </item>
+         <item row="7" column="1">
+          <widget class="QSpinBox" name="audioBufferingTime">
+           <property name="minimum">
+            <number>60</number>
+           </property>
+           <property name="maximum">
+            <number>20000</number>
+           </property>
+           <property name="value">
+            <number>700</number>
+           </property>
+          </widget>
+         </item>
+         <item row="7" column="0">
+          <widget class="QLabel" name="label_16">
+           <property name="text">
+            <string>Audio Buffering Time (milliseconds):</string>
+           </property>
+          </widget>
+         </item>
         </layout>
        </widget>
        <widget class="QWidget" name="videoPage">

+ 5 - 0
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;
 }
 

+ 16 - 6
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);
 }

+ 127 - 6
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<MonitorInfo> 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);
+}

+ 11 - 3
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);

+ 34 - 16
plugins/obs-ffmpeg/obs-ffmpeg-output.c

@@ -21,6 +21,9 @@
 #include <libavformat/avformat.h>
 #include <libswscale/swscale.h>
 
+/* 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 = {

+ 1 - 1
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);
 		}