| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380 | 
							- #include <util/dstr.hpp>
 
- #include <obs-module.h>
 
- #include <algorithm>
 
- #include <cstdlib>
 
- #include <initializer_list>
 
- #include <memory>
 
- #include <mutex>
 
- #include <vector>
 
- #ifndef _WIN32
 
- #include <AudioToolbox/AudioToolbox.h>
 
- #endif
 
- #define CA_LOG(level, format, ...) \
 
- 	blog(level, "[CoreAudio encoder]: " format, ##__VA_ARGS__)
 
- #define CA_LOG_ENCODER(format_name, encoder, level, format, ...) \
 
- 	blog(level, "[CoreAudio %s: '%s']: " format, \
 
- 			format_name, obs_encoder_get_name(encoder), \
 
- 			##__VA_ARGS__)
 
- #define CA_BLOG(level, format, ...) \
 
- 	CA_LOG_ENCODER(ca->format_name, ca->encoder, level, format, \
 
- 			##__VA_ARGS__)
 
- #define CA_CO_LOG(level, format, ...) \
 
- 	do { \
 
- 		if (ca) \
 
- 			CA_BLOG(level, format, ##__VA_ARGS__); \
 
- 		else \
 
- 			CA_LOG(level, format, ##__VA_ARGS__); \
 
- 	} while (false)
 
- #ifdef _WIN32
 
- #include "windows-imports.h"
 
- #endif
 
- using namespace std;
 
- namespace {
 
- struct asbd_builder {
 
- 	AudioStreamBasicDescription asbd;
 
- 	asbd_builder &sample_rate(Float64 rate)
 
- 	{
 
- 		asbd.mSampleRate = rate;
 
- 		return *this;
 
- 	}
 
- 	asbd_builder &format_id(UInt32 format)
 
- 	{
 
- 		asbd.mFormatID = format;
 
- 		return *this;
 
- 	}
 
- 	asbd_builder &format_flags(UInt32 flags)
 
- 	{
 
- 		asbd.mFormatFlags = flags;
 
- 		return *this;
 
- 	}
 
- 	asbd_builder &bytes_per_packet(UInt32 bytes)
 
- 	{
 
- 		asbd.mBytesPerPacket = bytes;
 
- 		return *this;
 
- 	}
 
- 	asbd_builder &frames_per_packet(UInt32 frames)
 
- 	{
 
- 		asbd.mFramesPerPacket = frames;
 
- 		return *this;
 
- 	}
 
- 	asbd_builder &bytes_per_frame(UInt32 bytes)
 
- 	{
 
- 		asbd.mBytesPerFrame = bytes;
 
- 		return *this;
 
- 	}
 
- 	asbd_builder &channels_per_frame(UInt32 channels)
 
- 	{
 
- 		asbd.mChannelsPerFrame = channels;
 
- 		return *this;
 
- 	}
 
- 	asbd_builder &bits_per_channel(UInt32 bits)
 
- 	{
 
- 		asbd.mBitsPerChannel = bits;
 
- 		return *this;
 
- 	}
 
- };
 
- struct ca_encoder {
 
- 	obs_encoder_t     *encoder = nullptr;
 
- 	const char        *format_name = nullptr;
 
- 	UInt32            format_id = 0;
 
- 	const initializer_list<UInt32> *allowed_formats = nullptr;
 
- 	AudioConverterRef converter = nullptr;
 
- 	size_t            output_buffer_size = 0;
 
- 	vector<uint8_t>   output_buffer;
 
- 	size_t            out_frames_per_packet = 0;
 
- 	size_t            in_packets = 0;
 
- 	size_t            in_frame_size = 0;
 
- 	size_t            in_bytes_required = 0;
 
- 	vector<uint8_t>   input_buffer;
 
- 	vector<uint8_t>   encode_buffer;
 
- 	uint64_t          total_samples = 0;
 
- 	uint64_t          samples_per_second = 0;
 
- 	vector<uint8_t>   extra_data;
 
- 	size_t            channels = 0;
 
- 	~ca_encoder()
 
- 	{
 
- 		if (converter)
 
- 			AudioConverterDispose(converter);
 
- 	}
 
- };
 
- typedef struct ca_encoder ca_encoder;
 
- }
 
- namespace std {
 
- #ifndef _WIN32
 
- template <>
 
- struct default_delete<remove_pointer<CFErrorRef>::type> {
 
- 	void operator()(remove_pointer<CFErrorRef>::type *err)
 
- 	{
 
- 		CFRelease(err);
 
- 	}
 
- };
 
- template <>
 
- struct default_delete<remove_pointer<CFStringRef>::type> {
 
- 	void operator()(remove_pointer<CFStringRef>::type *str)
 
- 	{
 
- 		CFRelease(str);
 
- 	}
 
- };
 
- #endif
 
- template <>
 
- struct default_delete<remove_pointer<AudioConverterRef>::type> {
 
- 	void operator()(AudioConverterRef converter)
 
- 	{
 
- 		AudioConverterDispose(converter);
 
- 	}
 
- };
 
- }
 
- template <typename T>
 
- using cf_ptr = unique_ptr<typename remove_pointer<T>::type>;
 
- #ifndef _MSC_VER
 
- __attribute__((__format__(__printf__, 3, 4)))
 
- #endif
 
- static void log_to_dstr(DStr &str, ca_encoder *ca, const char *fmt, ...)
 
- {
 
- 	dstr prev_str = *static_cast<dstr*>(str);
 
- 	va_list args;
 
- 	va_start(args, fmt);
 
- 	dstr_vcatf(str, fmt, args);
 
- 	va_end(args);
 
- 	if (str->array)
 
- 		return;
 
- 	char array[4096];
 
- 	va_start(args, fmt);
 
- 	vsnprintf(array, 4096, fmt, args);
 
- 	va_end(args);
 
- 	array[4095] = 0;
 
- 	if (!prev_str.array && !prev_str.len)
 
- 		CA_CO_LOG(LOG_ERROR, "Could not allocate buffer for logging:"
 
- 				"\n'%s'", array);
 
- 	else
 
- 		CA_CO_LOG(LOG_ERROR, "Could not allocate buffer for logging:"
 
- 				"\n'%s'\nPrevious log entries:\n%s",
 
- 				array, prev_str.array);
 
- 	bfree(prev_str.array);
 
- }
 
- static const char *flush_log(DStr &log)
 
- {
 
- 	if (!log->array || !log->len)
 
- 		return "";
 
- 	if (log->array[log->len - 1] == '\n') {
 
- 		log->array[log->len - 1] = 0; //Get rid of last newline
 
- 		log->len -= 1;
 
- 	}
 
- 	return log->array;
 
- }
 
- #define CA_CO_DLOG_(level, format) \
 
- 	CA_CO_LOG(level, format "%s%s", \
 
- 			log->array ? ":\n" : "", flush_log(log))
 
- #define CA_CO_DLOG(level, format, ...) \
 
- 	CA_CO_LOG(level, format "%s%s", ##__VA_ARGS__, \
 
- 			log->array ? ":\n" : "", flush_log(log))
 
- static const char *aac_get_name(void*)
 
- {
 
- 	return obs_module_text("CoreAudioAAC");
 
- }
 
- static const char *code_to_str(OSStatus code)
 
- {
 
- 	switch (code) {
 
- #define HANDLE_CODE(c) case c: return #c
 
- 	HANDLE_CODE(kAudio_UnimplementedError);
 
- 	HANDLE_CODE(kAudio_FileNotFoundError);
 
- 	HANDLE_CODE(kAudio_FilePermissionError);
 
- 	HANDLE_CODE(kAudio_TooManyFilesOpenError);
 
- 	HANDLE_CODE(kAudio_BadFilePathError);
 
- 	HANDLE_CODE(kAudio_ParamError);
 
- 	HANDLE_CODE(kAudio_MemFullError);
 
- 	HANDLE_CODE(kAudioConverterErr_FormatNotSupported);
 
- 	HANDLE_CODE(kAudioConverterErr_OperationNotSupported);
 
- 	HANDLE_CODE(kAudioConverterErr_PropertyNotSupported);
 
- 	HANDLE_CODE(kAudioConverterErr_InvalidInputSize);
 
- 	HANDLE_CODE(kAudioConverterErr_InvalidOutputSize);
 
- 	HANDLE_CODE(kAudioConverterErr_UnspecifiedError);
 
- 	HANDLE_CODE(kAudioConverterErr_BadPropertySizeError);
 
- 	HANDLE_CODE(kAudioConverterErr_RequiresPacketDescriptionsError);
 
- 	HANDLE_CODE(kAudioConverterErr_InputSampleRateOutOfRange);
 
- 	HANDLE_CODE(kAudioConverterErr_OutputSampleRateOutOfRange);
 
- #undef HANDLE_CODE
 
- 	default: break;
 
- 	}
 
- 	return NULL;
 
- }
 
- static DStr osstatus_to_dstr(OSStatus code)
 
- {
 
- 	DStr result;
 
- #ifndef _WIN32
 
- 	cf_ptr<CFErrorRef> err{CFErrorCreate(kCFAllocatorDefault,
 
- 			kCFErrorDomainOSStatus, code, NULL)};
 
- 	cf_ptr<CFStringRef> str{CFErrorCopyDescription(err.get())};
 
- 	CFIndex length   = CFStringGetLength(str.get());
 
- 	CFIndex max_size = CFStringGetMaximumSizeForEncoding(length,
 
- 			kCFStringEncodingUTF8);
 
- 	dstr_ensure_capacity(result, max_size);
 
- 	if (result->array && CFStringGetCString(str.get(), result->array,
 
- 				max_size, kCFStringEncodingUTF8)) {
 
- 		dstr_resize(result, strlen(result->array));
 
- 		return result;
 
- 	}
 
- #endif
 
- 	const char *code_str = code_to_str(code);
 
- 	dstr_printf(result, "%s%s%d%s",
 
- 			code_str ? code_str : "",
 
- 			code_str ? " (" : "",
 
- 			static_cast<int>(code),
 
- 			code_str ? ")" : "");
 
- 	return result;
 
- }
 
- static void log_osstatus(int log_level, ca_encoder *ca, const char *context,
 
- 		OSStatus code)
 
- {
 
- 	DStr str = osstatus_to_dstr(code);
 
- 	if (ca)
 
- 		CA_BLOG(log_level, "Error in %s: %s", context, str->array);
 
- 	else
 
- 		CA_LOG(log_level, "Error in %s: %s", context, str->array);
 
- }
 
- static const char *format_id_to_str(UInt32 format_id)
 
- {
 
- #define FORMAT_TO_STR(x) case x: return #x
 
- 	switch (format_id) {
 
- 	FORMAT_TO_STR(kAudioFormatLinearPCM);
 
- 	FORMAT_TO_STR(kAudioFormatAC3);
 
- 	FORMAT_TO_STR(kAudioFormat60958AC3);
 
- 	FORMAT_TO_STR(kAudioFormatAppleIMA4);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4AAC);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4CELP);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4HVXC);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4TwinVQ);
 
- 	FORMAT_TO_STR(kAudioFormatMACE3);
 
- 	FORMAT_TO_STR(kAudioFormatMACE6);
 
- 	FORMAT_TO_STR(kAudioFormatULaw);
 
- 	FORMAT_TO_STR(kAudioFormatALaw);
 
- 	FORMAT_TO_STR(kAudioFormatQDesign);
 
- 	FORMAT_TO_STR(kAudioFormatQDesign2);
 
- 	FORMAT_TO_STR(kAudioFormatQUALCOMM);
 
- 	FORMAT_TO_STR(kAudioFormatMPEGLayer1);
 
- 	FORMAT_TO_STR(kAudioFormatMPEGLayer2);
 
- 	FORMAT_TO_STR(kAudioFormatMPEGLayer3);
 
- 	FORMAT_TO_STR(kAudioFormatTimeCode);
 
- 	FORMAT_TO_STR(kAudioFormatMIDIStream);
 
- 	FORMAT_TO_STR(kAudioFormatParameterValueStream);
 
- 	FORMAT_TO_STR(kAudioFormatAppleLossless);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4AAC_HE);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4AAC_LD);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4AAC_ELD);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4AAC_ELD_SBR);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4AAC_HE_V2);
 
