diff --git a/.gitignore b/.gitignore index 90c8b90..387e07b 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ m4/ltversion.m4 m4/lt~obsolete.m4 missing stamp-h1 +aac-enc diff --git a/Makefile.am b/Makefile.am index 9743bfa..319cc0c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -32,6 +32,15 @@ lib_LTLIBRARIES = libfdk-aac.la libfdk_aac_la_LDFLAGS = -version-info @FDK_AAC_VERSION@ -no-undefined \ -export-symbols $(top_srcdir)/fdk-aac.sym +if EXAMPLE +bin_PROGRAMS = aac-enc$(EXEEXT) + +aac_enc_LDADD = libfdk-aac.la +aac_enc_SOURCES = aac-enc.c wavreader.c + +noinst_HEADERS = wavreader.h +endif + AACDEC_DIR = $(top_srcdir)/libAACdec/src AACDEC_SRC = \ $(AACDEC_DIR)/aacdec_drc.cpp \ diff --git a/aac-enc.c b/aac-enc.c new file mode 100644 index 0000000..1528881 --- /dev/null +++ b/aac-enc.c @@ -0,0 +1,234 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2011 Martin Storsjo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "libAACenc/include/aacenc_lib.h" +#include "wavreader.h" + +void usage(const char* name) { + fprintf(stderr, "%s [-r bitrate] [-t aot] [-a afterburner] [-s sbr] [-v vbr] in.wav out.aac\n", name); + fprintf(stderr, "Supported AOTs:\n"); + fprintf(stderr, "\t2\tAAC-LC\n"); + fprintf(stderr, "\t5\tHE-AAC\n"); + fprintf(stderr, "\t29\tHE-AAC v2\n"); + fprintf(stderr, "\t23\tAAC-LD\n"); + fprintf(stderr, "\t39\tAAC-ELD\n"); +} + +int main(int argc, char *argv[]) { + int bitrate = 64000; + int ch; + const char *infile, *outfile; + FILE *out; + void *wav; + int format, sample_rate, channels, bits_per_sample; + int input_size; + uint8_t* input_buf; + int16_t* convert_buf; + int aot = 2; + int afterburner = 1; + int eld_sbr = 0; + int vbr = 0; + HANDLE_AACENCODER handle; + CHANNEL_MODE mode; + AACENC_InfoStruct info = { 0 }; + while ((ch = getopt(argc, argv, "r:t:a:s:v:")) != -1) { + switch (ch) { + case 'r': + bitrate = atoi(optarg); + break; + case 't': + aot = atoi(optarg); + break; + case 'a': + afterburner = atoi(optarg); + break; + case 's': + eld_sbr = atoi(optarg); + break; + case 'v': + vbr = atoi(optarg); + break; + case '?': + default: + usage(argv[0]); + return 1; + } + } + if (argc - optind < 2) { + usage(argv[0]); + return 1; + } + infile = argv[optind]; + outfile = argv[optind + 1]; + + wav = wav_read_open(infile); + if (!wav) { + fprintf(stderr, "Unable to open wav file %s\n", infile); + return 1; + } + if (!wav_get_header(wav, &format, &channels, &sample_rate, &bits_per_sample, NULL)) { + fprintf(stderr, "Bad wav file %s\n", infile); + return 1; + } + if (format != 1) { + fprintf(stderr, "Unsupported WAV format %d\n", format); + return 1; + } + if (bits_per_sample != 16) { + fprintf(stderr, "Unsupported WAV sample depth %d\n", bits_per_sample); + return 1; + } + switch (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: + fprintf(stderr, "Unsupported WAV channels %d\n", channels); + return 1; + } + if (aacEncOpen(&handle, 0, channels) != AACENC_OK) { + fprintf(stderr, "Unable to open encoder\n"); + return 1; + } + if (aacEncoder_SetParam(handle, AACENC_AOT, aot) != AACENC_OK) { + fprintf(stderr, "Unable to set the AOT\n"); + return 1; + } + if (aot == 39 && eld_sbr) { + if (aacEncoder_SetParam(handle, AACENC_SBR_MODE, 1) != AACENC_OK) { + fprintf(stderr, "Unable to set SBR mode for ELD\n"); + return 1; + } + } + if (aacEncoder_SetParam(handle, AACENC_SAMPLERATE, sample_rate) != AACENC_OK) { + fprintf(stderr, "Unable to set the AOT\n"); + return 1; + } + if (aacEncoder_SetParam(handle, AACENC_CHANNELMODE, mode) != AACENC_OK) { + fprintf(stderr, "Unable to set the channel mode\n"); + return 1; + } + if (aacEncoder_SetParam(handle, AACENC_CHANNELORDER, 1) != AACENC_OK) { + fprintf(stderr, "Unable to set the wav channel order\n"); + return 1; + } + if (vbr) { + if (aacEncoder_SetParam(handle, AACENC_BITRATEMODE, vbr) != AACENC_OK) { + fprintf(stderr, "Unable to set the VBR bitrate mode\n"); + return 1; + } + } else { + if (aacEncoder_SetParam(handle, AACENC_BITRATE, bitrate) != AACENC_OK) { + fprintf(stderr, "Unable to set the bitrate\n"); + return 1; + } + } + if (aacEncoder_SetParam(handle, AACENC_TRANSMUX, 2) != AACENC_OK) { + fprintf(stderr, "Unable to set the ADTS transmux\n"); + return 1; + } + if (aacEncoder_SetParam(handle, AACENC_AFTERBURNER, afterburner) != AACENC_OK) { + fprintf(stderr, "Unable to set the afterburner mode\n"); + return 1; + } + if (aacEncEncode(handle, NULL, NULL, NULL, NULL) != AACENC_OK) { + fprintf(stderr, "Unable to initialize the encoder\n"); + return 1; + } + if (aacEncInfo(handle, &info) != AACENC_OK) { + fprintf(stderr, "Unable to get the encoder info\n"); + return 1; + } + + out = fopen(outfile, "wb"); + if (!out) { + perror(outfile); + return 1; + } + + input_size = channels*2*info.frameLength; + input_buf = (uint8_t*) malloc(input_size); + convert_buf = (int16_t*) malloc(input_size); + + while (1) { + AACENC_BufDesc in_buf = { 0 }, 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; + int read, i; + void *in_ptr, *out_ptr; + uint8_t outbuf[20480]; + AACENC_ERROR err; + + read = wav_read_data(wav, input_buf, input_size); + for (i = 0; i < read/2; i++) { + const uint8_t* in = &input_buf[2*i]; + convert_buf[i] = in[0] | (in[1] << 8); + } + if (read <= 0) { + in_args.numInSamples = -1; + } else { + in_ptr = convert_buf; + in_size = read; + in_elem_size = 2; + + in_args.numInSamples = read/2; + 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 = outbuf; + out_size = sizeof(outbuf); + 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(handle, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK) { + if (err == AACENC_ENCODE_EOF) + break; + fprintf(stderr, "Encoding failed\n"); + return 1; + } + if (out_args.numOutBytes == 0) + continue; + fwrite(outbuf, 1, out_args.numOutBytes, out); + } + free(input_buf); + free(convert_buf); + fclose(out); + wav_read_close(wav); + aacEncClose(&handle); + + return 0; +} + diff --git a/configure.ac b/configure.ac index cfa77ad..44fb2f1 100644 --- a/configure.ac +++ b/configure.ac @@ -7,7 +7,17 @@ AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([tar-ustar foreign]) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])]) +dnl Various options for configure +AC_ARG_ENABLE([example], + [AS_HELP_STRING([--enable-example], + [enable example encoding program (default is no)])], + [example=$enableval], [example=no]) + +dnl Automake conditionals to set +AM_CONDITIONAL(EXAMPLE, test x$example = xyes) + dnl Checks for programs. +AC_PROG_CC AC_PROG_CXX LT_INIT diff --git a/wavreader.c b/wavreader.c new file mode 100644 index 0000000..f672001 --- /dev/null +++ b/wavreader.c @@ -0,0 +1,163 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2009 Martin Storsjo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#include "wavreader.h" +#include +#include +#include +#include + +#define TAG(a, b, c, d) (((a) << 24) | ((b) << 16) | ((c) << 8) | (d)) + +struct wav_reader { + FILE *wav; + uint32_t data_length; + + int format; + int sample_rate; + int bits_per_sample; + int channels; + int byte_rate; + int block_align; +}; + +static uint32_t read_tag(struct wav_reader* wr) { + uint32_t tag = 0; + tag = (tag << 8) | fgetc(wr->wav); + tag = (tag << 8) | fgetc(wr->wav); + tag = (tag << 8) | fgetc(wr->wav); + tag = (tag << 8) | fgetc(wr->wav); + return tag; +} + +static uint32_t read_int32(struct wav_reader* wr) { + uint32_t value = 0; + value |= fgetc(wr->wav) << 0; + value |= fgetc(wr->wav) << 8; + value |= fgetc(wr->wav) << 16; + value |= fgetc(wr->wav) << 24; + return value; +} + +static uint16_t read_int16(struct wav_reader* wr) { + uint16_t value = 0; + value |= fgetc(wr->wav) << 0; + value |= fgetc(wr->wav) << 8; + return value; +} + +void* wav_read_open(const char *filename) { + struct wav_reader* wr = (struct wav_reader*) malloc(sizeof(*wr)); + long data_pos = 0; + memset(wr, 0, sizeof(*wr)); + + wr->wav = fopen(filename, "rb"); + if (wr->wav == NULL) { + free(wr); + return NULL; + } + + while (1) { + uint32_t tag, tag2, length; + tag = read_tag(wr); + if (feof(wr->wav)) + break; + length = read_int32(wr); + if (tag != TAG('R', 'I', 'F', 'F') || length < 4) { + fseek(wr->wav, length, SEEK_CUR); + continue; + } + tag2 = read_tag(wr); + length -= 4; + if (tag2 != TAG('W', 'A', 'V', 'E')) { + fseek(wr->wav, length, SEEK_CUR); + continue; + } + // RIFF chunk found, iterate through it + while (length >= 8) { + uint32_t subtag, sublength; + subtag = read_tag(wr); + if (feof(wr->wav)) + break; + sublength = read_int32(wr); + length -= 8; + if (length < sublength) + break; + if (subtag == TAG('f', 'm', 't', ' ')) { + if (sublength < 16) { + // Insufficient data for 'fmt ' + break; + } + wr->format = read_int16(wr); + wr->channels = read_int16(wr); + wr->sample_rate = read_int32(wr); + wr->byte_rate = read_int32(wr); + wr->block_align = read_int16(wr); + wr->bits_per_sample = read_int16(wr); + fseek(wr->wav, sublength - 16, SEEK_CUR); + } else if (subtag == TAG('d', 'a', 't', 'a')) { + data_pos = ftell(wr->wav); + wr->data_length = sublength; + fseek(wr->wav, sublength, SEEK_CUR); + } else { + fseek(wr->wav, sublength, SEEK_CUR); + } + length -= sublength; + } + if (length > 0) { + // Bad chunk? + fseek(wr->wav, length, SEEK_CUR); + } + } + fseek(wr->wav, data_pos, SEEK_SET); + return wr; +} + +void wav_read_close(void* obj) { + struct wav_reader* wr = (struct wav_reader*) obj; + fclose(wr->wav); + free(wr); +} + +int wav_get_header(void* obj, int* format, int* channels, int* sample_rate, int* bits_per_sample, unsigned int* data_length) { + struct wav_reader* wr = (struct wav_reader*) obj; + if (format) + *format = wr->format; + if (channels) + *channels = wr->channels; + if (sample_rate) + *sample_rate = wr->sample_rate; + if (bits_per_sample) + *bits_per_sample = wr->bits_per_sample; + if (data_length) + *data_length = wr->data_length; + return wr->format && wr->sample_rate; +} + +int wav_read_data(void* obj, unsigned char* data, unsigned int length) { + struct wav_reader* wr = (struct wav_reader*) obj; + int n; + if (wr->wav == NULL) + return -1; + if (length > wr->data_length) + length = wr->data_length; + n = fread(data, 1, length, wr->wav); + wr->data_length -= length; + return n; +} + diff --git a/wavreader.h b/wavreader.h new file mode 100644 index 0000000..57a13ff --- /dev/null +++ b/wavreader.h @@ -0,0 +1,37 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2009 Martin Storsjo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#ifndef WAVREADER_H +#define WAVREADER_H + +#ifdef __cplusplus +extern "C" { +#endif + +void* wav_read_open(const char *filename); +void wav_read_close(void* obj); + +int wav_get_header(void* obj, int* format, int* channels, int* sample_rate, int* bits_per_sample, unsigned int* data_length); +int wav_read_data(void* obj, unsigned char* data, unsigned int length); + +#ifdef __cplusplus +} +#endif + +#endif +