You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
313 lines
8.6 KiB
313 lines
8.6 KiB
/*
|
|
* encode_faac.c - encode audio frames using FAAC
|
|
* Written by Andrew Church <achurch@achurch.org>
|
|
*
|
|
* This file is part of transcode, a video stream processing tool.
|
|
* transcode is free software, distributable under the terms of the GNU
|
|
* General Public License (version 2 or later). See the file COPYING
|
|
* for details.
|
|
*/
|
|
|
|
#include "transcode.h"
|
|
#include "libtc/libtc.h"
|
|
#include "libtc/optstr.h"
|
|
#include "libtc/tcmodule-plugin.h"
|
|
|
|
#include <faac.h>
|
|
|
|
#define MOD_NAME "encode_faac.so"
|
|
#define MOD_VERSION "v0.1 (2006-10-11)"
|
|
#define MOD_CAP "Encodes audio to AAC using FAAC (currently BROKEN)"
|
|
#define MOD_AUTHOR "Andrew Church"
|
|
|
|
#define MOD_FEATURES \
|
|
TC_MODULE_FEATURE_ENCODE|TC_MODULE_FEATURE_AUDIO
|
|
|
|
#define MOD_FLAGS \
|
|
TC_MODULE_FLAG_RECONFIGURABLE
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Local data structure: */
|
|
|
|
typedef struct {
|
|
faacEncHandle handle;
|
|
unsigned long framesize; // samples per AAC frame
|
|
int bps; // bytes per sample
|
|
/* FAAC only takes complete frames as input, so we buffer as needed. */
|
|
uint8_t *audiobuf;
|
|
int audiobuf_len; // in samples
|
|
} PrivateData;
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* Module interface routines and data. */
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* faacmod_init: Initialize this instance of the module. See
|
|
* tcmodule-data.h for function details.
|
|
*/
|
|
|
|
static int faac_init(TCModuleInstance *self, uint32_t features)
|
|
{
|
|
PrivateData *pd;
|
|
|
|
TC_MODULE_SELF_CHECK(self, "init");
|
|
TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
|
|
|
|
self->userdata = pd = tc_malloc(sizeof(PrivateData));
|
|
if (!pd) {
|
|
tc_log_error(MOD_NAME, "init: out of memory!");
|
|
return TC_ERROR;
|
|
}
|
|
pd->handle = 0;
|
|
pd->audiobuf = NULL;
|
|
|
|
/* FIXME: shouldn't this test a specific flag? */
|
|
if (verbose) {
|
|
tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
|
|
if (verbose & TC_INFO) {
|
|
char *id, *copyright;
|
|
faacEncGetVersion(&id, ©right);
|
|
tc_log_info(MOD_NAME, "Using FAAC %s", id);
|
|
}
|
|
}
|
|
return TC_OK;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* faac_configure: Configure this instance of the module. See
|
|
* tcmodule-data.h for function details.
|
|
*/
|
|
|
|
static int faac_configure(TCModuleInstance *self,
|
|
const char *options, vob_t *vob)
|
|
{
|
|
PrivateData *pd;
|
|
int samplerate = vob->mp3frequency ? vob->mp3frequency : vob->a_rate;
|
|
int ret;
|
|
unsigned long dummy;
|
|
faacEncConfiguration conf;
|
|
|
|
TC_MODULE_SELF_CHECK(self, "configure");
|
|
|
|
pd = self->userdata;
|
|
|
|
/* Save bytes per sample */
|
|
pd->bps = (vob->dm_chan * vob->dm_bits) / 8;
|
|
|
|
/* Create FAAC handle (freeing any old one that might be left over) */
|
|
if (pd->handle)
|
|
faacEncClose(pd->handle);
|
|
pd->handle = faacEncOpen(samplerate, vob->dm_chan, &pd->framesize, &dummy);
|
|
if (!pd->handle) {
|
|
tc_log_error(MOD_NAME, "FAAC initialization failed");
|
|
return TC_ERROR;
|
|
}
|
|
|
|
/* Set up our default audio parameters */
|
|
/* why can't just use a pointer here? -- FR */
|
|
/* Because the function returns a pointer to an internal buffer --AC */
|
|
conf = *faacEncGetCurrentConfiguration(pd->handle);
|
|
conf.mpegVersion = MPEG4;
|
|
conf.aacObjectType = MAIN;
|
|
conf.allowMidside = 1;
|
|
conf.useLfe = 0;
|
|
conf.useTns = 1;
|
|
conf.bitRate = vob->mp3bitrate / vob->dm_chan;
|
|
conf.bandWidth = 0; // automatic configuration
|
|
conf.quantqual = 100; // FIXME: quality should be a per-module setting
|
|
conf.outputFormat = 1;
|
|
if (vob->dm_bits != 16) {
|
|
tc_log_error(MOD_NAME, "Only 16-bit samples supported");
|
|
return TC_ERROR;
|
|
}
|
|
conf.inputFormat = FAAC_INPUT_16BIT;
|
|
conf.shortctl = SHORTCTL_NORMAL;
|
|
|
|
ret = optstr_get(options, "quality", "%li", &conf.quantqual);
|
|
if (ret >= 0) {
|
|
if (verbose >= TC_INFO) {
|
|
tc_log_info(MOD_NAME, "using quality=%li", conf.quantqual);
|
|
}
|
|
}
|
|
|
|
if (!faacEncSetConfiguration(pd->handle, &conf)) {
|
|
tc_log_error(MOD_NAME, "Failed to set FAAC configuration");
|
|
faacEncClose(pd->handle);
|
|
pd->handle = 0;
|
|
return TC_ERROR;
|
|
}
|
|
|
|
/* Allocate local audio buffer */
|
|
if (pd->audiobuf)
|
|
free(pd->audiobuf);
|
|
pd->audiobuf = tc_malloc(pd->framesize * pd->bps);
|
|
if (!pd->audiobuf) {
|
|
tc_log_error(MOD_NAME, "Unable to allocate audio buffer");
|
|
faacEncClose(pd->handle);
|
|
pd->handle = 0;
|
|
return TC_ERROR;
|
|
}
|
|
|
|
return TC_OK;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* faac_inspect: Return the value of an option in this instance of the
|
|
* module. See tcmodule-data.h for function details.
|
|
*/
|
|
|
|
static int faac_inspect(TCModuleInstance *self,
|
|
const char *param, const char **value)
|
|
{
|
|
static char buf[TC_BUF_MAX];
|
|
|
|
TC_MODULE_SELF_CHECK(self, "inspect");
|
|
TC_MODULE_SELF_CHECK(param, "inspect");
|
|
|
|
if (optstr_lookup(param, "help")) {
|
|
tc_snprintf(buf, sizeof(buf),
|
|
"Overview:\n"
|
|
" Encodes audio to AAC using the FAAC library.\n"
|
|
"Options:\n"
|
|
" quality: set encoder quality [0-100]\n");
|
|
*value = buf;
|
|
}
|
|
return TC_OK;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* faac_stop: Reset this instance of the module. See tcmodule-data.h for
|
|
* function details.
|
|
*/
|
|
|
|
static int faac_stop(TCModuleInstance *self)
|
|
{
|
|
PrivateData *pd;
|
|
|
|
TC_MODULE_SELF_CHECK(self, "stop");
|
|
|
|
pd = self->userdata;
|
|
|
|
if (pd->handle) {
|
|
faacEncClose(pd->handle);
|
|
pd->handle = NULL;
|
|
}
|
|
|
|
return TC_OK;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* faac_fini: Clean up after this instance of the module. See
|
|
* tcmodule-data.h for function details.
|
|
*/
|
|
|
|
static int faac_fini(TCModuleInstance *self)
|
|
{
|
|
TC_MODULE_SELF_CHECK(self, "fini");
|
|
|
|
faac_stop(self);
|
|
tc_free(self->userdata);
|
|
self->userdata = NULL;
|
|
return TC_OK;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/**
|
|
* faac_encode: Encode a frame of data. See tcmodule-data.h for
|
|
* function details.
|
|
*/
|
|
|
|
static int faac_encode(TCModuleInstance *self,
|
|
aframe_list_t *in, aframe_list_t *out)
|
|
{
|
|
PrivateData *pd;
|
|
uint8_t *inptr;
|
|
int nsamples;
|
|
|
|
TC_MODULE_SELF_CHECK(self, "encode");
|
|
|
|
pd = self->userdata;
|
|
|
|
if (in) {
|
|
inptr = in->audio_buf;
|
|
nsamples = in->audio_size / pd->bps;
|
|
} else {
|
|
inptr = NULL;
|
|
nsamples = 0;
|
|
}
|
|
out->audio_len = 0;
|
|
|
|
while (pd->audiobuf_len + nsamples >= pd->framesize) {
|
|
int res;
|
|
const int tocopy = (pd->framesize - pd->audiobuf_len) * pd->bps;
|
|
ac_memcpy(pd->audiobuf + pd->audiobuf_len*pd->bps, inptr, tocopy);
|
|
inptr += tocopy;
|
|
nsamples -= tocopy / pd->bps;
|
|
pd->audiobuf_len = 0;
|
|
res = faacEncEncode(pd->handle, (int32_t *)pd->audiobuf, pd->framesize,
|
|
out->audio_buf + out->audio_len,
|
|
out->audio_size - out->audio_len);
|
|
if (res > out->audio_size - out->audio_len) {
|
|
tc_log_error(MOD_NAME,
|
|
"Output buffer overflow! Try a lower bitrate.");
|
|
return TC_ERROR;
|
|
}
|
|
out->audio_len += res;
|
|
}
|
|
|
|
if (nsamples > 0) {
|
|
ac_memcpy(pd->audiobuf + pd->audiobuf_len*pd->bps, inptr,
|
|
nsamples*pd->bps);
|
|
pd->audiobuf_len += nsamples;
|
|
}
|
|
return TC_OK;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
static const TCCodecID faac_codecs_in[] = { TC_CODEC_PCM, TC_CODEC_ERROR };
|
|
static const TCCodecID faac_codecs_out[] = { TC_CODEC_AAC, TC_CODEC_ERROR };
|
|
TC_MODULE_CODEC_FORMATS(faac);
|
|
|
|
TC_MODULE_INFO(faac);
|
|
|
|
static const TCModuleClass faac_class = {
|
|
TC_MODULE_CLASS_HEAD(faac),
|
|
|
|
.init = faac_init,
|
|
.fini = faac_fini,
|
|
.configure = faac_configure,
|
|
.stop = faac_stop,
|
|
.inspect = faac_inspect,
|
|
|
|
.encode_audio = faac_encode,
|
|
};
|
|
|
|
TC_MODULE_ENTRY_POINT(faac);
|
|
|
|
/*************************************************************************/
|
|
|
|
/*
|
|
* Local variables:
|
|
* c-file-style: "stroustrup"
|
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
|
* indent-tabs-mode: nil
|
|
* End:
|
|
*
|
|
* vim: expandtab shiftwidth=4:
|
|
*/
|