Skip to content

Commit

Permalink
[Audio] TrueHD rework - totally new MAT packer implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
thexai committed Apr 13, 2024
1 parent 72c6e74 commit a00cd50
Show file tree
Hide file tree
Showing 7 changed files with 578 additions and 196 deletions.
6 changes: 4 additions & 2 deletions xbmc/cores/AudioEngine/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ set(SOURCES AEResampleFactory.cpp
Utils/AELimiter.cpp
Utils/AEPackIEC61937.cpp
Utils/AEStreamInfo.cpp
Utils/AEUtil.cpp)
Utils/AEUtil.cpp
Utils/PackerMAT.cpp)

set(HEADERS AEResampleFactory.h
AESinkFactory.h
Expand Down Expand Up @@ -44,7 +45,8 @@ set(HEADERS AEResampleFactory.h
Utils/AERingBuffer.h
Utils/AEStreamData.h
Utils/AEStreamInfo.h
Utils/AEUtil.h)
Utils/AEUtil.h
Utils/PackerMAT.h)

if(TARGET ALSA::ALSA)
list(APPEND SOURCES Sinks/AESinkALSA.cpp
Expand Down
2 changes: 1 addition & 1 deletion xbmc/cores/AudioEngine/Engines/ActiveAE/ActiveAESink.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -911,7 +911,7 @@ void CActiveAESink::OpenSink()
m_needIecPack = NeedIECPacking();
if (m_needIecPack)
{
m_packer = std::make_unique<CAEBitstreamPacker>();
m_packer = std::make_unique<CAEBitstreamPacker>(m_requestedFormat.m_streamInfo);
m_requestedFormat.m_sampleRate = CAEBitstreamPacker::GetOutputRate(m_requestedFormat.m_streamInfo);
m_requestedFormat.m_channelLayout = CAEBitstreamPacker::GetOutputChannelMap(m_requestedFormat.m_streamInfo);
}
Expand Down
198 changes: 22 additions & 176 deletions xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,58 +10,26 @@

#include "AEPackIEC61937.h"
#include "AEStreamInfo.h"
#include "PackerMAT.h"
#include "utils/log.h"

#include <array>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

extern "C"
{
#include <libavutil/intreadwrite.h>
}

namespace
{
constexpr auto BURST_HEADER_SIZE = 8;
constexpr auto EAC3_MAX_BURST_PAYLOAD_SIZE = 24576 - BURST_HEADER_SIZE;
} // namespace

constexpr auto MAT_PKT_OFFSET = 61440;
constexpr auto MAT_FRAME_SIZE = 61424;

/* magic MAT format values, meaning is unknown at this point */
constexpr std::array<uint8_t, 20> mat_start_code = {
0x07, 0x9E, 0x00, 0x03, 0x84, 0x01, 0x01, 0x01, 0x80, 0x00,
0x56, 0xA5, 0x3B, 0xF4, 0x81, 0x83, 0x49, 0x80, 0x77, 0xE0,
};

constexpr std::array<uint8_t, 12> mat_middle_code = {
0xC3, 0xC1, 0x42, 0x49, 0x3B, 0xFA, 0x82, 0x83, 0x49, 0x80, 0x77, 0xE0,
};

constexpr std::array<uint8_t, 16> mat_end_code = {
0xC3, 0xC2, 0xC0, 0xC4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x97, 0x11,
};

struct MatCode
{
int pos;
const uint8_t* code;
unsigned int len;
};

std::array<MatCode, 3> MatCodes = {{
{0, mat_start_code.data(), mat_start_code.size()},
{30708, mat_middle_code.data(), mat_middle_code.size()},
{MAT_FRAME_SIZE - mat_end_code.size(), mat_end_code.data(), mat_end_code.size()},
}};

} // unnamed namespace

CAEBitstreamPacker::CAEBitstreamPacker()
CAEBitstreamPacker::CAEBitstreamPacker(const CAEStreamInfo& info)
{
Reset();

if (info.m_type == CAEStreamInfo::STREAM_TYPE_TRUEHD)
m_packerMAT = std::make_unique<CPackerMAT>();
}