- 	FORMAT_TO_STR(kAudioFormatMPEG4AAC_Spatial);
 
- 	FORMAT_TO_STR(kAudioFormatAMR);
 
- 	FORMAT_TO_STR(kAudioFormatAudible);
 
- 	FORMAT_TO_STR(kAudioFormatiLBC);
 
- 	FORMAT_TO_STR(kAudioFormatDVIIntelIMA);
 
- 	FORMAT_TO_STR(kAudioFormatMicrosoftGSM);
 
- 	FORMAT_TO_STR(kAudioFormatAES3);
 
- 	}
 
- #undef FORMAT_TO_STR
 
- 	return "Unknown format";
 
- }
 
- static void aac_destroy(void *data)
 
- {
 
- 	ca_encoder *ca = static_cast<ca_encoder*>(data);
 
- 	delete ca;
 
- }
 
- template <typename Func>
 
- static bool query_converter_property_raw(DStr &log, ca_encoder *ca,
 
- 		AudioFormatPropertyID property,
 
- 		const char *get_property_info, const char *get_property,
 
- 		AudioConverterRef converter, Func &&func)
 
- {
 
- 	UInt32 size = 0;
 
- 	OSStatus code = AudioConverterGetPropertyInfo(converter, property,
 
- 			&size, nullptr);
 
- 	if (code) {
 
- 		log_to_dstr(log, ca, "%s: %s\n", get_property_info,
 
- 				osstatus_to_dstr(code)->array);
 
- 		return false;
 
- 	}
 
- 	if (!size) {
 
- 		log_to_dstr(log, ca, "%s returned 0 size\n", get_property_info);
 
- 		return false;
 
- 	}
 
- 	vector<uint8_t> buffer;
 
- 	
 
- 	try {
 
- 		buffer.resize(size);
 
- 	} catch (...) {
 
- 		log_to_dstr(log, ca, "Failed to allocate %u bytes for %s\n",
 
- 				static_cast<uint32_t>(size), get_property);
 
- 		return false;
 
- 	}
 
- 	code = AudioConverterGetProperty(converter, property, &size,
 
- 			buffer.data());
 
- 	if (code) {
 
- 		log_to_dstr(log, ca, "%s: %s\n", get_property,
 
- 				osstatus_to_dstr(code)->array);
 
- 		return false;
 
- 	}
 
- 	func(size, static_cast<void*>(buffer.data()));
 
- 	return true;
 
- }
 
