Files
obs-studio/plugins/obs-libfdk/obs-libfdk.c
jp9000 c9df41c1e2 (API Change) Remove pointers from all typedefs
Typedef pointers are unsafe.  If you do:
typedef struct bla *bla_t;
then you cannot use it as a constant, such as: const bla_t, because
that constant will be to the pointer itself rather than to the
underlying data.  I admit this was a fundamental mistake that must
be corrected.

All typedefs that were pointer types will now have their pointers
removed from the type itself, and the pointers will be used when they
are actually used as variables/parameters/returns instead.

This does not break ABI though, which is pretty nice.
2014-09-25 21:48:11 -07:00

314 lines
7.4 KiB
C

#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(void)
{
return obs_module_text("LibFDK");
}
static obs_properties_t *libfdk_properties(void)
{
obs_properties_t *props = obs_properties_create();
obs_properties_add_int(props, "bitrate",
obs_module_text("Bitrate"), 32, 256, 32);
obs_properties_add_bool(props, "afterburner",
obs_module_text("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_get_int(settings, "bitrate") * 1000;
int afterburner = obs_data_get_bool(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_get_channels(audio);
enc->sample_rate = audio_output_get_sample_rate(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");
blog(LOG_INFO, "libfdk_aac bitrate: %d, channels: %d",
bitrate / 1000, enc->channels);
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",
.get_name = libfdk_getname,
.create = libfdk_create,
.destroy = libfdk_destroy,
.encode = libfdk_encode,
.get_frame_size = libfdk_frame_size,
.get_defaults = libfdk_defaults,
.get_properties = libfdk_properties,
.get_extra_data = libfdk_extra_data,
.get_audio_info = libfdk_audio_info
};
bool obs_module_load(void)
{
obs_register_encoder(&obs_libfdk_encoder);
return true;
}
OBS_DECLARE_MODULE()
OBS_MODULE_USE_DEFAULT_LOCALE("obs-libfdk", "en-US")