CAEBitstreamPacker::~CAEBitstreamPacker()
Expand All @@ -74,7 +42,8 @@ void CAEBitstreamPacker::Pack(CAEStreamInfo &info, uint8_t* data, int size)
switch (info.m_type)
{
case CAEStreamInfo::STREAM_TYPE_TRUEHD:
PackTrueHD(info, data, size);
m_packerMAT->PackTrueHD(data, size);
GetDataTrueHD();
break;

case CAEStreamInfo::STREAM_TYPE_DTSHD:
Expand Down Expand Up @@ -162,149 +131,26 @@ void CAEBitstreamPacker::Reset()
m_packedBuffer[0] = 0;
}

/* we need to pack 24 TrueHD audio units into the unknown MAT format before packing into IEC61937 */
void CAEBitstreamPacker::PackTrueHD(CAEStreamInfo &info, uint8_t* data, int size)
void CAEBitstreamPacker::GetDataTrueHD()
{
/* create the buffer if it doesn't already exist */
if (m_trueHD[0].empty())
{
m_trueHD[0].resize(MAT_FRAME_SIZE);
m_trueHD[1].resize(MAT_FRAME_SIZE);
m_thd = {};
}

if (size < 10)
if (!m_packerMAT)
return;

uint8_t* pBuf = m_trueHD[m_thd.bufferIndex].data();
const uint8_t* pData = data;

int totalFrameSize = size;
int dataRem = size;
int paddingRem = 0;
int ratebits = 0;
int nextCodeIdx = 0;
uint16_t inputTiming = 0;
bool havePacket = false;

if (AV_RB24(data + 4) == 0xf8726f)
{
/* major sync unit, fetch sample rate */
if (data[7] == 0xba)
ratebits = data[8] >> 4;
else if (data[7] == 0xbb)
ratebits = data[9] >> 4;
else
return;

m_thd.samplesPerFrame = 40 << (ratebits & 3);
}

if (!m_thd.samplesPerFrame)
return;

inputTiming = AV_RB16(data + 2);

if (m_thd.prevFrameSize)
{
uint16_t deltaSamples = inputTiming - m_thd.prevFrameTime;
/*
* One multiple-of-48kHz frame is 1/1200 sec and the IEC 61937 rate
* is 768kHz = 768000*4 bytes/sec.
* The nominal space per frame is therefore
* (768000*4 bytes/sec) * (1/1200 sec) = 2560 bytes.
* For multiple-of-44.1kHz frames: 1/1102.5 sec, 705.6kHz, 2560 bytes.
*
* 2560 is divisible by samplesPerFrame.
*/
int deltaBytes = deltaSamples * 2560 / m_thd.samplesPerFrame;

/* padding needed before this frame */
paddingRem = deltaBytes - m_thd.prevFrameSize;

// detects stream discontinuities
if (paddingRem < 0 || paddingRem >= MAT_FRAME_SIZE * 2)
{
m_thd = {}; // recovering after seek
return;
}
}

for (nextCodeIdx = 0; nextCodeIdx < static_cast<int>(MatCodes.size()); nextCodeIdx++)
if (m_thd.bufferFilled <= MatCodes[nextCodeIdx].pos)
break;
if (m_dataCountTrueHD > 0)
m_dataCountTrueHD--;

if (nextCodeIdx >= static_cast<int>(MatCodes.size()))
return;

while (paddingRem || dataRem || MatCodes[nextCodeIdx].pos == m_thd.bufferFilled)
// limits a bit MAT frames output speed as this is called every 1/1200 seconds and
// anyway only is possible obtain a MAT frame every 12 audio units (TrueHD has 24 units
// but are packed every 12 to reduce latency). As MAT packer can generate more than one
// MAT frame at time (but in average only one every 20 ms) this delays the output
// a little but still allows the queue to empty.
if (m_dataCountTrueHD == 0 && m_packerMAT->HaveOutput())
{
if (MatCodes[nextCodeIdx].pos == m_thd.bufferFilled)
{
/* time to insert MAT code */
int codeLen = MatCodes[nextCodeIdx].len;
int codeLenRemaining = codeLen;
memcpy(pBuf + MatCodes[nextCodeIdx].pos, MatCodes[nextCodeIdx].code, codeLen);
m_thd.bufferFilled += codeLen;

nextCodeIdx++;
if (nextCodeIdx == static_cast<int>(MatCodes.size()))
{
nextCodeIdx = 0;

/* this was the last code, move to the next MAT frame */
havePacket = true;
m_thd.outputBuffer = pBuf;
m_thd.bufferIndex ^= 1;
pBuf = m_trueHD[m_thd.bufferIndex].data();
m_thd.bufferFilled = 0;

/* inter-frame gap has to be counted as well, add it */
codeLenRemaining += MAT_PKT_OFFSET - MAT_FRAME_SIZE;
}

if (paddingRem)
{
/* consider the MAT code as padding */
const int countedAsPadding = std::min(paddingRem, codeLenRemaining);
paddingRem -= countedAsPadding;
codeLenRemaining -= countedAsPadding;
}
/* count the remainder of the code as part of frame size */
if (codeLenRemaining)
totalFrameSize += codeLenRemaining;
}

if (paddingRem)
{
const int paddingLen = std::min(MatCodes[nextCodeIdx].pos - m_thd.bufferFilled, paddingRem);

memset(pBuf + m_thd.bufferFilled, 0, paddingLen);
m_thd.bufferFilled += paddingLen;
paddingRem -= paddingLen;

if (paddingRem)
continue; /* time to insert MAT code */
}

if (dataRem)
{
const int dataLen = std::min(MatCodes[nextCodeIdx].pos - m_thd.bufferFilled, dataRem);

memcpy(pBuf + m_thd.bufferFilled, pData, dataLen);
m_thd.bufferFilled += dataLen;
pData += dataLen;
dataRem -= dataLen;
}
std::vector<uint8_t> mat{m_packerMAT->GetOutputFrame()};
m_dataSize = CAEPackIEC61937::PackTrueHD(mat.data() + IEC61937_DATA_OFFSET,
mat.size() - IEC61937_DATA_OFFSET, m_packedBuffer);
m_dataCountTrueHD = 3;
}

m_thd.prevFrameSize = totalFrameSize;
m_thd.prevFrameTime = inputTiming;

if (!havePacket)
return;

m_dataSize = CAEPackIEC61937::PackTrueHD(m_thd.outputBuffer, MAT_FRAME_SIZE, m_packedBuffer);
}

