aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin Storsjo <martin@martin.st>2017-06-27 14:47:01 +0300
committerMartin Storsjo <martin@martin.st>2023-10-06 14:49:49 +0300
commit1fbe8eb3bd0907aadf5a97283866ca797370bb18 (patch)
tree77e3291aa738b6e958af715ef72e85cba61feb81
parent226848a2a10c2b56a4385417a8b2e3fb048fd952 (diff)
downloadfdk-aac-1fbe8eb3bd0907aadf5a97283866ca797370bb18.tar.gz
fdk-aac-1fbe8eb3bd0907aadf5a97283866ca797370bb18.tar.bz2
fdk-aac-1fbe8eb3bd0907aadf5a97283866ca797370bb18.zip
Add a tool for testing decoding+encoding in a number of configurations
-rw-r--r--CMakeLists.txt11
-rw-r--r--Makefile.am4
-rw-r--r--sha1.c399
-rw-r--r--sha1.h85
-rw-r--r--test-encode-decode.c383
5 files changed, 882 insertions, 0 deletions
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 <paulej@packetizer.com>
+ * 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 <paulej@packetizer.com>
+ * 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 <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#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;
+}