Browse Source

Add libfdk encoder

BtbN 11 năm trước cách đây
mục cha
commit
ca7ba2e234

+ 9 - 4
obs/window-basic-main.cpp

@@ -328,14 +328,18 @@ bool OBSBasic::InitOutputs()
 
 bool OBSBasic::InitEncoders()
 {
-	aac = obs_audio_encoder_create("ffmpeg_aac", "aac", nullptr);
-	if (!aac)
-		return false;
-
 	x264 = obs_video_encoder_create("obs_x264", "h264", nullptr);
 	if (!x264)
 		return false;
 
+	aac = obs_audio_encoder_create("libfdk_aac", "aac", nullptr);
+
+	if(!aac)
+		aac = obs_audio_encoder_create("ffmpeg_aac", "aac", nullptr);
+
+	if (!aac)
+		return false;
+
 	return true;
 }
 
@@ -463,6 +467,7 @@ void OBSBasic::OBSInit()
 	 * automatically later */
 	obs_load_module("test-input");
 	obs_load_module("obs-ffmpeg");
+	obs_load_module("obs-libfdk");
 	obs_load_module("obs-x264");
 	obs_load_module("obs-outputs");
 	obs_load_module("rtmp-services");

+ 1 - 0
plugins/CMakeLists.txt

@@ -14,6 +14,7 @@ elseif("${CMAKE_SYSTEM_NAME}" MATCHES "Linux")
 endif()
 
 add_subdirectory(obs-x264)
+add_subdirectory(obs-libfdk)
 add_subdirectory(obs-ffmpeg)
 add_subdirectory(obs-outputs)
 add_subdirectory(rtmp-services)

+ 21 - 0
plugins/obs-libfdk/CMakeLists.txt

@@ -0,0 +1,21 @@
+project(obs-libfdk)
+
+find_package(Libfdk QUIET)
+if(NOT LIBFDK_FOUND)
+	message(STATUS "Libfdk not found - obs-libfdk plugin disabled")
+	return()
+endif()
+
+include_directories(${LIBFDK_INCLUDE_DIRS})
+add_definitions(${LIBFDK_DEFINITIONS})
+
+set(obs-libfdk_SOURCES
+	obs-libfdk.c)
+
+add_library(obs-libfdk MODULE
+	${obs-libfdk_SOURCES})
+target_link_libraries(obs-libfdk
+	libobs
+	${LIBFDK_LIBRARIES})
+
+install_obs_plugin(obs-libfdk)

+ 311 - 0
plugins/obs-libfdk/obs-libfdk.c

