0
0
mirror of https://github.com/obsproject/obs-studio.git synced 2024-09-19 20:32:15 +02:00

UI: Add Opus bitrate map and per encoder bitrate list

Also refactors AAC bitrate map too.
This commit is contained in:
tytan652 2022-12-17 18:16:05 +01:00
parent b2d686e8f4
commit b15684aa17
4 changed files with 232 additions and 42 deletions

View File

@ -23,18 +23,17 @@ static const char *EncoderName(const std::string &id)
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 step = obs_property_int_step(prop);
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);
if (format != OBS_COMBO_FORMAT_INT) {
@ -53,7 +52,7 @@ static void HandleListProperty(obs_property_t *prop, const char *id)
int bitrate =
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());
}
static void HandleEncoderProperties(const char *id)
static void HandleEncoderProperties(const char *id, std::vector<int> &bitrates)
{
auto DestroyProperties = [](obs_properties_t *props) {
obs_properties_destroy(props);
@ -112,10 +111,10 @@ static void HandleEncoderProperties(const char *id)
obs_property_type type = obs_property_get_type(bitrate);
switch (type) {
case OBS_PROPERTY_INT:
return HandleIntProperty(bitrate, id);
return HandleIntProperty(bitrate, bitrates);
case OBS_PROPERTY_LIST:
return HandleListProperty(bitrate, id);
return HandleListProperty(bitrate, id, bitrates);
default:
break;
@ -132,10 +131,75 @@ static const char *GetCodec(const char *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;
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, []() {
const string encoders[] = {
"ffmpeg_aac",
@ -148,7 +212,8 @@ static void PopulateBitrateMap()
struct obs_audio_info aoi;
obs_get_audio_info(&aoi);
HandleEncoderProperties(fallbackEncoder.c_str());
for (auto &bitrate : fallbackBitrates)
simpleAACBitrateMap[bitrate] = fallbackEncoder;
const char *id = nullptr;
for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
@ -160,48 +225,115 @@ static void PopulateBitrateMap()
end(encoders))
continue;
if (strcmp(GetCodec(id), "AAC") != 0)
if (strcmp(GetCodec(id), "aac") != 0)
continue;
HandleEncoderProperties(id);
std::string encoder = id;
if (encoderBitrates[encoder].empty())
continue;
for (auto &bitrate : encoderBitrates[encoder])
simpleAACBitrateMap[bitrate] = encoder;
}
for (auto &encoder : encoders) {
if (encoder == fallbackEncoder)
continue;
if (strcmp(GetCodec(encoder.c_str()), "AAC") != 0)
if (strcmp(GetCodec(encoder.c_str()), "aac") != 0)
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 "
"bitrates");
return;
}
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 encoder bitrate mapping:%s",
blog(LOG_DEBUG, "AAC simple encoder bitrate mapping:%s",
ss.str().c_str());
});
}
const map<int, std::string> &GetAACEncoderBitrateMap()
static map<int, std::string> simpleOpusBitrateMap;
static void PopulateSimpleOpusBitrateMap()
{
PopulateBitrateMap();
return bitrateMap;
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
<< " kbit/s: '" << EncoderName(entry.second) << "' ("
<< entry.second << ')';
blog(LOG_DEBUG, "Opus simple encoder bitrate mapping:%s",
ss.str().c_str());
});
}
const char *GetAACEncoderForBitrate(int bitrate)
const map<int, std::string> &GetSimpleAACEncoderBitrateMap()
{
auto &map_ = GetAACEncoderBitrateMap();
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 *GetSimpleOpusEncoderForBitrate(int bitrate)
{
auto &map_ = GetSimpleOpusEncoderBitrateMap();
auto res = map_.find(bitrate);
if (res == end(map_))
return NULL;
@ -210,13 +342,13 @@ const char *GetAACEncoderForBitrate(int bitrate)
#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 next = INVALID_BITRATE;
for (auto val : map_) {
for (auto val : map) {
if (next > val.first) {
if (val.first == bitrate)
return bitrate;
@ -234,3 +366,51 @@ int FindClosestAvailableAACBitrate(int bitrate)
return prev;
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;
}

View File

@ -3,7 +3,15 @@
#include <obs.hpp>
#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);

View File

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

View File

@ -262,7 +262,7 @@ static CodecDesc GetDefaultCodecDesc(const ff_format_desc *formatDesc,
static void PopulateAACBitrates(initializer_list<QComboBox *> boxes)
{
auto &bitrateMap = GetAACEncoderBitrateMap();
auto &bitrateMap = GetSimpleAACEncoderBitrateMap();
if (bitrateMap.empty())
return;
@ -1856,7 +1856,7 @@ void OBSBasicSettings::LoadSimpleOutputSettings()
curAMDPreset = amdPreset;
curAMDAV1Preset = amdAV1Preset;
audioBitrate = FindClosestAvailableAACBitrate(audioBitrate);
audioBitrate = FindClosestAvailableSimpleAACBitrate(audioBitrate);
ui->simpleOutputPath->setText(path);
ui->simpleNoSpace->setChecked(noSpace);
@ -2237,12 +2237,12 @@ void OBSBasicSettings::LoadAdvOutputAudioSettings()
const char *name6 =
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
const char *speakers =
@ -4465,7 +4465,8 @@ void RestrictResetBitrates(initializer_list<QComboBox *> boxes, int maxbitrate)
{
for (auto box : boxes) {
int idx = box->currentIndex();
int max_bitrate = FindClosestAvailableAACBitrate(maxbitrate);
int max_bitrate =
FindClosestAvailableSimpleAACBitrate(maxbitrate);
int count = box->count();
int max_idx = box->findText(
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) {
int default_bitrate =
FindClosestAvailableAACBitrate(maxbitrate / 2);
FindClosestAvailableSimpleAACBitrate(
maxbitrate / 2);
int default_idx = box->findText(QT_UTF8(
std::to_string(default_bitrate).c_str()));