514 lines
17 KiB
C++
514 lines
17 KiB
C++
#include "ac3Codec.h"
|
|
|
|
#include <sstream>
|
|
|
|
#include "avCodecs.h"
|
|
#include "bitStream.h"
|
|
#include "vodCoreException.h"
|
|
#include "vod_common.h"
|
|
|
|
#define max(a, b) (a > b ? a : b)
|
|
static const int NB_BLOCKS = 6; // number of PCM blocks inside an AC3 frame
|
|
static const int AC3_FRAME_SIZE = NB_BLOCKS * 256;
|
|
|
|
bool isSyncWord(uint8_t* buff) { return buff[0] == 0x0B && buff[1] == 0x77; }
|
|
|
|
bool isHDSyncWord(uint8_t* buff) { return buff[0] == 0xf8 && buff[1] == 0x72 && buff[2] == 0x6f; }
|
|
|
|
static const uint8_t eac3_blocks[4] = {1, 2, 3, 6};
|
|
|
|
const uint8_t ff_ac3_channels[8] = {2, 1, 2, 3, 3, 4, 4, 5};
|
|
|
|
// possible frequencies
|
|
const uint16_t ff_ac3_freqs[3] = {48000, 44100, 32000};
|
|
|
|
// possible bitrates
|
|
const uint16_t ff_ac3_bitratetab[19] = {32, 40, 48, 56, 64, 80, 96, 112, 128, 160,
|
|
192, 224, 256, 320, 384, 448, 512, 576, 640};
|
|
|
|
const uint16_t ff_ac3_frame_sizes[38][3] = {
|
|
{64, 69, 96}, {64, 70, 96}, {80, 87, 120}, {80, 88, 120}, {96, 104, 144},
|
|
{96, 105, 144}, {112, 121, 168}, {112, 122, 168}, {128, 139, 192}, {128, 140, 192},
|
|
{160, 174, 240}, {160, 175, 240}, {192, 208, 288}, {192, 209, 288}, {224, 243, 336},
|
|
{224, 244, 336}, {256, 278, 384}, {256, 279, 384}, {320, 348, 480}, {320, 349, 480},
|
|
{384, 417, 576}, {384, 418, 576}, {448, 487, 672}, {448, 488, 672}, {512, 557, 768},
|
|
{512, 558, 768}, {640, 696, 960}, {640, 697, 960}, {768, 835, 1152}, {768, 836, 1152},
|
|
{896, 975, 1344}, {896, 976, 1344}, {1024, 1114, 1536}, {1024, 1115, 1536}, {1152, 1253, 1728},
|
|
{1152, 1254, 1728}, {1280, 1393, 1920}, {1280, 1394, 1920}};
|
|
|
|
static const int AC3_ACMOD_MONO = 1;
|
|
static const int AC3_ACMOD_STEREO = 2;
|
|
|
|
const CodecInfo& AC3Codec::getCodecInfo()
|
|
{
|
|
if (m_true_hd_mode)
|
|
return trueHDCodecInfo;
|
|
if (isEAC3())
|
|
return eac3CodecInfo;
|
|
else
|
|
return ac3CodecInfo;
|
|
}
|
|
|
|
// returns NO_ERROR, or type of error
|
|
AC3Codec::AC3ParseError AC3Codec::parseHeader(uint8_t* buf, uint8_t* end)
|
|
{
|
|
BitStreamReader gbc{};
|
|
gbc.setBuffer(buf, end);
|
|
|
|
if (gbc.getBits(16) != 0x0B77) // sync_word
|
|
return AC3ParseError::SYNC;
|
|
|
|
// read ahead to bsid to make sure this is AC-3, not E-AC-3
|
|
int id = gbc.showBits(29) & 0x1F;
|
|
if (id > 16)
|
|
return AC3ParseError::BSID;
|
|
|
|
m_bsid = id;
|
|
if (m_bsid > 10) // bsid = 16 => EAC3
|
|
{
|
|
int numblkscod, strmtyp, substreamid, number_of_blocks_per_syncframe;
|
|
int acmod, lfeon, bsmod, fscod, dsurmod, pgmscle, extpgmscle, mixdef, paninfoe;
|
|
bsmod = fscod = dsurmod = pgmscle = extpgmscle = mixdef = paninfoe = 0;
|
|
|
|
strmtyp = gbc.getBits(2);
|
|
if (strmtyp == 3)
|
|
return AC3ParseError::SYNC; // invalid stream type
|
|
|
|
substreamid = gbc.getBits(3);
|
|
|
|
m_frame_size = (gbc.getBits(11) + 1) * 2;
|
|
if (m_frame_size < AC3_HEADER_SIZE)
|
|
return AC3ParseError::FRAME_SIZE; // invalid header size
|
|
|
|
fscod = gbc.getBits(2);
|
|
|
|
if (fscod == 3)
|
|
{
|
|
int m_fscod2 = gbc.getBits(2);
|
|
if (m_fscod2 == 3)
|
|
return AC3ParseError::SYNC;
|
|
|
|
numblkscod = 3;
|
|
m_sample_rate = ff_ac3_freqs[m_fscod2] / 2;
|
|
}
|
|
else
|
|
{
|
|
numblkscod = gbc.getBits(2);
|
|
m_sample_rate = ff_ac3_freqs[fscod];
|
|
}
|
|
number_of_blocks_per_syncframe = numblkscod == 0 ? 1 : (numblkscod == 1 ? 2 : (numblkscod == 2 ? 3 : 6));
|
|
acmod = gbc.getBits(3);
|
|
lfeon = gbc.getBit();
|
|
|
|
m_samples = eac3_blocks[numblkscod] << 8;
|
|
m_bit_rateExt = m_frame_size * m_sample_rate * 8 / m_samples;
|
|
|
|
gbc.skipBits(5); // skip bsid, already got it
|
|
for (int i = 0; i < (acmod ? 1 : 2); i++)
|
|
{
|
|
gbc.skipBits(5); // skip dialog normalization
|
|
if (gbc.getBit())
|
|
gbc.skipBits(8); // skip Compression gain word
|
|
}
|
|
|
|
if (strmtyp == 1)
|
|
{
|
|
if (gbc.getBit())
|
|
{
|
|
int chanmap = gbc.getBits(16);
|
|
if (chanmap & 0x7fe0) // mask standard 5.1 channels
|
|
m_extChannelsExists = true;
|
|
}
|
|
}
|
|
if (gbc.getBit()) // mixing metadata
|
|
{
|
|
if (acmod > 2)
|
|
gbc.skipBits(2); // dmixmod
|
|
if ((acmod & 1) && (acmod > 0x2))
|
|
gbc.skipBits(6); // ltrtcmixlev, lorocmixlev
|
|
if (acmod & 4)
|
|
gbc.skipBits(6); // ltrtsurmixlev, lorosurmixlev
|
|
if (lfeon && gbc.getBit())
|
|
gbc.skipBits(5); // lfemixlevcod
|
|
if (strmtyp == 0)
|
|
{
|
|
pgmscle = gbc.getBit();
|
|
if (pgmscle)
|
|
gbc.skipBits(6); // pgmscl
|
|
if (acmod == 0 && gbc.getBit())
|
|
gbc.skipBits(6); // pgmscl2
|
|
extpgmscle = gbc.getBit();
|
|
if (extpgmscle)
|
|
gbc.skipBits(6); // extpgmscl
|
|
mixdef = gbc.getBits(2);
|
|
if (mixdef == 1)
|
|
gbc.skipBits(5); // premixcmpsel, drcsrc, premixcmpscl
|
|
else if (mixdef == 2)
|
|
gbc.skipBits(12); // mixdata
|
|
else if (mixdef == 3)
|
|
{
|
|
int mixdeflen = gbc.getBits(5); //
|
|
if (gbc.getBit()) // mixdata2e
|
|
{
|
|
gbc.skipBits(5); // premixcmpsel, drcsrc, premixcmpscl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmlscl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmcscl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmrscl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmlscl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmrsscl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmlfescl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // dmixscl
|
|
if (gbc.getBit())
|
|
{
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmaux1scl
|
|
if (gbc.getBit())
|
|
gbc.skipBits(4); // extpgmaux2scl
|
|
}
|
|
}
|
|
if (gbc.getBit()) // mixdata3e
|
|
{
|
|
gbc.skipBits(5); // spchdat
|
|
if (gbc.getBit())
|
|
{
|
|
gbc.skipBits(7); // spchdat1, spchan1att
|
|
if (gbc.getBit())
|
|
gbc.skipBits(7); // spchdat2, spchan2att
|
|
}
|
|
}
|
|
for (int i = 0; i < mixdeflen; i++) gbc.skipBits(8); // mixdata
|
|
for (int i = 0; i < 7; i++) // mixdatafill
|
|
if (!gbc.showBits(1))
|
|
gbc.skipBit();
|
|
else
|
|
break;
|
|
}
|
|
if (acmod < 2)
|
|
{
|
|
paninfoe = gbc.getBit();
|
|
if (paninfoe)
|
|
gbc.skipBits(14); // panmean, paninfo
|
|
if (acmod == 0x0 && gbc.getBit())
|
|
gbc.skipBits(14); // panmean2, paninfo2
|
|
}
|
|
if (gbc.getBit())
|
|
{
|
|
if (numblkscod == 0)
|
|
gbc.skipBits(5); // blkmixcfginfo[0]
|
|
else
|
|
for (int blk = 0; blk < number_of_blocks_per_syncframe; blk++)
|
|
if (gbc.getBit())
|
|
gbc.skipBits(5); // blkmixcfginfo[blk]
|
|
}
|
|
}
|
|
}
|
|
if (gbc.getBit())
|
|
{
|
|
bsmod = gbc.getBits(3);
|
|
gbc.skipBits(2); // copyrightb, origbs
|
|
if (acmod == 2)
|
|
dsurmod = gbc.getBits(2);
|
|
}
|
|
m_mixinfoexists = pgmscle || extpgmscle || mixdef > 0 || paninfoe;
|
|
|
|
if (m_channels == 0) // no AC3 interleave
|
|
{
|
|
m_acmod = acmod;
|
|
m_lfeon = lfeon;
|
|
m_bsmod = bsmod;
|
|
m_fscod = fscod;
|
|
m_dsurmod = dsurmod;
|
|
m_channels = ff_ac3_channels[acmod] + lfeon;
|
|
}
|
|
}
|
|
else // AC-3
|
|
{
|
|
m_bsidBase = m_bsid; // id except AC3+ frames
|
|
m_samples = AC3_FRAME_SIZE;
|
|
gbc.skipBits(16); // m_crc1
|
|
m_fscod = gbc.getBits(2);
|
|
if (m_fscod == 3)
|
|
return AC3ParseError::SAMPLE_RATE;
|
|
|
|
m_frmsizecod = gbc.getBits(6);
|
|
if (m_frmsizecod > 37)
|
|
return AC3ParseError::FRAME_SIZE;
|
|
|
|
gbc.skipBits(5); // skip bsid, already got it
|
|
|
|
m_bsmod = gbc.getBits(3);
|
|
m_acmod = gbc.getBits(3);
|
|
if ((m_acmod & 1) && m_acmod != AC3_ACMOD_MONO) // 3 front channels
|
|
gbc.skipBits(2); // m_cmixlev
|
|
if (m_acmod & 4) // surround channel exists
|
|
gbc.skipBits(2); // m_surmixlev
|
|
if (m_acmod == AC3_ACMOD_STEREO)
|
|
m_dsurmod = gbc.getBits(2);
|
|
m_lfeon = gbc.getBit();
|
|
|
|
m_halfratecod = max(m_bsid, 8) - 8;
|
|
m_sample_rate = ff_ac3_freqs[m_fscod] >> m_halfratecod;
|
|
m_bit_rate = (ff_ac3_bitratetab[m_frmsizecod >> 1] * 1000) >> m_halfratecod;
|
|
m_channels = ff_ac3_channels[m_acmod] + m_lfeon;
|
|
m_frame_size = ff_ac3_frame_sizes[m_frmsizecod][m_fscod] * 2;
|
|
}
|
|
return AC3ParseError::NO_ERROR;
|
|
}
|
|
|
|
// returns frame length, or zero (error), or NOT_ENOUGH_BUFFER
|
|
int AC3Codec::decodeFrame(uint8_t* buf, uint8_t* end, int& skipBytes)
|
|
{
|
|
try
|
|
{
|
|
int rez = 0;
|
|
AC3ParseError err;
|
|
|
|
if (end - buf < 2)
|
|
return NOT_ENOUGH_BUFFER;
|
|
if (m_state != AC3State::stateDecodeAC3 && buf[0] == 0x0B && buf[1] == 0x77)
|
|
{
|
|
if (testDecodeTestFrame(buf, end))
|
|
m_state = AC3State::stateDecodeAC3;
|
|
}
|
|
|
|
if (m_state == AC3State::stateDecodeAC3)
|
|
{
|
|
skipBytes = 0;
|
|
err = parseHeader(buf, end);
|
|
|
|
if (err != AC3ParseError::NO_ERROR)
|
|
return 0; // parse error
|
|
|
|
m_frameDurationNano = (1000000000ull * m_samples) / m_sample_rate;
|
|
rez = m_frame_size;
|
|
}
|
|
|
|
if (getTestMode() && !m_true_hd_mode)
|
|
{
|
|
uint8_t* trueHDData = buf + rez;
|
|
if (end - trueHDData < 8)
|
|
return NOT_ENOUGH_BUFFER;
|
|
if (!isSyncWord(trueHDData) && isHDSyncWord(trueHDData + 4))
|
|
{
|
|
if (end - trueHDData < 21)
|
|
return NOT_ENOUGH_BUFFER;
|
|
m_true_hd_mode = mlp.decodeFrame(trueHDData, trueHDData + 21);
|
|
}
|
|
}
|
|
|
|
if ((m_true_hd_mode)) // ommit AC3+
|
|
{
|
|
uint8_t* trueHDData = buf + rez;
|
|
if (end - trueHDData < 7)
|
|
return NOT_ENOUGH_BUFFER;
|
|
if (m_state == AC3State::stateDecodeAC3)
|
|
{
|
|
// check if it is a real HD frame
|
|
|
|
if (trueHDData[0] != 0x0B || trueHDData[1] != 0x77 || !testDecodeTestFrame(trueHDData, end))
|
|
{
|
|
m_waitMoreData = true;
|
|
m_state = AC3State::stateDecodeTrueHDFirst;
|
|
}
|
|
return rez;
|
|
}
|
|
m_state = AC3State::stateDecodeTrueHD;
|
|
int trueHDFrameLen = (trueHDData[0] & 0x0f) << 8;
|
|
trueHDFrameLen += trueHDData[1];
|
|
trueHDFrameLen *= 2;
|
|
if (end - trueHDData < (int64_t)trueHDFrameLen + 7)
|
|
return NOT_ENOUGH_BUFFER;
|
|
if (!m_true_hd_mode)
|
|
{
|
|
m_true_hd_mode = mlp.decodeFrame(trueHDData, trueHDData + trueHDFrameLen);
|
|
}
|
|
uint8_t* nextFrame = trueHDData + trueHDFrameLen;
|
|
|
|
if (nextFrame[0] == 0x0B && nextFrame[1] == 0x77 && testDecodeTestFrame(nextFrame, end))
|
|
m_waitMoreData = false;
|
|
|
|
if (m_downconvertToAC3)
|
|
{
|
|
skipBytes = trueHDFrameLen;
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
return trueHDFrameLen;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (m_downconvertToAC3 && m_bsid > 10)
|
|
{
|
|
skipBytes = rez; // skip E-AC3 frame
|
|
return 0;
|
|
}
|
|
else
|
|
return rez;
|
|
}
|
|
}
|
|
catch (BitStreamException&)
|
|
{
|
|
return NOT_ENOUGH_BUFFER;
|
|
}
|
|
}
|
|
|
|
AC3Codec::AC3ParseError AC3Codec::testParseHeader(uint8_t* buf, uint8_t* end)
|
|
{
|
|
BitStreamReader gbc{};
|
|
gbc.setBuffer(buf, buf + 7);
|
|
|
|
int test_sync_word = gbc.getBits(16);
|
|
if (test_sync_word != 0x0B77)
|
|
return AC3ParseError::SYNC;
|
|
|
|
// read ahead to bsid to make sure this is AC-3, not E-AC-3
|
|
int test_bsid = gbc.showBits(29) & 0x1F;
|
|
/*
|
|
if (test_bsid != m_bsid)
|
|
return AC3_PARSE_ERROR_SYNC;
|
|
*/
|
|
|
|
if (test_bsid > 16)
|
|
{
|
|
return AC3ParseError::SYNC; // invalid stream type
|
|
}
|
|
else if (m_bsid > 10)
|
|
{
|
|
return AC3ParseError::SYNC; // doesn't used for EAC3
|
|
}
|
|
else
|
|
{
|
|
gbc.skipBits(16); // test_crc1
|
|
int test_fscod = gbc.getBits(2);
|
|
if (test_fscod == 3)
|
|
return AC3ParseError::SAMPLE_RATE;
|
|
|
|
int test_frmsizecod = gbc.getBits(6);
|
|
if (test_frmsizecod > 37)
|
|
return AC3ParseError::FRAME_SIZE;
|
|
|
|
gbc.skipBits(5); // skip bsid, already got it
|
|
|
|
int test_bsmod = gbc.getBits(3);
|
|
int test_acmod = gbc.getBits(3);
|
|
|
|
if (test_fscod != m_fscod || /*(test_frmsizecod>>1) != (m_frmsizecod>>1) ||*/
|
|
test_bsmod != m_bsmod /*|| test_acmod != m_acmod*/)
|
|
return AC3ParseError::SYNC;
|
|
|
|
if ((test_acmod & 1) && test_acmod != AC3_ACMOD_MONO)
|
|
gbc.skipBits(2); // test_cmixlev
|
|
|
|
if (m_acmod & 4)
|
|
gbc.skipBits(2); // test_surmixlev
|
|
|
|
if (m_acmod == AC3_ACMOD_STEREO)
|
|
{
|
|
int test_dsurmod = gbc.getBits(2);
|
|
if (test_dsurmod != m_dsurmod)
|
|
return AC3ParseError::SYNC;
|
|
}
|
|
int test_lfeon = gbc.getBit();
|
|
|
|
if (test_lfeon != m_lfeon)
|
|
return AC3ParseError::SYNC;
|
|
|
|
int test_halfratecod = max(test_bsid, 8) - 8;
|
|
int test_sample_rate = ff_ac3_freqs[test_fscod] >> test_halfratecod;
|
|
int test_bit_rate = (ff_ac3_bitratetab[test_frmsizecod >> 1] * 1000) >> test_halfratecod;
|
|
int test_channels = ff_ac3_channels[test_acmod] + test_lfeon;
|
|
int test_frame_size = ff_ac3_frame_sizes[test_frmsizecod][test_fscod] * 2;
|
|
if (test_halfratecod != m_halfratecod || test_sample_rate != m_sample_rate || test_bit_rate != m_bit_rate ||
|
|
test_channels != m_channels || test_frame_size != m_frame_size)
|
|
return AC3ParseError::SYNC;
|
|
}
|
|
return AC3ParseError::NO_ERROR;
|
|
}
|
|
|
|
bool AC3Codec::testDecodeTestFrame(uint8_t* buf, uint8_t* end)
|
|
{
|
|
return testParseHeader(buf, end) == AC3ParseError::NO_ERROR;
|
|
}
|
|
|
|
uint64_t AC3Codec::getFrameDurationNano()
|
|
{
|
|
if (m_bit_rateExt)
|
|
return m_bsid > 10 ? m_frameDurationNano : 0; // E-AC3. finish frame after AC3 frame
|
|
if (m_waitMoreData)
|
|
return 0; // AC3 HD
|
|
|
|
return m_frameDurationNano;
|
|
}
|
|
|
|
const std::string AC3Codec::getStreamInfo()
|
|
{
|
|
std::ostringstream str;
|
|
std::string hd_type;
|
|
if (mlp.m_subType == MlpSubType::stTRUEHD)
|
|
hd_type = "TRUE-HD";
|
|
else if (mlp.m_subType == MlpSubType::stMLP)
|
|
hd_type = "MLP";
|
|
else
|
|
hd_type = "UNKNOWN";
|
|
|
|
if (m_true_hd_mode)
|
|
{
|
|
if (isEAC3())
|
|
str << "E-";
|
|
str << "AC3 core + ";
|
|
str << hd_type;
|
|
if (mlp.m_substreams == 4)
|
|
str << " + ATMOS";
|
|
str << ". ";
|
|
|
|
str << "Peak bitrate: " << mlp.m_bitrate / 1000 << "Kbps (core " << m_bit_rate / 1000 << "Kbps) ";
|
|
str << "Sample Rate: " << mlp.m_samplerate / 1000 << "KHz ";
|
|
if (m_sample_rate != mlp.m_samplerate)
|
|
str << " (core " << m_sample_rate / 1000 << "Khz) ";
|
|
}
|
|
else
|
|
{
|
|
str << "Bitrate: " << (m_bit_rate + m_bit_rateExt) / 1000 << "Kbps ";
|
|
if (m_bit_rateExt)
|
|
str << "(core " << m_bit_rate / 1000 << "Kbps) ";
|
|
str << "Sample Rate: " << m_sample_rate / 1000 << "KHz ";
|
|
}
|
|
|
|
str << "Channels: ";
|
|
int channels = m_channels;
|
|
if (m_extChannelsExists)
|
|
channels += 2;
|
|
if (mlp.m_channels)
|
|
channels = mlp.m_channels;
|
|
|
|
if (m_lfeon)
|
|
str << (int)(channels - 1) << ".1";
|
|
else
|
|
str << (int)channels;
|
|
return str.str();
|
|
}
|
|
|
|
uint8_t* AC3Codec::findFrame(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
if (buffer == 0)
|
|
return 0;
|
|
uint8_t* curBuf = buffer;
|
|
while (curBuf < end - 1)
|
|
{
|
|
if (*curBuf == 0x0B && curBuf[1] == 0x77)
|
|
return curBuf;
|
|
else
|
|
curBuf++;
|
|
}
|
|
return 0;
|
|
}
|