diff options
Diffstat (limited to 'lib/faad2/frontend')
| -rw-r--r-- | lib/faad2/frontend/audio.c | 512 | ||||
| -rw-r--r-- | lib/faad2/frontend/audio.h | 64 | ||||
| -rw-r--r-- | lib/faad2/frontend/faad.man | 85 | ||||
| -rw-r--r-- | lib/faad2/frontend/getopt.c | 725 | ||||
| -rw-r--r-- | lib/faad2/frontend/getopt.h | 130 | ||||
| -rw-r--r-- | lib/faad2/frontend/main.c | 1378 | ||||
| -rw-r--r-- | lib/faad2/frontend/mp4read.c | 1109 | ||||
| -rw-r--r-- | lib/faad2/frontend/mp4read.h | 78 | ||||
| -rw-r--r-- | lib/faad2/frontend/unicode_support.c | 172 | ||||
| -rw-r--r-- | lib/faad2/frontend/unicode_support.h | 44 |
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 |