- #define EXPAND_CONVERTER_NAMES(x) x, \
 
- 	"AudioConverterGetPropertyInfo(" #x ")", \
 
- 	"AudioConverterGetProperty(" #x ")"
 
- template <typename Func>
 
- static bool enumerate_bitrates(DStr &log, ca_encoder *ca,
 
- 		AudioConverterRef converter, Func &&func)
 
- {
 
- 	auto helper = [&](UInt32 size, void *data)
 
- 	{
 
- 		auto range = static_cast<AudioValueRange*>(data);
 
- 		size_t num_ranges = size / sizeof(AudioValueRange);
 
- 		for (size_t i = 0; i < num_ranges; i++)
 
- 			func(static_cast<UInt32>(range[i].mMinimum),
 
- 					static_cast<UInt32>(range[i].mMaximum));
 
- 	};
 
- 	return query_converter_property_raw(log, ca, EXPAND_CONVERTER_NAMES(
 
- 			kAudioConverterApplicableEncodeBitRates),
 
- 			converter, helper);
 
- }
 
- static bool bitrate_valid(DStr &log, ca_encoder *ca,
 
- 		AudioConverterRef converter, UInt32 bitrate)
 
- {
 
- 	bool valid = false;
 
- 	auto helper = [&](UInt32 min_, UInt32 max_)
 
- 	{
 
- 		if (min_ == bitrate || max_ == bitrate)
 
- 			valid = true;
 
- 	};
 
- 	enumerate_bitrates(log, ca, converter, helper);
 
- 	return valid;
 
- }
 
- static bool create_encoder(DStr &log, ca_encoder *ca,
 
- 		AudioStreamBasicDescription *in,
 
- 		AudioStreamBasicDescription *out,
 
- 		UInt32 format_id, UInt32 bitrate, UInt32 samplerate,
 
- 		UInt32 rate_control)
 
- {
 
- #define STATUS_CHECK(c) \
 
- 	code = c; \
 
- 	if (code) { \
 
- 		log_to_dstr(log, ca, #c " returned %s", \
 
- 				osstatus_to_dstr(code)->array); \
 
- 		return false; \
 
- 	}
 
- 	Float64 srate = samplerate ?
 
- 		(Float64)samplerate :
 
- 		(Float64)ca->samples_per_second;
 
- 	auto out_ = asbd_builder()
 
- 		.sample_rate(srate)
 
- 		.channels_per_frame((UInt32)ca->channels)
 
- 		.format_id(format_id)
 
- 		.asbd;
 
- 	UInt32 size = sizeof(*out);
 
- 	OSStatus code;
 
- 	STATUS_CHECK(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
 
- 			0, NULL, &size, &out_));
 
- 	*out = out_;
 
- 	STATUS_CHECK(AudioConverterNew(in, out, &ca->converter))
 
- 	STATUS_CHECK(AudioConverterSetProperty(ca->converter,
 
- 			kAudioCodecPropertyBitRateControlMode,
 
- 			sizeof(rate_control), &rate_control));
 
- 	if (!bitrate_valid(log, ca, ca->converter, bitrate)) {
 
- 		log_to_dstr(log, ca, "Encoder does not support bitrate %u "
 
- 				"for format %s (0x%x)\n",
 
- 				(uint32_t)bitrate, format_id_to_str(format_id),
 
- 				(uint32_t)format_id);
 
- 		return false;
 
- 	}
 
- 	ca->format_id = format_id;
 
- 	return true;
 
- #undef STATUS_CHECK
 
- }
 
- static const initializer_list<UInt32> aac_formats = {
 
- 	kAudioFormatMPEG4AAC_HE_V2,
 
- 	kAudioFormatMPEG4AAC_HE,
 
- 	kAudioFormatMPEG4AAC,
 
- };
 
- static const initializer_list<UInt32> aac_lc_formats = {
 
- 	kAudioFormatMPEG4AAC,
 
- };
 
- static void *aac_create(obs_data_t *settings, obs_encoder_t *encoder)
 
