From 1fbe8eb3bd0907aadf5a97283866ca797370bb18 Mon Sep 17 00:00:00 2001 From: Martin Storsjo Date: Tue, 27 Jun 2017 14:47:01 +0300 Subject: [PATCH] Add a tool for testing decoding+encoding in a number of configurations --- CMakeLists.txt | 11 ++ Makefile.am | 4 + sha1.c | 399 +++++++++++++++++++++++++++++++++++++++++++ sha1.h | 85 +++++++++ test-encode-decode.c | 383 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 882 insertions(+) create mode 100644 sha1.c create mode 100644 sha1.h create mode 100644 test-encode-decode.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 8be9d64..35d8d98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -586,4 +586,15 @@ if(BUILD_PROGRAMS) ## Program target installation install(TARGETS aac-enc RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) + + ## Tool for testing the library + set(test_encode_decode_SOURCES + test-encode-decode.c + wavreader.c + wavreader.h + sha1.c + sha1.h) + + add_executable(test-encode-decode ${test_encode_decode_SOURCES}) + target_link_libraries(test-encode-decode PRIVATE fdk-aac) endif() diff --git a/Makefile.am b/Makefile.am index 8f55cf0..c71af1d 100644 --- a/Makefile.am +++ b/Makefile.am @@ -40,10 +40,14 @@ libfdk_aac_la_LDFLAGS = -version-info @FDK_AAC_VERSION@ -no-undefined \ if EXAMPLE bin_PROGRAMS = aac-enc$(EXEEXT) +noinst_PROGRAMS = test-encode-decode$(EXEEXT) aac_enc_LDADD = libfdk-aac.la aac_enc_SOURCES = aac-enc.c wavreader.c +test_encode_decode_LDADD = libfdk-aac.la +test_encode_decode_SOURCES = test-encode-decode.c wavreader.c sha1.c + noinst_HEADERS = wavreader.h endif diff --git a/sha1.c b/sha1.c new file mode 100644 index 0000000..08a93d1 --- /dev/null +++ b/sha1.c @@ -0,0 +1,399 @@ +/*! + * \copy + * Copyright (c) 1998, 2009 Paul E. Jones + * All rights reserved. + * + * 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 + * COPYRIGHT HOLDER 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. + * + */ + +/* + * Description: + * This file implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * The Secure Hashing Standard, which uses the Secure Hashing + * Algorithm (SHA), produces a 160-bit message digest for a + * given data stream. In theory, it is highly improbable that + * two messages will produce the same message digest. Therefore, + * this algorithm can serve as a means of providing a "fingerprint" + * for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code was + * written with the expectation that the processor has at least + * a 32-bit machine word size. If the machine word size is larger, + * the code should still function properly. One caveat to that + * is that the input functions taking characters and character + * arrays assume that only 8 bits of information are stored in each + * character. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated for + * messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is a + * multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the circular shift macro + */ +#define SHA1CircularShift(bits,word) \ + ((((word) << (bits)) & 0xFFFFFFFF) | \ + ((word) >> (32-(bits)))) + +/* Function prototypes */ +void SHA1ProcessMessageBlock(SHA1Context *); +void SHA1PadMessage(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Reset(SHA1Context *context) +{ + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Message_Digest[0] = 0x67452301; + context->Message_Digest[1] = 0xEFCDAB89; + context->Message_Digest[2] = 0x98BADCFE; + context->Message_Digest[3] = 0x10325476; + context->Message_Digest[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * digest array provided as a parameter. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * digest: [out] + * An array of characters where the digest is written. + * + * Returns: + * 1 if successful, 0 if it failed. + * + * Comments: + * + */ +int SHA1Result(SHA1Context *context, unsigned char *digest) +{ + int i; + + if (context->Corrupted) + { + return 0; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + context->Computed = 1; + } + + for (i = 0; i < SHA_DIGEST_LENGTH; i++) + digest[i] = context->Message_Digest[i / 4] >> (8 * (3 - (i % 4))); + + return 1; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion of + * the message. + * + * Parameters: + * context: [in/out] + * The SHA-1 context to update + * message_array: [in] + * An array of characters representing the next portion of the + * message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1Input( SHA1Context *context, + const unsigned char *message_array, + unsigned length) +{ + if (!length) + { + return; + } + + if (context->Computed || context->Corrupted) + { + context->Corrupted = 1; + return; + } + + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + /* Force it to 32 bits */ + context->Length_Low &= 0xFFFFFFFF; + if (context->Length_Low == 0) + { + context->Length_High++; + /* Force it to 32 bits */ + context->Length_High &= 0xFFFFFFFF; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in the SHAContext, especially the + * single character names, were used because those were the names + * used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const unsigned K[] = /* Constants defined in SHA-1 */ + { + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + unsigned temp; /* Temporary word value */ + unsigned W[80]; /* Word sequence */ + unsigned A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = ((unsigned) context->Message_Block[t * 4]) << 24; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 1]) << 16; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 2]) << 8; + W[t] |= ((unsigned) context->Message_Block[t * 4 + 3]); + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Message_Digest[0]; + B = context->Message_Digest[1]; + C = context->Message_Digest[2]; + D = context->Message_Digest[3]; + E = context->Message_Digest[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + temp &= 0xFFFFFFFF; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Message_Digest[0] = + (context->Message_Digest[0] + A) & 0xFFFFFFFF; + context->Message_Digest[1] = + (context->Message_Digest[1] + B) & 0xFFFFFFFF; + context->Message_Digest[2] = + (context->Message_Digest[2] + C) & 0xFFFFFFFF; + context->Message_Digest[3] = + (context->Message_Digest[3] + D) & 0xFFFFFFFF; + context->Message_Digest[4] = + (context->Message_Digest[4] + E) & 0xFFFFFFFF; + + context->Message_Block_Index = 0; +} + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call SHA1ProcessMessageBlock() + * appropriately. When it returns, it can be assumed that the + * message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * + * Returns: + * Nothing. + * + * Comments: + * + */ +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = (context->Length_High >> 24) & 0xFF; + context->Message_Block[57] = (context->Length_High >> 16) & 0xFF; + context->Message_Block[58] = (context->Length_High >> 8) & 0xFF; + context->Message_Block[59] = (context->Length_High) & 0xFF; + context->Message_Block[60] = (context->Length_Low >> 24) & 0xFF; + context->Message_Block[61] = (context->Length_Low >> 16) & 0xFF; + context->Message_Block[62] = (context->Length_Low >> 8) & 0xFF; + context->Message_Block[63] = (context->Length_Low) & 0xFF; + + SHA1ProcessMessageBlock(context); +} diff --git a/sha1.h b/sha1.h new file mode 100644 index 0000000..7ca10f8 --- /dev/null +++ b/sha1.h @@ -0,0 +1,85 @@ +/*! + * \copy + * Copyright (c) 1998, 2009 Paul E. Jones + * All rights reserved. + * + * 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 + * COPYRIGHT HOLDER 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. + * + */ + +/* + * Description: + * This class implements the Secure Hashing Standard as defined + * in FIPS PUB 180-1 published April 17, 1995. + * + * Many of the variable names in the SHA1Context, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * This structure will hold context information for the hashing + * operation + */ +typedef struct SHA1Context { + unsigned Message_Digest[5]; /* Message Digest (output) */ + + unsigned Length_Low; /* Message length in bits */ + unsigned Length_High; /* Message length in bits */ + + unsigned char Message_Block[64]; /* 512-bit message blocks */ + int Message_Block_Index; /* Index into message block array */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corruped? */ +} SHA1Context; + +/* + * Function Prototypes + */ +void SHA1Reset (SHA1Context*); +int SHA1Result (SHA1Context*, unsigned char*); +void SHA1Input (SHA1Context*, + const unsigned char*, + unsigned); + +#define SHA_DIGEST_LENGTH 20 + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/test-encode-decode.c b/test-encode-decode.c new file mode 100644 index 0000000..a216c18 --- /dev/null +++ b/test-encode-decode.c @@ -0,0 +1,383 @@ +/* ------------------------------------------------------------------ + * Copyright (C) 2017 Martin Storsjo + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either + * express or implied. + * See the License for the specific language governing permissions + * and limitations under the License. + * ------------------------------------------------------------------- + */ + +#include +#include +#include +#include +#include "aacenc_lib.h" +#include "aacdecoder_lib.h" +#include "wavreader.h" +#include "sha1.h" + + +static int encoder_input_samples, encoder_input_size; +static int decoder_output_skip; +static int16_t *encoder_input; +static int max_diff; +static uint64_t diff_sum, diff_samples; +static SHA1Context encode_hash, decode_hash; + +static void init_encoder_input(void) { + encoder_input_samples = 0; + max_diff = 0; + diff_sum = diff_samples = 0; +} + +static void free_encoder_input(void) { + free(encoder_input); + encoder_input = NULL; + encoder_input_size = 0; +} + +static void append_encoder_input(const int16_t *input, int samples) { + if (encoder_input_samples + samples > encoder_input_size) { + int size = 2*(encoder_input_samples + samples); + int16_t *ptr = realloc(encoder_input, size * sizeof(*encoder_input)); + if (!ptr) + abort(); + encoder_input = ptr; + encoder_input_size = size; + } + memcpy(encoder_input + encoder_input_samples, input, samples * sizeof(*input)); + encoder_input_samples += samples; +} + +static void compare_decoder_output(const int16_t *output, int samples) { + int i; + // TODO: Stereo upconvert? + SHA1Input(&decode_hash, (const unsigned char*) output, samples * sizeof(*output)); + if (decoder_output_skip > 0) { + int n = samples; + if (n > decoder_output_skip) + n = decoder_output_skip; + output += n; + samples -= n; + decoder_output_skip -= n; + if (samples <= 0) + return; + } + if (samples > encoder_input_samples) + samples = encoder_input_samples; + for (i = 0; i < samples; i++) { + int diff = abs(encoder_input[i] - output[i]); + if (diff > max_diff) + max_diff = diff; + diff_sum += diff; + diff_samples++; + } + memmove(encoder_input, encoder_input + samples, (encoder_input_samples - samples) * sizeof(*encoder_input)); + encoder_input_samples -= samples; +} + +static int decode(HANDLE_AACDECODER decoder, const uint8_t *ptr, int size, uint8_t *decoder_buffer, int decoder_buffer_size, int channels) { + AAC_DECODER_ERROR err; + CStreamInfo *info; + UINT valid, buffer_size; + SHA1Input(&encode_hash, ptr, size); + do { + valid = buffer_size = size; + err = aacDecoder_Fill(decoder, (UCHAR**) &ptr, &buffer_size, &valid); + ptr += buffer_size - valid; + size -= buffer_size - valid; + if (err == AAC_DEC_NOT_ENOUGH_BITS) + continue; + if (err != AAC_DEC_OK) + break; + err = aacDecoder_DecodeFrame(decoder, (INT_PCM *) decoder_buffer, decoder_buffer_size / sizeof(INT_PCM), 0); + if (!ptr && err != AAC_DEC_OK) + break; + if (err == AAC_DEC_NOT_ENOUGH_BITS) + continue; + if (err != AAC_DEC_OK) { + fprintf(stderr, "Decoding failed\n"); + return 1; + } + info = aacDecoder_GetStreamInfo(decoder); + if (info->numChannels != channels) { + fprintf(stderr, "Mismatched number of channels, input %d, output %d\n", channels, info->numChannels); + return 1; + } + compare_decoder_output((int16_t*) decoder_buffer, info->numChannels * info->frameSize); + } while (size > 0); + return 0; +} + +static int test_encode_decode(const char *infile, int aot, int afterburner, int eld_sbr, int vbr, int bitrate, int adts) { + void *wav; + int format, sample_rate, channels, bits_per_sample; + int input_size; + uint8_t* input_buf; + int16_t* convert_buf; + HANDLE_AACENCODER encoder; + CHANNEL_MODE mode; + AACENC_InfoStruct info = { 0 }; + HANDLE_AACDECODER decoder; + int ret = 0; + int decoder_buffer_size = 2048 * 2 * 8; + uint8_t* decoder_buffer = malloc(decoder_buffer_size); + int avg_diff; + + fprintf(stderr, "Testing encoding with aot %d afterburner %d eld_sbr %d vbr %d bitrate %d adts %d\n", aot, afterburner, eld_sbr, vbr, bitrate, adts); + init_encoder_input(); + + wav = wav_read_open(infile); + if (!wav) { + fprintf(stderr, "Unable to open wav file %s\n", infile); + return 1; + } + if (!wav_get_header(wav, &format, &channels, &sample_rate, &bits_per_sample, NULL)) { + fprintf(stderr, "Bad wav file %s\n", infile); + return 1; + } + if (format != 1) { + fprintf(stderr, "Unsupported WAV format %d\n", format); + return 1; + } + if (bits_per_sample != 16) { + fprintf(stderr, "Unsupported WAV sample depth %d\n", bits_per_sample); + return 1; + } + switch (channels) { + case 1: mode = MODE_1; break; + case 2: mode = MODE_2; break; + case 3: mode = MODE_1_2; break; + case 4: mode = MODE_1_2_1; break; + case 5: mode = MODE_1_2_2; break; + case 6: mode = MODE_1_2_2_1; break; + default: + fprintf(stderr, "Unsupported WAV channels %d\n", channels); + return 1; + } + if (aacEncOpen(&encoder, 0, channels) != AACENC_OK) { + fprintf(stderr, "Unable to open encoder\n"); + return 1; + } + if (aacEncoder_SetParam(encoder, AACENC_AOT, aot) != AACENC_OK) { + fprintf(stderr, "Unable to set the AOT\n"); + return 1; + } + if (aot == 39 && eld_sbr) { + if (aacEncoder_SetParam(encoder, AACENC_SBR_MODE, 1) != AACENC_OK) { + fprintf(stderr, "Unable to set SBR mode for ELD\n"); + return 1; + } + } + if (aacEncoder_SetParam(encoder, AACENC_SAMPLERATE, sample_rate) != AACENC_OK) { + fprintf(stderr, "Unable to set the AOT\n"); + return 1; + } + if (aacEncoder_SetParam(encoder, AACENC_CHANNELMODE, mode) != AACENC_OK) { + fprintf(stderr, "Unable to set the channel mode\n"); + return 1; + } + if (aacEncoder_SetParam(encoder, AACENC_CHANNELORDER, 1) != AACENC_OK) { + fprintf(stderr, "Unable to set the wav channel order\n"); + return 1; + } + if (vbr) { + if (aacEncoder_SetParam(encoder, AACENC_BITRATEMODE, vbr) != AACENC_OK) { + fprintf(stderr, "Unable to set the VBR bitrate mode\n"); + return 1; + } + } else { + if (aacEncoder_SetParam(encoder, AACENC_BITRATE, bitrate) != AACENC_OK) { + fprintf(stderr, "Unable to set the bitrate\n"); + return 1; + } + } + if (aacEncoder_SetParam(encoder, AACENC_TRANSMUX, adts ? 2 : 0) != AACENC_OK) { + fprintf(stderr, "Unable to set the ADTS transmux\n"); + return 1; + } + if (aacEncoder_SetParam(encoder, AACENC_AFTERBURNER, afterburner) != AACENC_OK) { + fprintf(stderr, "Unable to set the afterburner mode\n"); + return 1; + } + if (aacEncEncode(encoder, NULL, NULL, NULL, NULL) != AACENC_OK) { + fprintf(stderr, "Unable to initialize the encoder\n"); + return 1; + } + if (aacEncInfo(encoder, &info) != AACENC_OK) { + fprintf(stderr, "Unable to get the encoder info\n"); + return 1; + } + + input_size = channels*2*info.frameLength; + input_buf = (uint8_t*) malloc(input_size); + convert_buf = (int16_t*) malloc(input_size); + + decoder_output_skip = channels * info.nDelay; + + decoder = aacDecoder_Open(adts ? TT_MP4_ADTS : TT_MP4_RAW, 1); + if (!adts) { + UCHAR *bufArray[] = { info.confBuf }; + if (aacDecoder_ConfigRaw(decoder, (UCHAR**) bufArray, &info.confSize) != AAC_DEC_OK) { + fprintf(stderr, "Unable to set ASC\n"); + ret = 1; + goto end; + } + } + aacDecoder_SetParam(decoder, AAC_CONCEAL_METHOD, 1); + aacDecoder_SetParam(decoder, AAC_PCM_LIMITER_ENABLE, 0); + while (1) { + AACENC_BufDesc in_buf = { 0 }, out_buf = { 0 }; + AACENC_InArgs in_args = { 0 }; + AACENC_OutArgs out_args = { 0 }; + int in_identifier = IN_AUDIO_DATA; + int in_size, in_elem_size; + int out_identifier = OUT_BITSTREAM_DATA; + int out_size, out_elem_size; + int read, i; + void *in_ptr, *out_ptr; + uint8_t outbuf[20480]; + AACENC_ERROR err; + + read = wav_read_data(wav, input_buf, input_size); + for (i = 0; i < read/2; i++) { + const uint8_t* in = &input_buf[2*i]; + convert_buf[i] = in[0] | (in[1] << 8); + } + in_ptr = convert_buf; + in_size = read; + in_elem_size = 2; + + in_buf.numBufs = 1; + in_buf.bufs = &in_ptr; + in_buf.bufferIdentifiers = &in_identifier; + in_buf.bufSizes = &in_size; + in_buf.bufElSizes = &in_elem_size; + + if (read <= 0) { + in_args.numInSamples = -1; + } else { + in_args.numInSamples = read/2; + append_encoder_input(convert_buf, in_args.numInSamples); + } + out_ptr = outbuf; + out_size = sizeof(outbuf); + out_elem_size = 1; + out_buf.numBufs = 1; + out_buf.bufs = &out_ptr; + out_buf.bufferIdentifiers = &out_identifier; + out_buf.bufSizes = &out_size; + out_buf.bufElSizes = &out_elem_size; + + if ((err = aacEncEncode(encoder, &in_buf, &out_buf, &in_args, &out_args)) != AACENC_OK) { + if (err == AACENC_ENCODE_EOF) + break; + fprintf(stderr, "Encoding failed\n"); + ret = 1; + goto end; + } + if (out_args.numOutBytes == 0) + continue; + + if (decode(decoder, outbuf, out_args.numOutBytes, decoder_buffer, decoder_buffer_size, channels)) { + ret = 1; + goto end; + } + } + + if (encoder_input_samples > 0) { + fprintf(stderr, "%d unmatched samples left at the end\n", encoder_input_samples); + ret = 1; + goto end; + } + avg_diff = 0; + if (diff_samples > 0) + avg_diff = diff_sum / diff_samples; + if (/*max_diff > 10000 ||*/ avg_diff > ((aot == 23) ? 2500 : (aot == 29) ? 1500 : 300)) { + fprintf(stderr, "max_diff %d, avg_diff %d\n", max_diff, avg_diff); + ret = 1; + goto end; + } + +end: + free(input_buf); + free(convert_buf); + wav_read_close(wav); + aacEncClose(&encoder); + free(decoder_buffer); + aacDecoder_Close(decoder); + + return ret; +} + +int main(int argc, char *argv[]) { + const char* infile; + void *wav; + int sample_rate, channels; + int failures = 0; + int i; + unsigned char encode_digest[SHA_DIGEST_LENGTH], decode_digest[SHA_DIGEST_LENGTH]; + if (argc < 2) { + printf("%s input.wav\n", argv[0]); + return 1; + } + infile = argv[1]; + + wav = wav_read_open(infile); + if (!wav) { + fprintf(stderr, "Unable to open wav file %s\n", infile); + return 1; + } + if (!wav_get_header(wav, NULL, &channels, &sample_rate, NULL, NULL)) { + fprintf(stderr, "Bad wav file %s\n", infile); + return 1; + } + wav_read_close(wav); + + SHA1Reset(&encode_hash); + SHA1Reset(&decode_hash); + + failures += test_encode_decode(infile, 2, 0, 0, 0, 64000, 0); // AAC-LC, without afterburner + for (i = 0; i < 2; i++) + failures += test_encode_decode(infile, 2, 1, 0, 0, 64000, i); // AAC-LC + for (i = 1; i <= 5; i++) + failures += test_encode_decode(infile, 2, 1, 0, i, 0, 0); // AAC-LC VBR + if (channels == 2) { + // HE-AACv2 only works for stereo; HE-AACv1 gets upconverted to stereo (which we don't match properly) + for (i = 0; i < 2; i++) + failures += test_encode_decode(infile, 5, 1, 0, 0, 64000, i); // HE-AAC + for (i = 1; i <= 5; i++) + failures += test_encode_decode(infile, 5, 1, 0, i, 0, 0); // HE-AAC VBR + for (i = 0; i < 2; i++) + failures += test_encode_decode(infile, 29, 1, 0, 0, 64000, i); // HE-AACv2 + for (i = 1; i <= 5; i++) + failures += test_encode_decode(infile, 29, 1, 0, i, 0, 0); // HE-AACv2 VBR + } + if (channels == 1) + failures += test_encode_decode(infile, 23, 1, 0, 0, 64000, 0); // AAC-LD + failures += test_encode_decode(infile, 39, 1, 0, 0, 64000, 0); // AAC-ELD + failures += test_encode_decode(infile, 39, 1, 1, 0, 64000, 0); // AAC-ELD with SBR + + free_encoder_input(); + fprintf(stderr, "%d failures\n", failures); + SHA1Result(&encode_hash, encode_digest); + SHA1Result(&decode_hash, decode_digest); + printf("encode hash: "); + for (i = 0; i < SHA_DIGEST_LENGTH; i++) + printf("%02x", encode_digest[i]); + printf("\n"); + printf("decode hash: "); + for (i = 0; i < SHA_DIGEST_LENGTH; i++) + printf("%02x", decode_digest[i]); + printf("\n"); + return failures; +}