155 lines
4.7 KiB
C++
155 lines
4.7 KiB
C++
#include "mlpCodec.h"
|
|
|
|
#include "bitStream.h"
|
|
#include "vod_common.h"
|
|
|
|
static const int HD_SYNC_WORD = 0xf8726f;
|
|
|
|
static const uint8_t mlp_channels[32] = {1, 2, 3, 4, 3, 4, 5, 3, 4, 5, 4, 5, 6, 4, 5, 4,
|
|
5, 6, 5, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
|
|
|
static const uint8_t thd_chancount[13] = {
|
|
// LR C LFE LRs LRvh LRc LRrs Cs Ts LRsd LRw Cvh LFE2
|
|
2, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2, 1, 1};
|
|
|
|
// returns 48/96/192/44.1/88.2/176.4 kHz, or 0 on error
|
|
int MLPCodec::mlp_samplerate(int ratebits)
|
|
{
|
|
switch (ratebits)
|
|
{
|
|
case 0:
|
|
return 48000;
|
|
case 1:
|
|
return 96000;
|
|
case 2:
|
|
return 192000;
|
|
case 8:
|
|
return 44100;
|
|
case 9:
|
|
return 88200;
|
|
case 10:
|
|
return 176400;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
uint64_t MLPCodec::getFrameDurationNano() { return INTERNAL_PTS_FREQ * m_samples / m_samplerate; }
|
|
|
|
static int numChannels6(int chanmap)
|
|
{
|
|
int channels = 0;
|
|
for (int i = 0; i < 5; i++) channels += thd_chancount[i] * ((chanmap >> i) & 1);
|
|
return channels;
|
|
}
|
|
|
|
static int numChannels8(int chanmap)
|
|
{
|
|
int channels = 0;
|
|
for (int i = 0; i < 13; i++) channels += thd_chancount[i] * ((chanmap >> i) & 1);
|
|
return channels;
|
|
}
|
|
|
|
// returns frame position starting with sync_word 0xf8726f
|
|
uint8_t* MLPCodec::findFrame(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
uint8_t* curBuf = buffer;
|
|
while (curBuf < end)
|
|
{
|
|
if (curBuf[4] == 0xf8 && curBuf[5] == 0x72 && curBuf[6] == 0x6f)
|
|
return curBuf;
|
|
else
|
|
curBuf++;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int MLPCodec::getFrameSize(uint8_t* buffer) { return ((buffer[0] & 0x0f) << 9) + (buffer[1] << 1); }
|
|
|
|
bool MLPCodec::isMinorSync(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
/* The first nibble of a frame is a parity check of the 4-byte
|
|
access unit header and all the 2- or 4-byte substream headers. */
|
|
if (m_substreams == 0)
|
|
return false;
|
|
uint8_t parity_bits = 0;
|
|
int p = 0;
|
|
for (int i = -1; i < m_substreams; i++)
|
|
{
|
|
parity_bits ^= buffer[p++];
|
|
parity_bits ^= buffer[p++];
|
|
// dynamic_range_control data if extra_substream_word flag
|
|
if (i < 0 || buffer[p - 2] & 0x80)
|
|
{
|
|
parity_bits ^= buffer[p++];
|
|
parity_bits ^= buffer[p++];
|
|
}
|
|
}
|
|
return ((parity_bits >> 4) ^ (parity_bits & 0xF)) == 0xF;
|
|
}
|
|
|
|
// returns true if TrueHD or MLP frame, false on error
|
|
bool MLPCodec::decodeFrame(uint8_t* buffer, uint8_t* end)
|
|
{
|
|
if (end - buffer < 21)
|
|
return false;
|
|
BitStreamReader reader{};
|
|
reader.setBuffer(buffer + 4, end);
|
|
if (reader.getBits(24) != HD_SYNC_WORD) /* Sync words */
|
|
return isMinorSync(buffer, end);
|
|
|
|
int ratebits = 0;
|
|
int stream_type = reader.getBits(8);
|
|
|
|
if (stream_type == 0xbb) // MLP
|
|
{
|
|
m_subType = MlpSubType::stMLP;
|
|
reader.skipBits(8); // group1_bits, group2_bits
|
|
ratebits = reader.getBits(4);
|
|
m_samplerate = mlp_samplerate(ratebits);
|
|
if (m_samplerate == 0)
|
|
return false;
|
|
reader.skipBits(4); // group2_samplerate
|
|
reader.skipBits(11);
|
|
m_channels = mlp_channels[reader.getBits(5)];
|
|
}
|
|
else if (stream_type == 0xba) // True-HD
|
|
{
|
|
m_subType = MlpSubType::stTRUEHD;
|
|
ratebits = reader.getBits(4); // audio_sampling_frequency
|
|
m_samplerate = mlp_samplerate(ratebits);
|
|
if (m_samplerate == 0)
|
|
return false;
|
|
if (reader.getBits(2) > 0) // 6/8ch_multichannel_type: 0 = standard loudspeaker layout
|
|
return false;
|
|
reader.skipBits(6); // reserved, 2ch_presentation_channel_modifier, 6ch_presentation_channel_modifier
|
|
int channel_assign_6 = reader.getBits(5); // 6ch_presentation_channel_assignment
|
|
reader.skipBits(2); // 8ch_presentation_channel_modifier
|
|
int channel_assign_8 = reader.getBits(13); // 8ch_presentation_channel_assignment
|
|
|
|
if (channel_assign_8 > 0)
|
|
{
|
|
m_channels = numChannels8(channel_assign_8);
|
|
if (m_channels > 8)
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
m_channels = numChannels6(channel_assign_6);
|
|
if (m_channels > 6)
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
return false;
|
|
|
|
m_samples = 40 << (ratebits & 7);
|
|
if (reader.getBits(16) != 0xB752) // signature
|
|
return false;
|
|
reader.skipBits(32); // flags, reserved
|
|
reader.skipBit(); // is_vbr
|
|
m_bitrate = (reader.getBits(15) /* peak_data_rate */ * m_samplerate + 8) >> 4; // + 8 is for rounding to nearest
|
|
m_substreams = reader.getBits(4);
|
|
return true;
|
|
}
|