1
0
mirror of https://github.com/JvanKatwijk/qt-dab.git synced 2025-10-06 08:12:40 +02:00
Files
SDR-DAB_Qt-DAB/sources/backend/audio/mp4processor.cpp
2025-07-17 20:49:55 +02:00

418 lines
14 KiB
C++

#
/*
* Copyright (C) 2013 .. 2024
* Jan van Katwijk (J.vanKatwijk@gmail.com)
* Lazy Chair Computing
*
* This file is part of the Qt-DAB program
*
* Qt-DAB 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.
*
* Qt-DAB 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 Qt-DAB; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
************************************************************************
* may 15 2015. A real improvement on the code
* is the addition from Stefan Poeschel to create a
* header for the aac that matches, really a big help!!!!
*
* (2019:)Furthermore, the code in the "build_aacFile" function is
* his as well. Chapeau!
************************************************************************
*/
#include "mp4processor.h"
#include "radio.h"
#include "crc-handlers.h"
#include <cstring>
#include "charsets.h"
#include "pad-handler.h"
#include "bitWriter.h"
//
/**
* \class mp4Processor is the main handler for the aac frames
* the class proper processes input and extracts the aac frames
* that are processed by the "faadDecoder" class
*/
mp4Processor::mp4Processor (RadioInterface *mr,
int16_t bitRate,
RingBuffer<complex16> *b,
RingBuffer<uint8_t> *frameBuffer,
bool backgroundFlag,
FILE *dump):
my_padhandler (mr, backgroundFlag),
my_rsDecoder (8, 0435, 0, 1, 10),
aacDecoder (mr, b) {
myRadioInterface = mr;
this -> bitRate = bitRate; // input rate
this -> frameBuffer = frameBuffer;
this -> dump = dump;
connect (this, &mp4Processor::show_frameErrors,
mr, &RadioInterface::show_frameErrors);
connect (this, &mp4Processor::show_rsErrors,
mr, &RadioInterface::show_rsErrors);
connect (this, &mp4Processor::show_aacErrors,
mr, &RadioInterface::show_aacErrors);
connect (this, &mp4Processor::isStereo,
mr, &RadioInterface::setStereo);
connect (this, &mp4Processor::newFrame,
mr, &RadioInterface::newFrame);
connect (this, &mp4Processor::show_rsCorrections,
mr, &RadioInterface::show_rsCorrections);
// connect (this, &mp4Processor::show_emptyLabel,
// mr, &RadioInterface::show_label);
superFramesize = 110 * (bitRate / 8);
RSDims = bitRate / 8;
frameBytes. resize (RSDims * 120); // input
outVector . resize (RSDims * 110);
blockFillIndex = 0;
blocksInBuffer = 0;
frameCount = 0;
frameErrors = 0;
aacErrors = 0;
crcErrors = 0;
aacFrames = 0;
successFrames = 0;
rsErrors = 0;
totalCorrections = 0;
goodFrames = 0;
}
mp4Processor::~mp4Processor () {
}
/**
* \brief addtoFrame
*
* a DAB+ superframe consists of 5 consecutive DAB frames
* we add vector for vector to the superframe. Once we have
* 5 lengths of "old" frames, we check
* Note that the packing in the entry vector is still one bit
* per Byte, nbits is the number of Bits (i.e. containing bytes)
* the function adds nbits bits, packed in bytes, to the frame
*/
void mp4Processor::addtoFrame (const std::vector<uint8_t> &V) {
int16_t nbits = 24 * bitRate;
uint8_t temp = 0;
for (int i = 0; i < nbits / 8; i ++) { // in bytes
temp = 0;
for (int j = 0; j < 8; j ++)
temp = (temp << 1) | (V [i * 8 + j] & 01);
frameBytes [blockFillIndex * nbits / 8 + i] = temp;
}
//
blocksInBuffer ++;
blockFillIndex = (blockFillIndex + 1) % 5;
//
/**
* we take the last five blocks to look at
*/
if (blocksInBuffer >= 5) {
/// first, we show the "successrate"
if (++frameCount >= 25) {
frameCount = 0;
show_frameErrors (frameErrors);
frameErrors = 0;
}
/**
* starting for real: check the fire code
* if the firecode is OK, we handle the frame
* and adjust the buffer here for the next round
* if the firecode check fails, we shift one block
*/
handleRS (frameBytes, blockFillIndex * nbits / 8,
outVector, frameErrors, rsErrors);
if (frameErrors > 0) { // cannot fix the potential frame
blocksInBuffer = 4;
return;
}
if (!fc. check (outVector. data ())) {
blocksInBuffer = 4;
return;
}
if (!processSuperframe (outVector)) {
frameErrors ++;
blocksInBuffer = 0;
return;
}
// when here, both firecode and superframe are OK
// since we processed a full cycle of 5 blocks, we just start a
// new sequence, beginning with block blockFillIndex
blocksInBuffer = 0;
totalCorrections += rsErrors;
if (++ goodFrames >= 100) {
show_rsCorrections (totalCorrections, crcErrors);
totalCorrections = 0;
goodFrames = 0;
}
if (++successFrames > 25) {
show_rsErrors (rsErrors);
successFrames = 0;
rsErrors = 0;
}
}
}
/**
* \brief processSuperframe
*
* First, we know the firecode checker gave green light
* We correct the errors using RS
*/
void mp4Processor::handleRS (const std::vector<uint8_t> &frameBytes,
int16_t base,
std::vector<uint8_t> &outVector,
int16_t &errorLines, int16_t &repairs) {
uint8_t rsIn [120];
uint8_t rsOut [110];
int16_t ler;
errorLines = 0;
repairs = 0;
/**
* apply reed-solomon error repar
* OK, what we now have is a vector with RSDims * 120 uint8_t's
* the superframe, containing parity bytes for error repair
* take into account the interleaving that is applied.
*/
for (int j = 0; j < RSDims; j ++) {
for (int k = 0; k < 120; k ++)
rsIn [k] = frameBytes [(base + j + k * RSDims) % (RSDims * 120)];
ler = my_rsDecoder. dec (rsIn, rsOut, 135);
for (int k = 0; k < 110; k ++)
outVector [j + k * RSDims] = rsOut [k];
if (ler < 0)
errorLines ++;
else
repairs += ler;
}
}
bool mp4Processor::processSuperframe (std::vector<uint8_t> &outVector){
uint8_t num_aus;
int tmp;
stream_parms streamParameters;
// bits 0 .. 15 is firecode
// bit 16 is unused
streamParameters. dacRate = (outVector [2] >> 6) & 01; // bit 17
streamParameters. sbrFlag = (outVector [2] >> 5) & 01; // bit 18
streamParameters. aacChannelMode
= (outVector [2] >> 4) & 01; // bit 19
streamParameters. psFlag = (outVector [2] >> 3) & 01; // bit 20
streamParameters. mpegSurround = (outVector [2] & 07); // bits 21 .. 23
//
// added for the aac file writer
streamParameters. CoreSrIndex =
streamParameters. dacRate ?
(streamParameters. sbrFlag ? 6 : 3) :
(streamParameters. sbrFlag ? 8 : 5);
streamParameters. CoreChConfig =
streamParameters. aacChannelMode ? 2 : 1;
streamParameters. ExtensionSrIndex =
streamParameters. dacRate ? 3 : 5;
switch (2 * streamParameters. dacRate + streamParameters. sbrFlag) {
default: // cannot happen
case 0:
num_aus = 4;
au_start [0] = 8;
au_start [1] = outVector [3] * 16 + (outVector [4] >> 4);
au_start [2] = (outVector [4] & 0xf) * 256 +
outVector [5];
au_start [3] = outVector [6] * 16 +
(outVector [7] >> 4);
au_start [4] = 110 * (bitRate / 8);
break;
//
case 1:
num_aus = 2;
au_start [0] = 5;
au_start [1] = outVector [3] * 16 +
(outVector [4] >> 4);
au_start [2] = 110 * (bitRate / 8);
break;
//
case 2:
num_aus = 6;
au_start [0] = 11;
au_start [1] = outVector [3] * 16 + (outVector [4] >> 4);
au_start [2] = (outVector [4] & 0xf) * 256 + outVector [ 5];
au_start [3] = outVector [6] * 16 + (outVector [7] >> 4);
au_start [4] = (outVector [7] & 0xf) * 256 + outVector [8];
au_start [5] = outVector [9] * 16 + (outVector [10] >> 4);
au_start [6] = 110 * (bitRate / 8);
break;
//
case 3:
num_aus = 3;
au_start [0] = 6;
au_start [1] = outVector [3] * 16 + (outVector [4] >> 4);
au_start [2] = (outVector [4] & 0xf) * 256 + outVector [5];
au_start [3] = 110 * (bitRate / 8);
break;
}
/**
* OK, the result is N * 110 * 8 bits (still single bit per byte!!!)
* extract the AU's, and prepare a buffer, with the sufficient
* lengthy for conversion to PCM samples
*/
for (int i = 0; i < num_aus; i ++) {
int16_t aac_frame_length;
/// sanity check 1
if (au_start [i + 1] < au_start [i]) {
// fprintf (stderr, "%d %d (%d)\n", au_start [i], au_start [i + 1], i);
// should not happen, all errors were corrected
return false;
}
aac_frame_length = au_start [i + 1] - au_start [i] - 2;
// just a sanity check
if ((aac_frame_length >= 960) || (aac_frame_length < 0)) {
// fprintf (stderr, "aac_frame_length = %d\n", aac_frame_length);
// return false;
}
// but first the crc check
if (check_crc_bytes (&outVector [au_start [i]],
aac_frame_length)) {
//
// first prepare dumping
//
// It is a litle tricky, but we deal with foreground and background
// tasks. For the foreground task, the user might - or might not -
// push the button to store the aac data,
// for a background task - indicated by having the var "dump" not NULL,
// the aac output is always written to a file
std::vector<uint8_t> fileBuffer;
int segmentSize =
build_aacFile (aac_frame_length,
&streamParameters,
&(outVector [au_start [i]]),
fileBuffer);
if (dump == nullptr) {
frameBuffer -> putDataIntoBuffer (fileBuffer. data (),
segmentSize);
newFrame (segmentSize);
}
else
fwrite (fileBuffer. data (), 1, segmentSize, dump);
// first handle the pad data if any
if (dump == nullptr) {
if (((outVector [au_start [i + 0]] >> 5) & 07) == 4) {
int16_t count = outVector [au_start [i] + 1];
auto *buffer = dynVec (uint8_t, count);
// uint8_t *buffer = (uint8_t *) alloca (count * sizeof (uint8_t));
memcpy (buffer, &outVector [au_start [i] + 2], count);
uint8_t L0 = buffer [count - 1];
uint8_t L1 = buffer [count - 2];
my_padhandler. processPAD (buffer, count - 3, L1, L0);
}
else {// no PAD data, so no dynamic labe;
}
//
// then handle the audio
#ifdef __WITH_FDK_AAC__
tmp = aacDecoder. MP42PCM (&streamParameters,
fileBuffer. data (),
segmentSize);
#else
uint8_t theAudioUnit [2 * 960 + 10]; // sure, large enough
memcpy (theAudioUnit,
&outVector [au_start [i]], aac_frame_length);
memset (&theAudioUnit [aac_frame_length], 0, 10);
tmp = aacDecoder. MP42PCM (&streamParameters,
theAudioUnit,
aac_frame_length);
#endif
emit isStereo ((streamParameters. aacChannelMode == 1) ||
(streamParameters. psFlag == 1));
if (tmp <= 0)
aacErrors ++;
if (++aacFrames > 25) {
show_aacErrors (aacErrors);
aacErrors = 0;
aacFrames = 0;
}
}
}
else {
crcErrors ++;
}
//
// what would happen if the errors were in the 10 parity bytes
// rather than in the 110 payload bytes?
}
return true;
}
int mp4Processor::build_aacFile (int16_t aac_frame_len,
stream_parms *sp,
uint8_t *data,
std::vector<uint8_t> &fileBuffer) {
BitWriter au_bw;
au_bw. AddBits (0x2B7, 11); // syncword
au_bw. AddBits ( 0, 13); // audioMuxLengthBytes - written later
// AudioMuxElement(1)
au_bw. AddBits ( 0, 1); // useSameStreamMux
// StreamMuxConfig()
au_bw. AddBits ( 0, 1); // audioMuxVersion
au_bw. AddBits ( 1, 1); // allStreamsSameTimeFraming
au_bw. AddBits ( 0, 6); // numSubFrames
au_bw. AddBits ( 0, 4); // numProgram
au_bw. AddBits ( 0, 3); // numLayer
if (sp -> sbrFlag) {
au_bw. AddBits (0b00101, 5); // SBR
au_bw. AddBits (sp -> CoreSrIndex, 4); // samplingFrequencyIndex
au_bw. AddBits (sp -> CoreChConfig, 4); // channelConfiguration
au_bw. AddBits (sp -> ExtensionSrIndex, 4); // extensionSamplingFrequencyIndex
au_bw. AddBits (0b00010, 5); // AAC LC
au_bw. AddBits (0b100, 3); // GASpecificConfig() with 960 transform
} else {
au_bw. AddBits (0b00010, 5); // AAC LC
au_bw. AddBits (sp -> CoreSrIndex, 4); // samplingFrequencyIndex
au_bw. AddBits (sp -> CoreChConfig, 4); // channelConfiguration
au_bw. AddBits (0b100, 3); // GASpecificConfig() with 960 transform
}
au_bw. AddBits (0b000, 3); // frameLengthType
au_bw. AddBits (0xFF, 8); // latmBufferFullness
au_bw. AddBits ( 0, 1); // otherDataPresent
au_bw. AddBits ( 0, 1); // crcCheckPresent
// PayloadLengthInfo()
for (size_t i = 0; i < (size_t)(aac_frame_len / 255); i++)
au_bw. AddBits (0xFF, 8);
au_bw. AddBits (aac_frame_len % 255, 8);
au_bw. AddBytes (data, aac_frame_len);
au_bw. WriteAudioMuxLengthBytes ();
fileBuffer = au_bw. GetData ();
return fileBuffer. size ();
}