aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.am2
-rw-r--r--src/AACDecoder.cpp160
-rw-r--r--src/AACDecoder.h49
-rw-r--r--src/odr-audioenc.cpp28
-rw-r--r--src/wavfile.cpp19
-rw-r--r--src/wavfile.h7
6 files changed, 255 insertions, 10 deletions
diff --git a/Makefile.am b/Makefile.am
index eb5cda6..9c393d7 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -88,6 +88,8 @@ odr_audioenc_SOURCES = src/odr-audioenc.cpp \
src/JackInput.h \
src/VLCInput.cpp \
src/VLCInput.h \
+ src/AACDecoder.cpp \
+ src/AACDecoder.h \
src/SampleQueue.h \
src/encryption.c \
src/encryption.h \
diff --git a/src/AACDecoder.cpp b/src/AACDecoder.cpp
new file mode 100644
index 0000000..6cbe8c4
--- /dev/null
+++ b/src/AACDecoder.cpp
@@ -0,0 +1,160 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2011 Martin Storsjo
+ * Copyright (C) 2017 Matthias P. Braendli
+ * Copyright (C) 2016 Stefan Pöschel
+ *
+ * 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 "config.h"
+#include "AACDecoder.h"
+#include <stdexcept>
+
+AACDecoder::AACDecoder(const char* wavfilename) :
+ m_wav_writer(wavfilename)
+{
+ m_handle = aacDecoder_Open(TT_MP4_RAW, 1);
+ if (not m_handle) {
+ throw std::runtime_error("AACDecoder: error opening decoder");
+ }
+}
+
+void AACDecoder::decode_frame(uint8_t *data, size_t len)
+{
+ const bool dac_rate = data[2] & 0x40;
+ const bool sbr_flag = data[2] & 0x20;
+ const bool aac_channel_mode = data[2] & 0x10;
+ const bool ps_flag = data[2] & 0x08;
+ const uint8_t mpeg_surround_config = data[2] & 0x07;
+
+ const int core_sr_index = dac_rate ?
+ (sbr_flag ? 6 : 3) : (sbr_flag ? 8 : 5); // 24/48/16/32 kHz
+ const int core_ch_config = aac_channel_mode ? 2 : 1;
+ const int extension_sr_index = dac_rate ? 3 : 5; // 48/32 kHz
+
+ int au_start[6] = {};
+
+ int num_aus = dac_rate ? (sbr_flag ? 3 : 6) : (sbr_flag ? 2 : 4);
+ au_start[0] = dac_rate ? (sbr_flag ? 6 : 11) : (sbr_flag ? 5 : 8);
+ au_start[1] = data[3] << 4 | data[4] >> 4;
+
+ if (num_aus >= 3) {
+ au_start[2] = (data[4] & 0x0F) << 8 | data[5];
+ }
+
+ if (num_aus >= 4) {
+ au_start[3] = data[6] << 4 | data[7] >> 4;
+ }
+
+ if (num_aus == 6) {
+ au_start[4] = (data[7] & 0x0F) << 8 | data[8];
+ au_start[5] = data[9] << 4 | data[10] >> 4;
+ }
+
+ au_start[num_aus] = len; // end of the buffer
+
+ for (int i = 0; i < num_aus; i++) {
+ if (au_start[i] >= au_start[i+1]) {
+ throw std::runtime_error(" AU ordering check failed\n");
+ }
+ }
+
+ if (not m_decoder_set_up) {
+ std::vector<uint8_t> asc;
+
+ // AAC LC
+ asc.push_back(0b00010 << 3 | core_sr_index >> 1);
+ asc.push_back((core_sr_index & 0x01) << 7 | core_ch_config << 3 | 0b100);
+
+ if (sbr_flag) {
+ // add SBR
+ asc.push_back(0x56);
+ asc.push_back(0xE5);
+ asc.push_back(0x80 | (extension_sr_index << 3));
+
+ if (ps_flag) {
+ // add PS
+ asc.back() |= 0x05;
+ asc.push_back(0x48);
+ asc.push_back(0x80);
+ }
+ }
+
+ uint8_t* asc_array[1] {asc.data()};
+ const unsigned int asc_sizeof_array[1] {(unsigned int) asc.size()};
+
+ AAC_DECODER_ERROR init_result = aacDecoder_ConfigRaw(m_handle,
+ asc_array, asc_sizeof_array);
+ if (init_result != AAC_DEC_OK) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: error while aacDecoder_ConfigRaw: " +
+ std::to_string(init_result));
+ }
+
+ m_channels = aac_channel_mode || ps_flag ? 2 : 1;
+ size_t output_frame_len = 960 * 2 * m_channels * (sbr_flag ? 2 : 1);
+ m_output_frame.resize(output_frame_len);
+ fprintf(stderr, " Setting decoder output frame len %zu\n", output_frame_len);
+
+ const int sample_rate = dac_rate ? 48000 : 32000;
+ m_wav_writer.initialise_header(sample_rate);
+ m_decoder_set_up = true;
+
+ fprintf(stderr, " Set up decoder with %d Hz, %s%swith %d channels\n",
+ sample_rate, (sbr_flag ? "SBR " : ""), (ps_flag ? "PS " : ""),
+ m_channels);
+
+ }
+
+ const size_t AU_CRCLEN = 2;
+ for (int i = 0; i < num_aus; i++) {
+ uint8_t *au_data = data + au_start[i];
+ size_t au_len = au_start[i+1] - au_start[i] - AU_CRCLEN;
+ decode_au(au_data, au_len);
+ }
+}
+
+void AACDecoder::decode_au(uint8_t *data, size_t len)
+{
+ uint8_t* input_buffer[1] {data};
+ const unsigned int input_buffer_size[1] {(unsigned int) len};
+ unsigned int bytes_valid = len;
+
+ // fill internal input buffer
+ AAC_DECODER_ERROR result = aacDecoder_Fill(
+ m_handle, input_buffer, input_buffer_size, &bytes_valid);
+
+ if (result != AAC_DEC_OK) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: error while aacDecoder_Fill: " +
+ std::to_string(result));
+ }
+
+ if (bytes_valid) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: aacDecoder_Fill did not consume all bytes");
+ }
+
+ // decode audio
+ result = aacDecoder_DecodeFrame(m_handle,
+ (short int*)m_output_frame.data(), m_output_frame.size(), 0);
+ if (result != AAC_DEC_OK) {
+ throw std::runtime_error(
+ "AACDecoderFDKAAC: error while aacDecoder_DecodeFrame: " +
+ std::to_string(result));
+ }
+
+ m_wav_writer.write_data(m_output_frame.data(), m_output_frame.size());
+}
diff --git a/src/AACDecoder.h b/src/AACDecoder.h
new file mode 100644
index 0000000..2c09548
--- /dev/null
+++ b/src/AACDecoder.h
@@ -0,0 +1,49 @@
+/* ------------------------------------------------------------------
+ * Copyright (C) 2011 Martin Storsjo
+ * Copyright (C) 2017 Matthias P. Braendli
+ *
+ * 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.
+ * -------------------------------------------------------------------
+ */
+
+/*!
+ * \file AACDecoder.h
+ * \brief Uses FDK-AAC to decode the AAC format for loopback tests
+ */
+
+#pragma once
+
+#include <fdk-aac/aacdecoder_lib.h>
+#include <cstdint>
+#include <vector>
+#include "wavfile.h"
+
+class AACDecoder {
+ public:
+ AACDecoder(const char* wavfilename);
+
+ void decode_frame(uint8_t *data, size_t len);
+
+ private:
+ void decode_au(uint8_t *data, size_t len);
+
+ bool m_decoder_set_up = false;
+
+ int m_channels = 0;
+
+ WavWriter m_wav_writer;
+ HANDLE_AACDECODER m_handle;
+ std::vector<uint8_t> m_output_frame;
+};
+
diff --git a/src/odr-audioenc.cpp b/src/odr-audioenc.cpp
index 84218c8..edb98f7 100644
--- a/src/odr-audioenc.cpp
+++ b/src/odr-audioenc.cpp
@@ -53,6 +53,7 @@
#include "JackInput.h"
#include "VLCInput.h"
#include "SampleQueue.h"
+#include "AACDecoder.h"
#include "zmq.hpp"
#include "common.h"
#include "wavfile.h"
@@ -177,6 +178,7 @@ void usage(const char* name)
" --sbr Force the usage of SBR (HE-AAC)\n"
" --ps Force the usage of SBR and PS (HE-AACv2)\n"
" -B, --bandwidth=VALUE Set the AAC encoder bandwidth to VALUE [Hz].\n"
+ " --decode=FILE Decode the AAC back to a wav file (loopback test).\n"
" Output and pad parameters:\n"
" -o, --output=URI Output ZMQ uri. (e.g. 'tcp://localhost:9000')\n"
" -or- Output file uri. (e.g. 'file.dabp')\n"
@@ -419,6 +421,8 @@ int main(int argc, char *argv[])
AACENC_InfoStruct info = { 0 };
int aot = AOT_NONE;
+ std::string decode_wavfilename;
+
std::string dab_channel_mode;
int dab_psy_model = 1;
std::deque<uint8_t> toolame_output_buffer;
@@ -454,6 +458,7 @@ int main(int argc, char *argv[])
{"dabmode", required_argument, 0, 4 },
{"dabpsy", required_argument, 0, 5 },
{"device", required_argument, 0, 'd'},
+ {"decode", required_argument, 0, 6 },
{"format", required_argument, 0, 'f'},
{"input", required_argument, 0, 'i'},
{"jack", required_argument, 0, 'j'},
@@ -528,6 +533,9 @@ int main(int argc, char *argv[])
case 5: // DAB psy model
dab_psy_model = std::stoi(optarg);
break;
+ case 6: // Enable loopback decoder for AAC
+ decode_wavfilename = optarg;
+ break;
case 'a':
selected_encoder = encoder_selection_t::toolame_dab;
break;
@@ -756,6 +764,7 @@ int main(int argc, char *argv[])
vec_u8 input_buf;
HANDLE_AACENCODER encoder;
+ std::unique_ptr<AACDecoder> decoder;
if (selected_encoder == encoder_selection_t::fdk_dabplus) {
int subchannel_index = bitrate / 8;
@@ -777,6 +786,10 @@ int main(int argc, char *argv[])
input_size);
input_buf.resize(input_size);
+
+ if (not decode_wavfilename.empty()) {
+ decoder.reset(new AACDecoder(decode_wavfilename.c_str()));
+ }
}
else if (selected_encoder == encoder_selection_t::toolame_dab) {
int err = toolame_init();
@@ -821,6 +834,11 @@ int main(int argc, char *argv[])
}
input_buf.resize(channels * 1152 * BYTES_PER_SAMPLE);
+
+ if (not decode_wavfilename.empty()) {
+ fprintf(stderr, "--decode not supported for DAB\n");
+ return 1;
+ }
}
/* We assume that we need to call the encoder
@@ -1225,6 +1243,16 @@ int main(int argc, char *argv[])
}
}
+ if (numOutBytes != 0 and decoder) {
+ try {
+ decoder->decode_frame(outbuf.data(), numOutBytes);
+ }
+ catch (std::runtime_error &e) {
+ fprintf(stderr, "Decoding failed with: %s\n", e.what());
+ return 1;
+ }
+ }
+
/* Check if the encoder has generated output data.
* DAB+ requires RS encoding, which is not done in ODR-DabMux and not necessary
* for DAB.
diff --git a/src/wavfile.cpp b/src/wavfile.cpp
index dbc1e13..7de4ffc 100644
--- a/src/wavfile.cpp
+++ b/src/wavfile.cpp
@@ -211,7 +211,15 @@ struct wavfile_header {
int data_length;
};
-WavWriter::WavWriter(const char *filename, int rate)
+WavWriter::WavWriter(const char *filename)
+{
+ m_fd = fopen(filename, "w+");
+ if (not m_fd) {
+ throw std::runtime_error("Could not open wav file");
+ }
+}
+
+void WavWriter::initialise_header(int rate)
{
struct wavfile_header header;
@@ -233,11 +241,6 @@ WavWriter::WavWriter(const char *filename, int rate)
header.bits_per_sample = bits_per_sample;
header.data_length = 0;
- m_fd = fopen(filename, "w+");
- if (not m_fd) {
- throw std::runtime_error("Could not open wav file");
- }
-
fwrite(&header,sizeof(header),1,m_fd);
fflush(m_fd);
@@ -261,8 +264,8 @@ WavWriter::~WavWriter()
fclose(m_fd);
}
-void WavWriter::write_data(short data[], int length)
+void WavWriter::write_data(const uint8_t *data, int length)
{
- fwrite(data,sizeof(short),length,m_fd);
+ fwrite(data,sizeof(uint8_t),length,m_fd);
}
diff --git a/src/wavfile.h b/src/wavfile.h
index 92ea56f..a8cd4d9 100644
--- a/src/wavfile.h
+++ b/src/wavfile.h
@@ -20,6 +20,7 @@
#pragma once
#include <cstdio>
+#include <cstdint>
void* wav_read_open(const char *filename);
void wav_read_close(void* obj);
@@ -29,12 +30,14 @@ int wav_read_data(void* obj, unsigned char* data, unsigned int length);
class WavWriter {
public:
- WavWriter(const char *filename, int rate);
+ WavWriter(const char *filename);
~WavWriter();
WavWriter(const WavWriter& other) = delete;
WavWriter& operator=(const WavWriter& other) = delete;
- void write_data(short *data, int length);
+ void initialise_header(int rate);
+
+ void write_data(const uint8_t *data, int length);
private:
FILE *m_fd = nullptr;