audio-encoders.cpp 5.4 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
#include <algorithm>
#include <iomanip>
#include <map>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include <vector>

#include "audio-encoders.hpp"
P
Palana 已提交
11 12
#include "obs-app.hpp"
#include "window-main.hpp"
13 14 15 16 17

using namespace std;

static const string encoders[] = {
	"ffmpeg_aac",
18
	"mf_aac",
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
	"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));
}

J
jp9000 已提交
35
static map<int, const char *> bitrateMap;
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
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) {
J
jp9000 已提交
51 52 53 54 55
		blog(LOG_ERROR,
		     "Encoder '%s' (%s) returned bitrate "
		     "OBS_PROPERTY_LIST property of unhandled "
		     "format %d",
		     EncoderName(id), id, static_cast<int>(format));
56 57 58 59 60
		return;
	}

	const size_t count = obs_property_list_item_count(prop);
	for (size_t i = 0; i < count; i++) {
61 62 63
		if (obs_property_list_item_disabled(prop, i))
			continue;

J
jp9000 已提交
64 65
		int bitrate =
			static_cast<int>(obs_property_list_item_int(prop, i));
66 67 68 69
		bitrateMap[bitrate] = id;
	}
}

J
jp9000 已提交
70
static void HandleSampleRate(obs_property_t *prop, const char *id)
P
Palana 已提交
71
{
J
jp9000 已提交
72
	auto ReleaseData = [](obs_data_t *data) { obs_data_release(data); };
P
Palana 已提交
73
	std::unique_ptr<obs_data_t, decltype(ReleaseData)> data{
J
jp9000 已提交
74
		obs_encoder_defaults(id), ReleaseData};
P
Palana 已提交
75 76

	if (!data) {
J
jp9000 已提交
77 78 79 80
		blog(LOG_ERROR,
		     "Failed to get defaults for encoder '%s' (%s) "
		     "while populating bitrate map",
		     EncoderName(id), id);
P
Palana 已提交
81 82 83
		return;
	}

J
jp9000 已提交
84
	auto main = reinterpret_cast<OBSMainWindow *>(App()->GetMainWindow());
P
Palana 已提交
85 86 87 88 89 90
	if (!main) {
		blog(LOG_ERROR, "Failed to get main window while populating "
				"bitrate map");
		return;
	}

J
jp9000 已提交
91 92
	uint32_t sampleRate =
		config_get_uint(main->Config(), "Audio", "SampleRate");
P
Palana 已提交
93 94 95 96 97 98

	obs_data_set_int(data.get(), "samplerate", sampleRate);

	obs_property_modified(prop, data.get());
}

99 100
static void HandleEncoderProperties(const char *id)
{
J
jp9000 已提交
101
	auto DestroyProperties = [](obs_properties_t *props) {
102 103 104
		obs_properties_destroy(props);
	};
	std::unique_ptr<obs_properties_t, decltype(DestroyProperties)> props{
J
jp9000 已提交
105
		obs_get_encoder_properties(id), DestroyProperties};
106 107

	if (!props) {
J
jp9000 已提交
108 109 110 111
		blog(LOG_ERROR,
		     "Failed to get properties for encoder "
		     "'%s' (%s)",
		     EncoderName(id), id);
112 113 114
		return;
	}

J
jp9000 已提交
115 116
	obs_property_t *samplerate =
		obs_properties_get(props.get(), "samplerate");
P
Palana 已提交
117 118 119
	if (samplerate)
		HandleSampleRate(samplerate, id);

120 121 122 123 124 125 126 127 128 129
	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);

J
jp9000 已提交
130 131
	default:
		break;
132 133
	}

J
jp9000 已提交
134 135 136 137
	blog(LOG_ERROR,
	     "Encoder '%s' (%s) returned bitrate property "
	     "of unhandled type %d",
	     EncoderName(id), id, static_cast<int>(type));
138 139 140 141 142 143 144 145 146 147
}

static const char *GetCodec(const char *id)
{
	return NullToEmpty(obs_get_encoder_codec(id));
}

static const string aac_ = "AAC";
static void PopulateBitrateMap()
{
J
jp9000 已提交
148
	call_once(populateBitrateMap, []() {
P
pkviet 已提交
149 150 151 152
		struct obs_audio_info aoi;
		obs_get_audio_info(&aoi);
		uint32_t output_channels = get_audio_channels(aoi.speakers);

153 154 155 156
		HandleEncoderProperties(fallbackEncoder.c_str());

		const char *id = nullptr;
		for (size_t i = 0; obs_enum_encoder_types(i, &id); i++) {
J
jp9000 已提交
157
			auto Compare = [=](const string &val) {
158 159 160 161
				return val == NullToEmpty(id);
			};

			if (find_if(begin(encoders), end(encoders), Compare) !=
J
jp9000 已提交
162
			    end(encoders))
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
				continue;

			if (aac_ != GetCodec(id))
				continue;

			HandleEncoderProperties(id);
		}

		for (auto &encoder : encoders) {
			if (encoder == fallbackEncoder)
				continue;

			if (aac_ != GetCodec(encoder.c_str()))
				continue;

L
luz.paz 已提交
178
			// disable mf_aac if audio output is not stereo nor mono
P
pkviet 已提交
179 180 181
			if ((output_channels >= 3) && (encoder == "mf_aac"))
				continue;

182 183 184 185 186 187 188 189 190 191 192 193 194 195 196
			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 << ')';

197
		blog(LOG_DEBUG, "AAC encoder bitrate mapping:%s",
J
jp9000 已提交
198
		     ss.str().c_str());
199 200 201
	});
}

J
jp9000 已提交
202
const map<int, const char *> &GetAACEncoderBitrateMap()
203 204 205 206 207 208 209 210 211 212 213 214 215
{
	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;
}
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242

#define INVALID_BITRATE 10000

int FindClosestAvailableAACBitrate(int bitrate)
{
	auto &map_ = GetAACEncoderBitrateMap();
	int prev = 0;
	int next = INVALID_BITRATE;

	for (auto val : map_) {
		if (next > val.first) {
			if (val.first == bitrate)
				return bitrate;

			if (val.first < next && val.first > bitrate)
				next = val.first;
			if (val.first > prev && val.first < bitrate)
				prev = val.first;
		}
	}

	if (next != INVALID_BITRATE)
		return next;
	if (prev != 0)
		return prev;
	return 192;
}