diff --git a/obs/CMakeLists.txt b/obs/CMakeLists.txt index 973d49cff355eded7b0ecf6af5097472326808fe..d2d1381972b654810832f41a195b23c76573a8be 100644 --- a/obs/CMakeLists.txt +++ b/obs/CMakeLists.txt @@ -126,6 +126,7 @@ set(obs_SOURCES hotkey-edit.cpp source-label.cpp remote-text.cpp + audio-encoders.cpp qt-wrappers.cpp) set(obs_HEADERS @@ -166,6 +167,7 @@ set(obs_HEADERS hotkey-edit.hpp source-label.hpp remote-text.hpp + audio-encoders.hpp qt-wrappers.hpp) set(obs_UI diff --git a/obs/audio-encoders.cpp b/obs/audio-encoders.cpp new file mode 100644 index 0000000000000000000000000000000000000000..94ca79ef0e2513cbd960d07e85d01a273ec1daf4 --- /dev/null +++ b/obs/audio-encoders.cpp @@ -0,0 +1,167 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "audio-encoders.hpp" + +using namespace std; + +static const string encoders[] = { + "ffmpeg_aac", + "libfdk_aac", + "CoreAudio_AAC", +}; + +static const string &fallbackEncoder = encoders[0]; + +static const char *NullToEmpty(const char *str) +{ + return str ? str : ""; +} + +static const char *EncoderName(const char *id) +{ + return NullToEmpty(obs_encoder_get_display_name(id)); +} + +static map bitrateMap; +static once_flag populateBitrateMap; + +static void HandleIntProperty(obs_property_t *prop, const char *id) +{ + 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; +} + +static void HandleListProperty(obs_property_t *prop, const char *id) +{ + obs_combo_format format = obs_property_list_format(prop); + if (format != OBS_COMBO_FORMAT_INT) { + blog(LOG_ERROR, "Encoder '%s' (%s) returned bitrate " + "OBS_PROPERTY_LIST property of unhandled " + "format %d", + EncoderName(id), id, static_cast(format)); + return; + } + + const size_t count = obs_property_list_item_count(prop); + for (size_t i = 0; i < count; i++) { + int bitrate = static_cast( + obs_property_list_item_int(prop, i)); + bitrateMap[bitrate] = id; + } +} + +static void HandleEncoderProperties(const char *id) +{ + auto DestroyProperties = [](obs_properties_t *props) + { + obs_properties_destroy(props); + }; + std::unique_ptr props{ + obs_get_encoder_properties(id), + DestroyProperties}; + + if (!props) { + blog(LOG_ERROR, "Failed to get properties for encoder " + "'%s' (%s)", + EncoderName(id), id); + return; + } + + obs_property_t *bitrate = obs_properties_get(props.get(), "bitrate"); + + obs_property_type type = obs_property_get_type(bitrate); + switch (type) { + case OBS_PROPERTY_INT: + return HandleIntProperty(bitrate, id); + + case OBS_PROPERTY_LIST: + return HandleListProperty(bitrate, id); + + default: break; + } + + blog(LOG_ERROR, "Encoder '%s' (%s) returned bitrate property " + "of unhandled type %d", EncoderName(id), id, + static_cast(type)); +} + +static const char *GetCodec(const char *id) +{ + return NullToEmpty(obs_get_encoder_codec(id)); +} + +static const string aac_ = "AAC"; +static void PopulateBitrateMap() +{ + call_once(populateBitrateMap, []() + { + HandleEncoderProperties(fallbackEncoder.c_str()); + + const char *id = nullptr; + for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) { + auto Compare = [=](const string &val) + { + return val == NullToEmpty(id); + }; + + if (find_if(begin(encoders), end(encoders), Compare) != + end(encoders)) + continue; + + if (aac_ != GetCodec(id)) + continue; + + HandleEncoderProperties(id); + } + + for (auto &encoder : encoders) { + if (encoder == fallbackEncoder) + continue; + + if (aac_ != GetCodec(encoder.c_str())) + continue; + + HandleEncoderProperties(encoder.c_str()); + } + + if (bitrateMap.empty()) { + blog(LOG_ERROR, "Could not enumerate any AAC encoder " + "bitrates"); + return; + } + + ostringstream ss; + for (auto &entry : bitrateMap) + ss << "\n " << setw(3) << entry.first + << " kbit/s: '" << EncoderName(entry.second) << "' (" + << entry.second << ')'; + + blog(LOG_INFO, "AAC encoder bitrate mapping:%s", + ss.str().c_str()); + }); +} + +const map &GetAACEncoderBitrateMap() +{ + PopulateBitrateMap(); + return bitrateMap; +} + +const char *GetAACEncoderForBitrate(int bitrate) +{ + auto &map_ = GetAACEncoderBitrateMap(); + auto res = map_.find(bitrate); + if (res == end(map_)) + return NULL; + return res->second; +} diff --git a/obs/audio-encoders.hpp b/obs/audio-encoders.hpp new file mode 100644 index 0000000000000000000000000000000000000000..34bb391ea3976330f12d528f0d790ce569b5c1f8 --- /dev/null +++ b/obs/audio-encoders.hpp @@ -0,0 +1,6 @@ +#include + +#include + +const std::map &GetAACEncoderBitrateMap(); +const char *GetAACEncoderForBitrate(int bitrate);