aboutsummaryrefslogtreecommitdiffstats
path: root/src/AACDecoder.cpp
diff options
context:
space:
mode:
authorMatthias P. Braendli <matthias.braendli@mpb.li>2018-07-18 11:04:00 +0200
committerMatthias P. Braendli <matthias.braendli@mpb.li>2018-07-18 11:04:00 +0200
commit5d0b852bd60fd54c24316b5bc35d44ca5530b200 (patch)
tree1ed4c97661268dd5b55ab868a01c393f18243cb9 /src/AACDecoder.cpp
parent4714fe46d8ee1bfd43fbe936d1d50402411e3446 (diff)
downloadODR-SourceCompanion-5d0b852bd60fd54c24316b5bc35d44ca5530b200.tar.gz
ODR-SourceCompanion-5d0b852bd60fd54c24316b5bc35d44ca5530b200.tar.bz2
ODR-SourceCompanion-5d0b852bd60fd54c24316b5bc35d44ca5530b200.zip
Decode AAC to measure audio levels
Diffstat (limited to 'src/AACDecoder.cpp')
-rw-r--r--src/AACDecoder.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/src/AACDecoder.cpp b/src/AACDecoder.cpp
new file mode 100644
index 0000000..3f34ca0
--- /dev/null
+++ b/src/AACDecoder.cpp
@@ -0,0 +1,181 @@
+/* ------------------------------------------------------------------
+ * 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>
+#include <string>
+
+AACDecoder::AACDecoder()
+{
+ m_handle = aacDecoder_Open(TT_MP4_RAW, 1);
+ if (not m_handle) {
+ throw std::runtime_error("AACDecoder: error opening decoder");
+ }
+
+}
+
+AACDecoder::~AACDecoder()
+{
+ if (m_handle) {
+ aacDecoder_Close(m_handle);
+ }
+}
+
+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 or 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_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);
+ }
+}
+
+AACDecoder::peak_t AACDecoder::get_peaks()
+{
+ auto p = m_peak;
+ m_peak.peak_left = 0;
+ m_peak.peak_right = 0;
+ return p;
+}
+
+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));
+ }
+
+ for (int i = 0; i < m_output_frame.size(); i+=4) {
+ const uint8_t *input_buf = m_output_frame.data();
+ int16_t l = input_buf[i] | (input_buf[i+1] << 8);
+ int16_t r = input_buf[i+2] | (input_buf[i+3] << 8);
+ m_peak.peak_left = std::max(m_peak.peak_left, l);
+ m_peak.peak_right = std::max(m_peak.peak_right, r);
+ }
+}