| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384 | 
							- #include <util/darray.h>
 
- #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;
 
- 	const char        *format_name;
 
- 	UInt32            format_id;
 
- 	const initializer_list<UInt32> *allowed_formats;
 
- 	AudioConverterRef converter;
 
- 	size_t            output_buffer_size;
 
- 	vector<uint8_t>   output_buffer;
 
- 	size_t            out_frames_per_packet;
 
- 	size_t            in_packets;
 
- 	size_t            in_frame_size;
 
- 	size_t            in_bytes_required;
 
- 	DARRAY(uint8_t)   input_buffer;
 
- 	size_t            bytes_read;
 
- 	uint64_t          total_samples;
 
- 	uint64_t          samples_per_second;
 
- 	vector<uint8_t>   extra_data;
 
- 	size_t            channels;
 
- 	~ca_encoder()
 
- 	{
 
- 		if (converter)
 
- 			AudioConverterDispose(converter);
 
- 		da_free(input_buffer);
 
- 	}
 
- };
 
- 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;
 
- 	da_init(ca->input_buffer);
 
- 	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->bytes_read) {
 
- 		da_erase_range(ca->input_buffer, 0, ca->bytes_read);
 
- 		ca->bytes_read = 0;
 
- 	}
 
- 	if (ca->input_buffer.num < ca->in_bytes_required) {
 
- 		*ioNumberDataPackets = 0;
 
- 		ioData->mBuffers[0].mData = NULL;
 
- 		return 1;
 
- 	}
 
- 	*ioNumberDataPackets =
 
- 		(UInt32)(ca->in_bytes_required / ca->in_frame_size);
 
- 	ioData->mNumberBuffers = 1;
 
- 	ioData->mBuffers[0].mData = ca->input_buffer.array;
 
- 	ioData->mBuffers[0].mNumberChannels = (UInt32)ca->channels;
 
- 	ioData->mBuffers[0].mDataByteSize = (UInt32)ca->in_bytes_required;
 
- 	ca->bytes_read += ca->in_packets * ca->in_frame_size;
 
- 	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);
 
- 	da_push_back_array(ca->input_buffer, frame->data[0],
 
- 			frame->linesize[0]);
 
- 	if ((ca->input_buffer.num - ca->bytes_read) < 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->bytes_read / 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);
 
- 	}
 
- }
 
- 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 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, "Couldn't load CoreAudio AAC encoder");
 
- 		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
 
 
  |