Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Audio] TrueHD rework - totally new MAT packer implementation #24984

Merged
merged 1 commit into from
Apr 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
195 changes: 21 additions & 174 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,27 @@ 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())
// 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 send to packer 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 when thera are more that one frame at queue but still allows the queue to empty.
if (m_dataCountTrueHD > 0)
{
m_trueHD[0].resize(MAT_FRAME_SIZE);
m_trueHD[1].resize(MAT_FRAME_SIZE);
m_thd = {};
}

if (size < 10)
m_dataCountTrueHD--;
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 (nextCodeIdx >= static_cast<int>(MatCodes.size()))
return;

while (paddingRem || dataRem || MatCodes[nextCodeIdx].pos == m_thd.bufferFilled)
if (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);
const auto& mat = m_packerMAT->GetOutputFrame();

memcpy(pBuf + m_thd.bufferFilled, pData, dataLen);
m_thd.bufferFilled += dataLen;
pData += dataLen;
dataRem -= dataLen;
}
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
6 changes: 3 additions & 3 deletions xbmc/cores/AudioEngine/Utils/AEPackIEC61937.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ int CAEPackIEC61937::PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *des
return PackDTS(data, size, dest, littleEndian, OUT_FRAMESTOBYTES(DTS3_FRAME_SIZE), IEC61937_TYPE_DTS3);
}

int CAEPackIEC61937::PackTrueHD(uint8_t *data, unsigned int size, uint8_t *dest)
int CAEPackIEC61937::PackTrueHD(const uint8_t* data, unsigned int size, uint8_t* dest)
{
if (size == 0)
return OUT_FRAMESTOBYTES(TRUEHD_FRAME_SIZE);
Expand All @@ -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
4 changes: 2 additions & 2 deletions xbmc/cores/AudioEngine/Utils/AEPackIEC61937.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ class CAEPackIEC61937
static int PackDTS_512 (uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian);
static int PackDTS_1024(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian);
static int PackDTS_2048(uint8_t *data, unsigned int size, uint8_t *dest, bool littleEndian);
static int PackTrueHD (uint8_t *data, unsigned int size, uint8_t *dest);
static int PackDTSHD (uint8_t *data, unsigned int size, uint8_t *dest, unsigned int period);
static int PackTrueHD(const uint8_t* data, unsigned int size, uint8_t* dest);
static int PackDTSHD(uint8_t* data, unsigned int size, uint8_t* dest, unsigned int period);
static int PackPause(uint8_t *dest, unsigned int millis, unsigned int framesize, unsigned int samplerate, unsigned int rep_period, unsigned int encodedRate);
private:

Expand Down