Browse Source

UI: Add Opus bitrate map and per encoder bitrate list

Also refactors AAC bitrate map too.
tytan652 2 years ago
parent
commit
b15684aa17
4 changed files with 232 additions and 42 deletions
  1. 206 26
      UI/audio-encoders.cpp
  2. 11 3
      UI/audio-encoders.hpp
  3. 3 3
      UI/window-basic-main-outputs.cpp
  4. 12 10
      UI/window-basic-settings.cpp

+ 206 - 26
UI/audio-encoders.cpp

@@ -23,18 +23,17 @@ static const char *EncoderName(const std::string &id)
 	return NullToEmpty(obs_encoder_get_display_name(id.c_str()));
 	return NullToEmpty(obs_encoder_get_display_name(id.c_str()));
 }
 }
 
 
-static map<int, std::string> bitrateMap;
-
-static void HandleIntProperty(obs_property_t *prop, const char *id)
+static void HandleIntProperty(obs_property_t *prop, std::vector<int> &bitrates)
 {
 {
 	const int max_ = obs_property_int_max(prop);
 	const int max_ = obs_property_int_max(prop);
 	const int step = obs_property_int_step(prop);
 	const int step = obs_property_int_step(prop);
 
 
 	for (int i = obs_property_int_min(prop); i <= max_; i += step)
 	for (int i = obs_property_int_min(prop); i <= max_; i += step)
-		bitrateMap[i] = id;
+		bitrates.push_back(i);
 }
 }
 
 
-static void HandleListProperty(obs_property_t *prop, const char *id)
+static void HandleListProperty(obs_property_t *prop, const char *id,
+			       std::vector<int> &bitrates)
 {
 {
 	obs_combo_format format = obs_property_list_format(prop);
 	obs_combo_format format = obs_property_list_format(prop);
 	if (format != OBS_COMBO_FORMAT_INT) {
 	if (format != OBS_COMBO_FORMAT_INT) {
@@ -53,7 +52,7 @@ static void HandleListProperty(obs_property_t *prop, const char *id)
 
 
 		int bitrate =
 		int bitrate =
 			static_cast<int>(obs_property_list_item_int(prop, i));
 			static_cast<int>(obs_property_list_item_int(prop, i));
-		bitrateMap[bitrate] = id;
+		bitrates.push_back(bitrate);
 	}
 	}
 }
 }
 
 
@@ -86,7 +85,7 @@ static void HandleSampleRate(obs_property_t *prop, const char *id)
 	obs_property_modified(prop, data.get());
 	obs_property_modified(prop, data.get());
 }
 }
 
 
