summaryrefslogtreecommitdiff
path: root/lib/faad2/frontend
diff options
context:
space:
mode:
Diffstat (limited to 'lib/faad2/frontend')
-rw-r--r--lib/faad2/frontend/audio.c512
-rw-r--r--lib/faad2/frontend/audio.h64
-rw-r--r--lib/faad2/frontend/faad.man85
-rw-r--r--lib/faad2/frontend/getopt.c725
-rw-r--r--lib/faad2/frontend/getopt.h130
-rw-r--r--lib/faad2/frontend/main.c1378
-rw-r--r--lib/faad2/frontend/mp4read.c1109
-rw-r--r--lib/faad2/frontend/mp4read.h78
-rw-r--r--lib/faad2/frontend/unicode_support.c172
-rw-r--r--lib/faad2/frontend/unicode_support.h44
10 files changed, 4297 insertions, 0 deletions
diff --git a/lib/faad2/frontend/audio.c b/lib/faad2/frontend/audio.c
new file mode 100644
index 00000000..01da9518
--- /dev/null
+++ b/lib/faad2/frontend/audio.c
@@ -0,0 +1,512 @@
+/*
+** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
+**
+** 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-1307, USA.
+**
+** Any non-GPL usage of this software or parts of this software is strictly
+** forbidden.
+**
+** The "appropriate copyright message" mentioned in section 2c of the GPLv2
+** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com"
+**
+** Commercial non-GPL licensing of this software is possible.
+** For more info contact Nero AG through Mpeg4AAClicense@nero.com.
+**
+** $Id: audio.c,v 1.30 2015/01/22 09:40:52 knik Exp $
+**/
+
+#ifdef _WIN32
+#include <io.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <math.h>
+#include <neaacdec.h>
+#include <stdint.h>
+
+#include "unicode_support.h"
+#include "audio.h"
+
+static size_t write_wav_header(audio_file *aufile);
+static size_t write_wav_extensible_header(audio_file *aufile, long channelMask);
+static size_t write_audio_16bit(audio_file *aufile, void *sample_buffer,
+ unsigned int samples);
+static size_t write_audio_24bit(audio_file *aufile, void *sample_buffer,
+ unsigned int samples);
+static size_t write_audio_32bit(audio_file *aufile, void *sample_buffer,
+ unsigned int samples);
+static size_t write_audio_float(audio_file *aufile, void *sample_buffer,
+ unsigned int samples);
+
+audio_file *open_audio_file(char *infile, int samplerate, int channels,
+ int outputFormat, int fileType, long channelMask)
+{
+ audio_file *aufile = malloc(sizeof(audio_file));
+
+ aufile->outputFormat = outputFormat;
+
+ aufile->samplerate = samplerate;
+ aufile->channels = channels;
+ aufile->total_samples = 0;
+ aufile->fileType = fileType;
+ aufile->channelMask = channelMask;
+
+ switch (outputFormat)
+ {
+ case FAAD_FMT_16BIT:
+ aufile->bits_per_sample = 16;
+ break;
+ case FAAD_FMT_24BIT:
+ aufile->bits_per_sample = 24;
+ break;
+ case FAAD_FMT_32BIT:
+ case FAAD_FMT_FLOAT:
+ aufile->bits_per_sample = 32;
+ break;
+ default:
+ if (aufile) free(aufile);
+ return NULL;
+ }
+
+ if(infile[0] == '-')
+ {
+#ifdef _WIN32
+ _setmode(_fileno(stdout), O_BINARY);
+#endif
+ aufile->sndfile = stdout;
+ aufile->toStdio = 1;
+ } else {
+ aufile->toStdio = 0;
+ aufile->sndfile = faad_fopen(infile, "wb");
+ }
+
+ if (aufile->sndfile == NULL)
+ {
+ if (aufile) free(aufile);
+ return NULL;
+ }
+
+ if (aufile->fileType == OUTPUT_WAV)
+ {
+ if (aufile->channelMask)
+ write_wav_extensible_header(aufile, aufile->channelMask);
+ else
+ write_wav_header(aufile);
+ }
+
+ return aufile;
+}
+
+size_t write_audio_file(audio_file *aufile, void *sample_buffer, int samples)
+{
+ char *buf = (char *)sample_buffer;
+ switch (aufile->outputFormat)
+ {
+ case FAAD_FMT_16BIT:
+ return write_audio_16bit(aufile, buf, samples);
+ case FAAD_FMT_24BIT:
+ return write_audio_24bit(aufile, buf, samples);
+ case FAAD_FMT_32BIT:
+ return write_audio_32bit(aufile, buf, samples);
+ case FAAD_FMT_FLOAT:
+ return write_audio_float(aufile, buf, samples);
+ default:
+ return 0;
+ }
+ // return 0;
+}
+
+void close_audio_file(audio_file *aufile)
+{
+ if ((aufile->fileType == OUTPUT_WAV) && (aufile->toStdio == 0))
+ {
+ fseek(aufile->sndfile, 0, SEEK_SET);
+
+ if (aufile->channelMask)
+ write_wav_extensible_header(aufile, aufile->channelMask);
+ else
+ write_wav_header(aufile);
+ }
+
+ if (aufile->toStdio == 0)
+ fclose(aufile->sndfile);
+
+ if (aufile) free(aufile);
+}
+
+static size_t write_wav_header(audio_file *aufile)
+{
+ unsigned char header[44];
+ unsigned char* p = header;
+ unsigned int bytes = (aufile->bits_per_sample + 7) / 8;
+ float data_size = (float)bytes * aufile->total_samples;
+ unsigned long word32;
+
+ *p++ = 'R'; *p++ = 'I'; *p++ = 'F'; *p++ = 'F';
+
+ word32 = (data_size + (44 - 8) < (float)MAXWAVESIZE) ?
+ (unsigned long)data_size + (44 - 8) : (unsigned long)MAXWAVESIZE;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ *p++ = 'W'; *p++ = 'A'; *p++ = 'V'; *p++ = 'E';
+
+ *p++ = 'f'; *p++ = 'm'; *p++ = 't'; *p++ = ' ';
+
+ *p++ = 0x10; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
+
+ if (aufile->outputFormat == FAAD_FMT_FLOAT)
+ {
+ *p++ = 0x03; *p++ = 0x00;
+ } else {
+ *p++ = 0x01; *p++ = 0x00;
+ }
+
+ *p++ = (unsigned char)(aufile->channels >> 0);
+ *p++ = (unsigned char)(aufile->channels >> 8);
+
+ word32 = (unsigned long)(aufile->samplerate + 0.5);
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ word32 = aufile->samplerate * bytes * aufile->channels;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ word32 = bytes * aufile->channels;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+
+ *p++ = (unsigned char)(aufile->bits_per_sample >> 0);
+ *p++ = (unsigned char)(aufile->bits_per_sample >> 8);
+
+ *p++ = 'd'; *p++ = 'a'; *p++ = 't'; *p++ = 'a';
+
+ word32 = data_size < MAXWAVESIZE ?
+ (unsigned long)data_size : (unsigned long)MAXWAVESIZE;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ return fwrite(header, sizeof(header), 1, aufile->sndfile);
+}
+
+static size_t write_wav_extensible_header(audio_file *aufile, long channelMask)
+{
+ unsigned char header[68];
+ unsigned char* p = header;
+ unsigned int bytes = (aufile->bits_per_sample + 7) / 8;
+ float data_size = (float)bytes * aufile->total_samples;
+ unsigned long word32;
+
+ *p++ = 'R'; *p++ = 'I'; *p++ = 'F'; *p++ = 'F';
+
+ word32 = (data_size + (68 - 8) < (float)MAXWAVESIZE) ?
+ (unsigned long)data_size + (68 - 8) : (unsigned long)MAXWAVESIZE;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ *p++ = 'W'; *p++ = 'A'; *p++ = 'V'; *p++ = 'E';
+
+ *p++ = 'f'; *p++ = 'm'; *p++ = 't'; *p++ = ' ';
+
+ *p++ = /*0x10*/0x28; *p++ = 0x00; *p++ = 0x00; *p++ = 0x00;
+
+ /* WAVE_FORMAT_EXTENSIBLE */
+ *p++ = 0xFE; *p++ = 0xFF;
+
+ *p++ = (unsigned char)(aufile->channels >> 0);
+ *p++ = (unsigned char)(aufile->channels >> 8);
+
+ word32 = (unsigned long)(aufile->samplerate + 0.5);
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ word32 = aufile->samplerate * bytes * aufile->channels;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ word32 = bytes * aufile->channels;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+
+ *p++ = (unsigned char)(aufile->bits_per_sample >> 0);
+ *p++ = (unsigned char)(aufile->bits_per_sample >> 8);
+
+ /* cbSize */
+ *p++ = (unsigned char)(22);
+ *p++ = (unsigned char)(0);
+
+ /* WAVEFORMATEXTENSIBLE */
+
+ /* wValidBitsPerSample */
+ *p++ = (unsigned char)(aufile->bits_per_sample >> 0);
+ *p++ = (unsigned char)(aufile->bits_per_sample >> 8);
+
+ /* dwChannelMask */
+ word32 = channelMask;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ /* SubFormat */
+ if (aufile->outputFormat == FAAD_FMT_FLOAT)
+ {
+ /* KSDATAFORMAT_SUBTYPE_IEEE_FLOAT: 00000003-0000-0010-8000-00aa00389b71 */
+ *p++ = 0x03;
+ *p++ = 0x00;
+ *p++ = 0x00;
+ *p++ = 0x00;
+ *p++ = 0x00; *p++ = 0x00; *p++ = 0x10; *p++ = 0x00; *p++ = 0x80; *p++ = 0x00;
+ *p++ = 0x00; *p++ = 0xaa; *p++ = 0x00; *p++ = 0x38; *p++ = 0x9b; *p++ = 0x71;
+ } else {
+ /* KSDATAFORMAT_SUBTYPE_PCM: 00000001-0000-0010-8000-00aa00389b71 */
+ *p++ = 0x01;
+ *p++ = 0x00;
+ *p++ = 0x00;
+ *p++ = 0x00;
+ *p++ = 0x00; *p++ = 0x00; *p++ = 0x10; *p++ = 0x00; *p++ = 0x80; *p++ = 0x00;
+ *p++ = 0x00; *p++ = 0xaa; *p++ = 0x00; *p++ = 0x38; *p++ = 0x9b; *p++ = 0x71;
+ }
+
+ /* end WAVEFORMATEXTENSIBLE */
+
+ *p++ = 'd'; *p++ = 'a'; *p++ = 't'; *p++ = 'a';
+
+ word32 = data_size < MAXWAVESIZE ?
+ (unsigned long)data_size : (unsigned long)MAXWAVESIZE;
+ *p++ = (unsigned char)(word32 >> 0);
+ *p++ = (unsigned char)(word32 >> 8);
+ *p++ = (unsigned char)(word32 >> 16);
+ *p++ = (unsigned char)(word32 >> 24);
+
+ return fwrite(header, sizeof(header), 1, aufile->sndfile);
+}
+
+static size_t write_audio_16bit(audio_file *aufile, void *sample_buffer,
+ unsigned int samples)
+{
+ size_t ret;
+ unsigned int i;
+ short *sample_buffer16 = (short*)sample_buffer;
+ char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8);
+
+ aufile->total_samples += samples;
+
+ if (aufile->channels == 6 && aufile->channelMask)
+ {
+ for (i = 0; i < samples; i += aufile->channels)
+ {
+ short r1, r2, r3, r4, r5, r6;
+ r1 = sample_buffer16[i];
+ r2 = sample_buffer16[i+1];
+ r3 = sample_buffer16[i+2];
+ r4 = sample_buffer16[i+3];
+ r5 = sample_buffer16[i+4];
+ r6 = sample_buffer16[i+5];
+ sample_buffer16[i] = r2;
+ sample_buffer16[i+1] = r3;
+ sample_buffer16[i+2] = r1;
+ sample_buffer16[i+3] = r6;
+ sample_buffer16[i+4] = r4;
+ sample_buffer16[i+5] = r5;
+ }
+ }
+
+ for (i = 0; i < samples; i++)
+ {
+ data[i*2] = (char)(sample_buffer16[i] & 0xFF);
+ data[i*2+1] = (char)((sample_buffer16[i] >> 8) & 0xFF);
+ }
+
+ ret = fwrite(data, samples, aufile->bits_per_sample/8, aufile->sndfile);
+
+ if (data) free(data);
+
+ return ret;
+}
+
+static size_t write_audio_24bit(audio_file *aufile, void *sample_buffer,
+ unsigned int samples)
+{
+ size_t ret;
+ unsigned int i;
+ int32_t *sample_buffer24 = (int32_t*)sample_buffer;
+ char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8);
+
+ aufile->total_samples += samples;
+
+ if (aufile->channels == 6 && aufile->channelMask)
+ {
+ for (i = 0; i < samples; i += aufile->channels)
+ {
+ long r1, r2, r3, r4, r5, r6;
+ r1 = sample_buffer24[i];
+ r2 = sample_buffer24[i+1];
+ r3 = sample_buffer24[i+2];
+ r4 = sample_buffer24[i+3];
+ r5 = sample_buffer24[i+4];
+ r6 = sample_buffer24[i+5];
+ sample_buffer24[i] = r2;
+ sample_buffer24[i+1] = r3;
+ sample_buffer24[i+2] = r1;
+ sample_buffer24[i+3] = r6;
+ sample_buffer24[i+4] = r4;
+ sample_buffer24[i+5] = r5;
+ }
+ }
+
+ for (i = 0; i < samples; i++)
+ {
+ data[i*3] = (char)(sample_buffer24[i] & 0xFF);
+ data[i*3+1] = (char)((sample_buffer24[i] >> 8) & 0xFF);
+ data[i*3+2] = (char)((sample_buffer24[i] >> 16) & 0xFF);
+ }
+
+ ret = fwrite(data, samples, aufile->bits_per_sample/8, aufile->sndfile);
+
+ if (data) free(data);
+
+ return ret;
+}
+
+static size_t write_audio_32bit(audio_file *aufile, void *sample_buffer,
+ unsigned int samples)
+{
+ size_t ret;
+ unsigned int i;
+ int32_t *sample_buffer32 = (int32_t*)sample_buffer;
+ char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8);
+
+ aufile->total_samples += samples;
+
+ if (aufile->channels == 6 && aufile->channelMask)
+ {
+ for (i = 0; i < samples; i += aufile->channels)
+ {
+ long r1, r2, r3, r4, r5, r6;
+ r1 = sample_buffer32[i];
+ r2 = sample_buffer32[i+1];
+ r3 = sample_buffer32[i+2];
+ r4 = sample_buffer32[i+3];
+ r5 = sample_buffer32[i+4];
+ r6 = sample_buffer32[i+5];
+ sample_buffer32[i] = r2;
+ sample_buffer32[i+1] = r3;
+ sample_buffer32[i+2] = r1;
+ sample_buffer32[i+3] = r6;
+ sample_buffer32[i+4] = r4;
+ sample_buffer32[i+5] = r5;
+ }
+ }
+
+ for (i = 0; i < samples; i++)
+ {
+ data[i*4] = (char)(sample_buffer32[i] & 0xFF);
+ data[i*4+1] = (char)((sample_buffer32[i] >> 8) & 0xFF);
+ data[i*4+2] = (char)((sample_buffer32[i] >> 16) & 0xFF);
+ data[i*4+3] = (char)((sample_buffer32[i] >> 24) & 0xFF);
+ }
+
+ ret = fwrite(data, samples, aufile->bits_per_sample/8, aufile->sndfile);
+
+ if (data) free(data);
+
+ return ret;
+}
+
+static size_t write_audio_float(audio_file *aufile, void *sample_buffer,
+ unsigned int samples)
+{
+ size_t ret;
+ unsigned int i;
+ float *sample_buffer_f = (float*)sample_buffer;
+ unsigned char *data = malloc(samples*aufile->bits_per_sample*sizeof(char)/8);
+
+ aufile->total_samples += samples;
+
+ if (aufile->channels == 6 && aufile->channelMask)
+ {
+ for (i = 0; i < samples; i += aufile->channels)
+ {
+ float r1, r2, r3, r4, r5, r6;
+ r1 = sample_buffer_f[i];
+ r2 = sample_buffer_f[i+1];
+ r3 = sample_buffer_f[i+2];
+ r4 = sample_buffer_f[i+3];
+ r5 = sample_buffer_f[i+4];
+ r6 = sample_buffer_f[i+5];
+ sample_buffer_f[i] = r2;
+ sample_buffer_f[i+1] = r3;
+ sample_buffer_f[i+2] = r1;
+ sample_buffer_f[i+3] = r6;
+ sample_buffer_f[i+4] = r4;
+ sample_buffer_f[i+5] = r5;
+ }
+ }
+
+ for (i = 0; i < samples; i++)
+ {
+ int exponent, mantissa, negative = 0 ;
+ float in = sample_buffer_f[i];
+
+ data[i*4] = 0; data[i*4+1] = 0; data[i*4+2] = 0; data[i*4+3] = 0;
+ if (in == 0.0)
+ continue;
+
+ if (in < 0.0)
+ {
+ in *= -1.0;
+ negative = 1;
+ }
+ in = (float)frexp(in, &exponent);
+ exponent += 126;
+ in *= (float)0x1000000;
+ mantissa = (((int)in) & 0x7FFFFF);
+
+ if (negative)
+ data[i*4+3] |= 0x80;
+
+ if (exponent & 0x01)
+ data[i*4+2] |= 0x80;
+
+ data[i*4] = mantissa & 0xFF;
+ data[i*4+1] = (mantissa >> 8) & 0xFF;
+ data[i*4+2] |= (mantissa >> 16) & 0x7F;
+ data[i*4+3] |= (exponent >> 1) & 0x7F;
+ }
+
+ ret = fwrite(data, samples, aufile->bits_per_sample/8, aufile->sndfile);
+
+ if (data) free(data);
+
+ return ret;
+}
diff --git a/lib/faad2/frontend/audio.h b/lib/faad2/frontend/audio.h
new file mode 100644
index 00000000..5518105b
--- /dev/null
+++ b/lib/faad2/frontend/audio.h
@@ -0,0 +1,64 @@
+/*
+** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
+**
+** 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-1307, USA.
+**
+** Any non-GPL usage of this software or parts of this software is strictly
+** forbidden.
+**
+** The "appropriate copyright message" mentioned in section 2c of the GPLv2
+** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com"
+**
+** Commercial non-GPL licensing of this software is possible.
+** For more info contact Nero AG through Mpeg4AAClicense@nero.com.
+**
+** $Id: audio.h,v 1.19 2007/11/01 12:33:29 menno Exp $
+**/
+
+#ifndef AUDIO_H_INCLUDED
+#define AUDIO_H_INCLUDED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define MAXWAVESIZE 4294967040LU
+
+#define OUTPUT_WAV 1
+#define OUTPUT_RAW 2
+
+typedef struct
+{
+ int toStdio;
+ int outputFormat;
+ FILE *sndfile;
+ unsigned int fileType;
+ unsigned long samplerate;
+ unsigned int bits_per_sample;
+ unsigned int channels;
+ unsigned long total_samples;
+ long channelMask;
+} audio_file;
+
+audio_file *open_audio_file(char *infile, int samplerate, int channels,
+ int outputFormat, int fileType, long channelMask);
+size_t write_audio_file(audio_file *aufile, void *sample_buffer, int samples);
+void close_audio_file(audio_file *aufile);
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/lib/faad2/frontend/faad.man b/lib/faad2/frontend/faad.man
new file mode 100644
index 00000000..96d4a29e
--- /dev/null
+++ b/lib/faad2/frontend/faad.man
@@ -0,0 +1,85 @@
+.TH FAAD "1" "October 2006" "faad 2.5" ""
+.SH NAME
+faad \(em Process an Advanced Audio Codec stream
+
+.SH "SYNOPSIS"
+.B faad
+[options] [\-w | \-o <output_filename> | \-a <output_filename>] input_filename
+
+.SH "DESCRIPTION"
+This utility provides a command line interface to libfaad2. This program reads in MPEG\(hy4 AAC files, processes, and outputs them in either Microsoft WAV, MPEG\(hy4 AAC ADTS, or standard PCM formats.
+
+.SH "OPTIONS"
+.TP
+.BI \-a " <filename>" ", \-\^\-adtsout" " <filename>"
+Sets the processing to output to the specified file in MPEG\(hy4 AAC ADTS format
+.TP
+.BI \-b " <number>" ", \-\^\-bits" " <number>"
+Set the output (individual) sample format. The number takes one of the following values:
+.RS
+.RS
+1: 16\(hybit PCM data (default).
+.br
+2: 24\(hybit PCM data.
+.br
+3: 32\(hybit PCM data.
+.br
+4: 32\(hybit floating\(hypoint data.
+.br
+5: 64\(hybit floating\(hypoint data.
+.RE
+.RE
+.TP
+.B \-d ", \-\^\-downmix"
+Set the processing to downsample from 5.1 (surround sound and bass) channels to 2 channels (stereo).
+.TP
+.BI \-f " <number>" ", \-\^\-format" " <number>"
+Set the output file format. The number takes one of the following values:
+.RS
+.RS
+1: Microsoft WAV format (default).
+.br
+2: Raw PCM data.
+.RE
+.RE
+.TP
+.BI \-g
+Set the processing to not perform gapless decoding.
+.TP
+.B \-h ", \-\^\-help"
+Shows a usage summary.
+.TP
+.B \-i ", \-\^\-info"
+Shows information about the about the input file.
+.TP
+.BI \-l " <number>" ", \-\^\-objecttype" " <number>"
+Sets the MPEG\hy(4 profile and object type for the processing to use. The number takes one of the following values:
+.RS
+.RS
+1: Main object type.
+.br
+2: Low Complexity (LC) object type (default).
+.br
+4: Long Term Prediction (LTP) object type.
+.br
+23: Low Delay (LD) object type.
+.RE
+.RE
+.TP
+.BI \-o " <filename>" ", \-\^\-outfile" " <number>"
+Sets the filename for processing output.
+.TP
+.B \-q ", \-\^\-quiet"
+Quiet \- Suppresses status messages during processing.
+.TP
+.B \-t ", \-\^\-oldformat"
+Sets the processing to use the old MPEG\(hy4 AAC ADTS format when outputting in said format.
+.TP
+.B \-w ", \-\^\-stdio"
+Sets the processing output to be sent to the standard out.
+
+.SH "AUTHOR"
+Matthew W. S. Bell <matthew (at) bells23.org.uk>
+
+.SH "SEE ALSO"
+\fBfaac\fP(1) \ No newline at end of file
diff --git a/lib/faad2/frontend/getopt.c b/lib/faad2/frontend/getopt.c
new file mode 100644
index 00000000..f069a5d1
--- /dev/null
+++ b/lib/faad2/frontend/getopt.c
@@ -0,0 +1,725 @@
+/* Getopt for GNU.
+ NOTE: getopt is now part of the C library, so if you don't know what
+ "Keep this file name-space clean" means, talk to roland@gnu.ai.mit.edu
+ before changing it!
+
+ Copyright (C) 1987, 88, 89, 90, 91, 92, 1993
+ Free Software Foundation, Inc.
+
+ 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifndef __STDC__
+# ifndef const
+# define const
+# endif
+#endif
+
+/* This tells Alpha OSF/1 not to define a getopt prototype in <stdio.h>. */
+#ifndef _NO_PROTO
+#define _NO_PROTO
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+/* Comment out all this code if we are using the GNU C Library, and are not
+ actually compiling the library itself. This code is part of the GNU C
+ Library, but also included in many other GNU distributions. Compiling
+ and linking in this code is a waste when using the GNU C library
+ (especially if it is a shared library). Rather than having every GNU
+ program understand `configure --with-gnu-libc' and omit the object files,
+ it is simpler to just do this in the source for each such file. */
+
+#if defined (_LIBC) || !defined (__GNU_LIBRARY__) || !__MacOSX__
+
+
+/* This needs to come after some library #include
+ to get __GNU_LIBRARY__ defined. */
+#ifdef __GNU_LIBRARY__
+/* Don't include stdlib.h for non-GNU C libraries because some of them
+ contain conflicting prototypes for getopt. */
+#include <stdlib.h>
+#endif /* GNU C library. */
+
+/* If GETOPT_COMPAT is defined, `+' as well as `--' can introduce a
+ long-named option. Because this is not POSIX.2 compliant, it is
+ being phased out. */
+/* #define GETOPT_COMPAT */
+
+/* This version of `getopt' appears to the caller like standard Unix `getopt'
+ but it behaves differently for the user, since it allows the user
+ to intersperse the options with the other arguments.
+
+ As `getopt' works, it permutes the elements of ARGV so that,
+ when it is done, all the options precede everything else. Thus
+ all application programs are extended to handle flexible argument order.
+
+ Setting the environment variable POSIXLY_CORRECT disables permutation.
+ Then the behavior is completely standard.
+
+ GNU application programs can use a third alternative mode in which
+ they can distinguish the relative order of options and other arguments. */
+
+#include "getopt.h"
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+char *optarg = 0;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+/* XXX 1003.2 says this must be 1 before any call. */
+int optind = 0;
+
+/* The next char to be scanned in the option-element
+ in which the last option character we returned was found.
+ This allows us to pick up the scan where we left off.
+
+ If this is zero, or a null string, it means resume the scan
+ by advancing to the next ARGV-element. */
+
+static char *nextchar;
+
+/* Callers store zero here to inhibit the error message
+ for unrecognized options. */
+
+int opterr = 1;
+
+/* Set to an option character which was unrecognized.
+ This must be initialized on some systems to avoid linking in the
+ system's own getopt implementation. */
+
+#define BAD_OPTION '\0'
+int optopt = BAD_OPTION;
+
+/* Describe how to deal with options that follow non-option ARGV-elements.
+
+ If the caller did not specify anything,
+ the default is REQUIRE_ORDER if the environment variable
+ POSIXLY_CORRECT is defined, PERMUTE otherwise.
+
+ REQUIRE_ORDER means don't recognize them as options;
+ stop option processing when the first non-option is seen.
+ This is what Unix does.
+ This mode of operation is selected by either setting the environment
+ variable POSIXLY_CORRECT, or using `+' as the first character
+ of the list of option characters.
+
+ PERMUTE is the default. We permute the contents of ARGV as we scan,
+ so that eventually all the non-options are at the end. This allows options
+ to be given in any order, even with programs that were not written to
+ expect this.
+
+ RETURN_IN_ORDER is an option available to programs that were written
+ to expect options and other ARGV-elements in any order and that care about
+ the ordering of the two. We describe each non-option ARGV-element
+ as if it were the argument of an option with character code 1.
+ Using `-' as the first character of the list of option characters
+ selects this mode of operation.
+
+ The special argument `--' forces an end of option-scanning regardless
+ of the value of `ordering'. In the case of RETURN_IN_ORDER, only
+ `--' can cause `getopt' to return EOF with `optind' != ARGC. */
+
+static enum
+{
+ REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER
+} ordering;
+
+#ifdef __GNU_LIBRARY__
+/* We want to avoid inclusion of string.h with non-GNU libraries
+ because there are many ways it can cause trouble.
+ On some systems, it contains special magic macros that don't work
+ in GCC. */
+#include <string.h>
+#define my_index strchr
+#define my_strlen strlen
+#else
+
+/* Avoid depending on library functions or files
+ whose names are inconsistent. */
+
+#if __STDC__ || defined(PROTO)
+extern char *getenv(const char *name);
+extern int strcmp (const char *s1, const char *s2);
+
+static int my_strlen(const char *s);
+static char *my_index (const char *str, int chr);
+#else
+extern char *getenv (const char *name);
+#endif
+
+static int my_strlen(const char *str) {
+ int n = 0;
+ while (*str++)
+ n++;
+ return n;
+}
+
+static char *my_index(const char *str, int chr) {
+ while (*str)
+ {
+ if (*str == chr)
+ return (char *) str;
+ str++;
+ }
+ return 0;
+}
+
+#endif /* GNU C library. */
+
+/* Handle permutation of arguments. */
+
+/* Describe the part of ARGV that contains non-options that have
+ been skipped. `first_nonopt' is the index in ARGV of the first of them;
+ `last_nonopt' is the index after the last of them. */
+
+static int first_nonopt;
+static int last_nonopt;
+
+/* Exchange two adjacent subsequences of ARGV.
+ One subsequence is elements [first_nonopt,last_nonopt)
+ which contains all the non-options that have been skipped so far.
+ The other is elements [last_nonopt,optind), which contains all
+ the options processed since those non-options were skipped.
+
+ `first_nonopt' and `last_nonopt' are relocated so that they describe
+ the new indices of the non-options in ARGV after they are moved.
+
+ To perform the swap, we first reverse the order of all elements. So
+ all options now come before all non options, but they are in the
+ wrong order. So we put back the options and non options in original
+ order by reversing them again. For example:
+ original input: a b c -x -y
+ reverse all: -y -x c b a
+ reverse options: -x -y c b a
+ reverse non options: -x -y a b c
+*/
+
+#if __STDC__ || defined(PROTO)
+static void exchange (char **argv);
+#endif
+
+static void exchange (char **argv) {
+ char *temp, **first, **last;
+
+ /* Reverse all the elements [first_nonopt, optind) */
+ first = &argv[first_nonopt];
+ last = &argv[optind-1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+ /* Put back the options in order */
+ first = &argv[first_nonopt];
+ first_nonopt += (optind - last_nonopt);
+ last = &argv[first_nonopt - 1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+
+ /* Put back the non options in order */
+ first = &argv[first_nonopt];
+ last_nonopt = optind;
+ last = &argv[last_nonopt-1];
+ while (first < last) {
+ temp = *first; *first = *last; *last = temp; first++; last--;
+ }
+}
+
+/* Scan elements of ARGV (whose length is ARGC) for option characters
+ given in OPTSTRING.
+
+ If an element of ARGV starts with '-', and is not exactly "-" or "--",
+ then it is an option element. The characters of this element
+ (aside from the initial '-') are option characters. If `getopt'
+ is called repeatedly, it returns successively each of the option characters
+ from each of the option elements.
+
+ If `getopt' finds another option character, it returns that character,
+ updating `optind' and `nextchar' so that the next call to `getopt' can
+ resume the scan with the following option character or ARGV-element.
+
+ If there are no more option characters, `getopt' returns `EOF'.
+ Then `optind' is the index in ARGV of the first ARGV-element
+ that is not an option. (The ARGV-elements have been permuted
+ so that those that are not options now come last.)
+
+ OPTSTRING is a string containing the legitimate option characters.
+ If an option character is seen that is not listed in OPTSTRING,
+ return BAD_OPTION after printing an error message. If you set `opterr' to
+ zero, the error message is suppressed but we still return BAD_OPTION.
+
+ If a char in OPTSTRING is followed by a colon, that means it wants an arg,
+ so the following text in the same ARGV-element, or the text of the following
+ ARGV-element, is returned in `optarg'. Two colons mean an option that
+ wants an optional arg; if there is text in the current ARGV-element,
+ it is returned in `optarg', otherwise `optarg' is set to zero.
+
+ If OPTSTRING starts with `-' or `+', it requests different methods of
+ handling the non-option ARGV-elements.
+ See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above.
+
+ Long-named options begin with `--' instead of `-'.
+ Their names may be abbreviated as long as the abbreviation is unique
+ or is an exact match for some defined option. If they have an
+ argument, it follows the option name in the same ARGV-element, separated
+ from the option name by a `=', or else the in next ARGV-element.
+ When `getopt' finds a long-named option, it returns 0 if that option's
+ `flag' field is nonzero, the value of the option's `val' field
+ if the `flag' field is zero.
+
+ The elements of ARGV aren't really const, because we permute them.
+ But we pretend they're const in the prototype to be compatible
+ with other systems.
+
+ LONGOPTS is a vector of `struct option' terminated by an
+ element containing a name which is zero.
+
+ LONGIND returns the index in LONGOPT of the long-named option found.
+ It is only valid when a long-named option has been found by the most
+ recent call.
+
+ If LONG_ONLY is nonzero, '-' as well as '--' can introduce
+ long-named options. */
+
+int _getopt_internal(int argc, char *const *argv, const char *optstring, const struct option *longopts, int *longind, int long_only) {
+ int option_index;
+
+ optarg = 0;
+
+ /* Initialize the internal data when the first call is made.
+ Start processing options with ARGV-element 1 (since ARGV-element 0
+ is the program name); the sequence of previously skipped
+ non-option ARGV-elements is empty. */
+
+ if (optind == 0)
+ {
+ first_nonopt = last_nonopt = optind = 1;
+
+ nextchar = NULL;
+
+ /* Determine how to handle the ordering of options and nonoptions. */
+
+ if (optstring[0] == '-')
+ {
+ ordering = RETURN_IN_ORDER;
+ ++optstring;
+ }
+ else if (optstring[0] == '+')
+ {
+ ordering = REQUIRE_ORDER;
+ ++optstring;
+ }
+ else if (getenv ("POSIXLY_CORRECT") != NULL)
+ ordering = REQUIRE_ORDER;
+ else
+ ordering = PERMUTE;
+ }
+
+ if (nextchar == NULL || *nextchar == '\0')
+ {
+ if (ordering == PERMUTE)
+ {
+ /* If we have just processed some options following some non-options,
+ exchange them so that the options come first. */
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (last_nonopt != optind)
+ first_nonopt = optind;
+
+ /* Now skip any additional non-options
+ and extend the range of non-options previously skipped. */
+
+ while (optind < argc
+ && (argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ optind++;
+ last_nonopt = optind;
+ }
+
+ /* Special ARGV-element `--' means premature end of options.
+ Skip it like a null option,
+ then exchange with previous non-options as if it were an option,
+ then skip everything else like a non-option. */
+
+ if (optind != argc && !strcmp (argv[optind], "--"))
+ {
+ optind++;
+
+ if (first_nonopt != last_nonopt && last_nonopt != optind)
+ exchange ((char **) argv);
+ else if (first_nonopt == last_nonopt)
+ first_nonopt = optind;
+ last_nonopt = argc;
+
+ optind = argc;
+ }
+
+ /* If we have done all the ARGV-elements, stop the scan
+ and back over any non-options that we skipped and permuted. */
+
+ if (optind == argc)
+ {
+ /* Set the next-arg-index to point at the non-options
+ that we previously skipped, so the caller will digest them. */
+ if (first_nonopt != last_nonopt)
+ optind = first_nonopt;
+ return EOF;
+ }
+
+ /* If we have come to a non-option and did not permute it,
+ either stop the scan or describe it to the caller and pass it by. */
+
+ if ((argv[optind][0] != '-' || argv[optind][1] == '\0')
+#ifdef GETOPT_COMPAT
+ && (longopts == NULL
+ || argv[optind][0] != '+' || argv[optind][1] == '\0')
+#endif /* GETOPT_COMPAT */
+ )
+ {
+ if (ordering == REQUIRE_ORDER)
+ return EOF;
+ optarg = argv[optind++];
+ return 1;
+ }
+
+ /* We have found another option-ARGV-element.
+ Start decoding its characters. */
+
+ nextchar = (argv[optind] + 1
+ + (longopts != NULL && argv[optind][1] == '-'));
+ }
+
+ if (longopts != NULL
+ && ((argv[optind][0] == '-'
+ && (argv[optind][1] == '-' || long_only))
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ ))
+ {
+ const struct option *p;
+ char *s = nextchar;
+ int exact = 0;
+ int ambig = 0;
+ const struct option *pfound = NULL;
+ int indfound = 0;
+
+ while (*s && *s != '=')
+ s++;
+
+ /* Test all options for either exact match or abbreviated matches. */
+ for (p = longopts, option_index = 0; p->name;
+ p++, option_index++)
+ if (!strncmp (p->name, nextchar, s - nextchar))
+ {
+ if (s - nextchar == my_strlen (p->name))
+ {
+ /* Exact match found. */
+ pfound = p;
+ indfound = option_index;
+ exact = 1;
+ break;
+ }
+ else if (pfound == NULL)
+ {
+ /* First nonexact match found. */
+ pfound = p;
+ indfound = option_index;
+ }
+ else
+ /* Second nonexact match found. */
+ ambig = 1;
+ }
+
+ if (ambig && !exact)
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' is ambiguous\n",
+ argv[0], argv[optind]);
+ nextchar += my_strlen (nextchar);
+ optind++;
+ return BAD_OPTION;
+ }
+
+ if (pfound != NULL)
+ {
+ option_index = indfound;
+ optind++;
+ if (*s)
+ {
+ /* Don't test has_arg with >, because some C compilers don't
+ allow it to be used on enums. */
+ if (pfound->has_arg)
+ optarg = s + 1;
+ else
+ {
+ if (opterr)
+ {
+ if (argv[optind - 1][1] == '-')
+ /* --option */
+ fprintf (stderr,
+ "%s: option `--%s' doesn't allow an argument\n",
+ argv[0], pfound->name);
+ else
+ /* +option or -option */
+ fprintf (stderr,
+ "%s: option `%c%s' doesn't allow an argument\n",
+ argv[0], argv[optind - 1][0], pfound->name);
+ }
+ nextchar += my_strlen (nextchar);
+ return BAD_OPTION;
+ }
+ }
+ else if (pfound->has_arg == 1)
+ {
+ if (optind < argc)
+ optarg = argv[optind++];
+ else
+ {
+ if (opterr)
+ fprintf (stderr, "%s: option `%s' requires an argument\n",
+ argv[0], argv[optind - 1]);
+ nextchar += my_strlen (nextchar);
+ return optstring[0] == ':' ? ':' : BAD_OPTION;
+ }
+ }
+ nextchar += my_strlen (nextchar);
+ if (longind != NULL)
+ *longind = option_index;
+ if (pfound->flag)
+ {
+ *(pfound->flag) = pfound->val;
+ return 0;
+ }
+ return pfound->val;
+ }
+ /* Can't find it as a long option. If this is not getopt_long_only,
+ or the option starts with '--' or is not a valid short
+ option, then it's an error.
+ Otherwise interpret it as a short option. */
+ if (!long_only || argv[optind][1] == '-'
+#ifdef GETOPT_COMPAT
+ || argv[optind][0] == '+'
+#endif /* GETOPT_COMPAT */
+ || my_index (optstring, *nextchar) == NULL)
+ {
+ if (opterr)
+ {
+ if (argv[optind][1] == '-')
+ /* --option */
+ fprintf (stderr, "%s: unrecognized option `--%s'\n",
+ argv[0], nextchar);
+ else
+ /* +option or -option */
+ fprintf (stderr, "%s: unrecognized option `%c%s'\n",
+ argv[0], argv[optind][0], nextchar);
+ }
+ nextchar = (char *) "";
+ optind++;
+ return BAD_OPTION;
+ }
+ }
+
+ /* Look at and handle the next option-character. */
+
+ {
+ char c = *nextchar++;
+ char *temp = my_index (optstring, c);
+
+ /* Increment `optind' when we start to process its last character. */
+ if (*nextchar == '\0')
+ ++optind;
+
+ if (temp == NULL || c == ':')
+ {
+ if (opterr)
+ {
+#if 0
+ if (c < 040 || c >= 0177)
+ fprintf (stderr, "%s: unrecognized option, character code 0%o\n",
+ argv[0], c);
+ else
+ fprintf (stderr, "%s: unrecognized option `-%c'\n", argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: illegal option -- %c\n", argv[0], c);
+#endif
+ }
+ optopt = c;
+ return BAD_OPTION;
+ }
+ if (temp[1] == ':')
+ {
+ if (temp[2] == ':')
+ {
+ /* This is an option that accepts an argument optionally. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ optind++;
+ }
+ else
+ optarg = 0;
+ nextchar = NULL;
+ }
+ else
+ {
+ /* This is an option that requires an argument. */
+ if (*nextchar != '\0')
+ {
+ optarg = nextchar;
+ /* If we end this ARGV-element by taking the rest as an arg,
+ we must advance to the next element now. */
+ optind++;
+ }
+ else if (optind == argc)
+ {
+ if (opterr)
+ {
+#if 0
+ fprintf (stderr, "%s: option `-%c' requires an argument\n",
+ argv[0], c);
+#else
+ /* 1003.2 specifies the format of this message. */
+ fprintf (stderr, "%s: option requires an argument -- %c\n",
+ argv[0], c);
+#endif
+ }
+ optopt = c;
+ if (optstring[0] == ':')
+ c = ':';
+ else
+ c = BAD_OPTION;
+ }
+ else
+ /* We already incremented `optind' once;
+ increment it again when taking next ARGV-elt as argument. */
+ optarg = argv[optind++];
+ nextchar = NULL;
+ }
+ }
+ return c;
+ }
+}
+
+int getopt (int argc, char *const *argv, const char *optstring) {
+ return _getopt_internal (argc, argv, optstring,
+ (const struct option *) 0,
+ (int *) 0,
+ 0);
+}
+
+int getopt_long(int argc, char *const *argv, const char *options, const struct option *long_options, int *opt_index) {
+ return _getopt_internal (argc, argv, options, long_options, opt_index, 0);
+}
+
+#endif /* _LIBC or not __GNU_LIBRARY__. */
+
+#ifdef TEST
+
+/* Compile with -DTEST to make an executable for use in testing
+ the above definition of `getopt'. */
+
+int
+main (argc, argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ int digit_optind = 0;
+
+ while (1)
+ {
+ int this_option_optind = optind ? optind : 1;
+
+ c = getopt (argc, argv, "abc:d:0123456789");
+ if (c == EOF)
+ break;
+
+ switch (c)
+ {
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ if (digit_optind != 0 && digit_optind != this_option_optind)
+ printf ("digits occur in two different argv-elements.\n");
+ digit_optind = this_option_optind;
+ printf ("option %c\n", c);
+ break;
+
+ case 'a':
+ printf ("option a\n");
+ break;
+
+ case 'b':
+ printf ("option b\n");
+ break;
+
+ case 'c':
+ printf ("option c with value `%s'\n", optarg);
+ break;
+
+ case BAD_OPTION:
+ break;
+
+ default:
+ printf ("?? getopt returned character code 0%o ??\n", c);
+ }
+ }
+
+ if (optind < argc)
+ {
+ printf ("non-option ARGV-elements: ");
+ while (optind < argc)
+ printf ("%s ", argv[optind++]);
+ printf ("\n");
+ }
+
+ exit (0);
+}
+
+#endif /* TEST */
diff --git a/lib/faad2/frontend/getopt.h b/lib/faad2/frontend/getopt.h
new file mode 100644
index 00000000..3fd12771
--- /dev/null
+++ b/lib/faad2/frontend/getopt.h
@@ -0,0 +1,130 @@
+/* Declarations for getopt.
+ Copyright (C) 1989, 1990, 1991, 1992, 1993 Free Software Foundation, Inc.
+
+ 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, 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, 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#ifndef _GETOPT_H
+#define _GETOPT_H 1
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef __MacOSX__
+
+/* For communication from `getopt' to the caller.
+ When `getopt' finds an option that takes an argument,
+ the argument value is returned here.
+ Also, when `ordering' is RETURN_IN_ORDER,
+ each non-option ARGV-element is returned here. */
+
+extern char *optarg;
+
+/* Index in ARGV of the next element to be scanned.
+ This is used for communication to and from the caller
+ and for communication between successive calls to `getopt'.
+
+ On entry to `getopt', zero means this is the first call; initialize.
+
+ When `getopt' returns EOF, this is the index of the first of the
+ non-option elements that the caller should itself scan.
+
+ Otherwise, `optind' communicates from one call to the next
+ how much of ARGV has been scanned so far. */
+
+extern int optind;
+
+/* Callers store zero here to inhibit the error message `getopt' prints
+ for unrecognized options. */
+
+extern int opterr;
+
+/* Set to an option character which was unrecognized. */
+
+extern int optopt;
+#endif
+
+/* Describe the long-named options requested by the application.
+ The LONG_OPTIONS argument to getopt_long or getopt_long_only is a vector
+ of `struct option' terminated by an element containing a name which is
+ zero.
+
+ The field `has_arg' is:
+ no_argument (or 0) if the option does not take an argument,
+ required_argument (or 1) if the option requires an argument,
+ optional_argument (or 2) if the option takes an optional argument.
+
+ If the field `flag' is not NULL, it points to a variable that is set
+ to the value given in the field `val' when the option is found, but
+ left unchanged if the option is not found.
+
+ To have a long-named option do something other than set an `int' to
+ a compiled-in constant, such as set a value from `optarg', set the
+ option's `flag' field to zero and its `val' field to a nonzero
+ value (the equivalent single-letter option character, if there is
+ one). For long options that have a zero `flag' field, `getopt'
+ returns the contents of the `val' field. */
+
+struct option
+{
+#if __STDC__
+ const char *name;
+#else
+ char *name;
+#endif
+ /* has_arg can't be an enum because some compilers complain about
+ type mismatches in all the code that assumes it is an int. */
+ int has_arg;
+ int *flag;
+ int val;
+};
+
+/* Names for the values of the `has_arg' field of `struct option'. */
+
+#define no_argument 0
+#define required_argument 1
+#define optional_argument 2
+
+//#if __STDC__ || defined(PROTO)
+#if defined(__GNU_LIBRARY__)
+/* Many other libraries have conflicting prototypes for getopt, with
+ differences in the consts, in stdlib.h. To avoid compilation
+ errors, only prototype getopt for the GNU C library. */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+#endif /* not __GNU_LIBRARY__ */
+extern int getopt_long (int argc, char *const *argv, const char *shortopts,
+ const struct option *longopts, int *longind);
+extern int getopt_long_only (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind);
+
+/* Internal only. Users should not call this directly. */
+extern int _getopt_internal (int argc, char *const *argv,
+ const char *shortopts,
+ const struct option *longopts, int *longind,
+ int long_only);
+//#else /* not __STDC__ */
+extern int getopt (int argc, char *const *argv, const char *shortopts);
+//extern int getopt_long ();
+//extern int getopt_long_only ();
+
+//extern int _getopt_internal ();
+//#endif /* not __STDC__ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _GETOPT_H */
diff --git a/lib/faad2/frontend/main.c b/lib/faad2/frontend/main.c
new file mode 100644
index 00000000..6411bb1b
--- /dev/null
+++ b/lib/faad2/frontend/main.c
@@ -0,0 +1,1378 @@
+/*
+** FAAD2 - Freeware Advanced Audio (AAC) Decoder including SBR decoding
+** Copyright (C) 2003-2005 M. Bakker, Nero AG, http://www.nero.com
+**
+** 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-1307, USA.
+**
+** Any non-GPL usage of this software or parts of this software is strictly
+** forbidden.
+**
+** The "appropriate copyright message" mentioned in section 2c of the GPLv2
+** must read: "Code from FAAD2 is copyright (c) Nero AG, www.nero.com"
+**
+** Commercial non-GPL licensing of this software is possible.
+** For more info contact Nero AG through Mpeg4AAClicense@nero.com.
+**
+** $Id: main.c,v 1.89 2015/01/19 09:46:12 knik Exp $
+**/
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef _WIN32
+#define _CRT_SECURE_NO_WARNINGS
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <io.h>
+#ifndef __MINGW32__
+#define off_t __int64
+#endif
+#else
+#include <time.h>
+#endif
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <getopt.h>
+
+#include <neaacdec.h>
+
+#include "unicode_support.h"
+#include "audio.h"
+#include "mp4read.h"
+
+#ifndef min
+#define min(a,b) ( (a) < (b) ? (a) : (b) )
+#endif
+
+#define MAX_CHANNELS 6 /* make this higher to support files with
+ more channels */
+
+#define MAX_PERCENTS 384
+
+static int quiet = 0;
+
+static void faad_fprintf(FILE *stream, const char *fmt, ...)
+{
+ va_list ap;
+
+ if (!quiet)
+ {
+ va_start(ap, fmt);
+ vfprintf(stream, fmt, ap);
+ va_end(ap);
+ }
+
+#ifdef _WIN32
+ if (!_isatty(_fileno(stream)))
+ {
+ fflush(stream); /*ensure real-time progress output on Win32*/
+ }
+#endif
+}
+
+/* FAAD file buffering routines */
+typedef struct {
+ unsigned long bytes_into_buffer;
+ unsigned long bytes_consumed;
+ unsigned long file_offset;
+ unsigned char *buffer;
+ FILE *infile;
+ int at_eof;
+} aac_buffer;
+
+
+static int fill_buffer(aac_buffer *b)
+{
+ unsigned long bread;
+
+ if (b->bytes_consumed > 0)
+ {
+ if (b->bytes_into_buffer)
+ {
+ memmove((void*)b->buffer, (void*)(b->buffer + b->bytes_consumed),
+ b->bytes_into_buffer*sizeof(unsigned char));
+ }
+
+ if (!b->at_eof)
+ {
+ bread = (unsigned long)fread((void*)(b->buffer + b->bytes_into_buffer), 1,
+ b->bytes_consumed, b->infile);
+
+ if (bread != b->bytes_consumed)
+ b->at_eof = 1;
+
+ b->bytes_into_buffer += bread;
+ }
+
+ b->bytes_consumed = 0;
+
+ if (b->bytes_into_buffer > 3)
+ {
+ if (memcmp(b->buffer, "TAG", 3) == 0)
+ b->bytes_into_buffer = 0;
+ }
+ if (b->bytes_into_buffer > 11)
+ {
+ if (memcmp(b->buffer, "LYRICSBEGIN", 11) == 0)
+ b->bytes_into_buffer = 0;
+ }
+ if (b->bytes_into_buffer > 8)
+ {
+ if (memcmp(b->buffer, "APETAGEX", 8) == 0)
+ b->bytes_into_buffer = 0;
+ }
+ }
+
+ return 1;
+}
+
+static void advance_buffer(aac_buffer *b, unsigned int bytes)
+{
+ while ((b->bytes_into_buffer > 0) && (bytes > 0))
+ {
+ unsigned int chunk = bytes;
+ if (b->bytes_into_buffer < chunk)
+ chunk = (unsigned int)b->bytes_into_buffer;
+
+ bytes -= chunk;
+ b->file_offset += chunk;
+ b->bytes_consumed = chunk;
+ b->bytes_into_buffer -= chunk;
+
+ if (b->bytes_into_buffer == 0)
+ fill_buffer(b);
+ }
+}
+
+static void lookforheader(aac_buffer *b)
+{
+ int i = 0;
+ while (!b->at_eof )
+ {
+ if (b->bytes_into_buffer > 4)
+ {
+ if( ((b->buffer[0+i] == 0xff) && ((b->buffer[1+i] & 0xf6) == 0xf0)) ||
+ (b->buffer[0+i] == 'A' && b->buffer[1+i] == 'D' && b->buffer[2+i] == 'I' && b->buffer[3+i] == 'F'))
+ {
+ fill_buffer(b);
+ break;
+ } else {
+ i++;
+ b->file_offset += 1;
+ b->bytes_consumed += 1;
+ b->bytes_into_buffer -= 1;
+ }
+ }
+ else
+ {
+ fill_buffer(b);
+ i = 0;
+ }
+ }
+}
+
+static int adts_sample_rates[] = {96000,88200,64000,48000,44100,32000,24000,22050,16000,12000,11025,8000,7350,0,0,0};
+
+static int adts_parse(aac_buffer *b, int *bitrate, float *length)
+{
+ size_t frames;
+ unsigned int frame_length;
+ size_t t_framelength = 0;
+ int samplerate = 0;
+ float frames_per_sec, bytes_per_frame;
+
+ /* Read all frames to ensure correct time and bitrate */
+ for (frames = 0; /* */; frames++)
+ {
+ fill_buffer(b);
+
+ if (b->bytes_into_buffer > 7)
+ {
+ /* check syncword */
+ if (!((b->buffer[0] == 0xFF)&&((b->buffer[1] & 0xF6) == 0xF0)))
+ break;
+
+ if (frames == 0)
+ samplerate = adts_sample_rates[(b->buffer[2]&0x3c)>>2];
+
+ frame_length = ((((unsigned int)b->buffer[3] & 0x3)) << 11)
+ | (((unsigned int)b->buffer[4]) << 3) | (b->buffer[5] >> 5);
+ if (frame_length == 0)
+ break;
+
+ t_framelength += frame_length;
+
+ if (frame_length > b->bytes_into_buffer)
+ break;
+
+ advance_buffer(b, frame_length);
+ } else {
+ break;
+ }
+ }
+
+ frames_per_sec = (float)samplerate/1024.0f;
+ if (frames != 0)
+ bytes_per_frame = (float)t_framelength/(float)(frames*1000);
+ else
+ bytes_per_frame = 0;
+ *bitrate = (int)(8. * bytes_per_frame * frames_per_sec + 0.5);
+ if (frames_per_sec != 0)
+ *length = (float)frames/frames_per_sec;
+ else
+ *length = 1;
+
+ return 1;
+}
+
+/* MicroSoft channel definitions */
+#define SPEAKER_FRONT_LEFT 0x1
+#define SPEAKER_FRONT_RIGHT 0x2
+#define SPEAKER_FRONT_CENTER 0x4
+#define SPEAKER_LOW_FREQUENCY 0x8
+#define SPEAKER_BACK_LEFT 0x10
+#define SPEAKER_BACK_RIGHT 0x20
+#define SPEAKER_FRONT_LEFT_OF_CENTER 0x40
+#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x80
+#define SPEAKER_BACK_CENTER 0x100
+#define SPEAKER_SIDE_LEFT 0x200
+#define SPEAKER_SIDE_RIGHT 0x400
+#define SPEAKER_TOP_CENTER 0x800
+#define SPEAKER_TOP_FRONT_LEFT 0x1000
+#define SPEAKER_TOP_FRONT_CENTER 0x2000
+#define SPEAKER_TOP_FRONT_RIGHT 0x4000
+#define SPEAKER_TOP_BACK_LEFT 0x8000
+#define SPEAKER_TOP_BACK_CENTER 0x10000
+#define SPEAKER_TOP_BACK_RIGHT 0x20000
+#define SPEAKER_RESERVED 0x80000000
+
+static long aacChannelConfig2wavexChannelMask(NeAACDecFrameInfo *hInfo)
+{
+ if (hInfo->channels == 6 && hInfo->num_lfe_channels)
+ {
+ return SPEAKER_FRONT_LEFT + SPEAKER_FRONT_RIGHT +
+ SPEAKER_FRONT_CENTER + SPEAKER_LOW_FREQUENCY +
+ SPEAKER_BACK_LEFT + SPEAKER_BACK_RIGHT;
+ } else {
+ return 0;
+ }
+}
+
+static char *position2string(int position)
+{
+ switch (position)
+ {
+ case FRONT_CHANNEL_CENTER: return "Center front";
+ case FRONT_CHANNEL_LEFT: return "Left front";
+ case FRONT_CHANNEL_RIGHT: return "Right front";
+ case SIDE_CHANNEL_LEFT: return "Left side";
+ case SIDE_CHANNEL_RIGHT: return "Right side";
+ case BACK_CHANNEL_LEFT: return "Left back";
+ case BACK_CHANNEL_RIGHT: return "Right back";
+ case BACK_CHANNEL_CENTER: return "Center back";
+ case LFE_CHANNEL: return "LFE";
+ case UNKNOWN_CHANNEL: return "Unknown";
+ default: return "";
+ }
+
+ // return "";
+}
+
+static void print_channel_info(NeAACDecFrameInfo *frameInfo)
+{
+ /* print some channel info */
+ int i;
+ long channelMask = aacChannelConfig2wavexChannelMask(frameInfo);
+
+ faad_fprintf(stderr, " ---------------------\n");
+ if (frameInfo->num_lfe_channels > 0)
+ {
+ faad_fprintf(stderr, " | Config: %2d.%d Ch |", frameInfo->channels-frameInfo->num_lfe_channels, frameInfo->num_lfe_channels);
+ } else {
+ faad_fprintf(stderr, " | Config: %2d Ch |", frameInfo->channels);
+ }
+ if (channelMask)
+ faad_fprintf(stderr, " WARNING: channels are reordered according to\n");
+ else
+ faad_fprintf(stderr, "\n");
+ faad_fprintf(stderr, " ---------------------");
+ if (channelMask)
+ faad_fprintf(stderr, " MS defaults defined in WAVE_FORMAT_EXTENSIBLE\n");
+ else
+ faad_fprintf(stderr, "\n");
+ faad_fprintf(stderr, " | Ch | Position |\n");
+ faad_fprintf(stderr, " ---------------------\n");
+ for (i = 0; i < frameInfo->channels; i++)
+ {
+ faad_fprintf(stderr, " | %.2d | %-14s |\n", i, position2string((int)frameInfo->channel_position[i]));
+ }
+ faad_fprintf(stderr, " ---------------------\n");
+ faad_fprintf(stderr, "\n");
+}
+
+static int FindAdtsSRIndex(int sr)
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ {
+ if (sr == adts_sample_rates[i])
+ return i;
+ }
+ return 16 - 1;
+}
+
+static unsigned char *MakeAdtsHeader(int *dataSize, NeAACDecFrameInfo *hInfo, unsigned char old_format)
+{
+ unsigned char *data;
+ int profile = (hInfo->object_type - 1) & 0x3;
+ int sr_index = ((hInfo->sbr == SBR_UPSAMPLED) || (hInfo->sbr == NO_SBR_UPSAMPLED)) ?
+ FindAdtsSRIndex(hInfo->samplerate / 2) : FindAdtsSRIndex(hInfo->samplerate);
+ int skip = (old_format) ? 8 : 7;
+ int framesize = skip + hInfo->bytesconsumed;
+
+ if (hInfo->header_type == ADTS)
+ framesize -= skip;
+
+ *dataSize = 7;
+
+ data = malloc(*dataSize * sizeof(unsigned char));
+ memset(data, 0, *dataSize * sizeof(unsigned char));
+
+ data[0] += 0xFF; /* 8b: syncword */
+
+ data[1] += 0xF0; /* 4b: syncword */
+ /* 1b: mpeg id = 0 */
+ /* 2b: layer = 0 */
+ data[1] += 1; /* 1b: protection absent */
+
+ data[2] += ((profile << 6) & 0xC0); /* 2b: profile */
+ data[2] += ((sr_index << 2) & 0x3C); /* 4b: sampling_frequency_index */
+ /* 1b: private = 0 */
+ data[2] += ((hInfo->channels >> 2) & 0x1); /* 1b: channel_configuration */
+
+ data[3] += ((hInfo->channels << 6) & 0xC0); /* 2b: channel_configuration */
+ /* 1b: original */
+ /* 1b: home */
+ /* 1b: copyright_id */
+ /* 1b: copyright_id_start */
+ data[3] += ((framesize >> 11) & 0x3); /* 2b: aac_frame_length */
+
+ data[4] += ((framesize >> 3) & 0xFF); /* 8b: aac_frame_length */
+
+ data[5] += ((framesize << 5) & 0xE0); /* 3b: aac_frame_length */
+ data[5] += ((0x7FF >> 6) & 0x1F); /* 5b: adts_buffer_fullness */
+
+ data[6] += ((0x7FF << 2) & 0x3F); /* 6b: adts_buffer_fullness */
+ /* 2b: num_raw_data_blocks */
+
+ return data;
+}
+
+/* globals */
+char *progName;
+
+static const char *file_ext[] =
+{
+ NULL,
+ ".wav",
+ ".aif",
+ ".au",
+ ".au",
+ ".pcm",
+ NULL
+};
+
+static void usage(void)
+{
+ faad_fprintf(stdout, "\nUsage:\n");
+ faad_fprintf(stdout, "%s [options] infile.aac\n", progName);
+ faad_fprintf(stdout, "Options:\n");
+ faad_fprintf(stdout, " -h Shows this help screen.\n");
+ faad_fprintf(stdout, " -i Shows info about the input file.\n");
+ faad_fprintf(stdout, " -a X Write MPEG-4 AAC ADTS output file.\n");
+ faad_fprintf(stdout, " -t Assume old ADTS format.\n");
+ faad_fprintf(stdout, " -o X Set output filename.\n");
+ faad_fprintf(stdout, " -f X Set output format. Valid values for X are:\n");
+ faad_fprintf(stdout, " 1: Microsoft WAV format (default).\n");
+ faad_fprintf(stdout, " 2: RAW PCM data.\n");
+ faad_fprintf(stdout, " -b X Set output sample format. Valid values for X are:\n");
+ faad_fprintf(stdout, " 1: 16 bit PCM data (default).\n");
+ faad_fprintf(stdout, " 2: 24 bit PCM data.\n");
+ faad_fprintf(stdout, " 3: 32 bit PCM data.\n");
+ faad_fprintf(stdout, " 4: 32 bit floating point data.\n");
+ faad_fprintf(stdout, " 5: 64 bit floating point data.\n");
+ faad_fprintf(stdout, " -s X Force the samplerate to X (for RAW files).\n");
+ faad_fprintf(stdout, " -l X Set object type. Supported object types:\n");
+ faad_fprintf(stdout, " 1: Main object type.\n");
+ faad_fprintf(stdout, " 2: LC (Low Complexity) object type.\n");
+ faad_fprintf(stdout, " 4: LTP (Long Term Prediction) object type.\n");
+ faad_fprintf(stdout, " 23: LD (Low Delay) object type.\n");
+ faad_fprintf(stdout, " -d Down matrix 5.1 to 2 channels\n");
+ faad_fprintf(stdout, " -w Write output to stdio instead of a file.\n");
+ faad_fprintf(stdout, " -g Disable gapless decoding.\n");
+ faad_fprintf(stdout, " -q Quiet - suppresses status messages.\n");
+ faad_fprintf(stdout, " -j X Jump - start output X seconds into track (MP4 files only).\n");
+ faad_fprintf(stdout, "Example:\n");
+ faad_fprintf(stdout, " %s infile.aac\n", progName);
+ faad_fprintf(stdout, " %s infile.mp4\n", progName);
+ faad_fprintf(stdout, " %s -o outfile.wav infile.aac\n", progName);
+ faad_fprintf(stdout, " %s -w infile.aac > outfile.wav\n", progName);
+ faad_fprintf(stdout, " %s -a outfile.aac infile.aac\n", progName);
+ return;
+}
+
+static int decodeAACfile(char *aacfile, char *sndfile, char *adts_fn, int to_stdout,
+ int def_srate, unsigned char object_type, unsigned char outputFormat, int fileType,
+ unsigned char downMatrix, int infoOnly, int adts_out, unsigned char old_format,
+ float *song_length)
+{
+ int tagsize;
+ unsigned long samplerate;
+ unsigned char channels;
+ void *sample_buffer;
+
+ audio_file *aufile = NULL;
+
+ FILE *adtsFile = NULL;
+ unsigned char *adtsData;
+ int adtsDataSize;
+
+ NeAACDecHandle hDecoder;
+ NeAACDecFrameInfo frameInfo;
+ NeAACDecConfigurationPtr config;
+
+ char percents[MAX_PERCENTS];
+ int percent, old_percent = -1;
+ int fileread;
+ unsigned long bread;
+ long bused;
+ int header_type = 0;
+ int bitrate = 0;
+ float length = 0;
+
+ int first_time = 1;
+ int retval;
+ int streaminput = 0;
+
+ aac_buffer b;
+
+ memset(&b, 0, sizeof(aac_buffer));
+
+ if (adts_out)
+ {
+ adtsFile = faad_fopen(adts_fn, "wb");
+ if (adtsFile == NULL)
+ {
+ faad_fprintf(stderr, "Error opening file: %s\n", adts_fn);
+ return 1;
+ }
+ }
+
+ if (0 == strcmp(aacfile, "-"))
+ {
+ b.infile = stdin;
+#ifdef _WIN32
+ _setmode(_fileno(stdin), O_BINARY);
+#endif
+
+ }
+ else
+ {
+ b.infile = faad_fopen(aacfile, "rb");
+ if (b.infile == NULL)
+ {
+ /* unable to open file */
+ faad_fprintf(stderr, "Error opening file: %s\n", aacfile);
+ return 1;
+ }
+ }
+
+ retval = fseek(b.infile, 0, SEEK_END);
+#ifdef _WIN32
+ if (0 == strcmp(aacfile, "-")) {
+ retval = -1;
+ }
+#endif
+ if (retval )
+ {
+ faad_fprintf(stderr, "Input not seekable %s\n", aacfile);
+ fileread = -1;
+ streaminput = 1;
+ } else {
+ fileread = ftell(b.infile);
+ fseek(b.infile, 0, SEEK_SET);
+ };
+
+ b.buffer = (unsigned char*)malloc(FAAD_MIN_STREAMSIZE*MAX_CHANNELS);
+ if (!b.buffer)
+ {
+ faad_fprintf(stderr, "Memory allocation error\n");
+ return 0;
+ }
+ memset(b.buffer, 0, FAAD_MIN_STREAMSIZE*MAX_CHANNELS);
+
+ bread = (unsigned long)fread(b.buffer, 1, FAAD_MIN_STREAMSIZE*MAX_CHANNELS, b.infile);
+ b.bytes_into_buffer = bread;
+ b.bytes_consumed = 0;
+ b.file_offset = 0;
+
+ if (bread != FAAD_MIN_STREAMSIZE*MAX_CHANNELS)
+ b.at_eof = 1;
+
+ tagsize = 0;
+ if (!memcmp(b.buffer, "ID3", 3))
+ {
+ /* high bit is not used */
+ tagsize = (b.buffer[6] << 21) | (b.buffer[7] << 14) |
+ (b.buffer[8] << 7) | (b.buffer[9] << 0);
+
+ tagsize += 10;
+ advance_buffer(&b, tagsize);
+ fill_buffer(&b);
+ }
+
+ hDecoder = NeAACDecOpen();
+
+ /* Set the default object type and samplerate */
+ /* This is useful for RAW AAC files */
+ config = NeAACDecGetCurrentConfiguration(hDecoder);
+ if (def_srate)
+ config->defSampleRate = def_srate;
+ config->defObjectType = object_type;
+ config->outputFormat = outputFormat;
+ config->downMatrix = downMatrix;
+ config->useOldADTSFormat = old_format;
+ //config->dontUpSampleImplicitSBR = 1;
+ NeAACDecSetConfiguration(hDecoder, config);
+
+ /* get AAC infos for printing */
+ header_type = 0;
+ if (streaminput == 1)
+ lookforheader(&b);
+
+ if ((b.buffer[0] == 0xFF) && ((b.buffer[1] & 0xF6) == 0xF0))
+ {
+ if (streaminput == 1)
+ {
+ int /*frames,*/ frame_length;
+ float frames_per_sec, bytes_per_frame;
+ channels = 2;
+ samplerate = adts_sample_rates[(b.buffer[2]&0x3c)>>2];
+ frame_length = ((((unsigned int)b.buffer[3] & 0x3)) << 11)
+ | (((unsigned int)b.buffer[4]) << 3) | (b.buffer[5] >> 5);
+ frames_per_sec = (float)samplerate/1024.0f;
+ bytes_per_frame = (float)frame_length/(float)(1000);
+ bitrate = (int)(8. * bytes_per_frame * frames_per_sec + 0.5);
+ length = 1;
+ faad_fprintf(stderr, "Streamed input format samplerate %d channels %d.\n", samplerate, channels);
+ } else {
+ adts_parse(&b, &bitrate, &length);
+ fseek(b.infile, tagsize, SEEK_SET);
+
+ bread = (unsigned long)fread(b.buffer, 1, FAAD_MIN_STREAMSIZE*MAX_CHANNELS, b.infile);
+ if (bread != FAAD_MIN_STREAMSIZE*MAX_CHANNELS)
+ b.at_eof = 1;
+ else
+ b.at_eof = 0;
+ b.bytes_into_buffer = bread;
+ b.bytes_consumed = 0;
+ b.file_offset = tagsize;
+ }
+
+ header_type = 1;
+ }
+ else if (memcmp(b.buffer, "ADIF", 4) == 0)
+ {
+ int skip_size = (b.buffer[4] & 0x80) ? 9 : 0;
+ bitrate = ((unsigned int)(b.buffer[4 + skip_size] & 0x0F)<<19) |
+ ((unsigned int)b.buffer[5 + skip_size]<<11) |
+ ((unsigned int)b.buffer[6 + skip_size]<<3) |
+ ((unsigned int)b.buffer[7 + skip_size] & 0xE0);
+
+ length = (float)fileread;
+ if (length != 0)
+ {
+ length = ((float)length*8.f)/((float)bitrate) + 0.5f;
+ }
+
+ bitrate = (int)((float)bitrate/1000.0f + 0.5f);
+
+ header_type = 2;
+ }
+
+ *song_length = length;
+
+ fill_buffer(&b);
+ bused = NeAACDecInit(hDecoder, b.buffer, b.bytes_into_buffer, &samplerate, &channels);
+ if (bused < 0)
+ {
+ /* If some error initializing occured, skip the file */
+ faad_fprintf(stderr, "Error initializing decoder library.\n");
+ if (b.buffer)
+ free(b.buffer);
+ NeAACDecClose(hDecoder);
+ if (b.infile != stdin)
+ fclose(b.infile);
+ return 1;
+ }
+ advance_buffer(&b, bused);
+ fill_buffer(&b);
+
+ /* print AAC file info */
+ faad_fprintf(stderr, "%s file info:\n", aacfile);
+ switch (header_type)
+ {
+ case 0:
+ faad_fprintf(stderr, "RAW\n\n");
+ break;
+ case 1:
+ faad_fprintf(stderr, "ADTS, %.3f sec, %d kbps, %d Hz\n\n",
+ length, bitrate, samplerate);
+ break;
+ case 2:
+ faad_fprintf(stderr, "ADIF, %.3f sec, %d kbps, %d Hz\n\n",
+ length, bitrate, samplerate);
+ break;
+ }
+
+ // Override the logic of skipping 0-th output frame.
+ NeAACDecPostSeekReset(hDecoder, 1);
+
+ if (infoOnly)
+ {
+ NeAACDecClose(hDecoder);
+ if (b.infile != stdin)
+ fclose(b.infile);
+ if (b.buffer)
+ free(b.buffer);
+ return 0;
+ }
+
+ do
+ {
+ sample_buffer = NeAACDecDecode(hDecoder, &frameInfo,
+ b.buffer, b.bytes_into_buffer);
+
+ if (adts_out == 1)
+ {
+ int skip = (old_format) ? 8 : 7;
+ adtsData = MakeAdtsHeader(&adtsDataSize, &frameInfo, old_format);
+
+ /* write the adts header */
+ fwrite(adtsData, 1, adtsDataSize, adtsFile);
+
+ /* write the frame data */
+ if (frameInfo.header_type == ADTS)
+ fwrite(b.buffer + skip, 1, frameInfo.bytesconsumed - skip, adtsFile);
+ else
+ fwrite(b.buffer, 1, frameInfo.bytesconsumed, adtsFile);
+ }
+
+ /* update buffer indices */
+ advance_buffer(&b, frameInfo.bytesconsumed);
+
+ /* check if the inconsistent number of channels */
+ if (aufile != NULL && frameInfo.channels != aufile->channels)
+ frameInfo.error = 12;
+
+ if (frameInfo.error > 0)
+ {
+ faad_fprintf(stderr, "Error: %s\n",
+ NeAACDecGetErrorMessage(frameInfo.error));
+ }
+
+ /* open the sound file now that the number of channels are known */
+ if (first_time && !frameInfo.error)
+ {
+ /* print some channel info */
+ print_channel_info(&frameInfo);
+
+ if (!adts_out)
+ {
+ /* open output file */
+ if (!to_stdout)
+ {
+ aufile = open_audio_file(sndfile, frameInfo.samplerate, frameInfo.channels,
+ (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo));
+ } else {
+ aufile = open_audio_file("-", frameInfo.samplerate, frameInfo.channels,
+ (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo));
+ }
+ if (aufile == NULL)
+ {
+ if (b.buffer)
+ free(b.buffer);
+ NeAACDecClose(hDecoder);
+ if (b.infile != stdin)
+ fclose(b.infile);
+ return 0;
+ }
+ } else {
+ faad_fprintf(stderr, "Writing output MPEG-4 AAC ADTS file.\n\n");
+ }
+ first_time = 0;
+ }
+
+ percent = min((int)(b.file_offset*100)/fileread, 100);
+ if (percent > old_percent)
+ {
+ old_percent = percent;
+ snprintf(percents, MAX_PERCENTS, "%d%% decoding %s.", percent, aacfile);
+ faad_fprintf(stderr, "%s\r", percents);
+#ifdef _WIN32
+ SetConsoleTitle(percents);
+#endif
+ }
+
+ if ((frameInfo.error == 0) && (frameInfo.samples > 0) && (!adts_out))
+ {
+ if (write_audio_file(aufile, sample_buffer, frameInfo.samples) == 0)
+ break;
+ }
+
+ /* fill buffer */
+ fill_buffer(&b);
+
+ if (b.bytes_into_buffer == 0)
+ sample_buffer = NULL; /* to make sure it stops now */
+
+ } while (sample_buffer != NULL);
+
+ NeAACDecClose(hDecoder);
+
+ if (adts_out == 1)
+ {
+ fclose(adtsFile);
+ }
+
+ if (b.infile != stdin)
+ fclose(b.infile);
+
+ if (!first_time && !adts_out)
+ close_audio_file(aufile);
+
+ if (b.buffer)
+ free(b.buffer);
+
+ return frameInfo.error;
+}
+
+static int decodeMP4file(char *mp4file, char *sndfile, char *adts_fn, int to_stdout,
+ unsigned char outputFormat, int fileType, unsigned char downMatrix, int noGapless,
+ int infoOnly, int adts_out, float *song_length, float seek_to)
+{
+ /*int track;*/
+ unsigned long samplerate;
+ unsigned char channels;
+ void *sample_buffer;
+
+ uint32_t sampleId, startSampleId;
+
+ audio_file *aufile = NULL;
+
+ FILE *adtsFile = NULL;
+ unsigned char *adtsData;
+ int adtsDataSize;
+
+ NeAACDecHandle hDecoder;
+ NeAACDecConfigurationPtr config;
+ NeAACDecFrameInfo frameInfo = {0}; // should no-frames situation be flagged as an error?
+ mp4AudioSpecificConfig mp4ASC = {0};
+
+ char percents[MAX_PERCENTS];
+ int percent, old_percent = -1;
+
+ int first_time = 1;
+
+ /* for gapless decoding */
+ unsigned int useAacLength = 1;
+ unsigned int framesize;
+ unsigned decoded;
+
+ if (strcmp(mp4file, "-") == 0 ) {
+ faad_fprintf(stderr, "Cannot open stdin for MP4 input \n");
+ return 1;
+ }
+
+ if (!quiet)
+ {
+ mp4config.verbose.header = 1;
+ mp4config.verbose.tags = 1;
+ }
+ if (mp4read_open(mp4file))
+ {
+ /* unable to open file */
+ faad_fprintf(stderr, "Error opening file: %s\n", mp4file);
+ return 1;
+ }
+
+ hDecoder = NeAACDecOpen();
+
+ /* Set configuration */
+ config = NeAACDecGetCurrentConfiguration(hDecoder);
+ config->outputFormat = outputFormat;
+ config->downMatrix = downMatrix;
+ //config->dontUpSampleImplicitSBR = 1;
+ NeAACDecSetConfiguration(hDecoder, config);
+
+ if (adts_out)
+ {
+ adtsFile = faad_fopen(adts_fn, "wb");
+ if (adtsFile == NULL)
+ {
+ faad_fprintf(stderr, "Error opening file: %s\n", adts_fn);
+ return 1;
+ }
+ }
+
+ if(NeAACDecInit2(hDecoder, mp4config.asc.buf, mp4config.asc.size,
+ &samplerate, &channels) < 0)
+ {
+ /* If some error initializing occured, skip the file */
+ faad_fprintf(stderr, "Error initializing decoder library.\n");
+ NeAACDecClose(hDecoder);
+ mp4read_close();
+ return 1;
+ }
+
+ framesize = 1024;
+ useAacLength = 0;
+ decoded = 0;
+
+ if (mp4config.asc.size)
+ {
+ if (NeAACDecAudioSpecificConfig(mp4config.asc.buf, mp4config.asc.size, &mp4ASC) >= 0)
+ {
+ if (mp4ASC.frameLengthFlag == 1) framesize = 960;
+ if (mp4ASC.sbr_present_flag == 1 || mp4ASC.forceUpSampling) framesize *= 2;
+ }
+ }
+
+ /* print some mp4 file info */
+ faad_fprintf(stderr, "%s file info:\n\n", mp4file);
+ {
+ /*int k, j;*/
+ char *ot[6] = { "NULL", "MAIN AAC", "LC AAC", "SSR AAC", "LTP AAC", "HE AAC" };
+ float seconds;
+ seconds = (float)mp4config.samples/(float)mp4ASC.samplingFrequency;
+
+ *song_length = seconds;
+
+ faad_fprintf(stderr, "%s\t%.3f secs, %d ch, %d Hz\n\n", ot[(mp4ASC.objectTypeIndex > 5)?0:mp4ASC.objectTypeIndex],
+ seconds, mp4ASC.channelsConfiguration, mp4ASC.samplingFrequency);
+ }
+
+ if (infoOnly)
+ {
+ NeAACDecClose(hDecoder);
+ mp4read_close();
+ return 0;
+ }
+
+ startSampleId = 0;
+ if (seek_to > 0.1) {
+ int64_t sample = (int64_t)(seek_to * mp4config.samplerate / framesize);
+ uint32_t limit = ~0u;
+ startSampleId = sample < limit ? (uint32_t)sample : limit;
+ }
+
+ mp4read_seek(startSampleId);
+ for (sampleId = startSampleId; sampleId < mp4config.frame.nsamples; sampleId++)
+ {
+ /*int rc;*/
+ unsigned long dur;
+ unsigned int sample_count;
+
+ if (mp4read_frame())
+ break;
+
+ sample_buffer = NeAACDecDecode(hDecoder, &frameInfo, mp4config.bitbuf.data, mp4config.bitbuf.size);
+
+ if (!sample_buffer) {
+ /* unable to decode file, abort */
+ break;
+ }
+
+ if (adts_out == 1)
+ {
+ adtsData = MakeAdtsHeader(&adtsDataSize, &frameInfo, 0);
+
+ /* write the adts header */
+ fwrite(adtsData, 1, adtsDataSize, adtsFile);
+
+ fwrite(mp4config.bitbuf.data, 1, frameInfo.bytesconsumed, adtsFile);
+ }
+
+ dur = frameInfo.samples / frameInfo.channels;
+ decoded += dur;
+
+ if (decoded > mp4config.samples)
+ dur += mp4config.samples - decoded;
+
+ if (dur > framesize)
+ {
+ faad_fprintf(stderr, "Warning: excess frame detected in MP4 file.\n");
+ dur = framesize;
+ }
+
+ if (!noGapless)
+ {
+ if (useAacLength || (mp4config.samplerate != samplerate)) {
+ sample_count = frameInfo.samples;
+ } else {
+ sample_count = (unsigned int)(dur * frameInfo.channels);
+ if (sample_count > frameInfo.samples)
+ sample_count = frameInfo.samples;
+ }
+ } else {
+ sample_count = frameInfo.samples;
+ }
+
+ /* open the sound file now that the number of channels are known */
+ if (first_time && !frameInfo.error)
+ {
+ /* print some channel info */
+ print_channel_info(&frameInfo);
+
+ if (!adts_out)
+ {
+ /* open output file */
+ if(!to_stdout)
+ {
+ aufile = open_audio_file(sndfile, frameInfo.samplerate, frameInfo.channels,
+ (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo));
+ } else {
+#ifdef _WIN32
+ _setmode(_fileno(stdout), O_BINARY);
+#endif
+ aufile = open_audio_file("-", frameInfo.samplerate, frameInfo.channels,
+ (int)outputFormat, fileType, aacChannelConfig2wavexChannelMask(&frameInfo));
+ }
+ if (aufile == NULL)
+ {
+ NeAACDecClose(hDecoder);
+ mp4read_close();
+ return 0;
+ }
+ }
+ first_time = 0;
+ }
+
+ percent = min((int)(sampleId*100)/mp4config.frame.nsamples, 100);
+ if (percent > old_percent)
+ {
+ old_percent = percent;
+ snprintf(percents, MAX_PERCENTS, "%d%% decoding %s.", percent, mp4file);
+ faad_fprintf(stderr, "%s\r", percents);
+#ifdef _WIN32
+ SetConsoleTitle(percents);
+#endif
+ }
+
+ if ((frameInfo.error == 0) && (sample_count > 0) && (!adts_out))
+ {
+ if (write_audio_file(aufile, sample_buffer, sample_count) == 0)
+ break;
+ }
+
+ if (frameInfo.error > 0)
+ {
+ faad_fprintf(stderr, "Warning: %s\n",
+ NeAACDecGetErrorMessage(frameInfo.error));
+ }
+ }
+
+ NeAACDecClose(hDecoder);
+
+ if (adts_out == 1)
+ {
+ fclose(adtsFile);
+ }
+
+ mp4read_close();
+
+ if (!first_time && !adts_out)
+ close_audio_file(aufile);
+
+ return frameInfo.error;
+}
+
+static int faad_main(int argc, char *argv[])
+{
+ int result;
+ int infoOnly = 0;
+ int writeToStdio = 0;
+ int readFromStdin = 0;
+ unsigned char object_type = LC;
+ int def_srate = 0;
+ unsigned char downMatrix = 0;
+ int format = 1;
+ unsigned char outputFormat = FAAD_FMT_16BIT;
+ int outfile_set = 0;
+ int adts_out = 0;
+ unsigned char old_format = 0;
+ int showHelp = 0;
+ int mp4file = 0;
+ int noGapless = 0;
+ char *fnp;
+ char *aacFileName = NULL;
+ char *audioFileName = NULL;
+ char *adtsFileName = NULL;
+ float seekTo = 0;
+ unsigned char header[8];
+ size_t bread;
+ float length = 0;
+ FILE *hMP4File;
+ char *faad_id_string;
+ char *faad_copyright_string;
+
+/* System dependant types */
+#ifdef _WIN32
+ long begin;
+#else
+ clock_t begin;
+#endif
+
+ unsigned long cap = NeAACDecGetCapabilities();
+
+
+ /* begin process command line */
+ progName = argv[0];
+ while (1) {
+ int c = -1;
+ int option_index = 0;
+ static struct option long_options[] = {
+ { "quiet", 0, 0, 'q' },
+ { "outfile", 0, 0, 'o' },
+ { "adtsout", 0, 0, 'a' },
+ { "oldformat", 0, 0, 't' },
+ { "format", 0, 0, 'f' },
+ { "bits", 0, 0, 'b' },
+ { "samplerate", 0, 0, 's' },
+ { "objecttype", 0, 0, 'l' },
+ { "downmix", 0, 0, 'd' },
+ { "info", 0, 0, 'i' },
+ { "stdio", 0, 0, 'w' },
+ { "stdio", 0, 0, 'g' },
+ { "seek", 1, 0, 'j' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+ };
+
+ c = getopt_long(argc, argv, "o:a:s:f:b:l:j:wgdhitq",
+ long_options, &option_index);
+
+ if (c == -1)
+ break;
+
+ switch (c) {
+ case 'o':
+ if (optarg)
+ {
+ outfile_set = 1;
+ audioFileName = (char *) malloc(sizeof(char) * (strlen(optarg) + 1));
+ if (audioFileName == NULL)
+ {
+ faad_fprintf(stderr, "Error allocating memory for audioFileName.\n");
+ return 1;
+ }
+ strcpy(audioFileName, optarg);
+ }
+ break;
+ case 'a':
+ if (optarg)
+ {
+ adts_out = 1;
+ adtsFileName = (char *) malloc(sizeof(char) * (strlen(optarg) + 1));
+ if (adtsFileName == NULL)
+ {
+ faad_fprintf(stderr, "Error allocating memory for adtsFileName.\n");
+ return 1;
+ }
+ strcpy(adtsFileName, optarg);
+ }
+ break;
+ case 's':
+ if (optarg)
+ {
+ char dr[10];
+ if (sscanf(optarg, "%s", dr) < 1) {
+ def_srate = 0;
+ } else {
+ def_srate = atoi(dr);
+ }
+ }
+ break;
+ case 'f':
+ if (optarg)
+ {
+ char dr[10];
+ if (sscanf(optarg, "%s", dr) < 1)
+ {
+ format = 1;
+ } else {
+ format = atoi(dr);
+ if ((format < 1) || (format > 2))
+ showHelp = 1;
+ }
+ }
+ break;
+ case 'b':
+ if (optarg)
+ {
+ char dr[10];
+ if (sscanf(optarg, "%s", dr) < 1)
+ {
+ outputFormat = FAAD_FMT_16BIT; /* just use default */
+ } else {
+ int val = atoi(dr);
+ if ((val < 1) || (val > 5))
+ showHelp = 1;
+ if (val == 5) // Not yet unsupported by "audio".
+ showHelp = 1;
+ outputFormat = (unsigned char)val;
+ }
+ }
+ break;
+ case 'l':
+ if (optarg)
+ {
+ char dr[10];
+ if (sscanf(optarg, "%s", dr) < 1)
+ {
+ object_type = LC; /* default */
+ } else {
+ int val = atoi(dr);
+ if ((val != LC) &&
+ (val != MAIN) &&
+ (val != LTP) &&
+ (val != LD))
+ {
+ showHelp = 1;
+ }
+ object_type = (unsigned char)val;
+ }
+ }
+ break;
+ case 'j':
+ if (optarg)
+ {
+ seekTo = (float)atof(optarg);
+ }
+ break;
+ case 't':
+ old_format = 1;
+ break;
+ case 'd':
+ downMatrix = 1;
+ break;
+ case 'w':
+ writeToStdio = 1;
+ break;
+ case 'g':
+ noGapless = 1;
+ break;
+ case 'i':
+ infoOnly = 1;
+ break;
+ case 'h':
+ showHelp = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ default:
+ break;
+ }
+ }
+
+ NeAACDecGetVersion(&faad_id_string, &faad_copyright_string);
+
+ faad_fprintf(stderr, " *********** Ahead Software MPEG-4 AAC Decoder V%s ******************\n\n", faad_id_string);
+#ifndef BUILD_DATE
+#define BUILD_DATE __DATE__
+#endif
+ faad_fprintf(stderr, " Build: %s\n", BUILD_DATE);
+#undef BUILD_DATE
+ faad_fprintf(stderr, "%s", faad_copyright_string);
+ if (cap & FIXED_POINT_CAP)
+ faad_fprintf(stderr, " Fixed point version\n");
+ else
+ faad_fprintf(stderr, " Floating point version\n");
+ faad_fprintf(stderr, "\n");
+ faad_fprintf(stderr, " This program is free software; you can redistribute it and/or modify\n");
+ faad_fprintf(stderr, " it under the terms of the GNU General Public License.\n");
+ faad_fprintf(stderr, "\n");
+ faad_fprintf(stderr, " **************************************************************************\n\n");
+
+
+ /* check that we have at least two non-option arguments */
+ /* Print help if requested */
+ if (((argc - optind) < 1) || showHelp)
+ {
+ usage();
+ return 1;
+ }
+
+#if 0
+ /* only allow raw data on stdio */
+ if (writeToStdio == 1)
+ {
+ format = 2;
+ }
+#endif
+
+ /* point to the specified file name */
+ aacFileName = (char *) malloc(sizeof(char) * (strlen(argv[optind]) + 1));
+ if (aacFileName == NULL)
+ {
+ faad_fprintf(stderr, "Error allocating memory for aacFileName.\n");
+ return 1;
+ }
+ strcpy(aacFileName, argv[optind]);
+
+#ifdef _WIN32
+ begin = GetTickCount();
+#else
+ begin = clock();
+#endif
+
+ /* Only calculate the path and open the file for writing if
+ we are not writing to stdout.
+ */
+ if(!writeToStdio && !outfile_set)
+ {
+ audioFileName = (char *) malloc(sizeof(char) * (strlen(aacFileName) + strlen(file_ext[format]) + 1));
+ if (audioFileName == NULL)
+ {
+ faad_fprintf(stderr, "Error allocating memory for audioFileName.\n");
+ return 1;
+ }
+ strcpy(audioFileName, aacFileName);
+
+ fnp = (char *)strrchr(audioFileName,'.');
+
+ if (fnp)
+ fnp[0] = '\0';
+
+ strcat(audioFileName, file_ext[format]);
+ }
+
+ /* check for mp4 file */
+ if (0 == strcmp(aacFileName, "-")) {
+ faad_fprintf(stderr, "Reading from stdin: %s\n", aacFileName);
+ readFromStdin = 1;
+ hMP4File = stdin;
+#ifdef _WIN32
+ _setmode(_fileno(stdin), O_BINARY);
+#endif
+
+ } else {
+
+ mp4file = 0;
+ hMP4File = faad_fopen(aacFileName, "rb");
+ if (!hMP4File)
+ {
+ faad_fprintf(stderr, "Error opening file: %s\n", aacFileName);
+ return 1;
+ }
+ }
+
+ bread = fread(header, 1, 8, hMP4File);
+
+ if (! readFromStdin )
+ fclose(hMP4File);
+
+ if (bread != 8) {
+ faad_fprintf(stderr, "Error reading file.\n");
+ return 1;
+ }
+
+ if (header[4] == 'f' && header[5] == 't' && header[6] == 'y' && header[7] == 'p')
+ mp4file = 1;
+
+ if (!mp4file && seekTo != 0) {
+ faad_fprintf(stderr, "Warning: can only seek in MP4 files\n");
+ }
+
+ if (mp4file)
+ {
+ result = decodeMP4file(aacFileName, audioFileName, adtsFileName, writeToStdio,
+ outputFormat, format, downMatrix, noGapless, infoOnly, adts_out, &length, seekTo);
+ } else {
+
+ if (readFromStdin == 1) {
+ ungetc(header[7],hMP4File);
+ ungetc(header[6],hMP4File);
+ ungetc(header[5],hMP4File);
+ ungetc(header[4],hMP4File);
+ ungetc(header[3],hMP4File);
+ ungetc(header[2],hMP4File);
+ ungetc(header[1],hMP4File);
+ ungetc(header[0],hMP4File);
+ }
+
+ result = decodeAACfile(aacFileName, audioFileName, adtsFileName, writeToStdio,
+ def_srate, object_type, outputFormat, format, downMatrix, infoOnly, adts_out,
+ old_format, &length);
+ }
+
+ if (audioFileName != NULL)
+ free (audioFileName);
+ if (adtsFileName != NULL)
+ free (adtsFileName);
+
+ if (!result && !infoOnly)
+ {
+#ifdef _WIN32
+ float dec_length = (float)(GetTickCount()-begin)/1000.0f;
+ SetConsoleTitle("FAAD");
+#else
+ /* clock() grabs time since the start of the app but when we decode
+ multiple files, each file has its own starttime (begin).
+ */
+ float dec_length = (float)(clock() - begin)/(float)CLOCKS_PER_SEC;
+#endif
+ faad_fprintf(stderr, "Decoding %s took: %5.2f sec. %5.2fx real-time.\n", aacFileName,
+ dec_length, (dec_length > 0.01) ? (length/dec_length) : 0.);
+ }
+
+ free (aacFileName);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+ int argc_utf8, exit_code;
+ char **argv_utf8;
+ (void)argc;
+ (void)argv;
+ init_console_utf8(stderr);
+ init_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+ exit_code = faad_main(argc_utf8, argv_utf8);
+ free_commandline_arguments_utf8(&argc_utf8, &argv_utf8);
+ uninit_console_utf8();
+ return exit_code;
+#else
+ return faad_main(argc, argv);
+#endif
+}
diff --git a/lib/faad2/frontend/mp4read.c b/lib/faad2/frontend/mp4read.c
new file mode 100644
index 00000000..a157d7d6
--- /dev/null
+++ b/lib/faad2/frontend/mp4read.c
@@ -0,0 +1,1109 @@
+/****************************************************************************
+ MP4 input module
+
+ Copyright (C) 2017 Krzysztof Nikiel
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+
+#define _CRT_SECURE_NO_WARNINGS
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <time.h>
+#include <limits.h>
+
+#include "unicode_support.h"
+#include "mp4read.h"
+
+enum ATOM_TYPE
+{
+ ATOM_STOP = 0 /* end of atoms */ ,
+ ATOM_NAME /* plain atom */ ,
+ ATOM_DESCENT, /* starts group of children */
+ ATOM_ASCENT, /* ends group */
+ ATOM_DATA,
+};
+
+typedef int (*parse_t)(int);
+
+typedef struct
+{
+ uint16_t opcode;
+ const char *name;
+ parse_t parse;
+} creator_t;
+
+#define STOP() {ATOM_STOP, NULL, NULL}
+#define NAME(N) {ATOM_NAME, N, NULL}
+#define DESCENT() {ATOM_DESCENT, NULL, NULL}
+#define ASCENT() {ATOM_ASCENT, NULL, NULL}
+#define DATA(N, F) {ATOM_NAME, N, NULL}, {ATOM_DATA, NULL, F}
+
+mp4config_t mp4config = { 0 };
+
+static FILE *g_fin = NULL;
+
+enum {ERR_OK = 0, ERR_FAIL = -1, ERR_UNSUPPORTED = -2};
+
+#define freeMem(A) if (*(A)) {free(*(A)); *(A) = NULL;}
+
+static size_t datain(void *data, size_t size)
+{
+ return fread(data, 1, size, g_fin);
+}
+
+static int stringin(char *txt, int sizemax)
+{
+ int size;
+ for (size = 0; size < sizemax; size++)
+ {
+ if (fread(txt + size, 1, 1, g_fin) != 1)
+ return ERR_FAIL;
+ if (!txt[size])
+ break;
+ }
+ txt[sizemax-1] = '\0';
+
+ return size;
+}
+
+static uint32_t u32in(void)
+{
+ uint8_t u8[4];
+ datain(&u8, 4);
+ return (uint32_t)u8[3] | ((uint32_t)u8[2] << 8) | ((uint32_t)u8[1] << 16) | ((uint32_t)u8[0] << 24);
+}
+
+static uint16_t u16in(void)
+{
+ uint8_t u8[2];
+ datain(&u8, 2);
+ return (uint16_t)u8[1] | ((uint16_t)u8[0] << 8);
+}
+
+static int u8in(void)
+{
+ uint8_t u8;
+ datain(&u8, 1);
+ return u8;
+}
+
+static int ftypin(int size)
+{
+ enum {BUFSIZE = 40};
+ char buf[BUFSIZE];
+ uint32_t u32;
+
+ buf[4] = 0;
+ datain(buf, 4);
+ u32 = u32in();
+
+ if (mp4config.verbose.header)
+ fprintf(stderr, "Brand:\t\t\t%s(version %d)\n", buf, u32);
+
+ stringin(buf, BUFSIZE);
+
+ if (mp4config.verbose.header)
+ fprintf(stderr, "Compatible brands:\t%s\n", buf);
+
+ return size;
+}
+
+enum
+{ SECSINDAY = 24 * 60 * 60 };
+static char *mp4time(time_t t)
+{
+ int y;
+
+ // subtract some seconds from the start of 1904 to the start of 1970
+ for (y = 1904; y < 1970; y++)
+ {
+ t -= 365 * SECSINDAY;
+ if (!(y & 3))
+ t -= SECSINDAY;
+ }
+ return ctime(&t);
+}
+
+static int mdhdin(int size)
+{
+ // version/flags
+ u32in();
+ // Creation time
+ mp4config.ctime = u32in();
+ // Modification time
+ mp4config.mtime = u32in();
+ // Time scale
+ mp4config.samplerate = u32in();
+ // Duration
+ mp4config.samples = u32in();
+ // Language
+ u16in();
+ // pre_defined
+ u16in();
+
+ return size;
+}
+
+static int hdlr1in(int size)
+{
+ uint8_t buf[5];
+
+ buf[4] = 0;
+ // version/flags
+ u32in();
+ // pre_defined
+ u32in();
+ // Component subtype
+ datain(buf, 4);
+ if (mp4config.verbose.header)
+ fprintf(stderr, "*track media type: '%s': ", buf);
+ if (memcmp("soun", buf, 4))
+ {
+ if (mp4config.verbose.header)
+ fprintf(stderr, "unsupported, skipping\n");
+ return ERR_UNSUPPORTED;
+ }
+ else
+ {
+ if (mp4config.verbose.header)
+ fprintf(stderr, "OK\n");
+ }
+ // reserved
+ u32in();
+ u32in();
+ u32in();
+ // name
+ // null terminate
+ u8in();
+
+ return size;
+}
+
+static int stsdin(int size)
+{
+ // version/flags
+ u32in();
+ // Number of entries(one 'mp4a')
+ if (u32in() != 1) //fixme: error handling
+ return ERR_FAIL;
+
+ return size;
+}
+
+static int mp4ain(int size)
+{
+ // Reserved (6 bytes)
+ u32in();
+ u16in();
+ // Data reference index
+ u16in();
+ // Version
+ u16in();
+ // Revision level
+ u16in();
+ // Vendor
+ u32in();
+ // Number of channels
+ mp4config.channels = u16in();
+ // Sample size (bits)
+ mp4config.bits = u16in();
+ // Compression ID
+ u16in();
+ // Packet size
+ u16in();
+ // Sample rate (16.16)
+ // fractional framerate, probably not for audio
+ // rate integer part
+ u16in();
+ // rate reminder part
+ u16in();
+
+ return size;
+}
+
+
+static uint32_t getsize(void)
+{
+ int cnt;
+ uint32_t size = 0;
+ for (cnt = 0; cnt < 4; cnt++)
+ {
+ int tmp = u8in();
+
+ size <<= 7;
+ size |= (tmp & 0x7f);
+ if (!(tmp & 0x80))
+ break;
+ }
+ return size;
+}
+
+static int esdsin(int size)
+{
+ // descriptor tree:
+ // MP4ES_Descriptor
+ // MP4DecoderConfigDescriptor
+ // MP4DecSpecificInfoDescriptor
+ // MP4SLConfigDescriptor
+ enum
+ { TAG_ES = 3, TAG_DC = 4, TAG_DSI = 5, TAG_SLC = 6 };
+
+ // version/flags
+ u32in();
+ if (u8in() != TAG_ES)
+ return ERR_FAIL;
+ getsize();
+ // ESID
+ u16in();
+ // flags(url(bit 6); ocr(5); streamPriority (0-4)):
+ u8in();
+
+ if (u8in() != TAG_DC)
+ return ERR_FAIL;
+ getsize();
+ if (u8in() != 0x40) /* not MPEG-4 audio */
+ return ERR_FAIL;
+ // flags
+ u8in();
+ // buffer size (24 bits)
+ mp4config.buffersize = u16in() << 8;
+ mp4config.buffersize |= u8in();
+ // bitrate
+ mp4config.bitratemax = u32in();
+ mp4config.bitrateavg = u32in();
+
+ if (u8in() != TAG_DSI)
+ return ERR_FAIL;
+ mp4config.asc.size = getsize();
+ if (mp4config.asc.size > sizeof(mp4config.asc.buf))
+ return ERR_FAIL;
+ // get AudioSpecificConfig
+ datain(mp4config.asc.buf, mp4config.asc.size);
+
+ if (u8in() != TAG_SLC)
+ return ERR_FAIL;
+ getsize();
+ // "predefined" (no idea)
+ u8in();
+
+ return size;
+}
+
+/* stbl "Sample Table" layout:
+ * - stts "Time-to-Sample" - useless
+ * - stsc "Sample-to-Chunk" - condensed table chunk-to-num-samples
+ * - stsz "Sample Size" - size table
+ * - stco "Chunk Offset" - chunk starts
+ *
+ * When receiving stco we can combine stsc and stsz tables to produce final
+ * sample offsets.
+ */
+
+static int sttsin(int size)
+{
+ uint32_t ntts;
+
+ if (size < 8)
+ return ERR_FAIL;
+
+ // version/flags
+ u32in();
+ ntts = u32in();
+
+ if (ntts < 1)
+ return ERR_FAIL;
+
+ /* 2 x uint32_t per entry */
+ if (((size - 8u) / 8u) < ntts)
+ return ERR_FAIL;
+
+ return size;
+}
+
+static int stscin(int size)
+{
+ uint32_t i, tmp, firstchunk, prevfirstchunk, samplesperchunk;
+
+ if (size < 8)
+ return ERR_FAIL;
+
+ // version/flags
+ u32in();
+
+ mp4config.frame.nsclices = u32in();
+
+ tmp = sizeof(slice_info_t) * mp4config.frame.nsclices;
+ if (tmp < mp4config.frame.nsclices)
+ return ERR_FAIL;
+ mp4config.frame.map = malloc(tmp);
+ if (!mp4config.frame.map)
+ return ERR_FAIL;
+
+ /* 3 x uint32_t per entry */
+ if (((size - 8u) / 12u) < mp4config.frame.nsclices)
+ return ERR_FAIL;
+
+ prevfirstchunk = 0;
+ for (i = 0; i < mp4config.frame.nsclices; ++i) {
+ firstchunk = u32in();
+ samplesperchunk = u32in();
+ // id - unused
+ u32in();
+ if (firstchunk <= prevfirstchunk)
+ return ERR_FAIL;
+ if (samplesperchunk < 1)
+ return ERR_FAIL;
+ mp4config.frame.map[i].firstchunk = firstchunk;
+ mp4config.frame.map[i].samplesperchunk = samplesperchunk;
+ prevfirstchunk = firstchunk;
+ }
+
+ return size;
+}
+
+static int stszin(int size)
+{
+ uint32_t i, tmp;
+
+ if (size < 12)
+ return ERR_FAIL;
+
+ // version/flags
+ u32in();
+ // (uniform) Sample size
+ // TODO(eustas): add uniform sample size support?
+ u32in();
+ mp4config.frame.nsamples = u32in();
+
+ if (!mp4config.frame.nsamples)
+ return ERR_FAIL;
+
+ tmp = sizeof(frame_info_t) * mp4config.frame.nsamples;
+ if (tmp < mp4config.frame.nsamples)
+ return ERR_FAIL;
+ mp4config.frame.info = malloc(tmp);
+ if (!mp4config.frame.info)
+ return ERR_FAIL;
+
+ if ((size - 12u) / 4u < mp4config.frame.nsamples)
+ return ERR_FAIL;
+
+ for (i = 0; i < mp4config.frame.nsamples; i++)
+ {
+ mp4config.frame.info[i].len = u32in();
+ mp4config.frame.info[i].offset = 0;
+ if (mp4config.frame.maxsize < mp4config.frame.info[i].len)
+ mp4config.frame.maxsize = mp4config.frame.info[i].len;
+ }
+
+ return size;
+}
+
+static int stcoin(int size)
+{
+ uint32_t numchunks, chunkn, slicen, samplesleft, i, offset;
+ uint32_t nextoffset;
+
+ if (size < 8)
+ return ERR_FAIL;
+
+ // version/flags
+ u32in();
+
+ // Number of entries
+ numchunks = u32in();
+ if ((numchunks < 1) || ((numchunks + 1) == 0))
+ return ERR_FAIL;
+
+ if ((size - 8u) / 4u < numchunks)
+ return ERR_FAIL;
+
+ chunkn = 0;
+ samplesleft = 0;
+ slicen = 0;
+ offset = 0;
+
+ for (i = 0; i < mp4config.frame.nsamples; ++i) {
+ if (samplesleft == 0)
+ {
+ chunkn++;
+ if (chunkn > numchunks)
+ return ERR_FAIL;
+ if (slicen < mp4config.frame.nsclices &&
+ (slicen + 1) < mp4config.frame.nsclices) {
+ if (chunkn == mp4config.frame.map[slicen + 1].firstchunk)
+ slicen++;
+ }
+ samplesleft = mp4config.frame.map[slicen].samplesperchunk;
+ offset = u32in();
+ }
+ mp4config.frame.info[i].offset = offset;
+ nextoffset = offset + mp4config.frame.info[i].len;
+ if (nextoffset < offset)
+ return ERR_FAIL;
+ offset = nextoffset;
+ samplesleft--;
+ }
+
+ freeMem(&mp4config.frame.map);
+
+ return size;
+}
+
+#if 0
+static int tagtxt(char *tagname, const char *tagtxt)
+{
+ //int txtsize = strlen(tagtxt);
+ int size = 0;
+ //int datasize = txtsize + 16;
+
+#if 0
+ size += u32out(datasize + 8);
+ size += dataout(tagname, 4);
+ size += u32out(datasize);
+ size += dataout("data", 4);
+ size += u32out(1);
+ size += u32out(0);
+ size += dataout(tagtxt, txtsize);
+#endif
+
+ return size;
+}
+
+static int tagu32(char *tagname, int n /*number of stored fields*/)
+{
+ //int numsize = n * 4;
+ int size = 0;
+ //int datasize = numsize + 16;
+
+#if 0
+ size += u32out(datasize + 8);
+ size += dataout(tagname, 4);
+ size += u32out(datasize);
+ size += dataout("data", 4);
+ size += u32out(0);
+ size += u32out(0);
+#endif
+
+ return size;
+}
+#endif
+
+static int metain(int size)
+{
+ (void)size; /* why not used? */
+ // version/flags
+ u32in();
+
+ return ERR_OK;
+}
+
+static int hdlr2in(int size)
+{
+ uint8_t buf[4];
+
+ // version/flags
+ u32in();
+ // Predefined
+ u32in();
+ // Handler type
+ datain(buf, 4);
+ if (memcmp(buf, "mdir", 4))
+ return ERR_FAIL;
+ datain(buf, 4);
+ if (memcmp(buf, "appl", 4))
+ return ERR_FAIL;
+ // Reserved
+ u32in();
+ u32in();
+ // null terminator
+ u8in();
+
+ return size;
+}
+
+static int ilstin(int size)
+{
+ enum {NUMSET = 1, GENRE, EXTAG};
+ int read = 0;
+
+ static struct {
+ char *name;
+ char *id;
+ int flag;
+ } tags[] = {
+ {"Album ", "\xa9" "alb"},
+ {"Album Artist", "aART"},
+ {"Artist ", "\xa9" "ART"},
+ {"Comment ", "\xa9" "cmt"},
+ {"Cover image ", "covr"},
+ {"Compilation ", "cpil"},
+ {"Copyright ", "cprt"},
+ {"Date ", "\xa9" "day"},
+ {"Disc# ", "disk", NUMSET},
+ {"Genre ", "gnre", GENRE},
+ {"Grouping ", "\xa9" "grp"},
+ {"Lyrics ", "\xa9" "lyr"},
+ {"Title ", "\xa9" "nam"},
+ {"Rating ", "rtng"},
+ {"BPM ", "tmpo"},
+ {"Encoder ", "\xa9" "too"},
+ {"Track ", "trkn", NUMSET},
+ {"Composer ", "\xa9" "wrt"},
+ {0, "----", EXTAG},
+ {0},
+ };
+
+ static const char *genres[] = {
+ "Blues", "Classic Rock", "Country", "Dance",
+ "Disco", "Funk", "Grunge", "Hip-Hop",
+ "Jazz", "Metal", "New Age", "Oldies",
+ "Other", "Pop", "R&B", "Rap",
+ "Reggae", "Rock", "Techno", "Industrial",
+ "Alternative", "Ska", "Death Metal", "Pranks",
+ "Soundtrack", "Euro-Techno", "Ambient", "Trip-Hop",
+ "Vocal", "Jazz+Funk", "Fusion", "Trance",
+ "Classical", "Instrumental", "Acid", "House",
+ "Game", "Sound Clip", "Gospel", "Noise",
+ "Alternative Rock", "Bass", "Soul", "Punk",
+ "Space", "Meditative", "Instrumental Pop", "Instrumental Rock",
+ "Ethnic", "Gothic", "Darkwave", "Techno-Industrial",
+ "Electronic", "Pop-Folk", "Eurodance", "Dream",
+ "Southern Rock", "Comedy", "Cult", "Gangsta",
+ "Top 40", "Christian Rap", "Pop/Funk", "Jungle",
+ "Native US", "Cabaret", "New Wave", "Psychadelic",
+ "Rave", "Showtunes", "Trailer", "Lo-Fi",
+ "Tribal", "Acid Punk", "Acid Jazz", "Polka",
+ "Retro", "Musical", "Rock & Roll", "Hard Rock",
+ "Folk", "Folk-Rock", "National Folk", "Swing",
+ "Fast Fusion", "Bebob", "Latin", "Revival",
+ "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
+ "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock",
+ "Big Band", "Chorus", "Easy Listening", "Acoustic",
+ "Humour", "Speech", "Chanson", "Opera",
+ "Chamber Music", "Sonata", "Symphony", "Booty Bass",
+ "Primus", "Porn Groove", "Satire", "Slow Jam",
+ "Club", "Tango", "Samba", "Folklore",
+ "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle",
+ "Duet", "Punk Rock", "Drum Solo", "Acapella",
+ "Euro-House", "Dance Hall", "Goa", "Drum & Bass",
+ "Club - House", "Hardcore", "Terror", "Indie",
+ "BritPop", "Negerpunk", "Polsk Punk", "Beat",
+ "Christian Gangsta Rap", "Heavy Metal", "Black Metal", "Crossover",
+ "Contemporary Christian", "Christian Rock", "Merengue", "Salsa",
+ "Thrash Metal", "Anime", "JPop", "Synthpop",
+ "Unknown",
+ };
+
+ fprintf(stderr, "----------tag list-------------\n");
+ while(read < size)
+ {
+ int asize, dsize;
+ uint8_t id[5];
+ int cnt;
+ uint32_t type;
+
+ id[4] = 0;
+
+ asize = u32in();
+ read += asize;
+ asize -= 4;
+ if (datain(id, 4) < 4)
+ return ERR_FAIL;
+ asize -= 4;
+
+ for (cnt = 0; tags[cnt].id; cnt++)
+ {
+ if (!memcmp(id, tags[cnt].id, 4))
+ break;
+ }
+
+ if (tags[cnt].name)
+ fprintf(stderr, "%s : ", tags[cnt].name);
+ else
+ {
+ if (tags[cnt].flag != EXTAG)
+ fprintf(stderr, "'%s' : ", id);
+ }
+
+ dsize = u32in();
+ asize -= 4;
+ if (datain(id, 4) < 4)
+ return ERR_FAIL;
+ asize -= 4;
+
+ if (tags[cnt].flag != EXTAG)
+ {
+ if (memcmp(id, "data", 4))
+ return ERR_FAIL;
+ }
+ else
+ {
+ int spc;
+
+ if (memcmp(id, "mean", 4))
+ goto skip;
+ dsize -= 8;
+ while (dsize > 0)
+ {
+ u8in();
+ asize--;
+ dsize--;
+ }
+ if (asize >= 8)
+ {
+ dsize = u32in() - 8;
+ asize -= 4;
+ if (datain(id, 4) < 4)
+ return ERR_FAIL;
+ asize -= 4;
+ if (memcmp(id, "name", 4))
+ goto skip;
+ u32in();
+ asize -= 4;
+ dsize -= 4;
+ }
+ spc = 13 - dsize;
+ if (spc < 0) spc = 0;
+ while (dsize > 0)
+ {
+ fprintf(stderr, "%c",u8in());
+ asize--;
+ dsize--;
+ }
+ while (spc--)
+ fprintf(stderr, " ");
+ fprintf(stderr, ": ");
+ if (asize >= 8)
+ {
+ dsize = u32in() - 8;
+ asize -= 4;
+ if (datain(id, 4) < 4)
+ return ERR_FAIL;
+ asize -= 4;
+ if (memcmp(id, "data", 4))
+ goto skip;
+ u32in();
+ asize -= 4;
+ dsize -= 4;
+ }
+ while (dsize > 0)
+ {
+ fprintf(stderr, "%c",u8in());
+ asize--;
+ dsize--;
+ }
+ fprintf(stderr, "\n");
+
+ goto skip;
+ }
+ type = u32in();
+ asize -= 4;
+ u32in();
+ asize -= 4;
+
+ switch(type)
+ {
+ case 1:
+ while (asize > 0)
+ {
+ fprintf(stderr, "%c",u8in());
+ asize--;
+ }
+ break;
+ case 0:
+ switch(tags[cnt].flag)
+ {
+ case NUMSET:
+ u16in();
+ asize -= 2;
+
+ fprintf(stderr, "%d", u16in());
+ asize -= 2;
+ fprintf(stderr, "/%d", u16in());
+ asize -= 2;
+ break;
+ case GENRE:
+ {
+ uint16_t gnum = u16in();
+ asize -= 2;
+ if (!gnum)
+ goto skip;
+ gnum--;
+ if (gnum >= 147)
+ gnum = 147;
+ fprintf(stderr, "%s", genres[gnum]);
+ }
+ break;
+ default:
+ while(asize > 0)
+ {
+ fprintf(stderr, "%d/", u16in());
+ asize-=2;
+ }
+ }
+ break;
+ case 0x15:
+ //fprintf(stderr, "(8bit data)");
+ while(asize > 0)
+ {
+ fprintf(stderr, "%d", u8in());
+ asize--;
+ if (asize)
+ fprintf(stderr, "/");
+ }
+ break;
+ case 0xd:
+ fprintf(stderr, "(image data)");
+ break;
+ default:
+ fprintf(stderr, "(unknown data type)");
+ break;
+ }
+ fprintf(stderr, "\n");
+
+ skip:
+ // skip to the end of atom
+ while (asize > 0)
+ {
+ u8in();
+ asize--;
+ }
+ }
+ fprintf(stderr, "-------------------------------\n");
+
+ return size;
+}
+
+static creator_t *g_atom = 0;
+static int parse(uint32_t *sizemax)
+{
+ long apos = 0;
+ long aposmax = ftell(g_fin) + *sizemax;
+ uint32_t size;
+
+ if (g_atom->opcode != ATOM_NAME)
+ {
+ fprintf(stderr, "parse error: root is not a 'name' opcode\n");
+ return ERR_FAIL;
+ }
+ //fprintf(stderr, "looking for '%s'\n", (char *)g_atom->name);
+
+ // search for atom in the file
+ while (1)
+ {
+ char name[4];
+ uint32_t tmp;
+
+ apos = ftell(g_fin);
+ if (apos >= (aposmax - 8))
+ {
+ fprintf(stderr, "parse error: atom '%s' not found\n", g_atom->name);
+ return ERR_FAIL;
+ }
+ if ((tmp = u32in()) < 8)
+ {
+ fprintf(stderr, "invalid atom size %x @%lx\n", tmp, ftell(g_fin));
+ return ERR_FAIL;
+ }
+
+ size = tmp;
+ if (datain(name, 4) != 4)
+ {
+ // EOF
+ fprintf(stderr, "can't read atom name @%lx\n", ftell(g_fin));
+ return ERR_FAIL;
+ }
+
+ //fprintf(stderr, "atom: '%c%c%c%c'(%x)", name[0],name[1],name[2],name[3], size);
+
+ if (!memcmp(name, g_atom->name, 4))
+ {
+ //fprintf(stderr, "OK\n");
+ break;
+ }
+ //fprintf(stderr, "\n");
+
+ fseek(g_fin, apos + size, SEEK_SET);
+ }
+ *sizemax = size;
+ g_atom++;
+ if (g_atom->opcode == ATOM_DATA)
+ {
+ int err = g_atom->parse(size - 8);
+ if (err < ERR_OK)
+ {
+ fseek(g_fin, apos + size, SEEK_SET);
+ return err;
+ }
+ g_atom++;
+ }
+ if (g_atom->opcode == ATOM_DESCENT)
+ {
+ long apos2 = ftell(g_fin);
+
+ //fprintf(stderr, "descent\n");
+ g_atom++;
+ while (g_atom->opcode != ATOM_STOP)
+ {
+ uint32_t subsize = size - 8;
+ int ret;
+ if (g_atom->opcode == ATOM_ASCENT)
+ {
+ g_atom++;
+ break;
+ }
+ // TODO: does not feel well - we always return to the same point!
+ fseek(g_fin, apos2, SEEK_SET);
+ if ((ret = parse(&subsize)) < 0)
+ return ret;
+ }
+ //fprintf(stderr, "ascent\n");
+ }
+
+ fseek(g_fin, apos + size, SEEK_SET);
+
+ return ERR_OK;
+}
+
+static int moovin(int sizemax)
+{
+ long apos = ftell(g_fin);
+ uint32_t atomsize;
+ creator_t *old_atom = g_atom;
+ int err, ret = sizemax;
+
+ static creator_t mvhd[] = {
+ NAME("mvhd"),
+ STOP()
+ };
+ static creator_t trak[] = {
+ NAME("trak"),
+ DESCENT(),
+ NAME("tkhd"),
+ NAME("mdia"),
+ DESCENT(),
+ DATA("mdhd", mdhdin),
+ DATA("hdlr", hdlr1in),
+ NAME("minf"),
+ DESCENT(),
+ NAME("smhd"),
+ NAME("dinf"),
+ NAME("stbl"),
+ DESCENT(),
+ DATA("stsd", stsdin),
+ DESCENT(),
+ DATA("mp4a", mp4ain),
+ DESCENT(),
+ DATA("esds", esdsin),
+ ASCENT(),
+ ASCENT(),
+ DATA("stts", sttsin),
+ DATA("stsc", stscin),
+ DATA("stsz", stszin),
+ DATA("stco", stcoin),
+ STOP()
+ };
+
+ g_atom = mvhd;
+ atomsize = sizemax + apos - ftell(g_fin);
+ if (parse(&atomsize) < 0) {
+ g_atom = old_atom;
+ return ERR_FAIL;
+ }
+
+ fseek(g_fin, apos, SEEK_SET);
+
+ while (1)
+ {
+ //fprintf(stderr, "TRAK\n");
+ g_atom = trak;
+ atomsize = sizemax + apos - ftell(g_fin);
+ if (atomsize < 8)
+ break;
+ //fprintf(stderr, "PARSE(%x)\n", atomsize);
+ err = parse(&atomsize);
+ //fprintf(stderr, "SIZE: %x/%x\n", atomsize, sizemax);
+ if (err >= 0)
+ break;
+ if (err != ERR_UNSUPPORTED) {
+ ret = err;
+ break;
+ }
+ //fprintf(stderr, "UNSUPP\n");
+ }
+
+ g_atom = old_atom;
+ return ret;
+}
+
+
+static creator_t g_head[] = {
+ DATA("ftyp", ftypin),
+ STOP()
+};
+
+static creator_t g_moov[] = {
+ DATA("moov", moovin),
+ //DESCENT(),
+ //NAME("mvhd"),
+ STOP()
+};
+
+static creator_t g_meta1[] = {
+ NAME("moov"),
+ DESCENT(),
+ NAME("udta"),
+ DESCENT(),
+ DATA("meta", metain),
+ DESCENT(),
+ DATA("hdlr", hdlr2in),
+ DATA("ilst", ilstin),
+ STOP()
+};
+
+static creator_t g_meta2[] = {
+ DATA("meta", metain),
+ DESCENT(),
+ DATA("hdlr", hdlr2in),
+ DATA("ilst", ilstin),
+ STOP()
+};
+
+
+int mp4read_frame(void)
+{
+ if (mp4config.frame.current >= mp4config.frame.nsamples)
+ return ERR_FAIL;
+
+ // TODO(eustas): avoid no-op seeks
+ mp4read_seek(mp4config.frame.current);
+
+ mp4config.bitbuf.size = mp4config.frame.info[mp4config.frame.current].len;
+
+ if (fread(mp4config.bitbuf.data, 1, mp4config.bitbuf.size, g_fin)
+ != mp4config.bitbuf.size)
+ {
+ fprintf(stderr, "can't read frame data(frame %d@0x%x)\n",
+ mp4config.frame.current,
+ mp4config.frame.info[mp4config.frame.current].offset);
+
+ return ERR_FAIL;
+ }
+
+ mp4config.frame.current++;
+
+ return ERR_OK;
+}
+
+int mp4read_seek(uint32_t framenum)
+{
+ if (framenum > mp4config.frame.nsamples)
+ return ERR_FAIL;
+ if (fseek(g_fin, mp4config.frame.info[framenum].offset, SEEK_SET))
+ return ERR_FAIL;
+
+ mp4config.frame.current = framenum;
+
+ return ERR_OK;
+}
+
+static void mp4info(void)
+{
+ fprintf(stderr, "Modification Time:\t\t%s\n", mp4time(mp4config.mtime));
+ fprintf(stderr, "Samplerate:\t\t%d\n", mp4config.samplerate);
+ fprintf(stderr, "Total samples:\t\t%d\n", mp4config.samples);
+ fprintf(stderr, "Total channels:\t\t%d\n", mp4config.channels);
+ fprintf(stderr, "Bits per sample:\t%d\n", mp4config.bits);
+ fprintf(stderr, "Buffer size:\t\t%d\n", mp4config.buffersize);
+ fprintf(stderr, "Max bitrate:\t\t%d\n", mp4config.bitratemax);
+ fprintf(stderr, "Average bitrate:\t%d\n", mp4config.bitrateavg);
+ fprintf(stderr, "Frames:\t\t\t%d\n", mp4config.frame.nsamples);
+ fprintf(stderr, "ASC size:\t\t%d\n", mp4config.asc.size);
+ fprintf(stderr, "Duration:\t\t%.1f sec\n", (float)mp4config.samples/mp4config.samplerate);
+ if (mp4config.frame.nsamples)
+ fprintf(stderr, "Data offset:\t%x\n", mp4config.frame.info[0].offset);
+}
+
+int mp4read_close(void)
+{
+ freeMem(&mp4config.frame.info);
+ freeMem(&mp4config.frame.map);
+ freeMem(&mp4config.bitbuf.data);
+
+ return ERR_OK;
+}
+
+int mp4read_open(char *name)
+{
+ uint32_t atomsize;
+ int ret;
+
+ mp4read_close();
+
+ g_fin = faad_fopen(name, "rb");
+ if (!g_fin)
+ return ERR_FAIL;
+
+ if (mp4config.verbose.header)
+ fprintf(stderr, "**** MP4 header ****\n");
+ g_atom = g_head;
+ atomsize = INT_MAX;
+ if (parse(&atomsize) < 0)
+ goto err;
+ g_atom = g_moov;
+ atomsize = INT_MAX;
+ rewind(g_fin);
+ if ((ret = parse(&atomsize)) < 0)
+ {
+ fprintf(stderr, "parse:%d\n", ret);
+ goto err;
+ }
+
+ // alloc frame buffer
+ mp4config.bitbuf.data = malloc(mp4config.frame.maxsize);
+
+ if (!mp4config.bitbuf.data)
+ goto err;
+
+ if (mp4config.verbose.header)
+ {
+ mp4info();
+ fprintf(stderr, "********************\n");
+ }
+
+ if (mp4config.verbose.tags)
+ {
+ rewind(g_fin);
+ g_atom = g_meta1;
+ atomsize = INT_MAX;
+ ret = parse(&atomsize);
+ if (ret < 0)
+ {
+ rewind(g_fin);
+ g_atom = g_meta2;
+ atomsize = INT_MAX;
+ ret = parse(&atomsize);
+ }
+ }
+
+ return ERR_OK;
+err:
+ mp4read_close();
+ return ERR_FAIL;
+}
diff --git a/lib/faad2/frontend/mp4read.h b/lib/faad2/frontend/mp4read.h
new file mode 100644
index 00000000..e7b9f141
--- /dev/null
+++ b/lib/faad2/frontend/mp4read.h
@@ -0,0 +1,78 @@
+/****************************************************************************
+ MP4 input module
+
+ Copyright (C) 2017 Krzysztof Nikiel
+
+ 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 3 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, see <http://www.gnu.org/licenses/>.
+****************************************************************************/
+
+#include <stdint.h>
+
+typedef struct
+{
+ uint32_t len;
+ uint32_t offset;
+} frame_info_t;
+
+typedef struct
+{
+ uint32_t firstchunk;
+ uint32_t samplesperchunk;
+} slice_info_t;
+
+typedef struct
+{
+ uint32_t ctime, mtime;
+ uint32_t samplerate;
+ // total sound samples
+ uint32_t samples;
+ uint32_t channels;
+ // sample depth
+ uint32_t bits;
+ // buffer config
+ uint32_t buffersize;
+ uint32_t bitratemax;
+ uint32_t bitrateavg;
+ // frame size / offsets
+ struct
+ {
+ frame_info_t *info;
+ slice_info_t *map;
+ uint32_t nsamples;
+ uint32_t nsclices;
+ uint32_t current;
+ uint32_t maxsize;
+ } frame;
+ // AudioSpecificConfig data:
+ struct
+ {
+ uint8_t buf[10];
+ uint32_t size;
+ } asc;
+ struct {
+ uint32_t size;
+ uint8_t *data;
+ } bitbuf;
+ struct {
+ int header;
+ int tags;
+ } verbose;
+} mp4config_t;
+
+extern mp4config_t mp4config;
+
+int mp4read_open(char *name);
+int mp4read_seek(uint32_t framenum);
+int mp4read_frame(void);
+int mp4read_close(void);
diff --git a/lib/faad2/frontend/unicode_support.c b/lib/faad2/frontend/unicode_support.c
new file mode 100644
index 00000000..d3f50ac6
--- /dev/null
+++ b/lib/faad2/frontend/unicode_support.c
@@ -0,0 +1,172 @@
+/* Copyright (c) 2004-2012 LoRd_MuldeR <mulder2@gmx.de>
+ File: unicode_support.c
+
+ This file was originally part of a patch included with LameXP,
+ released under the same license as the original audio tools.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#define _CRT_SECURE_NO_WARNINGS
+#include <stdio.h>
+
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+
+#include "unicode_support.h"
+
+#include <stdlib.h>
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <shellapi.h>
+#include <io.h>
+
+static UINT g_old_output_cp = ((UINT)-1);
+
+static char *utf16_to_utf8(const wchar_t *input)
+{
+ char *Buffer;
+ int BuffSize = 0, Result = 0;
+
+ BuffSize = WideCharToMultiByte(CP_UTF8, 0, input, -1, NULL, 0, NULL, NULL);
+ Buffer = (char*) malloc(sizeof(char) * BuffSize);
+ if(Buffer)
+ {
+ Result = WideCharToMultiByte(CP_UTF8, 0, input, -1, Buffer, BuffSize, NULL, NULL);
+ }
+
+ return ((Result > 0) && (Result <= BuffSize)) ? Buffer : NULL;
+}
+
+static wchar_t *utf8_to_utf16(const char *input)
+{
+ wchar_t *Buffer;
+ int BuffSize = 0, Result = 0;
+
+ BuffSize = MultiByteToWideChar(CP_UTF8, 0, input, -1, NULL, 0);
+ Buffer = (wchar_t*) malloc(sizeof(wchar_t) * BuffSize);
+ if(Buffer)
+ {
+ Result = MultiByteToWideChar(CP_UTF8, 0, input, -1, Buffer, BuffSize);
+ }
+
+ return ((Result > 0) && (Result <= BuffSize)) ? Buffer : NULL;
+}
+
+void init_commandline_arguments_utf8(int *argc, char ***argv)
+{
+ int i, nArgs;
+ LPWSTR *szArglist;
+
+ szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
+
+ if(NULL == szArglist)
+ {
+ fprintf(stderr, "\nFATAL: CommandLineToArgvW failed\n\n");
+ exit(-1);
+ }
+
+ *argv = (char**) malloc(sizeof(char*) * nArgs);
+ *argc = nArgs;
+
+ if(NULL == *argv)
+ {
+ fprintf(stderr, "\nFATAL: Malloc failed\n\n");
+ exit(-1);
+ }
+
+ for(i = 0; i < nArgs; i++)
+ {
+ (*argv)[i] = utf16_to_utf8(szArglist[i]);
+ if(NULL == (*argv)[i])
+ {
+ fprintf(stderr, "\nFATAL: utf16_to_utf8 failed\n\n");
+ exit(-1);
+ }
+ }
+
+ LocalFree(szArglist);
+}
+
+void free_commandline_arguments_utf8(int *argc, char ***argv)
+{
+ int i = 0;
+
+ if(*argv != NULL)
+ {
+ for(i = 0; i < *argc; i++)
+ {
+ if((*argv)[i] != NULL)
+ {
+ free((*argv)[i]);
+ (*argv)[i] = NULL;
+ }
+ }
+ free(*argv);
+ *argv = NULL;
+ }
+}
+
+FILE *faad_fopen(const char *filename, const char *mode)
+{
+ FILE *ret = NULL;
+ wchar_t *filename_utf16 = utf8_to_utf16(filename);
+ wchar_t *mode_utf16 = utf8_to_utf16(mode);
+
+ if(filename_utf16 && mode_utf16)
+ {
+ ret = _wfopen(filename_utf16, mode_utf16);
+ }
+
+ if(filename_utf16) free(filename_utf16);
+ if(mode_utf16) free(mode_utf16);
+
+ return ret;
+}
+
+void init_console_utf8(FILE *const stream)
+{
+ if (_isatty(_fileno(stream)))
+ {
+ g_old_output_cp = GetConsoleOutputCP();
+ SetConsoleOutputCP(CP_UTF8);
+ }
+}
+
+void uninit_console_utf8(void)
+{
+ if(g_old_output_cp != ((UINT)-1))
+ {
+ SetConsoleOutputCP(g_old_output_cp);
+ }
+}
+
+#else
+
+FILE *faad_fopen(const char *filename, const char *mode)
+{
+ return fopen(filename, mode);
+}
+
+#endif
diff --git a/lib/faad2/frontend/unicode_support.h b/lib/faad2/frontend/unicode_support.h
new file mode 100644
index 00000000..07dacc93
--- /dev/null
+++ b/lib/faad2/frontend/unicode_support.h
@@ -0,0 +1,44 @@
+/* Copyright (c) 2004-2012 LoRd_MuldeR <mulder2@gmx.de>
+ File: unicode_support.h
+
+ This file was originally part of a patch included with LameXP,
+ released under the same license as the original audio tools.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ - Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+ - Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
+ CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*/
+#ifndef UNICODE_SUPPORT_H_INCLUDED
+#define UNICODE_SUPPORT_H_INCLUDED
+
+#include <stdio.h>
+
+#if defined WIN32 || defined _WIN32 || defined WIN64 || defined _WIN64
+void init_commandline_arguments_utf8(int *argc, char ***argv);
+void free_commandline_arguments_utf8(int *argc, char ***argv);
+void init_console_utf8(FILE *const stream);
+void uninit_console_utf8(void);
+#endif
+
+FILE *faad_fopen(const char *filename, const char *mode);
+
+#endif //UNICODE_SUPPORT_H_INCLUDED