void CAEBitstreamPacker::PackDTSHD(CAEStreamInfo &info, uint8_t* data, int size)
Expand Down
21 changes: 6 additions & 15 deletions xbmc/cores/AudioEngine/Utils/AEBitstreamPacker.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,17 @@
#include "AEPackIEC61937.h"

#include <list>
#include <memory>
#include <stdint.h>
#include <vector>

class CAEStreamInfo;
class CPackerMAT;

class CAEBitstreamPacker
{
public:
CAEBitstreamPacker();
CAEBitstreamPacker(const CAEStreamInfo& info);
~CAEBitstreamPacker();

void Pack(CAEStreamInfo &info, uint8_t* data, int size);
Expand All @@ -32,24 +34,13 @@ class CAEBitstreamPacker
static CAEChannelInfo GetOutputChannelMap(const CAEStreamInfo& info);

private:
void PackTrueHD(CAEStreamInfo &info, uint8_t* data, int size);
void GetDataTrueHD();
void PackDTSHD(CAEStreamInfo &info, uint8_t* data, int size);
void PackEAC3(CAEStreamInfo &info, uint8_t* data, int size);

/* we keep the trueHD and dtsHD buffers separate so that we can handle a fast stream switch */
std::vector<uint8_t> m_trueHD[2];
std::unique_ptr<CPackerMAT> m_packerMAT;

struct TrueHD
{
int prevFrameSize;
int samplesPerFrame;
int bufferFilled;
int bufferIndex;
uint16_t prevFrameTime;
uint8_t* outputBuffer;
};

TrueHD m_thd{};
unsigned int m_dataCountTrueHD{0};

std::vector<uint8_t> m_dtsHD;
unsigned int m_dtsHDSize = 0;
Expand Down
4 changes: 2 additions & 2 deletions xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ int CAEPackIEC61937::PackTrueHD(uint8_t *data, unsigned int size, uint8_t *dest)
struct IEC61937Packet *packet = (struct IEC61937Packet*)dest;
packet->m_preamble1 = IEC61937_PREAMBLE1;
packet->m_preamble2 = IEC61937_PREAMBLE2;
packet->m_type = IEC61937_TYPE_TRUEHD;
packet->m_length = size;
packet->m_type = IEC61937_TYPE_TRUEHD;
packet->m_length = 61424;

if (data == NULL)
data = packet->m_data;
Expand Down

0 comments on commit a00cd50

Please sign in to comment.