@@ -0,0 +1,311 @@
+#include <obs-module.h>
+
+#ifdef DEBUG
+# ifndef _DEBUG
+#  define _DEBUG
+# endif
+# undef DEBUG
+#endif
+
+#include <fdk-aac/aacenc_lib.h>
+
+
+static const char *libfdk_get_error(AACENC_ERROR err)
+{
+	switch(err) {
+	case AACENC_OK:
+		return "No error";
+	case AACENC_INVALID_HANDLE:
+		return "Invalid handle";
+	case AACENC_MEMORY_ERROR:
+		return "Memory allocation error";
+	case AACENC_UNSUPPORTED_PARAMETER:
+		return "Unsupported parameter";
+	case AACENC_INVALID_CONFIG:
+		return "Invalid config";
+	case AACENC_INIT_ERROR:
+		return "Initialization error";
+	case AACENC_INIT_AAC_ERROR:
+		return "AAC library initialization error";
+	case AACENC_INIT_SBR_ERROR:
+		return "SBR library initialization error";
+	case AACENC_INIT_TP_ERROR:
+		return "Transport library initialization error";
+	case AACENC_INIT_META_ERROR:
+		return "Metadata library initialization error";
+	case AACENC_ENCODE_ERROR:
+		return "Encoding error";
+	case AACENC_ENCODE_EOF:
+		return "End of file";
+	default:
+		return "Unknown error";
+	}
+}
+
+
+typedef struct libfdk_encoder {
+	obs_encoder_t encoder;
+
+	int channels, sample_rate;
+
+	HANDLE_AACENCODER fdkhandle;
+	AACENC_InfoStruct info;
+
+	uint64_t total_samples;
+
+	int frame_size_bytes;
+
+	uint8_t *packet_buffer;
+	int packet_buffer_size;
+} libfdk_encoder_t;
+
+static const char *libfdk_getname(const char *locale)
+{
+	UNUSED_PARAMETER(locale);
+	return "libfdk aac encoder";
+}
+
+static obs_properties_t libfdk_properties(const char *locale)
+{
+	obs_properties_t props = obs_properties_create(locale);
+
+	obs_properties_add_int(props, "bitrate", "Bitrate", 32, 256, 32);
+	obs_properties_add_bool(props, "afterburner", "Enable AAC Afterburner");
+
+	return props;
+}
+
+static void libfdk_defaults(obs_data_t settings)
+{
+	obs_data_set_default_int(settings, "bitrate", 128);
+	obs_data_set_default_bool(settings, "afterburner", true);
+}
+
+#define CHECK_LIBFDK(r) \
+	if((err = (r)) != AACENC_OK) { \
+		blog(LOG_ERROR, #r " failed: %s", libfdk_get_error(err)); \
+		goto fail; \
+	}
+
+static void *libfdk_create(obs_data_t settings, obs_encoder_t encoder)
+{
+	bool hasFdkHandle = false;
+	libfdk_encoder_t *enc = 0;
+	int bitrate = (int)obs_data_getint(settings, "bitrate") * 1000;
+	int afterburner = obs_data_getbool(settings, "afterburner") ? 1 : 0;
+	audio_t audio = obs_encoder_audio(encoder);
+	int mode = 0;
+	AACENC_ERROR err;
+
+	if (!bitrate) {
+		blog(LOG_ERROR, "Invalid bitrate");
+		return NULL;
+	}
+
+	enc = bzalloc(sizeof(libfdk_encoder_t));
+	enc->encoder = encoder;
+
+	enc->channels = (int)audio_output_channels(audio);
+	enc->sample_rate = audio_output_samplerate(audio);
+
+	switch(enc->channels) {
+	case 1:
+		mode = MODE_1;
+		break;
+	case 2:
+		mode = MODE_2;
+		break;
+	case 3:
+		mode = MODE_1_2;
+		break;
+	case 4:
+		mode = MODE_1_2_1;
+		break;
+	case 5:
+		mode = MODE_1_2_2;
+		break;
+	case 6:
+		mode = MODE_1_2_2_1;
+		break;
+	default:
+		blog(LOG_ERROR, "Invalid channel count");
+		goto fail;
+	}
+
+	CHECK_LIBFDK(aacEncOpen(&enc->fdkhandle, 0, enc->channels));
+	hasFdkHandle = true;
+
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AOT,
+	                                 2)); // MPEG-4 AAC-LC
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_SAMPLERATE,
+	                                 enc->sample_rate));
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELMODE, mode));
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_CHANNELORDER, 1));
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATEMODE, 0));
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_BITRATE, bitrate));
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_TRANSMUX, 0));
+	CHECK_LIBFDK(aacEncoder_SetParam(enc->fdkhandle, AACENC_AFTERBURNER,
+	                                 afterburner));
+
+	CHECK_LIBFDK(aacEncEncode(enc->fdkhandle, NULL, NULL, NULL, NULL));
+
+	CHECK_LIBFDK(aacEncInfo(enc->fdkhandle, &enc->info));
+
+	enc->frame_size_bytes = enc->info.frameLength * 2 * enc->channels;
+
+	enc->packet_buffer_size = enc->channels * 768;
+	if(enc->packet_buffer_size < 8192)
+		enc->packet_buffer_size = 8192;
+
+	enc->packet_buffer = bmalloc(enc->packet_buffer_size);
+
+	blog(LOG_INFO, "libfdk_aac encoder created");
+
+	return enc;
+
+fail:
+
+	if(hasFdkHandle)
+		aacEncClose(&enc->fdkhandle);
+
+	if(enc->packet_buffer)
+		bfree(enc->packet_buffer);
+
+	if(enc)
+		bfree(enc);
+
+	blog(LOG_WARNING, "libfdk_aac encoder creation failed");
+
+	return 0;
+}
+
+static void libfdk_destroy(void *data)
+{
+	libfdk_encoder_t *enc = data;
+
+	aacEncClose(&enc->fdkhandle);
+
+	bfree(enc->packet_buffer);
+	bfree(enc);
+
+	blog(LOG_INFO, "libfdk_aac encoder destroyed");
+}
+
+static bool libfdk_encode(void *data, struct encoder_frame *frame,
+                          struct encoder_packet *packet, bool *received_packet)
+{
+	libfdk_encoder_t *enc = data;
+
+	AACENC_BufDesc in_buf = { 0 };
+	AACENC_BufDesc out_buf = { 0 };
+	AACENC_InArgs in_args = { 0 };
+	AACENC_OutArgs out_args = { 0 };
+	int in_identifier = IN_AUDIO_DATA;
+	int in_size, in_elem_size;
+	int out_identifier = OUT_BITSTREAM_DATA;
+	int out_size, out_elem_size;
+	void *in_ptr;
+	void *out_ptr;
+	AACENC_ERROR err;
+
+
+	in_ptr = frame->data[0];
+	in_size = enc->frame_size_bytes;
+	in_elem_size = 2;
+
+	in_args.numInSamples = enc->info.frameLength * enc->channels;
+	in_buf.numBufs = 1;
+	in_buf.bufs = &in_ptr;
+	in_buf.bufferIdentifiers = &in_identifier;
+	in_buf.bufSizes = &in_size;
+	in_buf.bufElSizes = &in_elem_size;
+
+	out_ptr = enc->packet_buffer;
+	out_size = enc->packet_buffer_size;
+	out_elem_size = 1;
+
+	out_buf.numBufs = 1;
+	out_buf.bufs = &out_ptr;
+	out_buf.bufferIdentifiers = &out_identifier;
+	out_buf.bufSizes = &out_size;
+	out_buf.bufElSizes = &out_elem_size;
+
+	if((err = aacEncEncode(enc->fdkhandle, &in_buf, &out_buf, &in_args,
+	                       &out_args)) != AACENC_OK) {
+		blog(LOG_ERROR, "Failed to encode frame: %s", libfdk_get_error(err));
+		return false;
+	}
+
+	enc->total_samples += enc->info.frameLength;
+
+	if(out_args.numOutBytes == 0) {
+		*received_packet = false;
+		return true;
+	}
+
+	*received_packet = true;
+
+	packet->pts  = enc->total_samples -
+	               enc->info.encoderDelay; // TODO: Just a guess, find out if that's actualy right
+	packet->dts  = enc->total_samples - enc->info.encoderDelay;
+	packet->data = enc->packet_buffer;
+	packet->size = out_args.numOutBytes;
+	packet->type = OBS_ENCODER_AUDIO;
+	packet->timebase_num = 1;
+	packet->timebase_den = enc->sample_rate;
+
+	return true;
+}
+
+static bool libfdk_extra_data(void *data, uint8_t **extra_data, size_t *size)
+{
+	libfdk_encoder_t *enc = data;
+
+	*size = enc->info.confSize;
+	*extra_data = enc->info.confBuf;
+
+	return true;
+}
+
+static bool libfdk_audio_info(void *data, struct audio_convert_info *info)
+{
+	UNUSED_PARAMETER(data);
+
+	memset(info, 0, sizeof(struct audio_convert_info));
+	info->format = AUDIO_FORMAT_16BIT;
+
+	return true;
+}
+
+static size_t libfdk_frame_size(void *data)
+{
+	libfdk_encoder_t *enc = data;
+
+	return enc->info.frameLength;
+}
+
+struct obs_encoder_info obs_libfdk_encoder = {
+	.id         = "libfdk_aac",
+	.type       = OBS_ENCODER_AUDIO,
+	.codec      = "AAC",
+	.getname    = libfdk_getname,
+	.create     = libfdk_create,
+	.destroy    = libfdk_destroy,
+	.encode     = libfdk_encode,
+	.frame_size = libfdk_frame_size,
+	.defaults   = libfdk_defaults,
+	.properties = libfdk_properties,
+	.extra_data = libfdk_extra_data,
+	.audio_info = libfdk_audio_info
+};
+
+bool obs_module_load(uint32_t libobs_ver)
+{
+	UNUSED_PARAMETER(libobs_ver);
+
+	obs_register_encoder(&obs_libfdk_encoder);
+
+	return true;
+}
+
+OBS_DECLARE_MODULE()