- {
 
- #define STATUS_CHECK(c) \
 
- 	code = c; \
 
- 	if (code) { \
 
- 		log_osstatus(LOG_ERROR, ca.get(), #c, code); \
 
- 		return nullptr; \
 
- 	}
 
- 	UInt32 bitrate = (UInt32)obs_data_get_int(settings, "bitrate") * 1000;
 
- 	if (!bitrate) {
 
- 		CA_LOG_ENCODER("AAC", encoder, LOG_ERROR,
 
- 				"Invalid bitrate specified");
 
- 		return NULL;
 
- 	}
 
- 	const enum audio_format format = AUDIO_FORMAT_FLOAT;
 
- 	if (is_audio_planar(format)) {
 
- 		CA_LOG_ENCODER("AAC", encoder, LOG_ERROR,
 
- 				"Got non-interleaved audio format %d", format);
 
- 		return NULL;
 
- 	}
 
- 	unique_ptr<ca_encoder> ca;
 
- 	try {
 
- 		ca.reset(new ca_encoder());
 
- 	} catch (...) {
 
- 		CA_LOG_ENCODER("AAC", encoder, LOG_ERROR,
 
- 				"Could not allocate encoder");
 
- 		return nullptr;
 
- 	}
 
- 	ca->encoder = encoder;
 
- 	ca->format_name = "AAC";
 
- 	audio_t *audio = obs_encoder_audio(encoder);
 
- 	const struct audio_output_info *aoi = audio_output_get_info(audio);
 
- 	ca->channels = audio_output_get_channels(audio);
 
- 	ca->samples_per_second = audio_output_get_sample_rate(audio);
 
- 	size_t bytes_per_frame  = get_audio_size(format, aoi->speakers, 1);
 
- 	size_t bits_per_channel = get_audio_bytes_per_channel(format) * 8;
 
- 	auto in = asbd_builder()
 
- 		.sample_rate((Float64)ca->samples_per_second)
 
- 		.channels_per_frame((UInt32)ca->channels)
 
- 		.bytes_per_frame((UInt32)bytes_per_frame)
 
- 		.frames_per_packet(1)
 
- 		.bytes_per_packet((UInt32)(1 * bytes_per_frame))
 
- 		.bits_per_channel((UInt32)bits_per_channel)
 
- 		.format_id(kAudioFormatLinearPCM)
 
- 		.format_flags(kAudioFormatFlagsNativeEndian |
 
- 			kAudioFormatFlagIsPacked |
 
- 			kAudioFormatFlagIsFloat |
 
- 			0)
 
- 		.asbd;
 
- 	AudioStreamBasicDescription out;
 
- 	UInt32 rate_control = kAudioCodecBitRateControlMode_Constant;
 
- 	if (obs_data_get_bool(settings, "allow he-aac")) {
 
- 		ca->allowed_formats = &aac_formats;
 
- 	} else {
 
- 		ca->allowed_formats = &aac_lc_formats;
 
- 	}
 
- 	auto samplerate =
 
- 		static_cast<UInt32>(obs_data_get_int(settings, "samplerate"));
 
- 	DStr log;
 
- 	bool encoder_created = false;
 
- 	for (UInt32 format_id : *ca->allowed_formats) {
 
- 		log_to_dstr(log, ca.get(), "Trying format %s (0x%x)\n",
 
- 				format_id_to_str(format_id),
 
- 				(uint32_t)format_id);
 
- 		if (!create_encoder(log, ca.get(), &in, &out, format_id,
 
- 					bitrate, samplerate, rate_control))
 
- 			continue;
 
- 		encoder_created = true;
 
- 		break;
 
- 	}
 
- 	if (!encoder_created) {
 
- 		CA_CO_DLOG(LOG_ERROR, "Could not create encoder for "
 
- 				"selected format%s",
 
- 				ca->allowed_formats->size() == 1 ? "" : "s");
 
- 		return nullptr;
 
- 	}
 
- 	if (log->len)
 
- 		CA_CO_DLOG_(LOG_DEBUG, "Encoder created");
 
- 	OSStatus code;
 
- 	UInt32 converter_quality = kAudioConverterQuality_Max;
 
- 	STATUS_CHECK(AudioConverterSetProperty(ca->converter,
 
- 			kAudioConverterCodecQuality,
 
- 			sizeof(converter_quality), &converter_quality));
 
- 	STATUS_CHECK(AudioConverterSetProperty(ca->converter,
 
- 			kAudioConverterEncodeBitRate,
 
- 			sizeof(bitrate), &bitrate));
 
- 	UInt32 size = sizeof(in);
 
- 	STATUS_CHECK(AudioConverterGetProperty(ca->converter,
 
- 			kAudioConverterCurrentInputStreamDescription,
 
- 			&size, &in));
 
- 	size = sizeof(out);
 
- 	STATUS_CHECK(AudioConverterGetProperty(ca->converter,
 
- 			kAudioConverterCurrentOutputStreamDescription,
 
- 			&size, &out));
 
- 	ca->in_frame_size     = in.mBytesPerFrame;
 
- 	ca->in_packets        = out.mFramesPerPacket / in.mFramesPerPacket;
 
- 	ca->in_bytes_required = ca->in_packets * ca->in_frame_size;
 
- 	ca->out_frames_per_packet = out.mFramesPerPacket;
 
- 	ca->output_buffer_size = out.mBytesPerPacket;
 
- 	if (out.mBytesPerPacket == 0) {
 
- 		UInt32 max_packet_size = 0;
 
- 		size = sizeof(max_packet_size);
 
- 		
 
- 		code = AudioConverterGetProperty(ca->converter,
 
- 				kAudioConverterPropertyMaximumOutputPacketSize,
 
- 				&size, &max_packet_size);
 
- 		if (code) {
 
- 			log_osstatus(LOG_WARNING, ca.get(),
 
- 					"AudioConverterGetProperty(PacketSz)",
 
- 					code);
 
- 			ca->output_buffer_size = 32768;
 
- 		} else {
 
- 			ca->output_buffer_size = max_packet_size;
 
- 		}
 
- 	}
 
- 	try {
 
- 		ca->output_buffer.resize(ca->output_buffer_size);
 
- 	} catch (...) {
 
- 		CA_BLOG(LOG_ERROR, "Failed to allocate output buffer");
 
- 		return nullptr;
 
- 	}
 
- 	const char *format_name =
 
- 		out.mFormatID == kAudioFormatMPEG4AAC_HE_V2 ? "HE-AAC v2" :
 
- 		out.mFormatID == kAudioFormatMPEG4AAC_HE    ? "HE-AAC" : "AAC";
 
- 	CA_BLOG(LOG_INFO, "settings:\n"
 
- 			"\tmode:          %s\n"
 
- 			"\tbitrate:       %u\n"
 
- 			"\tsample rate:   %llu\n"
 
- 			"\tcbr:           %s\n"
 
- 			"\toutput buffer: %lu",
 
- 			format_name, (unsigned int)bitrate / 1000,
 
- 			ca->samples_per_second,
 
- 			rate_control == kAudioCodecBitRateControlMode_Constant ?
 
- 			"on" : "off",
 
- 			(unsigned long)ca->output_buffer_size);
 
- 	return ca.release();
 
- #undef STATUS_CHECK
 
- }
 
- static OSStatus complex_input_data_proc(AudioConverterRef inAudioConverter,
 
- 		UInt32 *ioNumberDataPackets, AudioBufferList *ioData,
 
- 		AudioStreamPacketDescription **outDataPacketDescription,
 
- 		void *inUserData)
 
- {
 
- 	UNUSED_PARAMETER(inAudioConverter);
 
- 	UNUSED_PARAMETER(outDataPacketDescription);
 
- 	ca_encoder *ca = static_cast<ca_encoder*>(inUserData);
 
- 	if (ca->input_buffer.size() < ca->in_bytes_required) {
 
- 		*ioNumberDataPackets = 0;
 
- 		ioData->mBuffers[0].mData = NULL;
 
- 		return 1;
 
- 	}
 
- 	auto start = begin(ca->input_buffer);
 
- 	auto stop  = begin(ca->input_buffer) + ca->in_bytes_required;
 
- 	ca->encode_buffer.assign(start, stop);
 
- 	ca->input_buffer.erase(start, stop);
 
- 	*ioNumberDataPackets =
 
- 		(UInt32)(ca->in_bytes_required / ca->in_frame_size);
 
- 	ioData->mNumberBuffers = 1;
 
- 	ioData->mBuffers[0].mData = ca->encode_buffer.data();
 
- 	ioData->mBuffers[0].mNumberChannels = (UInt32)ca->channels;
 
- 	ioData->mBuffers[0].mDataByteSize = (UInt32)ca->in_bytes_required;
 
- 	return 0;
 
- }
 
- #ifdef _MSC_VER
 
- // disable warning that recommends if ((foo = bar > 0) == false) over
 
- // if (!(foo = bar > 0))
 
- #pragma warning(push)
 
- #pragma warning(disable: 4706)
 
- #endif
 
- static bool aac_encode(void *data, struct encoder_frame *frame,
 
- 		struct encoder_packet *packet, bool *received_packet)
 
- {
 
- 	ca_encoder *ca = static_cast<ca_encoder*>(data);
 
- 	ca->input_buffer.insert(end(ca->input_buffer),
 
- 			frame->data[0], frame->data[0] + frame->linesize[0]);
 
- 	if (ca->input_buffer.size() < ca->in_bytes_required)
 
- 		return true;
 
- 	UInt32 packets = 1;
 
- 	AudioBufferList buffer_list = { 0 };
 
- 	buffer_list.mNumberBuffers = 1;
 
- 	buffer_list.mBuffers[0].mNumberChannels = (UInt32)ca->channels;
 
- 	buffer_list.mBuffers[0].mDataByteSize = (UInt32)ca->output_buffer_size;
 
- 	buffer_list.mBuffers[0].mData = ca->output_buffer.data();
 
- 	AudioStreamPacketDescription out_desc = { 0 };
 
- 	OSStatus code = AudioConverterFillComplexBuffer(ca->converter,
 
- 			complex_input_data_proc, ca, &packets,
 
- 			&buffer_list, &out_desc);
 
- 	if (code && code != 1) {
 
- 		log_osstatus(LOG_ERROR, ca, "AudioConverterFillComplexBuffer",
 
- 				code);
 
- 		return false;
 
- 	}
 
- 	if (!(*received_packet = packets > 0))
 
- 		return true;
 
- 	packet->pts = ca->total_samples;
 
- 	packet->dts = ca->total_samples;
 
- 	packet->timebase_num = 1;
 
- 	packet->timebase_den = (uint32_t)ca->samples_per_second;
 
- 	packet->type = OBS_ENCODER_AUDIO;
 
- 	packet->size = out_desc.mDataByteSize;
 
- 	packet->data =
 
- 		(uint8_t*)buffer_list.mBuffers[0].mData + out_desc.mStartOffset;
 
- 	ca->total_samples += ca->in_bytes_required / ca->in_frame_size;
 
- 	return true;
 
- }
 
- #ifdef _MSC_VER
 
- #pragma warning(pop)
 
- #endif
 
- static void aac_audio_info(void *data, struct audio_convert_info *info)
 
- {
 
- 	UNUSED_PARAMETER(data);
 
- 	info->format = AUDIO_FORMAT_FLOAT;
 
- }
 
- static size_t aac_frame_size(void *data)
 
- {
 
- 	ca_encoder *ca = static_cast<ca_encoder*>(data);
 
- 	return ca->out_frames_per_packet;
 
- }
 
- /* The following code was extracted from encca_aac.c in HandBrake's libhb */
 
- #define MP4ESDescrTag                   0x03
 
- #define MP4DecConfigDescrTag            0x04
 
- #define MP4DecSpecificDescrTag          0x05
 
- // based off of mov_mp4_read_descr_len from mov.c in ffmpeg's libavformat
 
- static int read_descr_len(uint8_t **buffer)
 
- {
 
- 	int len = 0;
 
- 	int count = 4;
 
- 	while (count--)
 
- 	{
 
- 		int c = *(*buffer)++;
 
- 		len = (len << 7) | (c & 0x7f);
 
- 		if (!(c & 0x80))
 
- 			break;
 
- 	}
 
- 	return len;
 
- }
 
- // based off of mov_mp4_read_descr from mov.c in ffmpeg's libavformat
 
- static int read_descr(uint8_t **buffer, int *tag)
 
- {
 
- 	*tag = *(*buffer)++;
 
- 	return read_descr_len(buffer);
 
- }
 
- // based off of mov_read_esds from mov.c in ffmpeg's libavformat
 
- static void read_esds_desc_ext(uint8_t* desc_ext, vector<uint8_t> &buffer,
 
- 		bool version_flags)
 
- {
 
- 	uint8_t *esds = desc_ext;
 
- 	int tag, len;
 
- 	if (version_flags)
 
- 		esds += 4; // version + flags
 
- 	read_descr(&esds, &tag);
 
- 	esds += 2;     // ID
 
- 	if (tag == MP4ESDescrTag)
 
- 		esds++;    // priority
 
- 	read_descr(&esds, &tag);
 
- 	if (tag == MP4DecConfigDescrTag) {
 
- 		esds++;    // object type id
 
- 		esds++;    // stream type
 
- 		esds += 3; // buffer size db
 
- 		esds += 4; // max bitrate
 
- 		esds += 4; // average bitrate
 
- 		len = read_descr(&esds, &tag);
 
- 		if (tag == MP4DecSpecificDescrTag)
 
- 			try {
 
- 				buffer.assign(esds, esds + len);
 
- 			} catch (...) {
 
- 				//leave buffer empty
 
- 			}
 
- 	}
 
- }
 
- /* extracted code ends here */
 
- static void query_extra_data(ca_encoder *ca)
 
- {
 
- 	UInt32 size = 0;
 
- 	OSStatus code;
 
- 	code = AudioConverterGetPropertyInfo(ca->converter,
 
- 			kAudioConverterCompressionMagicCookie,
 
- 			&size, NULL);
 
- 	if (code) {
 
- 		log_osstatus(LOG_ERROR, ca,
 
- 				"AudioConverterGetPropertyInfo(magic_cookie)",
 
- 				code);
 
- 		return;
 
- 	}
 
- 	if (!size) {
 
- 		CA_BLOG(LOG_WARNING, "Got 0 data size info for magic_cookie");
 
- 		return;
 
- 	}
 
- 	vector<uint8_t> extra_data;
 
- 	
 
- 	try {
 
- 		extra_data.resize(size);
 
- 	} catch (...) {
 
- 		CA_BLOG(LOG_WARNING, "Could not allocate extra data buffer");
 
- 		return;
 
- 	}
 
- 	code = AudioConverterGetProperty(ca->converter,
 
- 			kAudioConverterCompressionMagicCookie,
 
- 			&size, extra_data.data());
 
- 	if (code) {
 
- 		log_osstatus(LOG_ERROR, ca,
 
- 				"AudioConverterGetProperty(magic_cookie)",
 
- 				code);
 
- 		return;
 
- 	}
 
- 	if (!size) {
 
- 		CA_BLOG(LOG_WARNING, "Got 0 data size for magic_cookie");
 
- 		return;
 
- 	}
 
- 	read_esds_desc_ext(extra_data.data(), ca->extra_data, false);
 
- }
 
- static bool aac_extra_data(void *data, uint8_t **extra_data, size_t *size)
 
- {
 
- 	ca_encoder *ca = static_cast<ca_encoder*>(data);
 
- 	if (!ca->extra_data.size())
 
- 		query_extra_data(ca);
 
- 	if (!ca->extra_data.size())
 
- 		return false;
 
- 	*extra_data = ca->extra_data.data();
 
- 	*size = ca->extra_data.size();
 
- 	return true;
 
- }
 
- static asbd_builder fill_common_asbd_fields(asbd_builder builder,
 
- 		bool in=false)
 
- {
 
- 	UInt32 bytes_per_frame = 8;
 
- 	UInt32 channels = 2;
 
- 	UInt32 bits_per_channel = bytes_per_frame / channels * 8;
 
- 	builder.channels_per_frame(channels);
 
- 	if (in) {
 
- 		builder
 
- 			.bytes_per_frame(bytes_per_frame)
 
- 			.frames_per_packet(1)
 
- 			.bytes_per_packet(1 * bytes_per_frame)
 
- 			.bits_per_channel(bits_per_channel);
 
- 	}
 
- 	return builder;
 
- }
 
- static AudioStreamBasicDescription get_default_in_asbd()
 
- {
 
- 	return fill_common_asbd_fields(asbd_builder(), true)
 
- 		.sample_rate(44100)
 
- 		.format_id(kAudioFormatLinearPCM)
 
- 		.format_flags(kAudioFormatFlagsNativeEndian |
 
- 			kAudioFormatFlagIsPacked |
 
- 			kAudioFormatFlagIsFloat |
 
- 			0)
 
- 		.asbd;
 
- }
 
- static asbd_builder get_default_out_asbd_builder()
 
- {
 
- 	return fill_common_asbd_fields(asbd_builder())
 
- 		.sample_rate(44100);
 
- }
 
- static cf_ptr<AudioConverterRef> get_converter(DStr &log, ca_encoder *ca,
 
- 		AudioStreamBasicDescription out,
 
- 		AudioStreamBasicDescription in = get_default_in_asbd())
 
- {
 
- 	UInt32 size = sizeof(out);
 
- 	OSStatus code;
 
- #define STATUS_CHECK(x) \
 
- 	code = x; \
 
- 	if (code) { \
 
- 		log_to_dstr(log, ca, "%s: %s\n", #x, \
 
- 				osstatus_to_dstr(code)->array); \
 
- 		return nullptr; \
 
- 	}
 
- 	STATUS_CHECK(AudioFormatGetProperty(kAudioFormatProperty_FormatInfo,
 
- 			0, NULL, &size, &out));
 
- 	AudioConverterRef converter;
 
- 	STATUS_CHECK(AudioConverterNew(&in, &out, &converter));
 
- 	return cf_ptr<AudioConverterRef>{converter};
 
- #undef STATUS_CHECK
 
- }
 
- static bool find_best_match(DStr &log, ca_encoder *ca, UInt32 bitrate,
 
- 		UInt32 &best_match)
 
- {
 
- 	UInt32 actual_bitrate = bitrate * 1000;
 
- 	bool found_match = false;
 
- 	auto handle_bitrate = [&](UInt32 candidate)
 
- 	{
 
- 		if (abs(static_cast<intmax_t>(actual_bitrate - candidate)) <
 
- 		    abs(static_cast<intmax_t>(actual_bitrate - best_match))) {
 
- 			log_to_dstr(log, ca, "Found new best match %u\n",
 
- 					static_cast<uint32_t>(candidate));
 
- 			found_match = true;
 
- 			best_match = candidate;
 
- 		}
 
- 	};
 
- 	auto helper = [&](UInt32 min_, UInt32 max_)
 
- 	{
 
- 		handle_bitrate(min_);
 
- 		if (min_ == max_)
 
- 			return;
 
- 		log_to_dstr(log, ca, "Got actual bit rate range: %u<->%u\n",
 
- 				static_cast<uint32_t>(min_),
 
- 				static_cast<uint32_t>(max_));
 
- 		handle_bitrate(max_);
 
- 	};
 
- 	for (UInt32 format_id : aac_formats) {
 
- 		log_to_dstr(log, ca, "Trying %s (0x%x)\n",
 
- 				format_id_to_str(format_id), format_id);
 
- 		auto out = get_default_out_asbd_builder()
 
- 			.format_id(format_id)
 
- 			.asbd;
 
- 		auto converter = get_converter(log, ca, out);
 
- 		if (converter)
 
- 			enumerate_bitrates(log, ca, converter.get(),
 
- 					helper);
 
- 		else
 
- 			log_to_dstr(log, ca, "Could not get converter\n");
 
- 	}
 
- 	best_match /= 1000;
 
- 	return found_match;
 
- }
 
- static UInt32 find_matching_bitrate(UInt32 bitrate)
 
- {
 
- 	static UInt32 match = bitrate;
 
- 	static once_flag once;
 
- 	call_once(once, [&]()
 
- 	{
 
- 		DStr log;
 
- 		ca_encoder *ca = nullptr;
 
- 		if (!find_best_match(log, ca, bitrate, match)) {
 
- 			CA_CO_DLOG(LOG_ERROR, "No matching bitrates found for "
 
- 				"target bitrate %u",
 
- 				static_cast<uint32_t>(bitrate));
 
- 			match = bitrate;
 
- 			return;
 
- 		}
 
- 		if (match != bitrate) {
 
- 			CA_CO_DLOG(LOG_INFO, "Default bitrate (%u) isn't "
 
- 				"supported, returning %u as closest match",
 
- 				static_cast<uint32_t>(bitrate),
 
- 				static_cast<uint32_t>(match));
 
- 			return;
 
- 		}
 
- 		if (log->len)
 
- 			CA_CO_DLOG(LOG_DEBUG, "Default bitrate matching log "
 
- 					"for bitrate %u",
 
- 					static_cast<uint32_t>(bitrate));
 
- 	});
 
- 	return match;
 
- }
 
- static void aac_defaults(obs_data_t *settings)
 
- {
 
- 	obs_data_set_default_int(settings, "samplerate", 0); //match input
 
- 	obs_data_set_default_int(settings, "bitrate",
 
- 			find_matching_bitrate(128));
 
- 	obs_data_set_default_bool(settings, "allow he-aac", true);
 
- }
 
- template <typename Func>
 
- static bool query_property_raw(DStr &log, ca_encoder *ca,
 
- 		AudioFormatPropertyID property,
 
- 		const char *get_property_info, const char *get_property,
 
- 		AudioStreamBasicDescription &desc, Func &&func)
 
- {
 
- 	UInt32 size = 0;
 
- 	OSStatus code = AudioFormatGetPropertyInfo(property,
 
- 			sizeof(AudioStreamBasicDescription), &desc, &size);
 
- 	if (code) {
 
- 		log_to_dstr(log, ca, "%s: %s\n", get_property_info,
 
- 				osstatus_to_dstr(code)->array);
 
- 		return false;
 
- 	}
 
- 	if (!size) {
 
- 		log_to_dstr(log, ca, "%s returned 0 size\n", get_property_info);
 
- 		return false;
 
- 	}
 
- 	vector<uint8_t> buffer;
 
- 	
 
- 	try {
 
- 		buffer.resize(size);
 
- 	} catch (...) {
 
- 		log_to_dstr(log, ca, "Failed to allocate %u bytes for %s\n",
 
- 				static_cast<uint32_t>(size), get_property);
 
- 		return false;
 
- 	}
 
- 	code = AudioFormatGetProperty(property,
 
- 			sizeof(AudioStreamBasicDescription), &desc, &size,
 
- 			buffer.data());
 
- 	if (code) {
 
- 		log_to_dstr(log, ca, "%s: %s\n", get_property,
 
- 				osstatus_to_dstr(code)->array);
 
- 		return false;
 
- 	}
 
- 	func(size, static_cast<void*>(buffer.data()));
 
- 	return true;
 
- }
 
- #define EXPAND_PROPERTY_NAMES(x) x, \
 
- 	"AudioFormatGetPropertyInfo(" #x ")", \
 
- 	"AudioFormatGetProperty(" #x ")"
 
- template <typename Func>
 
- static bool enumerate_samplerates(DStr &log, ca_encoder *ca,
 
- 		AudioStreamBasicDescription &desc, Func &&func)
 
- {
 
- 	auto helper = [&](UInt32 size, void *data)
 
- 	{
 
- 		auto range = static_cast<AudioValueRange*>(data);
 
- 		size_t num_ranges = size / sizeof(AudioValueRange);
 
- 		for (size_t i = 0; i < num_ranges; i++)
 
- 			func(range[i]);
 
- 	};
 
- 	return query_property_raw(log, ca, EXPAND_PROPERTY_NAMES(
 
- 			kAudioFormatProperty_AvailableEncodeSampleRates),
 
- 			desc, helper);
 
- }
 
- #if 0
 
- // Unused because it returns bitrates that aren't actually usable, i.e.
 
- // Available bitrates vs Applicable bitrates
 
- template <typename Func>
 
- static bool enumerate_bitrates(DStr &log, ca_encoder *ca,
 
- 		AudioStreamBasicDescription &desc, Func &&func)
 
- {
 
- 	auto helper = [&](UInt32 size, void *data)
 
- 	{
 
- 		auto range = static_cast<AudioValueRange*>(data);
 
- 		size_t num_ranges = size / sizeof(AudioValueRange);
 
- 		for (size_t i = 0; i < num_ranges; i++)
 
- 			func(range[i]);
 
- 	};
 
- 	return query_property_raw(log, ca, EXPAND_PROPERTY_NAMES(
 
- 			kAudioFormatProperty_AvailableEncodeBitRates),
 
- 			desc, helper);
 
- }
 
- #endif
 
- static vector<UInt32> get_samplerates(DStr &log, ca_encoder *ca)
 
- {
 
- 	vector<UInt32> samplerates;
 
- 	auto handle_samplerate = [&](UInt32 rate)
 
- 	{
 
- 		if (find(begin(samplerates), end(samplerates), rate) ==
 
- 				end(samplerates)) {
 
- 			log_to_dstr(log, ca, "Adding sample rate %u\n",
 
- 					static_cast<uint32_t>(rate));
 
- 			samplerates.push_back(rate);
 
- 		} else {
 
- 			log_to_dstr(log, ca, "Sample rate %u already added\n",
 
- 					static_cast<uint32_t>(rate));
 
- 		}
 
- 	};
 
- 	auto helper = [&](const AudioValueRange &range)
 
- 	{
 
- 		auto min_ = static_cast<UInt32>(range.mMinimum);
 
- 		auto max_ = static_cast<UInt32>(range.mMaximum);
 
- 		handle_samplerate(min_);
 
- 		if (min_ == max_)
 
- 			return;
 
- 		log_to_dstr(log, ca, "Got actual sample rate range: %u<->%u\n",
 
- 				static_cast<uint32_t>(min_),
 
- 				static_cast<uint32_t>(max_));
 
- 		handle_samplerate(max_);
 
- 	};
 
- 	for (UInt32 format : (ca ? *ca->allowed_formats : aac_formats)) {
 
- 		log_to_dstr(log, ca, "Trying %s (0x%x)\n",
 
- 				format_id_to_str(format),
 
- 				static_cast<uint32_t>(format));
 
- 		auto asbd = asbd_builder()
 
- 			.format_id(format)
 
- 			.asbd;
 
- 		enumerate_samplerates(log, ca, asbd, helper);
 
- 	}
 
- 	return samplerates;
 
- }
 
- static void add_samplerates(obs_property_t *prop, ca_encoder *ca)
 
- {
 
- 	obs_property_list_add_int(prop,
 
- 			obs_module_text("UseInputSampleRate"), 0);
 
- 	DStr log;
 
- 	auto samplerates = get_samplerates(log, ca);
 
- 	if (!samplerates.size()) {
 
- 		CA_CO_DLOG_(LOG_ERROR, "Couldn't find available sample rates");
 
- 		return;
 
- 	}
 
- 	if (log->len)
 
- 		CA_CO_DLOG_(LOG_DEBUG, "Sample rate enumeration log");
 
- 	sort(begin(samplerates), end(samplerates));
 
- 	DStr buffer;
 
- 	for (UInt32 samplerate : samplerates) {
 
- 		dstr_printf(buffer, "%d", static_cast<uint32_t>(samplerate));
 
- 		obs_property_list_add_int(prop, buffer->array, samplerate);
 
- 	}
 
- }
 
- #define NBSP "\xC2\xA0"
 
- static vector<UInt32> get_bitrates(DStr &log, ca_encoder *ca,
 
- 		Float64 samplerate)
 
- {
 
- 	vector<UInt32> bitrates;
 
- 	auto handle_bitrate = [&](UInt32 bitrate)
 
- 	{
 
- 		if (find(begin(bitrates), end(bitrates), bitrate) ==
 
- 				end(bitrates)) {
 
- 			log_to_dstr(log, ca, "Adding bitrate %u\n",
 
- 					static_cast<uint32_t>(bitrate));
 
- 			bitrates.push_back(bitrate);
 
- 		} else {
 
- 			log_to_dstr(log, ca, "Bitrate %u already added\n",
 
- 					static_cast<uint32_t>(bitrate));
 
- 		}
 
- 	};
 
- 	auto helper = [&](UInt32 min_, UInt32 max_)
 
- 	{
 
- 		handle_bitrate(min_);
 
- 		if (min_ == max_)
 
- 			return;
 
- 		log_to_dstr(log, ca, "Got actual bitrate range: %u<->%u\n",
 
- 				static_cast<uint32_t>(min_),
 
- 				static_cast<uint32_t>(max_));
 
- 		handle_bitrate(max_);
 
- 	};
 
- 	for (UInt32 format_id : (ca ? *ca->allowed_formats : aac_formats)) {
 
- 		log_to_dstr(log, ca, "Trying %s (0x%x) at %g" NBSP "hz\n",
 
- 				format_id_to_str(format_id),
 
- 				static_cast<uint32_t>(format_id),
 
- 				samplerate);
 
- 		auto out = get_default_out_asbd_builder()
 
- 			.format_id(format_id)
 
- 			.sample_rate(samplerate)
 
- 			.asbd;
 
- 		auto converter = get_converter(log, ca, out);
 
- 		if (converter)
 
- 			enumerate_bitrates(log, ca, converter.get(), helper);
 
- 	}
 
- 	return bitrates;
 
- }
 
- static void add_bitrates(obs_property_t *prop, ca_encoder *ca,
 
- 		Float64 samplerate=44100., UInt32 *selected=nullptr)
 
- {
 
- 	obs_property_list_clear(prop);
 
- 	DStr log;
 
- 	auto bitrates = get_bitrates(log, ca, samplerate);
 
- 	if (!bitrates.size()) {
 
- 		CA_CO_DLOG_(LOG_ERROR, "Couldn't find available bitrates");
 
- 		return;
 
- 	}
 
- 	if (log->len)
 
- 		CA_CO_DLOG_(LOG_DEBUG, "Bitrate enumeration log");
 
- 	bool selected_in_range = true;
 
- 	if (selected) {
 
- 		selected_in_range = find(begin(bitrates), end(bitrates),
 
- 				*selected * 1000) != end(bitrates);
 
- 		if (!selected_in_range)
 
- 			bitrates.push_back(*selected * 1000);
 
- 	}
 
- 	sort(begin(bitrates), end(bitrates));
 
- 	DStr buffer;
 
- 	for (UInt32 bitrate : bitrates) {
 
- 		dstr_printf(buffer, "%u", (uint32_t)bitrate / 1000);
 
- 		size_t idx = obs_property_list_add_int(prop, buffer->array,
 
- 				bitrate / 1000);
 
- 		if (selected_in_range || bitrate / 1000 != *selected)
 
- 			continue;
 
- 		obs_property_list_item_disable(prop, idx, true);
 
- 	}
 
- }
 
- static bool samplerate_updated(obs_properties_t *props, obs_property_t *prop,
 
- 		obs_data_t *settings)
 
- {
 
- 	auto samplerate =
 
- 		static_cast<UInt32>(obs_data_get_int(settings, "samplerate"));
 
- 	if (!samplerate)
 
- 		samplerate = 44100;
 
- 	prop = obs_properties_get(props, "bitrate");
 
- 	if (prop) {
 
- 		auto bitrate = static_cast<UInt32>(
 
- 				obs_data_get_int(settings, "bitrate"));
 
- 		add_bitrates(prop, nullptr, samplerate, &bitrate);
 
- 		return true;
 
- 	}
 
- 	return false;
 
- }
 
- static obs_properties_t *aac_properties(void *data)
 
- {
 
- 	ca_encoder *ca = static_cast<ca_encoder*>(data);
 
- 	obs_properties_t *props = obs_properties_create();
 
- 	obs_property_t *p = obs_properties_add_list(props, "samplerate",
 
- 			obs_module_text("OutputSamplerate"),
 
- 			OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
 
- 	add_samplerates(p, ca);
 
- 	obs_property_set_modified_callback(p, samplerate_updated);
 
- 	p = obs_properties_add_list(props, "bitrate",
 
- 			obs_module_text("Bitrate"),
 
- 			OBS_COMBO_TYPE_LIST, OBS_COMBO_FORMAT_INT);
 
- 	add_bitrates(p, ca);
 
- 	obs_properties_add_bool(props, "allow he-aac",
 
- 			obs_module_text("AllowHEAAC"));
 
- 	return props;
 
- }
 
- OBS_DECLARE_MODULE()
 
- OBS_MODULE_USE_DEFAULT_LOCALE("coreaudio-encoder", "en-US")
 
- bool obs_module_load(void)
 
- {
 
- #ifdef _WIN32
 
- 	if (!load_core_audio()) {
 
- 		CA_LOG(LOG_WARNING, "CoreAudio AAC encoder not installed on "
 
- 				"the system or couldn't be loaded");
 
- 		return true;
 
- 	}
 
- 	CA_LOG(LOG_INFO, "Adding CoreAudio AAC encoder");
 
- #endif
 
- 	struct obs_encoder_info aac_info{};
 
- 	aac_info.id = "CoreAudio_AAC";
 
- 	aac_info.type = OBS_ENCODER_AUDIO;
 
- 	aac_info.codec = "AAC";
 
- 	aac_info.get_name = aac_get_name;
 
- 	aac_info.destroy = aac_destroy;
 
- 	aac_info.create = aac_create;
 
- 	aac_info.encode = aac_encode;
 
- 	aac_info.get_frame_size = aac_frame_size;
 
- 	aac_info.get_audio_info = aac_audio_info;
 
- 	aac_info.get_extra_data = aac_extra_data;
 
- 	aac_info.get_defaults = aac_defaults;
 
- 	aac_info.get_properties = aac_properties;
 
- 	obs_register_encoder(&aac_info);
 
- 	return true;
 
- }
 
- #ifdef _WIN32
 
- void obs_module_unload(void)
 
- {
 
- 	unload_core_audio();
 
- }
 
- #endif
 
 
  |