-static void HandleEncoderProperties(const char *id)
+static void HandleEncoderProperties(const char *id, std::vector<int> &bitrates)
 {
 {
 	auto DestroyProperties = [](obs_properties_t *props) {
 	auto DestroyProperties = [](obs_properties_t *props) {
 		obs_properties_destroy(props);
 		obs_properties_destroy(props);
@@ -112,10 +111,10 @@ static void HandleEncoderProperties(const char *id)
 	obs_property_type type = obs_property_get_type(bitrate);
 	obs_property_type type = obs_property_get_type(bitrate);
 	switch (type) {
 	switch (type) {
 	case OBS_PROPERTY_INT:
 	case OBS_PROPERTY_INT:
-		return HandleIntProperty(bitrate, id);
+		return HandleIntProperty(bitrate, bitrates);
 
 
 	case OBS_PROPERTY_LIST:
 	case OBS_PROPERTY_LIST:
-		return HandleListProperty(bitrate, id);
+		return HandleListProperty(bitrate, id, bitrates);
 
 
 	default:
 	default:
 		break;
 		break;
@@ -132,10 +131,75 @@ static const char *GetCodec(const char *id)
 	return NullToEmpty(obs_get_encoder_codec(id));
 	return NullToEmpty(obs_get_encoder_codec(id));
 }
 }
 
 
-static void PopulateBitrateMap()
+static std::vector<int> fallbackBitrates;
+static map<std::string, std::vector<int>> encoderBitrates;
+
+static void PopulateBitrateLists()
 {
 {
 	static once_flag once;
 	static once_flag once;
 
 
+	call_once(once, []() {
+		struct obs_audio_info aoi;
+		obs_get_audio_info(&aoi);
+		uint32_t output_channels = get_audio_channels(aoi.speakers);
+
+		/* NOTE: ffmpeg_aac and ffmpeg_opus have the same properties
+		 * their bitrates will also be used as a fallback */
+		HandleEncoderProperties("ffmpeg_aac", fallbackBitrates);
+
+		if (fallbackBitrates.empty())
+			blog(LOG_ERROR, "Could not enumerate fallback encoder "
+					"bitrates");
+
+		ostringstream ss;
+		for (auto &bitrate : fallbackBitrates)
+			ss << "\n	" << setw(3) << bitrate << " kbit/s:";
+
+		blog(LOG_DEBUG, "Fallback encoder bitrates:%s",
+		     ss.str().c_str());
+
+		const char *id = nullptr;
+		for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
+			if (obs_get_encoder_type(id) != OBS_ENCODER_AUDIO)
+				continue;
+
+			if (strcmp(id, "ffmpeg_aac") == 0 ||
+			    strcmp(id, "ffmpeg_opus") == 0)
+				continue;
+
+			std::string encoder = id;
+
+			HandleEncoderProperties(id, encoderBitrates[encoder]);
+
+			if (encoderBitrates[encoder].empty())
+				blog(LOG_ERROR,
+				     "Could not enumerate %s encoder "
+				     "bitrates",
+				     id);
+
+			ostringstream ss;
+			for (auto &bitrate : encoderBitrates[encoder])
+				ss << "\n	" << setw(3) << bitrate
+				   << " kbit/s";
+
+			blog(LOG_DEBUG, "%s (%s) encoder bitrates:%s",
+			     EncoderName(id), id, ss.str().c_str());
+		}
+
+		if (encoderBitrates.empty() && fallbackBitrates.empty())
+			blog(LOG_ERROR, "Could not enumerate any audio encoder "
+					"bitrates");
+	});
+}
+
+static map<int, std::string> simpleAACBitrateMap;
+
+static void PopulateSimpleAACBitrateMap()
+{
+	PopulateBitrateLists();
+
+	static once_flag once;
+
 	call_once(once, []() {
 	call_once(once, []() {
 		const string encoders[] = {
 		const string encoders[] = {
 			"ffmpeg_aac",
 			"ffmpeg_aac",
@@ -148,7 +212,8 @@ static void PopulateBitrateMap()
 		struct obs_audio_info aoi;
 		struct obs_audio_info aoi;
 		obs_get_audio_info(&aoi);
 		obs_get_audio_info(&aoi);
 
 
-		HandleEncoderProperties(fallbackEncoder.c_str());
+		for (auto &bitrate : fallbackBitrates)
+			simpleAACBitrateMap[bitrate] = fallbackEncoder;
 
 
 		const char *id = nullptr;
 		const char *id = nullptr;
 		for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
 		for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
@@ -160,48 +225,115 @@ static void PopulateBitrateMap()
 			    end(encoders))
 			    end(encoders))
 				continue;
 				continue;
 
 
-			if (strcmp(GetCodec(id), "AAC") != 0)
+			if (strcmp(GetCodec(id), "aac") != 0)
+				continue;
+
+			std::string encoder = id;
+			if (encoderBitrates[encoder].empty())
 				continue;
 				continue;
 
 
-			HandleEncoderProperties(id);
+			for (auto &bitrate : encoderBitrates[encoder])
+				simpleAACBitrateMap[bitrate] = encoder;
 		}
 		}
 
 
 		for (auto &encoder : encoders) {
 		for (auto &encoder : encoders) {
 			if (encoder == fallbackEncoder)
 			if (encoder == fallbackEncoder)
 				continue;
 				continue;
 
 
-			if (strcmp(GetCodec(encoder.c_str()), "AAC") != 0)
+			if (strcmp(GetCodec(encoder.c_str()), "aac") != 0)
 				continue;
 				continue;
 
 
-			HandleEncoderProperties(encoder.c_str());
+			for (auto &bitrate : encoderBitrates[encoder])
+				simpleAACBitrateMap[bitrate] = encoder;
 		}
 		}
 
 
-		if (bitrateMap.empty()) {
+		if (simpleAACBitrateMap.empty()) {
 			blog(LOG_ERROR, "Could not enumerate any AAC encoder "
 			blog(LOG_ERROR, "Could not enumerate any AAC encoder "
 					"bitrates");
 					"bitrates");
 			return;
 			return;
 		}
 		}
 
 
 		ostringstream ss;
 		ostringstream ss;
-		for (auto &entry : bitrateMap)
+		for (auto &entry : simpleAACBitrateMap)
+			ss << "\n	" << setw(3) << entry.first
+			   << " kbit/s: '" << EncoderName(entry.second) << "' ("
+			   << entry.second << ')';
+
+		blog(LOG_DEBUG, "AAC simple encoder bitrate mapping:%s",
+		     ss.str().c_str());
+	});
+}
+
+static map<int, std::string> simpleOpusBitrateMap;
+
+static void PopulateSimpleOpusBitrateMap()
+{
+	PopulateBitrateLists();
+
+	static once_flag once;
+
+	call_once(once, []() {
+		struct obs_audio_info aoi;
+		obs_get_audio_info(&aoi);
+		uint32_t output_channels = get_audio_channels(aoi.speakers);
+
+		for (auto &bitrate : fallbackBitrates)
+			simpleOpusBitrateMap[bitrate] = "ffmpeg_opus";
+
+		const char *id = nullptr;
+		for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
+			if (strcmp(GetCodec(id), "opus") != 0)
+				continue;
+
+			std::string encoder = id;
+			if (encoderBitrates[encoder].empty())
+				continue;
+
+			for (auto &bitrate : encoderBitrates[encoder])
+				simpleOpusBitrateMap[bitrate] = encoder;
+		}
+
+		if (simpleOpusBitrateMap.empty()) {
+			blog(LOG_ERROR, "Could not enumerate any Opus encoder "
+					"bitrates");
+			return;
+		}
+
+		ostringstream ss;
+		for (auto &entry : simpleOpusBitrateMap)
 			ss << "\n	" << setw(3) << entry.first
 			ss << "\n	" << setw(3) << entry.first
 			   << " kbit/s: '" << EncoderName(entry.second) << "' ("
 			   << " kbit/s: '" << EncoderName(entry.second) << "' ("
 			   << entry.second << ')';
 			   << entry.second << ')';
 
 
-		blog(LOG_DEBUG, "AAC encoder bitrate mapping:%s",
+		blog(LOG_DEBUG, "Opus simple encoder bitrate mapping:%s",
 		     ss.str().c_str());
 		     ss.str().c_str());
 	});
 	});
 }
 }
 
 
