From: Andre Date: Thu, 4 May 2006 17:37:53 +0000 (+0200) Subject: First workable version of the aac decoder X-Git-Tag: v0.2.14~153 X-Git-Url: http://git.tuebingen.mpg.de/?p=paraslash.git;a=commitdiff_plain;h=b862ca3d38e4d130ecbe8aedb4bbb3e1659aa70a First workable version of the aac decoder --- diff --git a/aacdec.c b/aacdec.c new file mode 100644 index 00000000..aa835390 --- /dev/null +++ b/aacdec.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2006 Andre Noll + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + */ + +/** \file aacdec.c paraslash's mp3 decoder */ + +#include "para.h" + +#include "list.h" +#include "filter.h" +#include "error.h" +#include +#include "string.h" + +#include + +/** the output buffer size */ +#define MAX_CHANNELS 6 +#define AAC_OUTBUF_SIZE (FAAD_MIN_STREAMSIZE * MAX_CHANNELS) + +/** + * data specific to the aacdec filter + * + * \sa filter, filter_node + */ +struct private_mp4dec_data { + NeAACDecHandle decoder; + NeAACDecConfigurationPtr config; + NeAACDecFrameInfo frame_info; + mp4AudioSpecificConfig mp4ASC; + + int initialized; + char *inbuf; + int inbuf_len; + int consumed; + long unsigned consumed_total; + + int noffsets; + int *offset; + int offset_pos; + +#if 0 + int nsamples; + int *table; + int table_pos; +#endif +}; + +static int read_mp4_descr_length(struct private_mp4dec_data *padd) +{ + uint8_t b; + uint8_t numBytes = 0; + uint32_t length = 0; + + do { + b = padd->inbuf[padd->consumed + numBytes]; + numBytes++; + length = (length << 7) | (b & 0x7F); + } while + ((b & 0x80) && numBytes < 4); + padd->consumed += numBytes; + return length; +} + +static int find_esds(struct private_mp4dec_data *padd) +{ + for (; padd->consumed < padd->inbuf_len; padd->consumed++) { + char *p = padd->inbuf + padd->consumed; + int decoder_length; + + if (p[0] != 'e' || p[1] != 's' || p[2] != 'd' || p[3] != 's') + continue; + padd->consumed += 8; + p = padd->inbuf + padd->consumed; + PARA_INFO_LOG("found esds: %d, next: %x\n", padd->consumed, *p); + if (*p == 3) + padd->consumed += 8; + else + padd->consumed += 6; + p = padd->inbuf + padd->consumed; + PARA_INFO_LOG("next: %x\n", *p); + if (*p != 4) + continue; + padd->consumed += 18; + p = padd->inbuf + padd->consumed; + PARA_INFO_LOG("next: %x\n", *p); + if (*p != 5) + continue; + padd->consumed++; + decoder_length = read_mp4_descr_length(padd); + PARA_INFO_LOG("decoder length: %d\n", decoder_length); + p = padd->inbuf + padd->consumed; + PARA_INFO_LOG("decoder data0: %x\n", *p & 0xff); + p++; + PARA_INFO_LOG("decoder data1: %x\n", *p & 0xff); + return decoder_length; + } + return -E_ESDS; +} + +static int read_int32(struct private_mp4dec_data *padd, unsigned *result) +{ + uint8_t *d = (uint8_t*)(padd->inbuf + padd->consumed); + if (padd->consumed + 4 > padd->inbuf_len) + return -1; + padd->consumed += 4; + *result = (d[0] << 24) | (d[1] << 16) | (d[2] << 8) | d[3]; + return 1; +} + +#if 0 +int fill_table(struct private_mp4dec_data *padd) +{ + int i, ret; + + for (i = padd->table_pos; i < padd->nsamples; i++) { + ret = read_int32(padd, &padd->table[i]); + if (ret < 0) + return -1; + PARA_DEBUG_LOG("sample #%d: %d\n", i, padd->table[i]); + padd->table_pos++; + } + return 1; + +} +int find_stsz(struct private_mp4dec_data *padd) +{ + int ret; + + for (; padd->consumed < padd->inbuf_len; padd->consumed++) { + char *p = padd->inbuf + padd->consumed; + + if (p[0] != 's' || p[1] != 't' || p[2] != 's' || p[3] != 'z') + continue; + PARA_INFO_LOG("found stsz: %d\n", padd->consumed); + padd->consumed += 12; + ret = -E_STSZ; + if (read_int32(padd, &padd->nsamples) < 0) + goto out; + PARA_INFO_LOG("num samples: %d\n", padd->nsamples); + padd->table = para_malloc(padd->nsamples * sizeof(int)); + return 1; + } + ret = -E_STSZ; +out: + return ret; +} +#endif + +static int fill_offset_table(struct private_mp4dec_data *padd) +{ + int i, ret; + + for (i = padd->offset_pos; i < padd->noffsets; i++) { + ret = read_int32(padd, &padd->offset[i]); + if (ret < 0) + return -1; + PARA_DEBUG_LOG("offset #%d: %d\n", i, padd->offset[i]); + padd->offset_pos++; + } + return 1; +} + +static int find_stco(struct private_mp4dec_data *padd) +{ + int ret; + + for (; padd->consumed < padd->inbuf_len; padd->consumed++) { + char *p = padd->inbuf + padd->consumed; + + if (p[0] != 's' || p[1] != 't' || p[2] != 'c' || p[3] != 'o') + continue; + PARA_INFO_LOG("found stco: %d\n", padd->consumed); + padd->consumed += 8; + ret = read_int32(padd, &padd->noffsets); + padd->offset = para_malloc(padd->noffsets * sizeof(int)); + PARA_INFO_LOG("num entries: %d\n", padd->noffsets); + return 1; + } + return -E_STCO; +} + +static ssize_t mp4dec(char *inbuffer, size_t len, struct filter_node *fn) +{ + struct private_mp4dec_data *padd = fn->private_data; + struct filter_chain_info *fci = fn->fci; + unsigned long rate = 0; + unsigned char channels = 0; + int i, ret, nbytes; + unsigned char *p, *outbuffer; + + if (fn->loaded > fn->bufsize * 4 / 5) + return 0; + if (len < 10 && !*fci->eof) + return 0; + padd->consumed = 0; + padd->inbuf = inbuffer; + padd->inbuf_len = len; + + if (!padd->initialized) { + ret = find_esds(padd); + if (ret < 0) + goto out; + + p = inbuffer + padd->consumed; + ret = E_AACDEC_INIT; + if (NeAACDecInit2(padd->decoder, p, ret, &rate, &channels) < 0) { + PARA_INFO_LOG("header not found, consumed: %d\n", + padd->consumed); + goto out; + } + fci->samplerate = rate; + fci->channels = channels; + PARA_INFO_LOG("rate: %u, channels: %d\n", fci->samplerate, + fci->channels); + padd->initialized = 1; + } + padd->consumed = 0; +#if 0 + if (!padd->table_pos) { + ret = find_stsz(padd); + if (ret < 0) + goto out; + } + if (padd->table_pos < padd->nsamples) { + fill_table(padd); + ret = padd->consumed; + goto out; + } +#endif + if (!padd->offset_pos) { +// ret = find_stco(padd); +// if (ret < 0) +// goto out; + ret = len; + if (find_stco(padd) < 0) + goto out; + } + if (padd->offset_pos < padd->noffsets) { + fill_offset_table(padd); + ret = padd->consumed; + goto out; + } + PARA_INFO_LOG("consumed total: %lu, first_chunk: %d\n", + padd->consumed_total, padd->offset[0]); + ret = len; + if (padd->consumed_total + len < padd->offset[0]) + goto out; + if (padd->consumed_total < padd->offset[0]) + padd->consumed = padd->offset[0] - padd->consumed_total; + p = padd->inbuf + padd->consumed; + outbuffer = NeAACDecDecode(padd->decoder, &padd->frame_info, p, + len - padd->consumed); + PARA_INFO_LOG("frame_error: %d, consumed: %lu + %d + %lu\n", + padd->frame_info.error, padd->consumed_total, + padd->consumed, padd->frame_info.bytesconsumed); + ret = -E_AAC_DECODE; + if (padd->frame_info.error != 0) { + PARA_ERROR_LOG("%s\n", NeAACDecGetErrorMessage( + padd->frame_info.error)); + goto out; + } + padd->consumed += padd->frame_info.bytesconsumed; + ret = padd->consumed; + if (!padd->frame_info.samples) + goto out; + nbytes = padd->frame_info.samples; + for (i = 0; i < padd->frame_info.samples; i++) { + short *s = (short *)outbuffer; + fn->buf[fn->loaded++] = s[i] & 0xff; + fn->buf[fn->loaded++] = (s[i] >> 8) & 0xff; + } + ret = padd->consumed; +out: + if (ret > 0) + padd->consumed_total += ret; + return ret; +} + +static void mp4dec_open(struct filter_node *fn) +{ + fn->private_data = para_calloc(sizeof(struct private_mp4dec_data)); + struct private_mp4dec_data *padd = fn->private_data; + + fn->bufsize = AAC_OUTBUF_SIZE; + fn->buf = para_calloc(fn->bufsize); + + padd->decoder = NeAACDecOpen(); + padd->config = NeAACDecGetCurrentConfiguration(padd->decoder); + padd->config->defObjectType = LC; + padd->config->outputFormat = FAAD_FMT_16BIT; + padd->config->downMatrix = 0; + NeAACDecSetConfiguration(padd->decoder, padd->config); +} + +static void mp4dec_close(struct filter_node *fn) +{ + struct private_mp4dec_data *padd = fn->private_data; + + NeAACDecClose(padd->decoder); + free(fn->buf); + fn->buf = NULL; + free(padd); + fn->private_data = NULL; +} + +/** + * the init function of the mp3dec filter + * + * \sa filter::init + */ +void aacdec_init(struct filter *f) +{ + f->open = mp4dec_open; + f->convert = mp4dec; + f->close = mp4dec_close; +} diff --git a/audiod.h b/audiod.h index e6bd9b33..4cd35954 100644 --- a/audiod.h +++ b/audiod.h @@ -3,10 +3,11 @@ int num_filters(int audio_format_num); int get_audio_format_num(char *name); enum { AUDIO_FORMAT_MP3, - AUDIO_FORMAT_OGG + AUDIO_FORMAT_OGG, + AUDIO_FORMAT_AAC, + NUM_AUDIO_FORMATS }; -#define NUM_AUDIO_FORMATS (AUDIO_FORMAT_OGG + 1) extern const char *audio_formats[]; -#define DEFINE_AUDIO_FORMAT_ARRAY const char *audio_formats[] = {"mp3", "ogg", NULL} +#define DEFINE_AUDIO_FORMAT_ARRAY const char *audio_formats[] = {"mp3", "ogg", "aac", NULL} #define MAX_STREAM_SLOTS 5 diff --git a/configure.ac b/configure.ac index b404da7f..cdef1db2 100644 --- a/configure.ac +++ b/configure.ac @@ -214,6 +214,19 @@ if test "$have_ogg" = "yes"; then else AC_MSG_WARN([no ogg vorbis support in para_server/para_filter]) fi +########################################################################### faad +have_faad=yes +AC_CHECK_HEADER(neaacdec.h, [], have_faad=no) +AC_CHECK_LIB([faad], [NeAACDecOpen], [], have_faad=no) +if test "$have_faad" = "yes"; then + AC_DEFINE(HAVE_FAAD, 1, define to 1 if you want to build the aacdec filter) + filter_errlist_objs="$filter_errlist_objs aacdec" + audiod_errlist_objs="$audiod_errlist_objs aacdec" + filter_ldflags="$filter_ldflags -lfaad" + audiod_ldflags="$audiod_ldflags -lfaad" +else + AC_MSG_WARN([no aac support in para_audiod/para_filter]) +fi ########################################################################### mad have_mad="yes" AC_CHECK_HEADERS([mad.h], [], [ @@ -370,6 +383,7 @@ paraslash configuration: mysql support: $have_mysql ogg vorbis support: $have_ogg mp3dec support (libmad): $have_mad +aac support (libfaad): $have_faad ortp support: $have_ortp unix socket credentials: $have_ucred supported writers for para_write: $write_writers diff --git a/error.h b/error.h index d0c3afc2..614390f2 100644 --- a/error.h +++ b/error.h @@ -49,6 +49,7 @@ enum para_subsystem { SS_OGG, SS_MP3, SS_MP3DEC, + SS_AACDEC, SS_SERVER, SS_AFS, SS_MYSQL_SELECTOR, @@ -335,6 +336,12 @@ extern const char **para_errlist[]; #define WRITE_COMMON_ERRORS \ PARA_ERROR(WRITE_COMMON_SYNTAX, "syntax error in write option"), \ +#define AACDEC_ERRORS \ + PARA_ERROR(AACDEC_INIT, "failed to init aac decoder"), \ + PARA_ERROR(ESDS, "did not find esds atom"), \ + PARA_ERROR(STSZ, "did not find stsz atom"), \ + PARA_ERROR(STCO, "did not find stco atom"), \ + PARA_ERROR(AAC_DECODE, "aac decode error"), \ /* these do not need error handling (yet) */ #define SERVER_ERRORS @@ -437,6 +444,7 @@ SS_ENUM(WAV); SS_ENUM(COMPRESS); SS_ENUM(OGGDEC); SS_ENUM(MP3DEC); +SS_ENUM(AACDEC); SS_ENUM(FILTER); SS_ENUM(MP3); SS_ENUM(OGG); diff --git a/filter.h b/filter.h index 70f4f4fe..2fb9ad17 100644 --- a/filter.h +++ b/filter.h @@ -330,6 +330,13 @@ DECLARE_EXTERN_FILTER_INIT(mp3dec); #define MP3DEC_FILTER #endif +#ifdef HAVE_FAAD +DECLARE_EXTERN_FILTER_INIT(aacdec); +#define AACDEC_FILTER FILTER_INIT(aacdec) +#else +#define AACDEC_FILTER +#endif + #ifdef HAVE_OGGVORBIS DECLARE_EXTERN_FILTER_INIT(oggdec); #define OGGDEC_FILTER FILTER_INIT(oggdec) @@ -344,6 +351,7 @@ DECLARE_EXTERN_FILTER_INIT(oggdec); FILTER_INIT(wav) \ FILTER_INIT(compress) \ MP3DEC_FILTER \ + AACDEC_FILTER \ OGGDEC_FILTER \ { .name = NULL } }; /** \endcond */