-const map<int, std::string> &GetAACEncoderBitrateMap()
+const map<int, std::string> &GetSimpleAACEncoderBitrateMap()
 {
 {
-	PopulateBitrateMap();
-	return bitrateMap;
+	PopulateSimpleAACBitrateMap();
+	return simpleAACBitrateMap;
+}
+
+const map<int, std::string> &GetSimpleOpusEncoderBitrateMap()
+{
+	PopulateSimpleOpusBitrateMap();
+	return simpleOpusBitrateMap;
+}
+
+const char *GetSimpleAACEncoderForBitrate(int bitrate)
+{
+	auto &map_ = GetSimpleAACEncoderBitrateMap();
+	auto res = map_.find(bitrate);
+	if (res == end(map_))
+		return NULL;
+	return res->second.c_str();
 }
 }
 
 
-const char *GetAACEncoderForBitrate(int bitrate)
+const char *GetSimpleOpusEncoderForBitrate(int bitrate)
 {
 {
-	auto &map_ = GetAACEncoderBitrateMap();
+	auto &map_ = GetSimpleOpusEncoderBitrateMap();
 	auto res = map_.find(bitrate);
 	auto res = map_.find(bitrate);
 	if (res == end(map_))
 	if (res == end(map_))
 		return NULL;
 		return NULL;
@@ -210,13 +342,13 @@ const char *GetAACEncoderForBitrate(int bitrate)
 
 
 #define INVALID_BITRATE 10000
 #define INVALID_BITRATE 10000
 
 
-int FindClosestAvailableAACBitrate(int bitrate)
+static int FindClosestAvailableSimpleBitrate(int bitrate,
+					     const map<int, std::string> &map)
 {
 {
-	auto &map_ = GetAACEncoderBitrateMap();
 	int prev = 0;
 	int prev = 0;
 	int next = INVALID_BITRATE;
 	int next = INVALID_BITRATE;
 
 
-	for (auto val : map_) {
+	for (auto val : map) {
 		if (next > val.first) {
 		if (next > val.first) {
 			if (val.first == bitrate)
 			if (val.first == bitrate)
 				return bitrate;
 				return bitrate;
@@ -234,3 +366,51 @@ int FindClosestAvailableAACBitrate(int bitrate)
 		return prev;
 		return prev;
 	return 192;
 	return 192;
 }
 }
+
+int FindClosestAvailableSimpleAACBitrate(int bitrate)
+{
+	return FindClosestAvailableSimpleBitrate(
+		bitrate, GetSimpleAACEncoderBitrateMap());
+}
+
+int FindClosestAvailableSimpleOpusBitrate(int bitrate)
+{
+	return FindClosestAvailableSimpleBitrate(
+		bitrate, GetSimpleOpusEncoderBitrateMap());
+}
+
+const std::vector<int> &GetAudioEncoderBitrates(const char *id)
+{
+	std::string encoder = id;
+	PopulateBitrateLists();
+	if (encoderBitrates[encoder].empty())
+		return fallbackBitrates;
+	return encoderBitrates[encoder];
+}
+
+int FindClosestAvailableAudioBitrate(const char *id, int bitrate)
+{
+	int prev = 0;
+	int next = INVALID_BITRATE;
+	std::string encoder = id;
+
+	for (auto val : encoderBitrates[encoder].empty()
+				? fallbackBitrates
+				: encoderBitrates[encoder]) {
+		if (next > val) {
+			if (val == bitrate)
+				return bitrate;
+
+			if (val < next && val > bitrate)
+				next = val;
+			if (val > prev && val < bitrate)
+				prev = val;
+		}
+	}
+
+	if (next != INVALID_BITRATE)
+		return next;
+	if (prev != 0)
+		return prev;
+	return 192;
+}

+ 11 - 3
UI/audio-encoders.hpp

@@ -3,7 +3,15 @@
 #include <obs.hpp>
 #include <obs.hpp>
 
 
 #include <map>
 #include <map>
+#include <vector>
 
 
-const std::map<int, std::string> &GetAACEncoderBitrateMap();
-const char *GetAACEncoderForBitrate(int bitrate);
-int FindClosestAvailableAACBitrate(int bitrate);
+const std::map<int, std::string> &GetSimpleAACEncoderBitrateMap();
+const char *GetSimpleAACEncoderForBitrate(int bitrate);
+int FindClosestAvailableSimpleAACBitrate(int bitrate);
+
+const std::map<int, std::string> &GetSimpleOpusEncoderBitrateMap();
+const char *GetSimpleOpusEncoderForBitrate(int bitrate);
+int FindClosestAvailableSimpleOpusBitrate(int bitrate);
+
+const std::vector<int> &GetAudioEncoderBitrates(const char *id);
+int FindClosestAvailableAudioBitrate(const char *id, int bitrate);

+ 3 - 3
UI/window-basic-main-outputs.cpp

@@ -189,7 +189,7 @@ static void OBSStopVirtualCam(void *data, calldata_t *params)
 static bool CreateAACEncoder(OBSEncoder &res, string &id, int bitrate,
 static bool CreateAACEncoder(OBSEncoder &res, string &id, int bitrate,
 			     const char *name, size_t idx)
 			     const char *name, size_t idx)
 {
 {
-	const char *id_ = GetAACEncoderForBitrate(bitrate);
+	const char *id_ = GetSimpleAACEncoderForBitrate(bitrate);
 	if (!id_) {
 	if (!id_) {
 		id.clear();
 		id.clear();
 		res = nullptr;
 		res = nullptr;
@@ -665,7 +665,7 @@ int SimpleOutput::GetAudioBitrate() const
 	int bitrate = (int)config_get_uint(main->Config(), "SimpleOutput",
 	int bitrate = (int)config_get_uint(main->Config(), "SimpleOutput",
 					   "ABitrate");
 					   "ABitrate");
 
 
-	return FindClosestAvailableAACBitrate(bitrate);
+	return FindClosestAvailableSimpleAACBitrate(bitrate);
 }
 }
 
 
 void SimpleOutput::Update()
 void SimpleOutput::Update()
@@ -1904,7 +1904,7 @@ int AdvancedOutput::GetAudioBitrate(size_t i) const
 		"Track4Bitrate", "Track5Bitrate", "Track6Bitrate",
 		"Track4Bitrate", "Track5Bitrate", "Track6Bitrate",
 	};
 	};
 	int bitrate = (int)config_get_uint(main->Config(), "AdvOut", names[i]);
 	int bitrate = (int)config_get_uint(main->Config(), "AdvOut", names[i]);
-	return FindClosestAvailableAACBitrate(bitrate);
+	return FindClosestAvailableSimpleAACBitrate(bitrate);
 }
 }
 
 
 inline void AdvancedOutput::SetupVodTrack(obs_service_t *service)
 inline void AdvancedOutput::SetupVodTrack(obs_service_t *service)

+ 12 - 10
UI/window-basic-settings.cpp

@@ -262,7 +262,7 @@ static CodecDesc GetDefaultCodecDesc(const ff_format_desc *formatDesc,
 
 
 static void PopulateAACBitrates(initializer_list<QComboBox *> boxes)
 static void PopulateAACBitrates(initializer_list<QComboBox *> boxes)
 {
 {
-	auto &bitrateMap = GetAACEncoderBitrateMap();
+	auto &bitrateMap = GetSimpleAACEncoderBitrateMap();
 	if (bitrateMap.empty())
 	if (bitrateMap.empty())
 		return;
 		return;
 
 
@@ -1856,7 +1856,7 @@ void OBSBasicSettings::LoadSimpleOutputSettings()
 	curAMDPreset = amdPreset;
 	curAMDPreset = amdPreset;
 	curAMDAV1Preset = amdAV1Preset;
 	curAMDAV1Preset = amdAV1Preset;
 
 
-	audioBitrate = FindClosestAvailableAACBitrate(audioBitrate);
+	audioBitrate = FindClosestAvailableSimpleAACBitrate(audioBitrate);
 
 
 	ui->simpleOutputPath->setText(path);
 	ui->simpleOutputPath->setText(path);
 	ui->simpleNoSpace->setChecked(noSpace);
 	ui->simpleNoSpace->setChecked(noSpace);
@@ -2237,12 +2237,12 @@ void OBSBasicSettings::LoadAdvOutputAudioSettings()
 	const char *name6 =
 	const char *name6 =
 		config_get_string(main->Config(), "AdvOut", "Track6Name");
 		config_get_string(main->Config(), "AdvOut", "Track6Name");
 
 
-	track1Bitrate = FindClosestAvailableAACBitrate(track1Bitrate);
-	track2Bitrate = FindClosestAvailableAACBitrate(track2Bitrate);
-	track3Bitrate = FindClosestAvailableAACBitrate(track3Bitrate);
-	track4Bitrate = FindClosestAvailableAACBitrate(track4Bitrate);
-	track5Bitrate = FindClosestAvailableAACBitrate(track5Bitrate);
-	track6Bitrate = FindClosestAvailableAACBitrate(track6Bitrate);
+	track1Bitrate = FindClosestAvailableSimpleAACBitrate(track1Bitrate);
+	track2Bitrate = FindClosestAvailableSimpleAACBitrate(track2Bitrate);
+	track3Bitrate = FindClosestAvailableSimpleAACBitrate(track3Bitrate);
+	track4Bitrate = FindClosestAvailableSimpleAACBitrate(track4Bitrate);
+	track5Bitrate = FindClosestAvailableSimpleAACBitrate(track5Bitrate);
+	track6Bitrate = FindClosestAvailableSimpleAACBitrate(track6Bitrate);
 
 
 	// restrict list of bitrates when multichannel is OFF
 	// restrict list of bitrates when multichannel is OFF
 	const char *speakers =
 	const char *speakers =
@@ -4465,7 +4465,8 @@ void RestrictResetBitrates(initializer_list<QComboBox *> boxes, int maxbitrate)
 {
 {
 	for (auto box : boxes) {
 	for (auto box : boxes) {
 		int idx = box->currentIndex();
 		int idx = box->currentIndex();
-		int max_bitrate = FindClosestAvailableAACBitrate(maxbitrate);
+		int max_bitrate =
+			FindClosestAvailableSimpleAACBitrate(maxbitrate);
 		int count = box->count();
 		int count = box->count();
 		int max_idx = box->findText(
 		int max_idx = box->findText(
 			QT_UTF8(std::to_string(max_bitrate).c_str()));
 			QT_UTF8(std::to_string(max_bitrate).c_str()));
@@ -4475,7 +4476,8 @@ void RestrictResetBitrates(initializer_list<QComboBox *> boxes, int maxbitrate)
 
 
 		if (idx > max_idx) {
 		if (idx > max_idx) {
 			int default_bitrate =
 			int default_bitrate =
-				FindClosestAvailableAACBitrate(maxbitrate / 2);
+				FindClosestAvailableSimpleAACBitrate(
+					maxbitrate / 2);
 			int default_idx = box->findText(QT_UTF8(
 			int default_idx = box->findText(QT_UTF8(
 				std::to_string(default_bitrate).c_str()));
 				std::to_string(default_bitrate).c_str()));