/*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license that can
 *  be found in the License.html file in the root of the source tree.
 */

//---------------------------------------------------------------------------
// Pre-compilation
#include "MediaInfo/PreComp.h"
#ifdef __BORLANDC__
    #pragma hdrstop
#endif
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Setup.h"
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#if defined(MEDIAINFO_DTS_YES)
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
#include "MediaInfo/Audio/File_Dts.h"
#include "ZenLib/Utils.h"
#include "ZenLib/BitStream.h"
#include "MediaInfo/TimeCode.h"
#include "MediaInfo/MediaInfo_Config_MediaInfo.h"
#if MEDIAINFO_EVENTS
    #include "MediaInfo/MediaInfo_Events.h"
#endif //MEDIAINFO_EVENTS
#include <algorithm>
using namespace ZenLib;
using namespace std;
//---------------------------------------------------------------------------

namespace MediaInfoLib
{

//***************************************************************************
// Infos
//***************************************************************************

//---------------------------------------------------------------------------
static constexpr int64u CHUNK_AUPR_HDR=0x415550522D484452LL;
static constexpr int64u CHUNK_BUILDVER=0x4255494C44564552LL;
static constexpr int64u CHUNK_CORESSMD=0x434F524553534D44LL;
static constexpr int64u CHUNK_DTSHDHDR=0x4454534844484452LL;
static constexpr int64u CHUNK_EXTSS_MD=0x45585453535F4D44LL;
static constexpr int64u CHUNK_FILEINFO=0x46494C45494E464FLL;
static constexpr int64u CHUNK_TIMECODE=0x54494D45434F4445LL;
static constexpr int64u CHUNK_STRMDATA=0x5354524D44415441LL;

//---------------------------------------------------------------------------
void Merge_FillTimeCode(File__Analyze& In, const string& Prefix, const TimeCode& TC_Time, float FramesPerSecondF, bool DropFrame, TimeCode::rounding Rounding=TimeCode::Nearest, int32u Frequency=0);

//---------------------------------------------------------------------------
static const float TC_Frame_Rate_Table[]=
{
    // Value 0 means "not indicated"
    24/1.001,
    24,
    25,
    30/1.001, // Drop
    30/1.001,
    30, // Drop
    30,
};
static constexpr size_t TC_Frame_Rate_Table_Size=sizeof(TC_Frame_Rate_Table)/sizeof(*TC_Frame_Rate_Table);
static bool TC_Frame_Rate_IsDrop(int8u Value)
{
    return Value==4 || Value==6;
}

//---------------------------------------------------------------------------
static const int16u DTS_HD_RefClockCode[]=
{
    32000,
    44100,
    48000,
};
static constexpr size_t DTS_HD_RefClockCode_Size=sizeof(DTS_HD_RefClockCode)/sizeof(*DTS_HD_RefClockCode);

//---------------------------------------------------------------------------
static const int16u CRC_CCIT_Table[256]=
{
    0x0000, 0x2110, 0x4220, 0x6330, 0x8440, 0xA550, 0xC660, 0xE770,
    0x0881, 0x2991, 0x4AA1, 0x6BB1, 0x8CC1, 0xADD1, 0xCEE1, 0xEFF1,
    0x3112, 0x1002, 0x7332, 0x5222, 0xB552, 0x9442, 0xF772, 0xD662,
    0x3993, 0x1883, 0x7BB3, 0x5AA3, 0xBDD3, 0x9CC3, 0xFFF3, 0xDEE3,
    0x6224, 0x4334, 0x2004, 0x0114, 0xE664, 0xC774, 0xA444, 0x8554,
    0x6AA5, 0x4BB5, 0x2885, 0x0995, 0xEEE5, 0xCFF5, 0xACC5, 0x8DD5,
    0x5336, 0x7226, 0x1116, 0x3006, 0xD776, 0xF666, 0x9556, 0xB446,
    0x5BB7, 0x7AA7, 0x1997, 0x3887, 0xDFF7, 0xFEE7, 0x9DD7, 0xBCC7,
    0xC448, 0xE558, 0x8668, 0xA778, 0x4008, 0x6118, 0x0228, 0x2338,
    0xCCC9, 0xEDD9, 0x8EE9, 0xAFF9, 0x4889, 0x6999, 0x0AA9, 0x2BB9,
    0xF55A, 0xD44A, 0xB77A, 0x966A, 0x711A, 0x500A, 0x333A, 0x122A,
    0xFDDB, 0xDCCB, 0xBFFB, 0x9EEB, 0x799B, 0x588B, 0x3BBB, 0x1AAB,
    0xA66C, 0x877C, 0xE44C, 0xC55C, 0x222C, 0x033C, 0x600C, 0x411C,
    0xAEED, 0x8FFD, 0xECCD, 0xCDDD, 0x2AAD, 0x0BBD, 0x688D, 0x499D,
    0x977E, 0xB66E, 0xD55E, 0xF44E, 0x133E, 0x322E, 0x511E, 0x700E,
    0x9FFF, 0xBEEF, 0xDDDF, 0xFCCF, 0x1BBF, 0x3AAF, 0x599F, 0x788F,
    0x8891, 0xA981, 0xCAB1, 0xEBA1, 0x0CD1, 0x2DC1, 0x4EF1, 0x6FE1,
    0x8010, 0xA100, 0xC230, 0xE320, 0x0450, 0x2540, 0x4670, 0x6760,
    0xB983, 0x9893, 0xFBA3, 0xDAB3, 0x3DC3, 0x1CD3, 0x7FE3, 0x5EF3,
    0xB102, 0x9012, 0xF322, 0xD232, 0x3542, 0x1452, 0x7762, 0x5672,
    0xEAB5, 0xCBA5, 0xA895, 0x8985, 0x6EF5, 0x4FE5, 0x2CD5, 0x0DC5,
    0xE234, 0xC324, 0xA014, 0x8104, 0x6674, 0x4764, 0x2454, 0x0544,
    0xDBA7, 0xFAB7, 0x9987, 0xB897, 0x5FE7, 0x7EF7, 0x1DC7, 0x3CD7,
    0xD326, 0xF236, 0x9106, 0xB016, 0x5766, 0x7676, 0x1546, 0x3456,
    0x4CD9, 0x6DC9, 0x0EF9, 0x2FE9, 0xC899, 0xE989, 0x8AB9, 0xABA9,
    0x4458, 0x6548, 0x0678, 0x2768, 0xC018, 0xE108, 0x8238, 0xA328,
    0x7DCB, 0x5CDB, 0x3FEB, 0x1EFB, 0xF98B, 0xD89B, 0xBBAB, 0x9ABB,
    0x754A, 0x545A, 0x376A, 0x167A, 0xF10A, 0xD01A, 0xB32A, 0x923A,
    0x2EFD, 0x0FED, 0x6CDD, 0x4DCD, 0xAABD, 0x8BAD, 0xE89D, 0xC98D,
    0x267C, 0x076C, 0x645C, 0x454C, 0xA23C, 0x832C, 0xE01C, 0xC10C,
    0x1FEF, 0x3EFF, 0x5DCF, 0x7CDF, 0x9BAF, 0xBABF, 0xD98F, 0xF89F,
    0x176E, 0x367E, 0x554E, 0x745E, 0x932E, 0xB23E, 0xD10E, 0xF01E,
};
uint16_t Dts_CRC_CCIT_Compute(const uint8_t* Buffer, size_t Size)
{
    uint16_t C = 0xFFFF; 
    const uint8_t *End = Buffer + Size;

    while (Buffer < End)
        C = (C >> 8) ^ CRC_CCIT_Table[((uint8_t)C) ^ *Buffer++];

    return C;
}

//---------------------------------------------------------------------------
// A DTS:X extension sync word only announces DTS:X metadata. It does not
// prove that the presentation contains dynamic objects. Probe the CRC-valid
// primary type-241 presentation metadata, matching the decoder path.
class dtsx_bit_reader
{
public:
    dtsx_bit_reader(const int8u* Data, size_t Size)
        : Data(Data), BitLimit(Size*8), BitOffset(0), IsValid(true) {}

    int32u Get(size_t Bits)
    {
        if (Bits>32 || BitOffset>BitLimit || Bits>BitLimit-BitOffset)
        {
            IsValid=false;
            return 0;
        }
        int32u Value=0;
        for (size_t i=0; i<Bits; ++i)
        {
            Value<<=1;
            Value|=(Data[BitOffset>>3]>>(7-(BitOffset&7)))&1;
            ++BitOffset;
        }
        return Value;
    }

    void Skip(size_t Bits)
    {
        if (BitOffset>BitLimit || Bits>BitLimit-BitOffset)
            IsValid=false;
        else
            BitOffset+=Bits;
    }

    bool Limit(size_t Bits)
    {
        if (Bits>BitLimit || BitOffset>Bits)
        {
            IsValid=false;
            return false;
        }
        BitLimit=Bits;
        return true;
    }

    size_t Offset() const { return BitOffset; }
    bool Valid() const { return IsValid; }

private:
    const int8u* Data;
    size_t BitLimit;
    size_t BitOffset;
    bool IsValid;
};

struct dtsx_exss_probe
{
    bool StaticFieldsPresent=false;
    int8u SizeFieldBits=0;
    int32u HeaderSize=0;
    int32u FrameSize=0;
    int32u FrameDuration=0;
    int8u AssetCount=0;
    int8u SpeakerMetadataPresent=0;
    int8u SpeakerMaskCount=0;
    int8u SpeakerCounts[8]{};
    int32u AssetSizes[8]{};
    size_t AssetHeaderBitOffsets[8]{};
};

struct dtsx_asset_probe
{
    int32u HeaderSize=0;
    int32u ChannelCount=0;
    bool OneToOneMapping=false;
    bool EmbeddedStereo=false;
    bool EmbeddedSixChannel=false;
    bool SpeakerMaskPresent=false;
    int32u SpeakerActivityMask=0;
    int8u CodingMode=0;
    int16u CodingComponents=0;
    int32u ComponentSize[12]{};
    int32u ComponentOffset[12]{};
    bool XllMetadataPresent=false;
    bool XllObjectMetadataPresent=false;
    int32u XllMetadataOffset=0;
    vector<int8u> MetadataElementSizes;
    vector<int8u> AssociatedChunkTypes;
    vector<int16u> AssociatedChunkExtents;
};

static int8u DtsX_SpeakerCount(int32u Mask)
{
    static const int8u Counts[20]={
        1, 2, 2, 1, 1, 2, 2, 1, 1, 2,
        2, 2, 1, 2, 1, 2, 1, 2, 2, 2,
    };
    int8u Count=0;
    for (size_t i=0; i<20; ++i)
        if (Mask&(1<<i))
            Count+=Counts[i];
    return Count;
}

static int8u DtsX_AddedSpeakerCount(const int8u* Buffer, size_t Size, size_t Offset, int32u FallbackReferenceMask)
{
    if (Offset>=Size)
        return 0;
    dtsx_bit_reader Source(Buffer+Offset, Size-Offset);
    int8u ChunkId=(int8u)Source.Get(8);
    Source.Skip(8); // Association flags
    if (ChunkId<2 || ChunkId>4)
        return 0;
    Source.Skip(1+4+4);
    if (Source.Get(1))
        Source.Skip(1);
    int32u ReferenceMask;
    if (Source.Get(1))
    {
        Source.Skip(2+3);
        int8u MaskWords=(int8u)(Source.Get(3)+1);
        ReferenceMask=Source.Get(4*MaskWords);
    }
    else
    {
        Source.Skip(4);
        ReferenceMask=FallbackReferenceMask;
    }
    if (Source.Get(1))
    {
        Source.Skip(6);
        if (Source.Get(1))
            Source.Skip(6);
    }
    int8u MaskWords=(int8u)(Source.Get(3)+1);
    int32u OutputMask=Source.Get(4*MaskWords);
    // A type-69 chunk is supplemental only when the combined-mix matrix
    // identifies actual speakers added to the coded bed.
    int8u ReferenceCount=DtsX_SpeakerCount(ReferenceMask);
    int8u AddedCount=DtsX_SpeakerCount(OutputMask&~ReferenceMask);
    bool FirstGainPresent=false;
    if (ChunkId<=3)
    {
        Source.Skip(5+6);
        FirstGainPresent=Source.Get(1)!=0;
        if (Source.Get(1))
        {
            Source.Skip(6);
            if (FirstGainPresent && Source.Get(1))
                Source.Skip(6);
        }
    }
    else
        Source.Skip(4);
    bool DownmixPresent=Source.Get(1)!=0;
    bool CoefficientsPresent=DownmixPresent || Source.Get(1)!=0;
    if (!CoefficientsPresent || !ReferenceCount || !AddedCount || ReferenceCount>32 || AddedCount>32)
        return 0;
    Source.Skip(6);
    int8u CommonGainSelector=(int8u)Source.Get(FirstGainPresent?1:3);
    if (CommonGainSelector)
        Source.Skip(6*(1<<(CommonGainSelector-1)));
    for (size_t Added=0; Added<AddedCount; ++Added)
    {
        int32u UpdateMask=Source.Get(ReferenceCount);
        if (!UpdateMask)
            return 0;
        for (size_t Reference=0; Reference<ReferenceCount; ++Reference)
            if ((UpdateMask&((int32u)1<<Reference)) && Source.Get(6)>61)
                return 0;
    }
    return Source.Valid()?AddedCount:0;
}

static bool DtsX_ExssHeader(const int8u* Buffer, size_t Size, dtsx_exss_probe& Header)
{
    dtsx_bit_reader Source(Buffer, Size);
    if (Source.Get(32)!=0x64582025)
        return false;
    Source.Skip(8);
    int8u StreamIndex=(int8u)Source.Get(2);
    bool LongSizeFields=Source.Get(1)!=0;
    Header.SizeFieldBits=LongSizeFields?20:16;
    Header.HeaderSize=Source.Get(LongSizeFields?12:8)+1;
    Header.FrameSize=Source.Get(Header.SizeFieldBits)+1;
    if (!Source.Valid() || Header.HeaderSize>Header.FrameSize || Header.FrameSize>Size || !Source.Limit(Header.HeaderSize*8))
        return false;

    Header.StaticFieldsPresent=Source.Get(1)!=0;
    int8u PresentationCount=1;
    if (Header.StaticFieldsPresent)
    {
        Source.Skip(2);
        Header.FrameDuration=(Source.Get(3)+1)<<9;
        if (Source.Get(1))
            Source.Skip(36);
        PresentationCount=(int8u)(Source.Get(3)+1);
        Header.AssetCount=(int8u)(Source.Get(3)+1);
        int8u ActiveExSSMasks[8]{};
        for (size_t i=0; i<PresentationCount; ++i)
            ActiveExSSMasks[i]=(int8u)Source.Get(StreamIndex+1);
        for (size_t i=0; i<PresentationCount; ++i)
            for (size_t j=0; j<=StreamIndex; ++j)
                if (ActiveExSSMasks[i]&(1<<j))
                    Source.Skip(8);
        Header.SpeakerMetadataPresent=(int8u)Source.Get(1);
        if (Header.SpeakerMetadataPresent)
        {
            Source.Skip(2);
            int8u SpeakerMaskBits=(int8u)Source.Get(2);
            Header.SpeakerMaskCount=(int8u)(Source.Get(2)+1);
            for (size_t i=0; i<Header.SpeakerMaskCount; ++i)
                Header.SpeakerCounts[i]=DtsX_SpeakerCount(Source.Get(4*(SpeakerMaskBits+1)));
        }
    }
    else
        Header.AssetCount=1;

    if (PresentationCount>8 || Header.AssetCount>8)
        return false;
    for (size_t i=0; i<Header.AssetCount; ++i)
        Header.AssetSizes[i]=Source.Get(Header.SizeFieldBits)+1;
    for (size_t i=0; i<Header.AssetCount; ++i)
    {
        Header.AssetHeaderBitOffsets[i]=Source.Offset();
        int32u AssetHeaderSize=Source.Get(9)+1;
        if (AssetHeaderSize<2)
            return false;
        Source.Skip(AssetHeaderSize*8-9);
    }
    return Source.Valid();
}

static bool DtsX_AssetHeader(const int8u* Buffer, size_t Size, const dtsx_exss_probe& Exss, size_t AssetOrdinal, dtsx_asset_probe& Asset)
{
    if (AssetOrdinal>=Exss.AssetCount || Exss.AssetHeaderBitOffsets[AssetOrdinal]>=Size*8)
        return false;
    dtsx_bit_reader Source(Buffer, Size);
    Source.Skip(Exss.AssetHeaderBitOffsets[AssetOrdinal]);
    size_t AssetStart=Source.Offset();
    Asset.HeaderSize=Source.Get(9)+1;
    if (!Source.Valid() || Asset.HeaderSize<2 || AssetStart+Asset.HeaderSize*8>Size*8)
        return false;
    Source.Get(3); // AssetIndex

    bool MixMetadataPresent=false;
    if (Exss.StaticFieldsPresent)
    {
        if (Source.Get(1))
            Source.Skip(4);
        if (Source.Get(1))
            Source.Skip(24);
        if (Source.Get(1))
            Source.Skip((Source.Get(10)+1)*8);
        Source.Skip(5+4);
        Asset.ChannelCount=Source.Get(8)+1;
        Asset.OneToOneMapping=Source.Get(1)!=0;
        if (Asset.OneToOneMapping)
        {
            if (Asset.ChannelCount>2)
                Asset.EmbeddedStereo=Source.Get(1)!=0;
            if (Asset.ChannelCount>6)
                Asset.EmbeddedSixChannel=Source.Get(1)!=0;
            Asset.SpeakerMaskPresent=Source.Get(1)!=0;
            int8u MaskBits=0;
            if (Asset.SpeakerMaskPresent)
            {
                MaskBits=(int8u)(4*(Source.Get(2)+1));
                Asset.SpeakerActivityMask=Source.Get(MaskBits);
            }
            int8u RemapSets=(int8u)Source.Get(3);
            for (size_t Set=0; Set<RemapSets; ++Set)
            {
                int8u Destinations=DtsX_SpeakerCount(Source.Get(MaskBits));
                int8u CoefficientBits=(int8u)(Source.Get(5)+1);
                for (size_t Destination=0; Destination<Destinations; ++Destination)
                {
                    int32u CoefficientMask=Source.Get(CoefficientBits);
                    int8u CoefficientCount=0;
                    for (size_t Bit=0; Bit<CoefficientBits; ++Bit)
                        CoefficientCount+=(CoefficientMask>>Bit)&1;
                    Source.Skip(5*CoefficientCount);
                }
            }
        }
        else
            Source.Skip(3);
    }

    bool LanguagePresent=Source.Get(1)!=0;
    if (LanguagePresent)
        Source.Skip(8);
    if (Source.Get(1))
        Source.Skip(5);
    if (LanguagePresent && Asset.EmbeddedStereo)
        Source.Skip(8);
    if (Exss.SpeakerMetadataPresent)
    {
        MixMetadataPresent=Source.Get(1)!=0;
        if (MixMetadataPresent)
        {
            Source.Skip(7);
            int8u AdjustmentMode=(int8u)Source.Get(2);
            Source.Skip(AdjustmentMode<=2?3:8);
            bool PerSpeakerAdjustment=Source.Get(1)!=0;
            for (size_t i=0; i<Exss.SpeakerMaskCount; ++i)
                Source.Skip(6*(PerSpeakerAdjustment?Exss.SpeakerCounts[i]:1));

            int32u ChannelSets[3]={Asset.ChannelCount, 0, 0};
            size_t ChannelSetCount=1;
            if (Asset.EmbeddedSixChannel)
                ChannelSets[ChannelSetCount++]=6;
            if (Asset.EmbeddedStereo)
                ChannelSets[ChannelSetCount++]=2;
            for (size_t Config=0; Config<Exss.SpeakerMaskCount; ++Config)
                for (size_t Set=0; Set<ChannelSetCount; ++Set)
                    for (size_t Channel=0; Channel<ChannelSets[Set]; ++Channel)
                    {
                        int32u Mask=Source.Get(Exss.SpeakerCounts[Config]);
                        int8u Active=0;
                        for (size_t Bit=0; Bit<Exss.SpeakerCounts[Config]; ++Bit)
                            Active+=(Mask>>Bit)&1;
                        Source.Skip(6*Active);
                    }
        }
    }

    Asset.CodingMode=(int8u)Source.Get(2);
    if (!Asset.CodingMode)
    {
        Asset.CodingComponents=(int16u)Source.Get(12);
        int32u ComponentOffset=0;
        for (size_t Component=4; Component<=8; ++Component)
        {
            if (!(Asset.CodingComponents&(1<<Component)))
                continue;
            int32u ComponentSize=(Source.Get(Component==7?12:14)+3)&~3;
            Asset.ComponentSize[Component]=ComponentSize;
            Asset.ComponentOffset[Component]=ComponentOffset;
            ComponentOffset+=ComponentSize;
            if (Component==4 && Source.Get(1))
                Source.Skip(2);
            if (Component==8 && Source.Get(1))
                Source.Skip(2);
        }
        if (Asset.CodingComponents&(1<<9))
        {
            Asset.ComponentOffset[9]=ComponentOffset;
            Asset.ComponentSize[9]=Source.Get(Exss.SizeFieldBits)+1;
            if (Source.Get(1))
            {
                Source.Skip(4);
                int8u DelayBits=(int8u)(Source.Get(5)+1);
                Source.Skip(DelayBits+Exss.SizeFieldBits);
            }
        }
        if (Asset.CodingComponents&(1<<10))
            Source.Skip(16);
        if (Asset.CodingComponents&(1<<11))
            Source.Skip(16);
    }
    else if (Asset.CodingMode==1)
        Source.Skip(Exss.SizeFieldBits);
    else
        Source.Skip(14);

    const bool HasXll=Asset.CodingMode==1 || (Asset.CodingMode==0 && (Asset.CodingComponents&(1<<9)));
    // Private XLL navigation may continue beyond the nominal asset header.
    // The decoder reads this tail from the containing ExSS frame.
    const size_t XllPrivateEnd=Size*8;
    if (HasXll && Source.Offset()+3<=XllPrivateEnd)
        Source.Skip(3);
    if (Asset.OneToOneMapping && Exss.StaticFieldsPresent && Exss.SpeakerMetadataPresent && !MixMetadataPresent && Source.Offset()<XllPrivateEnd)
    {
        if (Source.Get(1) && Source.Offset()<XllPrivateEnd)
        {
            bool PerSpeaker=Source.Get(1)!=0;
            Source.Skip(6*(PerSpeaker?Asset.ChannelCount:1));
        }
    }
    if (Source.Offset()<XllPrivateEnd)
        Source.Skip(1);
    if (Source.Offset()<XllPrivateEnd && Source.Get(1) && Source.Offset()+4<=XllPrivateEnd)
    {
        Source.Skip(4);
        if (Source.Offset()+8*(Exss.FrameDuration>>8)<=XllPrivateEnd)
            Source.Skip(8*(Exss.FrameDuration>>8));
    }
    if (HasXll && Source.Offset()<XllPrivateEnd)
        Source.Skip(1);
    if (Asset.SpeakerMaskPresent && Source.Offset()+2<=XllPrivateEnd)
        Source.Skip(2);
    if (HasXll && Source.Offset()+2<=XllPrivateEnd)
    {
        Asset.XllMetadataPresent=Source.Get(1)!=0;
        Asset.XllObjectMetadataPresent=Source.Get(1)!=0;
        if ((Asset.XllMetadataPresent || Asset.XllObjectMetadataPresent) && Source.Offset()+Exss.SizeFieldBits<=XllPrivateEnd)
        {
            Asset.XllMetadataOffset=Source.Get(Exss.SizeFieldBits);
            if (Asset.XllMetadataPresent && Source.Offset()+4<=XllPrivateEnd)
            {
                int8u Count=(int8u)(Source.Get(4)+1);
                if (Source.Offset()+Count*8<=XllPrivateEnd)
                    for (size_t i=0; i<Count; ++i)
                        Asset.MetadataElementSizes.push_back((int8u)Source.Get(8));
            }
            if (Asset.XllObjectMetadataPresent && Source.Offset()+4<=XllPrivateEnd)
            {
                int8u Count=(int8u)(Source.Get(4)+1);
                if (Source.Offset()+Count*8+Count*15<=XllPrivateEnd)
                {
                    for (size_t i=0; i<Count; ++i)
                        Asset.AssociatedChunkTypes.push_back((int8u)Source.Get(8));
                    for (size_t i=0; i<Count; ++i)
                        Asset.AssociatedChunkExtents.push_back((int16u)(Source.Get(15)+1));
                }
            }
        }
    }
    return Source.Valid() && Source.Offset()<=XllPrivateEnd;
}

static bool DtsX_PresentationObjectCount(const int8u* Data, size_t Size, int8u AssociationMode, int8u& ObjectCount)
{
    if (Size<2 || Data[0]!=241)
        return false;
    int8u Flags=Data[1];
    int8u SelectorBits=3;
    bool AlternateAssociation=false;
    bool ShortForm=false;
    switch (AssociationMode)
    {
        case 1: SelectorBits=1; ShortForm=(Flags&0x80)!=0; break;
        case 2: SelectorBits=2; AlternateAssociation=(Flags&0x80)!=0; ShortForm=(Flags&0x40)!=0; break;
        case 3:
        case 4: AlternateAssociation=(Flags&0x40)!=0; ShortForm=(Flags&0x20)!=0; break;
        default: AlternateAssociation=(Flags&0x20)!=0;
    }
    bool Primary=true;
    if (!AlternateAssociation)
        Primary=(Flags&(1<<(7-SelectorBits)))!=0;
    if (!Primary || ShortForm)
        return false;

    dtsx_bit_reader Source(Data+2, Size-2);
    Source.Skip(4+1+4+1);
    if (Source.Get(1))
        Source.Skip(1);
    if (!Source.Get(1))
        return false;
    ObjectCount=(int8u)(Source.Get(4)+1);
    return Source.Valid();
}

static bool DtsX_ReadBitsAt(const int8u* Buffer, size_t Size, size_t BitOffset, size_t BitCount, int32u& Value)
{
    Value=0;
    if (BitCount>32 || BitOffset>((size_t)-1)-BitCount)
        return false;
    size_t End=BitOffset+BitCount;
    if (End/8>Size || (End/8==Size && End%8))
        return false;
    for (size_t Bit=0; Bit<BitCount; ++Bit)
    {
        size_t Absolute=BitOffset+Bit;
        Value=(Value<<1)|((Buffer[Absolute>>3]>>(7-(Absolute&7)))&1);
    }
    return true;
}

static bool DtsX_FourChannelSetHeader(const int8u* Buffer, size_t Size, size_t ByteOffset)
{
    if (ByteOffset>Size)
        return false;
    const int8u* Header=Buffer+ByteOffset;
    size_t HeaderAvailable=Size-ByteOffset;
    int32u HeaderSizeMinusOne, ChannelsMinusOne;
    if (!DtsX_ReadBitsAt(Header, HeaderAvailable, 0, 10, HeaderSizeMinusOne)
     || !DtsX_ReadBitsAt(Header, HeaderAvailable, 10, 4, ChannelsMinusOne)
     || ChannelsMinusOne+1!=4)
        return false;
    size_t BitOffset=14+ChannelsMinusOne+1;
    int32u PcmMinusOne, StorageMinusOne, FrequencyIndex, FrequencyModifier, ReplacementSet;
    if (!DtsX_ReadBitsAt(Header, HeaderAvailable, BitOffset, 5, PcmMinusOne)
     || !DtsX_ReadBitsAt(Header, HeaderAvailable, BitOffset+5, 5, StorageMinusOne)
     || !DtsX_ReadBitsAt(Header, HeaderAvailable, BitOffset+10, 4, FrequencyIndex)
     || !DtsX_ReadBitsAt(Header, HeaderAvailable, BitOffset+14, 2, FrequencyModifier)
     || !DtsX_ReadBitsAt(Header, HeaderAvailable, BitOffset+16, 2, ReplacementSet))
        return false;
    size_t HeaderSize=HeaderSizeMinusOne+1;
    int32u Pcm=PcmMinusOne+1;
    int32u Storage=StorageMinusOne+1;
    return HeaderSize<=HeaderAvailable
        && Pcm<=Storage
        && (Storage==16 || Storage==20 || Storage==24)
        && FrequencyIndex==12
        && !FrequencyModifier
        && !ReplacementSet
        && !Dts_CRC_CCIT_Compute(Header, HeaderSize);
}

static bool DtsX_AlternateGeometryAt(const int8u* Control, size_t Size, size_t BitOffset)
{
    int32u SegmentLog2, SegmentSampleLog2, NavigationSizeMinusOne;
    if (!DtsX_ReadBitsAt(Control, Size, BitOffset, 4, SegmentLog2)
     || !DtsX_ReadBitsAt(Control, Size, BitOffset+4, 4, SegmentSampleLog2)
     || !DtsX_ReadBitsAt(Control, Size, BitOffset+8, 5, NavigationSizeMinusOne)
     || SegmentLog2>3 || SegmentSampleLog2>9)
        return false;
    int32u Segments=1U<<SegmentLog2;
    int32u SegmentSamples=1U<<SegmentSampleLog2;
    int32u NavigationSize=NavigationSizeMinusOne+1;
    return Segments<=8 && Segments*SegmentSamples==512 && NavigationSize>=4 && NavigationSize<=20;
}

static bool DtsX_AlternateCrcPrefixEnd(const int8u* Buffer, size_t Size, size_t Offset, size_t& PrefixEnd)
{
    static const int8u OuterSuffix[6]={0x03, 0x34, 0x38, 0x8C, 0x4F, 0x00};
    PrefixEnd=(size_t)-1;
    if (Offset>Size || Size-Offset<4)
        return false;
    const int8u* Payload=Buffer+Offset;
    size_t PayloadSize=Size-Offset;
    for (size_t Candidate=4; Candidate+sizeof(OuterSuffix)<=PayloadSize; ++Candidate)
    {
        bool Matches=true;
        for (size_t i=0; i<sizeof(OuterSuffix); ++i)
            if (Payload[Candidate+i]!=OuterSuffix[i])
                Matches=false;
        if (!Matches || Dts_CRC_CCIT_Compute(Payload, Candidate))
            continue;
        if (PrefixEnd!=(size_t)-1)
            return false;
        PrefixEnd=Candidate;
    }
    return PrefixEnd!=(size_t)-1;
}

static bool DtsX_AlternateFourChannelSet(const int8u* Buffer, size_t Size, size_t Offset, int32u Sync)
{
    static const size_t OuterSuffixSize=6;
    size_t PrefixEnd;
    if (!DtsX_AlternateCrcPrefixEnd(Buffer, Size, Offset, PrefixEnd))
        return false;
    const int8u* Payload=Buffer+Offset;
    size_t PayloadSize=Size-Offset;
    size_t ControlStart=PrefixEnd+OuterSuffixSize;
    if (ControlStart>=PayloadSize)
        return false;
    int8u Tag=Payload[ControlStart];
    size_t ControlSize=Tag==0xB2?7:(Tag>=0xC2 && Tag<=0xC6?8:0);
    if (!ControlSize || ControlSize>PayloadSize-ControlStart)
        return false;
    size_t LastGeometryBit=Sync==0xF14000D3?31:(Sync==0xF14000D4?26:25);
    size_t CommonBit=(size_t)-1;
    for (size_t Bit=18; Bit<=LastGeometryBit; ++Bit)
    {
        if (!DtsX_AlternateGeometryAt(Payload+ControlStart, ControlSize, Bit))
            continue;
        if (CommonBit!=(size_t)-1)
            return false;
        CommonBit=Bit;
    }
    if (CommonBit==(size_t)-1 || CommonBit<14)
        return false;
    int32u EncodedSpan;
    if (!DtsX_ReadBitsAt(Payload+ControlStart, ControlSize, 9, CommonBit-14, EncodedSpan))
        return false;
    size_t Nominal=(size_t)EncodedSpan*2+ControlStart+12;
    bool Found=false;
    for (size_t Adjustment=0; Adjustment<2; ++Adjustment)
    {
        if (Adjustment && !Nominal)
            continue;
        size_t Candidate=Nominal-Adjustment;
        if (!DtsX_FourChannelSetHeader(Payload, PayloadSize, Candidate))
            continue;
        if (Found)
            return false;
        Found=true;
    }
    return Found;
}

static int8u DtsX_ConfirmedSupplementalChannelCount(const int8u* Buffer, size_t Size, size_t Offset, int32u Sync)
{
    if (Sync>=0xF14000D0 && Sync<=0xF14000D4)
        return DtsX_AlternateFourChannelSet(Buffer, Size, Offset, Sync)?4:0;
    return 0;
}

static bool DtsX_ExtensionProbe(const int8u* Buffer, size_t Size, int8u& ObjectCount, int8u& SupplementalChannelCount)
{
    bool HasValidatedEvidence=false;
    ObjectCount=0;
    SupplementalChannelCount=0;
    for (size_t Offset=0; Offset+4<=Size; Offset+=4)
    {
        int32u Sync=BigEndian2int32u(Buffer+Offset);
        if (Sync!=0x02000850 && Sync!=0xF14000D0 && Sync!=0xF14000D1
         && Sync!=0xF14000D2 && Sync!=0xF14000D3 && Sync!=0xF14000D4)
            continue;
        int8u ConfirmedSupplementalChannelCount=DtsX_ConfirmedSupplementalChannelCount(Buffer, Size, Offset, Sync);
        if (ConfirmedSupplementalChannelCount)
        {
            SupplementalChannelCount=max(SupplementalChannelCount, ConfirmedSupplementalChannelCount);
            HasValidatedEvidence=true;
        }
        int8u Count=0;
        // D3 is both the DTS:X extension sync and a primary type-241
        // presentation header, so its payload carries the real object count.
        size_t PrefixEnd;
        if (Sync==0xF14000D3
         && DtsX_AlternateCrcPrefixEnd(Buffer, Size, Offset, PrefixEnd)
         && DtsX_PresentationObjectCount(Buffer+Offset, PrefixEnd, 1, Count))
        {
            ObjectCount=max(ObjectCount, Count);
            HasValidatedEvidence=true;
        }
    }
    return HasValidatedEvidence;
}

static int8u DtsX_ObjectCount(const int8u* Buffer, size_t Size, int8u& SupplementalChannelCount)
{
    dtsx_exss_probe Exss;
    if (!DtsX_ExssHeader(Buffer, Size, Exss))
        return 0;

    int8u MaximumObjectCount=0;
    int32u AssetPayloadOffset=Exss.HeaderSize;
    for (size_t AssetOrdinal=0; AssetOrdinal<Exss.AssetCount; ++AssetOrdinal)
    {
        dtsx_asset_probe Asset;
        if (!DtsX_AssetHeader(Buffer, Size, Exss, AssetOrdinal, Asset)
         || !Asset.XllMetadataPresent
         || Asset.MetadataElementSizes.empty()
         || !(Asset.CodingMode==1 || (Asset.CodingComponents&(1<<9))))
        {
            AssetPayloadOffset+=Exss.AssetSizes[AssetOrdinal];
            continue;
        }
        size_t MetadataOffset=AssetPayloadOffset+Asset.ComponentOffset[9]+Asset.XllMetadataOffset;
        size_t MetadataSize=2;
        for (auto ElementSize : Asset.MetadataElementSizes)
            MetadataSize+=2+ElementSize;
        if (MetadataOffset>Size)
        {
            AssetPayloadOffset+=Exss.AssetSizes[AssetOrdinal];
            continue;
        }
        size_t MetadataAvailable=Size-MetadataOffset;
        if (MetadataSize>MetadataAvailable || Dts_CRC_CCIT_Compute(Buffer+MetadataOffset, MetadataSize))
        {
            // Private XLL navigation may overread the nominal final element
            // size. The in-band CRC is authoritative, as in the decoder.
            size_t MetadataSizeWithoutLastPayload=MetadataSize-Asset.MetadataElementSizes.back();
            bool Recovered=false;
            for (size_t LastSize=0; LastSize<=0xFF; ++LastSize)
            {
                size_t RecoveredSize=MetadataSizeWithoutLastPayload+LastSize;
                if (RecoveredSize<=MetadataAvailable && !Dts_CRC_CCIT_Compute(Buffer+MetadataOffset, RecoveredSize))
                {
                    Asset.MetadataElementSizes.back()=(int8u)LastSize;
                    MetadataSize=RecoveredSize;
                    Recovered=true;
                    break;
                }
            }
            if (!Recovered)
            {
                AssetPayloadOffset+=Exss.AssetSizes[AssetOrdinal];
                continue;
            }
        }
        size_t ElementOffset=MetadataOffset;
        for (auto ElementSize : Asset.MetadataElementSizes)
        {
            if (ElementOffset+2+ElementSize>MetadataOffset+MetadataSize)
                break;
            int8u ObjectCount=0;
            if (DtsX_PresentationObjectCount(Buffer+ElementOffset, Size-ElementOffset, Exss.AssetCount, ObjectCount))
                MaximumObjectCount=max(MaximumObjectCount, ObjectCount);
            ElementOffset+=2+ElementSize;
        }
        size_t AssociatedOffset=MetadataOffset+MetadataSize;
        int8u AddedSpeakerCount=DtsX_AddedSpeakerCount(Buffer, Size, MetadataOffset, Asset.SpeakerActivityMask);
        for (size_t i=0; i<Asset.AssociatedChunkTypes.size() && i<Asset.AssociatedChunkExtents.size(); ++i)
        {
            size_t Extent=Asset.AssociatedChunkExtents[i];
            if (AssociatedOffset>=Size)
                break;
            size_t ChunkEnd=AssociatedOffset+min(Extent, Size-AssociatedOffset);
            if (Asset.AssociatedChunkTypes[i]==69 && AddedSpeakerCount==4)
                for (size_t Candidate=AssociatedOffset; Candidate<ChunkEnd; ++Candidate)
                    if (DtsX_FourChannelSetHeader(Buffer, ChunkEnd, Candidate))
                    {
                        SupplementalChannelCount=max(SupplementalChannelCount, (int8u)4);
                        break;
                    }
            AssociatedOffset=ChunkEnd;
        }
        AssetPayloadOffset+=Exss.AssetSizes[AssetOrdinal];
    }

    return MaximumObjectCount;
}

//---------------------------------------------------------------------------
static const char*  DTS_FrameType[]=
{
    "Termination",
    "Normal",
};

//---------------------------------------------------------------------------
static const int32u DTS_SamplingRate[]=
{
        0,  8000,  16000,  32000,      0,      0,  11025,  22050,
    44100,     0,      0,  12000,  24000,  48000,  96000, 192000,
};

//---------------------------------------------------------------------------
static const int32u DTS_BitRate[]=
{
      32000,   56000,   64000,   96000,  112000,  128000,  192000,  224000,
     256000,  320000,  384000,  448000,  512000,  576000,  640000,  754500,
     960000, 1024000, 1152000, 1280000, 1344000, 1408000, 1411200, 1472000,
    1509750, 1920000, 2048000, 3072000, 3840000,       0,       0,       0,
};

//---------------------------------------------------------------------------
static const int8u DTS_Channels[]=
{
    1, 2, 2, 2, 2, 3, 3, 4,
    4, 5, 6, 6, 6, 7, 8, 8,
};

//---------------------------------------------------------------------------
static const int8u DTS_Resolution[]=
{16, 20, 24, 24};

//---------------------------------------------------------------------------
const char*  DTS_ChannelPositions[16]=
{
    "Front: C",
    "Front: C C",
    "Front: L R",
    "Front: L R",
    "Front: L R",
    "Front: L C R",
    "Front: L R, Back: C",
    "Front: L C R, Back: C",
    "Front: L R, Side: L R",
    "Front: L C R, Side: L R",
    "Front: L C C R, Side: L R",
    "Front: L C R, Side: L R",
    "Front: L R, Side: L R, Back: L R",
    "Front: L C R, Side: L R, Back: L R",
    "Front: L R, Side: L R, Back: L C C R",
    "Front: L C R, Side: L R, Back: L C R",
};

//---------------------------------------------------------------------------
const char*  DTS_ChannelPositions2[16]=
{
    "1/0/0",
    "2/0/0",
    "2/0/0",
    "2/0/0",
    "2/0/0",
    "3/0/0",
    "2/1/0",
    "3/1/0",
    "2/2/0",
    "3/2/0",
    "4/2/0",
    "3/2/0",
    "2/2/2",
    "3/2/2",
    "2/2/4",
    "3/2/3",
};

//---------------------------------------------------------------------------
static const char*  DTS_ChannelLayout[16]=
{
    "M",
    "M M",
    "L R",
    "L R",
    "Lt Rt",
    "C L R",
    "L R Cs",
    "C L R Cs",
    "L R Ls Rs",
    "C L R Ls Rs",
    "Cl Cr L R Ls Rs",
    "C L R Ls Rs",
    "C L R Ls Rs Lrs Rrs",
    "C L R Ls Rs Lrs Rrs",
    "L R Ls Rs Rls Cs Cs Rrs",
    "C L R Ls Rs Rls Cs Rrs",
};

//---------------------------------------------------------------------------
static const char* DTS_ExtensionAudioDescriptor[]=
{
    "Channel Extension",
    "Reserved",
    "Frequency Extension",
    "Channel Extension + Frequency Extension",
    "",
    "",
    "",
    "",
};

//---------------------------------------------------------------------------
std::string DTS_HD_SpeakerActivityMask (int16u SpeakerActivityMask, bool AddCs=false, bool AddLrsRrs=false)
{
    std::string Text;
    if ((SpeakerActivityMask&0x0003)==0x0003)
        Text+="Front: L C R";
    else
    {
        if (SpeakerActivityMask&0x0001)
            Text+="Front: C";
        if (SpeakerActivityMask&0x0002)
            Text+="Front: L R";
    }

    if (SpeakerActivityMask&0x0004)
        Text+=", Side: L R";

    if (SpeakerActivityMask&0x0010 || AddCs)
        Text+=", Back: C";

    if ((SpeakerActivityMask&0x00A0)==0x00A0)
        Text+=", High: L C R";
    else
    {
        if (SpeakerActivityMask&0x0020 || AddLrsRrs)
            Text+=", High: L R";
        if (SpeakerActivityMask&0x0080)
            Text+=", High: C";
    }

    if (SpeakerActivityMask&0x0800)
        Text+=", Side: L R";

    if (SpeakerActivityMask&0x0040)
        Text+=", Back: L R";

    if (SpeakerActivityMask&0x0100)
        Text+=", TopCtrSrrd";
    if (SpeakerActivityMask&0x0200)
        Text+=", Ctr: L R";
    if (SpeakerActivityMask&0x0400)
        Text+=", Wide: L R";
    if (SpeakerActivityMask&0x2000)
        Text+=", HiSide: L R";

    if ((SpeakerActivityMask&0xC000)==0x0C000)
        Text+=", HiRear: L C R";
    else
    {
        if (SpeakerActivityMask&0x4000)
            Text+=", HiRear: C";
        if (SpeakerActivityMask&0x8000)
            Text+=", HiRear: L R";
    }

    if (SpeakerActivityMask&0x0008)
        Text+=", LFE";
    if (SpeakerActivityMask&0x1000)
        Text+=", LFE2";

    return Text;
}

//---------------------------------------------------------------------------
std::string DTS_HD_SpeakerActivityMask2 (int16u SpeakerActivityMask, bool AddCs=false, bool AddLrsRrs=false)
{
    std::string Text;
    if ((SpeakerActivityMask&0x0003)==0x0003)
        Text+="3";
    else
    {
        if (SpeakerActivityMask&0x0001)
            Text+="1";
        else if (SpeakerActivityMask&0x0002)
            Text+="2";
        else
            Text+="0";
    }

    if (SpeakerActivityMask&0x0004)
        Text+="/2";
    else if ((SpeakerActivityMask&0x0840)==0x0000)
        Text+="/0";

    if (SpeakerActivityMask&0x0010 || AddCs)
        Text+="/1";
    else if ((SpeakerActivityMask&0x0840)==0x0000)
        Text+="/0";

    if ((SpeakerActivityMask&0x00A0)==0x00A0)
        Text+=".3";
    else
    {
        if (SpeakerActivityMask&0x0020 || AddLrsRrs)
            Text+=".2";
        if (SpeakerActivityMask&0x0080)
            Text+=".2";
    }

    if (SpeakerActivityMask&0x0800)
        Text+="/2";
    if (SpeakerActivityMask&0x0040)
        Text+="/2";

    if (SpeakerActivityMask&0x0100)
        Text+=".1";
    if (SpeakerActivityMask&0x0200)
        Text+=".2";
    if (SpeakerActivityMask&0x0400)
        Text+=".2";
    if (SpeakerActivityMask&0x2000)
        Text+=".2";

    if ((SpeakerActivityMask&0xC000)==0x0C000)
        Text+=".3";
    else
    {
        if (SpeakerActivityMask&0x4000)
            Text+=".1";
        if (SpeakerActivityMask&0x8000)
            Text+=".2";
    }

    if (SpeakerActivityMask&0x0008)
        Text+=".1";
    if (SpeakerActivityMask&0x1000)
        Text+=".1";

    return Text;
}

//---------------------------------------------------------------------------
std::string DTS_HD_SpeakerActivityMask_ChannelLayout (int16u SpeakerActivityMask, bool AddCs=false, bool AddLrsRrs=false)
{
    std::string Text;
    if (SpeakerActivityMask==1)
        return "M";
    if (SpeakerActivityMask&0x0001)
        Text+=" C";
    if (SpeakerActivityMask&0x0002)
        Text+=" L R";
    if (SpeakerActivityMask&0x0004)
        Text+=" Ls Rs";
    if (SpeakerActivityMask&0x0008)
        Text+=" LFE";
    if (SpeakerActivityMask&0x0010 || AddCs)
        Text+=" Cs";
    if (SpeakerActivityMask&0x0020)
        Text+=" Lh Rh";
    if (SpeakerActivityMask&0x0040 || AddLrsRrs)
        Text+=" Lsr Rsr";
    if (SpeakerActivityMask&0x0080)
        Text+=" Ch";
    if (SpeakerActivityMask&0x0100)
        Text+=" Oh";
    if (SpeakerActivityMask&0x0200)
        Text+=" Lc Rc";
    if (SpeakerActivityMask&0x0400)
        Text+=" Lw Rw";
    if (SpeakerActivityMask&0x0800)
        Text+=" Lss Rss";
    if (SpeakerActivityMask&0x1000)
        Text+=" LFE2";
    if (SpeakerActivityMask&0x2000)
        Text+=" Lhs Rhs";
    if (SpeakerActivityMask&0x4000)
        Text+=" Chr";
    if (SpeakerActivityMask&0x8000)
        Text+=" Lhr";

    if (!Text.empty())
        Text.erase(0, 1);
    return Text;
}

//---------------------------------------------------------------------------
static const char* DTS_HD_TypeDescriptor[]=
{
    "Music",
    "Effects",
    "Dialog",
    "Commentary",
    "Visually Impaired",
    "Hearing  Impaired",
    "Isolated Music Object",
    "Music and Effects",
    "Dialog and Commentary",
    "Effects and Commentary",
    "Isolated Music Object and Commentary",
    "Isolated Music Object and Effects",
    "Karaoke",
    "Music, Effects, Dialog",
    "Complete Audio Presentation",
    "",
};

//---------------------------------------------------------------------------
static int32u DTS_HD_MaximumSampleRate[]=
{
      8000,
     16000,
     32000,
     64000,
    128000,
     22050,
     44100,
     88200,
    176400,
    352800,
     12000,
     24000,
     48000,
     96000,
    192000,
    384000,
};

//---------------------------------------------------------------------------
static int8u DTS_HD_SamplePerFrames_Factor[]=
{
    0,
    1,
    2,
    3,
    4,
    1,
    2,
    3,
    4,
    5,
    0,
    1,
    2,
    3,
    4,
    5,
};

//---------------------------------------------------------------------------
enum extension
{
    Ext_Padding4,
    Ext_Core,
    Ext_LBR,
    Ext_X96,
    Ext_XLL,
    Ext_XXCH,
    Ext_XCh,
    Ext_XBR,
    Ext_Aux,
    Ext_Max
};
static int32u DTS_Extension_Mapping[]=
{
    0x00000000,
    0x02b09261,
    0x0A801921,
    0x1D95F262,
    0x41A29547,
    0x47004A03,
    0x5A5A5A5A,
    0x655E315E,
    0x9A1105A0,
};
static_assert(sizeof(DTS_Extension_Mapping)/sizeof(decltype(*DTS_Extension_Mapping))==Ext_Max, "");
static const char* DTS_Extension_Names[]=
{
    "Padding",
    "Core",
    "LBR (Low Bit Rate)",
    "X96 (96 kHz / 24-bit)",
    "XLL (LossLess)",
    "XXCH (More Than 5.1 Channels)",
    "XCh (6.1 Channels)",
    "XBR (Extended Bit Rate)",
    "Aux",
};
static_assert(sizeof(DTS_Extension_Names)/sizeof(decltype(*DTS_Extension_Names))==Ext_Max, "");
static size_t DTS_Extension_Index_Get(int32u SyncWord)
{
    for (size_t i=0; i<Ext_Max; i++)
        if (SyncWord==DTS_Extension_Mapping[i])
            return i;
    return (size_t)-1;
}

//***************************************************************************
// Constructor/Destructor
//***************************************************************************

//---------------------------------------------------------------------------
File_Dts::File_Dts()
:File_Dts_Common()
{
    //Configuration
    ParserName="Dts";
    #if MEDIAINFO_EVENTS
        ParserIDs[0]=MediaInfo_Parser_Dts;
        StreamIDs_Width[0]=0;
    #endif //MEDIAINFO_EVENTS
    #if MEDIAINFO_TRACE
        Trace_Layers_Update(8); //Stream
    #endif //MEDIAINFO_TRACE
    MustSynchronize=true;
    Buffer_TotalBytes_FirstSynched_Max=64*1024;
    PTS_DTS_Needed=true;
    StreamSource=IsStream;

    //In
    Frame_Count_Valid=0;

    //Buffer
    Save_Buffer=NULL;

    //Temp
    HD_size=0;
    Primary_Frame_Byte_Size=0;
    HD_SpeakerActivityMask=(int16u)-1;
    channel_arrangement=(int8u)-1;
    sample_frequency=(int8u)-1;
    bit_rate=(int8u)-1;
    lfe_effects=(int8u)-1;
    bits_per_sample=(int8u)-1;
    ExtensionAudioDescriptor=(int8u)-1;
    HD_BitResolution=(int8u)-1;
    HD_BitResolution_Real=(int8u)-1;
    HD_MaximumSampleRate=(int8u)-1;
    HD_MaximumSampleRate_Real=(int8u)-1;
    HD_TotalNumberChannels=(int8u)-1;
    HD_ExSSFrameDurationCode=(int8u)-1;
    DtsXObjectCount=0;
    DtsXSupplementalChannelCount=0;
    DtsXObjectCountProbeDone=false;
    AuxiliaryData=false;
    ExtendedCoding=false;
    ES=false;
    Core_Exists=false;
}

//***************************************************************************
// Streams management
//***************************************************************************

//---------------------------------------------------------------------------
void File_Dts::Streams_Fill_Extension()
{
    bool AddCs=false;
    //bool AddLrsRrs=false;
    //bool AddX=false;

    if (HD_TotalNumberChannels!=(int8u)-1)
    {
        int8u i=HD_TotalNumberChannels;
        int8u Core_Core_Channels=DTS_Channels[Core_Core_AMODE];
        if (Presence[presence_Core_Core] && Core_Core_LFF)
            Core_Core_Channels++;

        if (Presence[presence_Core_XXCH] && i<Core_Core_Channels+Core_XXCH_nuNumChSetsInXXCH)
        {
            //switch (Core_XXCH_nuNumChSetsInXXCH)
            //{
            //    case 1: AddCs=true; break;
            //    case 2: AddLrsRrs=true; break;
            //    default: AddX=true;
            //}

            //i=Core_Core_Channels+Core_XXCH_nuNumChSetsInXXCH;
        }
        if (Presence[presence_Core_XCh] && i<Core_Core_Channels+Core_XCh_AMODE)
        {
            //AddCs=true;

            //i=Core_Core_Channels+Core_XCh_AMODE;
        }
        if (!Presence[presence_Core_XCh] && ES && i<Core_Core_Channels+1)
        {
            AddCs=true;

            i=Core_Core_Channels+1; // Must count the Matrixed additional channels
        }

        Data[Channels].push_back(Ztring::ToZtring(i));
    }
    else
    {
        Data[Channels].push_back(Ztring());
    }
    if (HD_SpeakerActivityMask!=(int16u)-1) // && !AddX)
    {
        Data[ChannelPositions].push_back(DTS_HD_SpeakerActivityMask(HD_SpeakerActivityMask, AddCs).c_str()); //AddLrsRrs
        Data[ChannelPositions2].push_back(DTS_HD_SpeakerActivityMask2(HD_SpeakerActivityMask, AddCs).c_str()); //AddLrsRrs
        Data[ChannelLayout].push_back(DTS_HD_SpeakerActivityMask_ChannelLayout(HD_SpeakerActivityMask, AddCs).c_str()); //AddLrsRrs
    }
    else
    {
        Data[ChannelPositions].push_back(Ztring());
        Data[ChannelPositions2].push_back(Ztring());
        Data[ChannelLayout].push_back(Ztring());
    }
    if (HD_BitResolution_Real!=(int8u)-1)
    {
        Data[BitDepth].push_back(Ztring::ToZtring(HD_BitResolution_Real));
    }
    else if (HD_BitResolution!=(int8u)-1)
    {
        Data[BitDepth].push_back(Ztring::ToZtring(HD_BitResolution));
    }
    else
    {
        Data[BitDepth].push_back(Ztring());
    }
    if (HD_MaximumSampleRate_Real!=(int8u)-1)
    {
        Data[SamplingRate].push_back(Ztring::ToZtring(DTS_HD_MaximumSampleRate[HD_MaximumSampleRate_Real]));
        Data[SamplesPerFrame].push_back(Ztring::ToZtring(HD_ExSSFrameDurationCode<<(7+DTS_HD_SamplePerFrames_Factor[HD_MaximumSampleRate_Real])));
    }
    else if (HD_MaximumSampleRate!=(int8u)-1)
    {
        Data[SamplingRate].push_back(Ztring::ToZtring(DTS_HD_MaximumSampleRate[HD_MaximumSampleRate]));
        Data[SamplesPerFrame].push_back(Ztring::ToZtring(HD_ExSSFrameDurationCode<<(7+DTS_HD_SamplePerFrames_Factor[HD_MaximumSampleRate])));
    }
    else
    {
        Data[SamplingRate].push_back(Ztring());
        Data[SamplesPerFrame].push_back(Ztring());
    }
    Data[BitRate].push_back(Ztring::ToZtring(BitRate_Get(true), 0));
    Data[BitRate_Mode].push_back(__T("CBR"));
    Data[Compression_Mode].push_back(__T("Lossy"));
}

//---------------------------------------------------------------------------
void File_Dts::Streams_Fill_Core_ES()
{
    Streams_Fill_Core();

    if (!ES && !Presence[presence_Core_XCh])
        return;

    int8u Channels_Before=Data[Channels][Data[Channels].size()-1].To_int8u();
    Data[Channels].pop_back();
    Data[ChannelPositions].pop_back();
    Data[ChannelPositions2].pop_back();
    Data[ChannelLayout].pop_back();

    Data[Channels].push_back(Ztring::ToZtring(Channels_Before+(Presence[presence_Core_XCh]?Core_XCh_AMODE:1)));
    if (Core_Core_AMODE == 9 && (!Presence[presence_Core_XCh] || Core_XCh_AMODE == 1))
    {
        Data[ChannelPositions].push_back(Ztring(__T("Front: L C R, Side: L R, Back: C"))+(Core_Core_LFF?__T(", LFE"):__T("")));
        Data[ChannelPositions2].push_back(Ztring(__T("3/2/1"))+(Core_Core_LFF?__T(".1"):__T("")));
        Data[ChannelLayout].push_back(Ztring(__T("C L R Ls Rs Cs"))+(Core_Core_LFF?__T(" LFE"):__T("")));
    }
    else if (Core_Core_AMODE == 9 && (Presence[presence_Core_XCh] && Core_XCh_AMODE == 2))
    {
        Data[ChannelPositions].push_back(Ztring(__T("Front: L C R, Side: L R, Back: L R"))+(Core_Core_LFF?__T(", LFE"):__T("")));
        Data[ChannelPositions2].push_back(Ztring(__T("3/2/2"))+(Core_Core_LFF?__T(".1"):__T("")));
        Data[ChannelLayout].push_back(Ztring(__T("C L R Ls Rs Lrs Rrs"))+(Core_Core_LFF?__T(" LFE"):__T("")));
    }
    else
    {
        Ztring Debug=__T("Debug, Core_Core_AMODE=")+Ztring::ToZtring(Core_Core_AMODE);
        if(Presence[presence_Core_XCh])
           Debug +=__T(", Core_XCh_AMODE=")+Ztring::ToZtring(Core_XCh_AMODE);
        Debug +=__T(", Core_Core_LFF=")+Ztring::ToZtring(Core_Core_LFF);
        Data[ChannelPositions].push_back(Debug);
        Data[ChannelPositions2].push_back(Debug);
        Data[ChannelLayout].push_back(Debug);
    }
}

//---------------------------------------------------------------------------
void File_Dts::Streams_Fill_Core(bool With96k)
{
    Ztring Core_BitRate;
    if (bit_rate<25)
        Core_BitRate=Ztring::ToZtring(BitRate_Get(), 0);
    else if (bit_rate==29)
        Core_BitRate=__T("Open");
    else
        Core_BitRate=__T("Unknown");

    if (Core_Core_AMODE<16)
    {
        Data[Channels].push_back(Ztring::ToZtring(DTS_Channels[Core_Core_AMODE]+(Core_Core_LFF?1:0)));
        Data[ChannelPositions].push_back(Ztring().From_UTF8(DTS_ChannelPositions[Core_Core_AMODE])+(Core_Core_LFF?__T(", LFE"):__T("")));
        Data[ChannelPositions2].push_back(Ztring().From_UTF8(DTS_ChannelPositions2[Core_Core_AMODE])+(Core_Core_LFF?__T(".1"):__T(".0")));
        Data[ChannelLayout].push_back(Ztring().From_UTF8(DTS_ChannelLayout[Core_Core_AMODE])+(Core_Core_LFF?__T(" LFE"):__T("")));
    }
    else
    {
        Data[Channels].push_back(__T("User Defined"));
        Data[ChannelPositions].push_back(Ztring(__T("User Defined"))+(Core_Core_LFF?__T(", LFE"):__T("")));
        Data[ChannelPositions2].push_back(Ztring(__T("User Defined"))+(Core_Core_LFF?__T(".1"):__T(".0")));
        Data[ChannelLayout].push_back(Ztring(__T("User Defined"))+(Core_Core_LFF?__T(" LFE"):__T("")));
    }
    Data[BitDepth].push_back(Ztring::ToZtring(DTS_Resolution[bits_per_sample]));
    Data[SamplingRate].push_back(Ztring::ToZtring(DTS_SamplingRate[sample_frequency]*(1+With96k)));
    Data[SamplesPerFrame].push_back(Ztring::ToZtring(Number_Of_PCM_Sample_Blocks*32*(1+With96k)));
    Data[BitRate].push_back(Core_BitRate);
    Data[BitRate_Mode].push_back(__T("CBR"));
    Data[Compression_Mode].push_back(__T("Lossy"));
}

//---------------------------------------------------------------------------
void File_Dts::Streams_Fill()
{
    Fill(Stream_General, 0, General_Format, "DTS");

    Stream_Prepare(Stream_Audio);
    Fill(Stream_Audio, 0, Audio_Format, "DTS");
    Ztring DtsXBedChannelConfiguration;
    Ztring DtsXBedChannelPositions;
    Ztring DtsXBedChannelPositions2;
    size_t DtsXBedChannelCount=0;

    // IMAX DTS:X
    if (Presence[presence_Extended_IMAX])
    {
        Data[Profiles].push_back(__T("IMAX"));
        Streams_Fill_Extension();
        if (DtsXObjectCount)
        {
            Ztring Objects=__T("Objects (")+Ztring::ToZtring(DtsXObjectCount)+__T(")");
            Data[ChannelPositions].back()+=__T(", ")+Objects;
            Data[ChannelPositions2].back()+=__T(".?");
            Data[ChannelLayout].back()+=__T(" ")+Objects;
        }
        Data[BitRate].pop_back();
        Data[BitRate_Mode].pop_back();
        Data[BitRate].push_back(__T("Unknown"));
        Data[BitRate_Mode].push_back(__T("VBR"));
        if (Presence[presence_Extended_XLL])
        {
            Data[Compression_Mode].pop_back();
            Data[Compression_Mode].push_back(__T("Lossless"));
        }
    }

    // DTS:X
    if (Presence[presence_Extended_X])
    {
        Data[Profiles].push_back(__T("X"));
        Streams_Fill_Extension();
        if (DtsXSupplementalChannelCount && !Data[ChannelLayout].back().empty() && !Data[Channels].back().empty())
        {
            DtsXBedChannelConfiguration=Data[ChannelLayout].back()+__T(" Tfl Tfr Tbl Tbr");
            if (!Data[ChannelPositions].back().empty())
            {
                DtsXBedChannelPositions=Data[ChannelPositions].back();
                Ztring Heights=__T(", TopFront: L R, TopRear: L R");
                size_t LfePos=DtsXBedChannelPositions.find(__T(", LFE"));
                if (LfePos==string::npos)
                    DtsXBedChannelPositions+=Heights;
                else
                    DtsXBedChannelPositions.insert(LfePos, Heights);
            }
            if (!Data[ChannelPositions2].back().empty())
                DtsXBedChannelPositions2=Data[ChannelPositions2].back()+__T(".4");
            DtsXBedChannelCount=Data[Channels].back().To_int64u()+DtsXSupplementalChannelCount;
        }
        if (DtsXObjectCount)
        {
            Ztring Objects=__T("Objects (")+Ztring::ToZtring(DtsXObjectCount)+__T(")");
            Data[ChannelPositions].back()+=__T(", ")+Objects;
            Data[ChannelPositions2].back()+=__T(".?");
            Data[ChannelLayout].back()+=__T(" ")+Objects;
        }
        Data[BitRate].pop_back();
        Data[BitRate_Mode].pop_back();
        Data[BitRate].push_back(__T("Unknown"));
        Data[BitRate_Mode].push_back(__T("VBR"));
        if (Presence[presence_Extended_XLL])
        {
            Data[Compression_Mode].pop_back();
            Data[Compression_Mode].push_back(__T("Lossless"));
        }
    }

    // DTS Express
    if (Presence[presence_Extended_LBR])
    {
        Data[Profiles].push_back(__T("Express"));
        Streams_Fill_Extension();
    }

    // DTS-HD MA
    if (Presence[presence_Extended_XLL])
    {
        Data[Profiles].push_back(__T("MA"));
        Streams_Fill_Extension();
        Data[BitRate].pop_back();
        Data[BitRate_Mode].pop_back();
        Data[Compression_Mode].pop_back();
        Data[BitRate].push_back(__T("Unknown"));
        Data[BitRate_Mode].push_back(__T("VBR"));
        Data[Compression_Mode].push_back(__T("Lossless"));
    }

    // DTS-HD XXCH
    if (Presence[presence_Extended_XXCH])
    {
        Data[Profiles].push_back(__T("XXCH"));
        Streams_Fill_Extension();
    }

    // DTS-HD XCh
    if (Presence[presence_Extended_XCh])
    {
        Data[Profiles].push_back(__T("XCH"));
        Streams_Fill_Extension();
    }

    // DTS-HD XBR
    if (Presence[presence_Extended_XBR])
    {
        Data[Profiles].push_back(__T("XBR"));
        Streams_Fill_Extension();
    }

    // DTS-HD 96/24
    if (Presence[presence_Extended_X96])
    {
        Data[Profiles].push_back(__T("X96"));
        Streams_Fill_Extension();
    }

    // DTS XXCH
    if (Presence[presence_Core_XXCH])
    {
        Data[Profiles].push_back(__T("XXCh"));
        Streams_Fill_Core();
    }

    // DTS 96/24
    if (Presence[presence_Core_X96])
    {
        Data[Profiles].push_back(__T("x96"));
        Streams_Fill_Core(true);
    }

    // DTS XCh
    if (Presence[presence_Core_XCh])
    {
        Data[Profiles].push_back(__T("ES Discrete"));
        Streams_Fill_Core_ES();
    }

    // ES
    if (ES)
    {
        Data[Profiles].push_back(__T("ES Matrix"));
        Streams_Fill_Core_ES();
    }

    // Core
    if (Presence[presence_Core_Core])
    {
        Data[Profiles].push_back(__T("Core"));
        Streams_Fill_Core();
    }

    // Cleanup up
    for (auto& Item : Data)
    {
        Item.Separator_Set(0, __T(" / "));
        if (Item.size()>1)
        {
            bool IsNotSame=false;
            for (size_t i=1; i<Item.size(); ++i)
                if (Item[i]!=Item[0])
                    IsNotSame=true;
            if (!IsNotSame)
                Item.resize(1);
        }
    }
    if (Data[Profiles].size()==1 && Data[Profiles][0]==__T("Core"))
        Data[Profiles].clear(); //Core is the default one

    // Filling
    bool LegacyStreamDisplay=MediaInfoLib::Config.LegacyStreamDisplay_Get();
    Fill(Stream_Audio, 0, Audio_Format_Profile, Data[Profiles].Read());
    Fill(Stream_Audio, 0, Audio_Codec, (Data[Profiles].Find(__T("MA"))!=string::npos || Data[Profiles].Find(__T("HRA"))!=string::npos)?"DTS-HD":"DTS");
    Fill(Stream_General, 0, General_Format_Profile, Retrieve(Stream_Audio, 0, Audio_Format_Profile));
    Fill(Stream_Audio, 0, Audio_Channel_s_, LegacyStreamDisplay?Data[Channels].Read():Data[Channels].Read(0));
    Fill(Stream_Audio, 0, Audio_ChannelPositions, LegacyStreamDisplay?Data[ChannelPositions].Read():Data[ChannelPositions].Read(0));
    Fill(Stream_Audio, 0, Audio_ChannelPositions_String2, LegacyStreamDisplay?Data[ChannelPositions2].Read():Data[ChannelPositions2].Read(0));
    Fill(Stream_Audio, 0, Audio_ChannelLayout, LegacyStreamDisplay?Data[ChannelLayout].Read():Data[ChannelLayout].Read(0));
    Fill(Stream_Audio, 0, Audio_BitDepth, LegacyStreamDisplay?Data[BitDepth].Read():Data[BitDepth].Read(0));
    Fill(Stream_Audio, 0, Audio_SamplingRate, LegacyStreamDisplay?Data[SamplingRate].Read():Data[SamplingRate].Read(0));
    Fill(Stream_Audio, 0, Audio_SamplesPerFrame, LegacyStreamDisplay?Data[SamplesPerFrame].Read():Data[SamplesPerFrame].Read(0));
    Fill(Stream_Audio, 0, Audio_BitRate, LegacyStreamDisplay?Data[BitRate].Read():(Data[BitRate].Read(0)==__T("Unknown")?Ztring():Data[BitRate].Read(0)));
    Fill(Stream_Audio, 0, Audio_BitRate_Mode, LegacyStreamDisplay?Data[BitRate_Mode].Read():Data[BitRate_Mode].Read(0), true);
    Fill(Stream_General, 0, General_OverallBitRate_Mode, Retrieve(Stream_Audio, 0, Audio_BitRate_Mode));
    Fill(Stream_Audio, 0, Audio_Compression_Mode, LegacyStreamDisplay?Data[Compression_Mode].Read():Data[Compression_Mode].Read(0), true);
    if (DtsXBedChannelCount)
    {
        Ztring DtsXChannelLayout=DtsXBedChannelConfiguration;
        Ztring DtsXChannelPositions=DtsXBedChannelPositions;
        Ztring DtsXChannelPositions2=DtsXBedChannelPositions2;
        if (DtsXObjectCount)
        {
            DtsXChannelLayout+=__T(" Objects (")+Ztring::ToZtring(DtsXObjectCount)+__T(")");
            if (!DtsXChannelPositions.empty())
                DtsXChannelPositions+=__T(", Objects (")+Ztring::ToZtring(DtsXObjectCount)+__T(")");
            if (!DtsXChannelPositions2.empty())
                DtsXChannelPositions2+=__T(".?");
        }
        Fill(Stream_Audio, 0, Audio_Channel_s_, DtsXBedChannelCount, 10, true);
        Fill(Stream_Audio, 0, Audio_ChannelLayout, DtsXChannelLayout, true);
        if (!DtsXChannelPositions.empty())
            Fill(Stream_Audio, 0, Audio_ChannelPositions, DtsXChannelPositions, true);
        if (!DtsXChannelPositions2.empty())
            Fill(Stream_Audio, 0, Audio_ChannelPositions_String2, DtsXChannelPositions2, true);
    }

    // Cleanup up
    for (size_t Pos=0; Pos<10; ++Pos)
        Data[Pos].clear();
}

//---------------------------------------------------------------------------
void File_Dts::Streams_Finish()
{
    Fill(Stream_Audio, 0, Audio_Format_Settings_Endianness, BigEndian?"Big":"Little", Unlimited, true, true);
    Fill(Stream_Audio, 0, Audio_Format_Settings_Mode, Word?"16":"14", Unlimited, true, true);

    if (FrameInfo.PTS!=(int64u)-1 && FrameInfo.PTS>PTS_Begin)
    {
        Fill(Stream_Audio, 0, Audio_Duration, ((float64)(FrameInfo.PTS-PTS_Begin))/1000000, 0, true);
        float64 SamplesPerFrame=Retrieve(Stream_Audio, 0, Audio_SamplesPerFrame).To_float64();
        float64 SamplingRate=Retrieve(Stream_Audio, 0, Audio_SamplingRate).To_float64();
        if (SamplesPerFrame && SamplingRate)
            Fill(Stream_Audio, 0, Audio_FrameCount, ((float64)(FrameInfo.PTS-PTS_Begin))/1000000000/(SamplesPerFrame/SamplingRate), 0, true);
    }
}

//***************************************************************************
// Buffer - File header
//***************************************************************************

//---------------------------------------------------------------------------
bool File_Dts_Common::FileHeader_Begin()
{
    //Must have enough buffer for having header
    if (Buffer_Size<4)
        return false; //Must wait for more data

    //False positives detection: Detect WAV files, the parser can't detect it easily, there is only 70 bytes of beginning for saying WAV
    switch (CC4(Buffer))
    {
        case 0x52494646 : //"RIFF"
        case 0x000001FD : //MPEG-PS private
                            Finish("DTS");
                            return false;
        default         :   ;
    }

    //All should be OK...
    if (!Frame_Count_Valid)
        Frame_Count_Valid=Config->ParseSpeed>=0.3?32:(IsSub?1:2);
    return true;
}

//---------------------------------------------------------------------------
void File_Dts_Common::FileHeader_Parse()
{
    //DTSHDHDR header
    //https://www.atsc.org/wp-content/uploads/2015/03/Non-Real-Time-Content-Delivery.pdf
    if (IsSub || CC8(Buffer)!=CHUNK_DTSHDHDR || CC4(Buffer+8))
        return;
    int64u StreamSize{ static_cast<int64u>(-1) };
    int16u Bitw_Stream_Metadata{};
    bool Header_Parsed{ false };
    int64u Num_Samples_Orig_Audio_At_Max_Fs{ 0 };
    int32u Num_Frames_Total{};
    int32u TimeStamp{};
    int32u Max_Sample_Rate_Hz{ 0 };
    int32u Ext_Ss_Avg_Bit_Rate_Kbps{ 0 };
    int32u Ext_Ss_Peak_Bit_Rate_Kbps{ 0 };
    int16u Core_Ss_Bit_Rate_Kbps{ 0 };
    int16u Samples_Per_Frame_At_Max_Fs{ 0 };
    int16u Codec_Delay_At_Max_Fs{ 0 };
    int8u RefClockCode{};
    int8u TC_Frame_Rate{ static_cast<int8u>(-1) };
    while (StreamSize==-1 && Element_Size-Element_Offset>=16)
    {
        int64u Name, Size;
        Element_Begin1("Element");
        Element_Begin1("Header");
        Get_C8 (Name,                                           "Name");
        Get_B8 (Size,                                           "Size");
        Element_End0();
        Ztring ToShow;
        for (int i=0; i<8; i++)
            ToShow.append(1, (ZenLib::Char)((Name>>(56-i*8)))&0xFF);
        Element_Name(ToShow);
        if (Name!=CHUNK_STRMDATA && Size>Element_Size-Element_Offset)
        {
            Element_End0();
            Element_WaitForMoreData();
            return;
        }
        auto End=Element_Offset+Size;
        switch (Name)
        {
            case CHUNK_AUPR_HDR:
            {
                int16u Bitw_Aupres_Metadata;
                Skip_B1(                                        "Audio_Pres_Index");
                Get_B2 (Bitw_Aupres_Metadata,                   "Bitw_Aupres_Metadata");
                Skip_Flags(Bitw_Aupres_Metadata, 3,             "Presence of a LBR coding componen");
                Skip_Flags(Bitw_Aupres_Metadata, 2,             "Presence of a lossless coding component");
                Skip_Flags(Bitw_Aupres_Metadata, 1,             "Location of a backward compatible core coding component");
                Skip_Flags(Bitw_Aupres_Metadata, 0,             "Presence of a backward compatible core coding component");
                Get_B3 (Max_Sample_Rate_Hz,                     "Max_Sample_Rate_Hz");
                Get_B4 (Num_Frames_Total,                       "Num_Frames_Total");
                Get_B2 (Samples_Per_Frame_At_Max_Fs,            "Samples_Per_Frame_At_Max_Fs");
                Get_B5 (Num_Samples_Orig_Audio_At_Max_Fs,       "Num_Samples_Orig_Audio_At_Max_Fs");
                Skip_B2(                                        "Channel_Mask");
                Get_B2 (Codec_Delay_At_Max_Fs,                  "Codec_Delay_At_Max_Fs");
                if ((Bitw_Aupres_Metadata&3)==3)
                {
                    Skip_B3(                                    "BC_Core_Max_Sample_Rate_Hz");
                    Skip_B2(                                    "BC_Core_Bit_Rate_Kbps");
                    Skip_B2(                                    "BC_Core_Channel_Mask");
                }
                if (Bitw_Aupres_Metadata&4)
                {
                    Skip_B1(                                    "LSB_Trim_Percent");
                }
                break;
            }
            case CHUNK_CORESSMD:
            {
                Skip_B3(                                        "Core_Ss_Max_Sample_Rate_Hz");
                Get_B2 (Core_Ss_Bit_Rate_Kbps,                  "Core_Ss_Bit_Rate_Kbps");
                Skip_B2(                                        "Core_Ss_Channel_Mask");
                Skip_B4(                                        "Core_Ss_Frame_Payload_In_Bytes");
                break;
            }
            case CHUNK_DTSHDHDR:
            {
                Skip_B4(                                        "Hdr_Version");
                Get_B1 (RefClockCode,                           "Time_Code RefClockCode");
                RefClockCode>>=6;
                Param_Info1C(RefClockCode<DTS_HD_RefClockCode_Size, DTS_HD_RefClockCode[RefClockCode]);
                Get_B4 (TimeStamp,                              "Time_Code TimeStamp");
                Get_B1 (TC_Frame_Rate,                          "TC_Frame_Rate"); Param_Info1C(TC_Frame_Rate && TC_Frame_Rate<TC_Frame_Rate_Table_Size-1, TC_Frame_Rate_Table[TC_Frame_Rate-1]);
                Get_B2 (Bitw_Stream_Metadata,                   "Bitw_Stream_Metadata");
                Skip_Flags(Bitw_Stream_Metadata, 4,             "Presence of an extension sub-stream(s)");
                Skip_Flags(Bitw_Stream_Metadata, 3,             "Presence of a core sub-stream");
                Skip_Flags(Bitw_Stream_Metadata, 2,             "Navigation table");
                Skip_Flags(Bitw_Stream_Metadata, 1,             "Peak bit rate smoothing");
                Skip_Flags(Bitw_Stream_Metadata, 0,             "Variable bit-rate");
                Skip_B1(                                        "Num_Audio_Presentations");
                Skip_B1(                                        "Number_Of_Ext_Sub_Streams");
                break;
            }
            case CHUNK_EXTSS_MD:
            {
                Get_B3 (Ext_Ss_Avg_Bit_Rate_Kbps,               "Ext_Ss_Avg_Bit_Rate_Kbps");
                if (Bitw_Stream_Metadata&1)
                {
                    Get_B3 (Ext_Ss_Peak_Bit_Rate_Kbps,          "Ext_Ss_Peak_Bit_Rate_Kbps");
                    Skip_B2(                                    "Pbr_Smooth_Buff_Size_Kb");
                }
                else
                {
                    Skip_B4(                                    "Ext_Ss_Frame_Payload_In_Bytes");

                }
                break;
            }
            case CHUNK_STRMDATA:
                StreamSize=Size;
                break;
        }
        if (Name!=CHUNK_STRMDATA)
        {
            Skip_XX(End-Element_Offset,                         End-Element_Offset<=3?"Dword_Align":"(Unknown)");
            Element_Offset=End;
        }
        Element_End0();
    }
    if (StreamSize==-1)
    {
        Element_WaitForMoreData();
        return;
    }

    FILLING_BEGIN()
        Fill(Stream_Audio, 0, Audio_BitRate_Mode, (Bitw_Stream_Metadata&1)?"VBR":"CBR");
        if (RefClockCode<DTS_HD_RefClockCode_Size && TC_Frame_Rate)
        {
            auto RefClock=DTS_HD_RefClockCode[RefClockCode];
            TimeCode TC(TimeStamp, RefClock-1, TimeCode::Timed());
            Fill(Stream_Audio, 0, Audio_Delay, TC.ToSeconds()*1000, 3);
            Merge_FillTimeCode(*this, "TimeCode", TC, (TC_Frame_Rate<TC_Frame_Rate_Table_Size-1)?TC_Frame_Rate_Table[TC_Frame_Rate-1]:0, TC_Frame_Rate_IsDrop(TC_Frame_Rate), TimeCode::Floor, RefClock);
        }
        if (Num_Frames_Total)
            Fill(Stream_Audio, 0, Audio_FrameCount, Num_Frames_Total);
        if (Max_Sample_Rate_Hz && Samples_Per_Frame_At_Max_Fs && Num_Samples_Orig_Audio_At_Max_Fs)
        {
            int64u SamplingCount=Num_Samples_Orig_Audio_At_Max_Fs;
            if (Codec_Delay_At_Max_Fs>=Samples_Per_Frame_At_Max_Fs)
                SamplingCount+=Codec_Delay_At_Max_Fs; // If less, we guess it is a delay inside the frame and first samples should be discarded, else it is complete frames, so need to add to sample count as the frames are decoded
            Fill(Stream_Audio, 0, Audio_SamplingCount, SamplingCount);
        }
        int32u BitRate_Nominal=Core_Ss_Bit_Rate_Kbps+Ext_Ss_Avg_Bit_Rate_Kbps;
        if (BitRate_Nominal)
        {
            BitRate_Nominal*=1000;
            Fill(Stream_General, 0, General_OverallBitRate, BitRate_Nominal);
            Fill(Stream_Audio, 0, Audio_BitRate_Nominal, BitRate_Nominal);
        }
        if (Ext_Ss_Peak_Bit_Rate_Kbps)
        {
            int32u BitRate_Maximum=Core_Ss_Bit_Rate_Kbps+Ext_Ss_Peak_Bit_Rate_Kbps;
            BitRate_Maximum*=1000;
            Fill(Stream_General, 0, General_OverallBitRate_Maximum, BitRate_Maximum);
            Fill(Stream_Audio, 0, Audio_BitRate_Maximum, BitRate_Maximum);
        }
        Fill(Stream_Audio, 0, Audio_StreamSize, StreamSize);

        Stream_Offset_Max=File_Offset+Element_Offset+StreamSize;
    FILLING_END()
}


//***************************************************************************
// Buffer - Gobal
//***************************************************************************

//---------------------------------------------------------------------------
bool File_Dts_Common::Header_Begin()
{
    if (Stream_Offset_Max==-1 || File_Offset+Buffer_Offset!=Stream_Offset_Max || File_Size==-1)
        return true;

    //Handling DTSHDHDR footer
    if (File_Offset+Buffer_Size<File_Size)
    {
        return false; //Wait for more data
    }
    Element_Begin1("File Footer");
    while (Element_Size-Element_Offset>=16)
    {
        int64u Name, Size;
        Element_Begin1("Element");
        Element_Begin1("Header");
        Get_C8 (Name,                                           "Name");
        Get_B8 (Size,                                           "Size");
        Element_End0();
        Ztring ToShow;
        for (int i=0; i<8; i++)
            ToShow.append(1, (ZenLib::Char)((Name>>(56-i*8)))&0xFF);
        Element_Name(ToShow);
        auto End=Element_Offset+Size;
        switch (Name)
        {
            case CHUNK_BUILDVER:
            {
                size_t Element_Offset_End=(size_t)Element_Offset;
                while (Element_Offset_End<End && Buffer[Buffer_Offset+Element_Offset_End])
                    Element_Offset_End++;
                Skip_UTF8(Element_Offset_End-Element_Offset,    "Description");
                if (Element_Offset<End)
                    Element_Offset++; //Null termination
                break;
            }
            case CHUNK_FILEINFO:
            {
                if (!Size)
                    break;
                int8u FILEINFO_Text_Byte_Size;
                Get_B1 (FILEINFO_Text_Byte_Size,                "FILEINFO_Text_Byte_Size");
                if (FILEINFO_Text_Byte_Size && FILEINFO_Text_Byte_Size<Size-1)
                {
                    Skip_UTF8(FILEINFO_Text_Byte_Size-1,        "Description");
                    Element_Offset++; //Null termination
                }
                else
                    Skip_XX(End-Element_Offset,                 "(Unknown)");
                break;
            }
            case CHUNK_TIMECODE:
            {
                int64u Start_Sample, Reference_Sample;
                int32u Timecode_Clock, Start_Residual, Reference_Residual;
                int8u Timecode_Frame_Rate;
                Get_B4 (Timecode_Clock,                         "Timecode Clock");
                Get_B1 (Timecode_Frame_Rate,                    "Timecode Frame Rate"); Param_Info1C(Timecode_Frame_Rate && Timecode_Frame_Rate<TC_Frame_Rate_Table_Size-1, TC_Frame_Rate_Table[Timecode_Frame_Rate-1]);
                Get_B8 (Start_Sample,                           "Start samples since midnight");
                Get_B4 (Start_Residual,                         "Start Residual");
                Get_B8 (Reference_Sample,                       "Reference samples since midnight");
                Get_B4 (Reference_Residual,                     "Reference Residual");
                break;
            }
        }
        Skip_XX(End-Element_Offset,                             End-Element_Offset<=3?"Dword_Align":"(Unknown)");
        Element_Offset=End;
        Element_End0();
    }
    Element_End0();

    Buffer_Offset=Buffer_Size;
    return false;
}

//***************************************************************************
// Buffer - Synchro
//***************************************************************************

//---------------------------------------------------------------------------
bool File_Dts::Synchronize()
{
    //Synchronizing
    while (Buffer_Offset+6<=Buffer_Size)
    {
        if (!FrameSynchPoint_Test())
            return false; //Need more data
        if (Synched)
            break;
        Buffer_Offset++;
    }

    //Parsing last bytes if needed
    if (Buffer_Offset+6>Buffer_Size)
    {
        if (Buffer_Offset+5==Buffer_Size)
        {
            int64u Value=CC5(Buffer+Buffer_Offset);
            if ((Value&0xFFFFFFFFFCLL)!=0x7FFE8001FCLL  //16 bits and big    endian Core
             && (Value&0xFFFFFFFF00LL)!=0xFE7F018000LL  //16 bits and little endian Core
             && (Value&0xFFFFFFFFF7LL)!=0x1FFFE80007LL  //14 bits and big    endian Core
             && (Value&0xFFFFFFFFF0LL)!=0xFF1F00E8F0LL  //14 bits and little endian Core
             && (Value&0xFFFFFFFF00LL)!=0x6458202500LL) //16 bits and big    endian HD
                Buffer_Offset++;
        }
        if (Buffer_Offset+4==Buffer_Size)
        {
            int32u Value=CC4(Buffer+Buffer_Offset);
            if (Value!=0x7FFE8001  //16 bits and big    endian Core
             && Value!=0xFE7F0180  //16 bits and little endian Core
             && Value!=0x1FFFE800  //14 bits and big    endian Core
             && Value!=0xFF1F00E8  //14 bits and little endian Core
             && Value!=0x64582025) //16 bits and big    endian HD
                Buffer_Offset++;
        }
        if (Buffer_Offset+3==Buffer_Size)
        {
            int32u Value=CC3(Buffer+Buffer_Offset);
            if (Value!=0x7FFE80  //16 bits and big    endian Core
             && Value!=0xFE7F01  //16 bits and little endian Core
             && Value!=0x1FFFE8  //14 bits and big    endian Core
             && Value!=0xFF1F00  //14 bits and little endian Core
             && Value!=0x645820) //16 bits and big    endian HD
                Buffer_Offset++;
        }
        if (Buffer_Offset+2==Buffer_Size)
        {
            int16u Value=CC2(Buffer+Buffer_Offset);
            if (Value!=0x7FFE  //16 bits and big    endian Core
             && Value!=0xFE7F  //16 bits and little endian Core
             && Value!=0x1FFF  //14 bits and big    endian Core
             && Value!=0xFF1F  //14 bits and little endian Core
             && Value!=0x6458) //16 bits and big    endian HD
                Buffer_Offset++;
        }
        if (Buffer_Offset+1==Buffer_Size)
        {
            int8u Value=CC1(Buffer+Buffer_Offset);
            if (Value!=0x7F  //16 bits and big    endian Core
             && Value!=0xFE  //16 bits and little endian Core
             && Value!=0x1F  //14 bits and big    endian Core
             && Value!=0xFF  //14 bits and little endian Core
             && Value!=0x64) //16 bits and big    endian HD
                Buffer_Offset++;
        }
        return false;
    }

    //Synched
    return true;
}

//---------------------------------------------------------------------------
bool File_Dts::Synched_Test()
{
    //Padding
    while (Buffer_Offset<Buffer_Size && Buffer[Buffer_Offset]==0x00)
        Buffer_Offset++;

    //Must have enough buffer for having header
    if (Buffer_Offset+6>Buffer_Size)
        return false;

    //Quick test of synchro
    if (!FrameSynchPoint_Test())
        return false; //Need more data
    if (!Synched)
    {
        if (Stream_Offset_Max!=-1 && File_Offset+Buffer_Offset==Stream_Offset_Max && File_Size!=-1)
            Synched=true; // It is the file footer
        return true;
    }

    //We continue
    return true;
}

//---------------------------------------------------------------------------
void File_Dts::Read_Buffer_Unsynched()
{
    FrameInfo=frame_info();
}

//***************************************************************************
// Buffer - Demux
//***************************************************************************

//---------------------------------------------------------------------------
#if MEDIAINFO_DEMUX
bool File_Dts::Demux_UnpacketizeContainer_Test()
{
    int32u Value=CC4(Buffer+Buffer_Offset);
    if (Value==0x7FFE8001)
    {
        int16u Size=((BigEndian2int24u(Buffer+Buffer_Offset+5)>>4)&0x3FFF)+1;
        Demux_Offset=Buffer_Offset+Size;

        if (Buffer_Offset+Size>Buffer_Size && File_Offset+Buffer_Size!=File_Size)
            return false; //No complete frame

        Demux_UnpacketizeContainer_Demux();
    }

    return true;
}
#endif //MEDIAINFO_DEMUX

//***************************************************************************
// Buffer - Per element
//***************************************************************************

//---------------------------------------------------------------------------
void File_Dts::Header_Parse()
{
    Header_Fill_Size(Original_Size);
}

//***************************************************************************
// Elements
//***************************************************************************

//---------------------------------------------------------------------------
void File_Dts::Data_Parse()
{
    if (Save_Buffer)
    {
        File_Offset+=Buffer_Offset;
        swap(Buffer, Save_Buffer);
        swap(Buffer_Offset, Save_Buffer_Offset);
        swap(Buffer_Size, Save_Buffer_Size);
        if (!Word)
            Element_Size=Element_Size*14/16;
    }

    //Parsing
    if (!Presence[presence_Extended_X] || !DtsXSupplementalChannelCount)
    {
        int8u BufferedDtsXObjectCount=0;
        int8u BufferedDtsXSupplementalChannelCount=0;
        size_t DtsXProbeSize=Buffer_Size-Buffer_Offset;
        if (DtsXProbeSize>Element_Size)
            DtsXProbeSize=(size_t)Element_Size;
        if (DtsXProbeSize>0x10000)
            DtsXProbeSize=0x10000;
        if (DtsX_ExtensionProbe(Buffer+Buffer_Offset, DtsXProbeSize, BufferedDtsXObjectCount, BufferedDtsXSupplementalChannelCount))
        {
            Presence.set(presence_Extended_X);
            DtsXObjectCount=max(DtsXObjectCount, BufferedDtsXObjectCount);
            DtsXSupplementalChannelCount=max(DtsXSupplementalChannelCount, BufferedDtsXSupplementalChannelCount);
        }
    }

    int32u Sync;
    Peek_B4 (Sync);
    if (Sync==0x64582025)
    {
        if (Presence[presence_Extended_X] || !DtsXObjectCountProbeDone)
        {
            int8u FrameDtsXSupplementalChannelCount=0;
            int8u FrameDtsXObjectCount=DtsX_ObjectCount(Buffer+Buffer_Offset, Element_Size, FrameDtsXSupplementalChannelCount);
            if (FrameDtsXObjectCount || FrameDtsXSupplementalChannelCount)
            {
                DtsXObjectCount=max(DtsXObjectCount, FrameDtsXObjectCount);
                DtsXSupplementalChannelCount=max(DtsXSupplementalChannelCount, FrameDtsXSupplementalChannelCount);
                Presence.set(presence_Extended_X);
            }
            if (!Presence[presence_Extended_X])
                DtsXObjectCountProbeDone=true;
        }

        //HD
        Element_Name("HD");
        Element_Info1(Ztring::ToZtring(Frame_Count-(Core_Exists?1:0)));
        Element_Code=1;

        Element_Begin1("Header");
        int16u ExtSSHeaderSize;
        int8u  SubStreamIndex, NumAssets, NumAudioPresent;
        bool isBlownUpHeader, StaticFieldsPresent;
        Skip_B4(                                                "Sync");
        Skip_B1(                                                "UserDefinedBits");
        BS_Begin();
        int64u EndBS=Data_BS_Remain();
        Get_S1 (2, SubStreamIndex,                              "ExtSSIndex");
        Get_SB (isBlownUpHeader,                                "HeaderSizeType");
        int8u ExtraSize=((int8u)isBlownUpHeader)<<2;
        Get_S2 ( 8+ExtraSize, ExtSSHeaderSize,                  "ExtSSHeaderSize");
        if (ExtSSHeaderSize<4)
        {
            Element_End0();
            return;
        }
        auto CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, ExtSSHeaderSize-4);
        if (CRC)
        {
            Element_Info1("CRC NOK");
            Element_End0();
            return;
        }
        Param_Info2(ExtSSHeaderSize+1, " bytes");
        Get_S3 (16+ExtraSize, HD_size,                          "ExtSSFsize"); HD_size++; Param_Info2(HD_size, " bytes");
        TESTELSE_SB_GET(StaticFieldsPresent,                    "StaticFieldsPresent");
            std::vector<int32u> ActiveExSSMasks;
            Info_S1(2, RefClockCode,                            "RefClockCode"); Param_Info1C(RefClockCode<DTS_HD_RefClockCode_Size, DTS_HD_RefClockCode[RefClockCode]);
            Get_S1 (3, HD_ExSSFrameDurationCode,                "ExSSFrameDurationCode"); HD_ExSSFrameDurationCode++; Param_Info1(HD_ExSSFrameDurationCode);
            TEST_SB_SKIP(                                       "TimeStampFlag");
                Skip_S5(36,                                     "TimeStamp");
            TEST_SB_END();
            Get_S1 (3, NumAudioPresent,                         "NumAudioPresent"); NumAudioPresent++; Param_Info2(NumAudioPresent, " channels");
            Get_S1 (3, NumAssets,                               "NumAssets"); NumAssets++; Param_Info2(NumAssets, " assets");
            for (int8u Pos=0; Pos<NumAudioPresent; Pos++)
            {
                int32u ActiveExSSMask;
                Get_S4 (SubStreamIndex+1, ActiveExSSMask,       "ActiveExSSMask");
                ActiveExSSMasks.push_back(ActiveExSSMask);
            }
            for (int8u Pos=0; Pos<NumAudioPresent; Pos++)
                for (int8u Pos2=0; Pos2<SubStreamIndex+1; Pos2+=2)
                    if (ActiveExSSMasks[Pos]%2)
                        Skip_S1(8,                              "ActiveAssetMask");
            TEST_SB_SKIP(                                       "MixMetadataEnbl");
                int8u Bits4MixOutMask, NumMixOutConfigs;
                Skip_S1(2,                                      "MixMetadataAdjLevel");
                Get_S1 (2, Bits4MixOutMask,                     "Bits4MixOutMask"); Bits4MixOutMask=4+Bits4MixOutMask*4; Param_Info2(Bits4MixOutMask, " bits");
                Get_S1 (2, NumMixOutConfigs,                    "NumMixOutConfigs"); NumMixOutConfigs++; Param_Info2(NumMixOutConfigs, " configs");
                for (int8u Pos=0; Pos<NumMixOutConfigs; Pos++)
                    Skip_S1(Bits4MixOutMask,                    "NumMixOutCh");
            TEST_SB_END();
        TESTELSE_SB_ELSE("Static fields present");
            NumAudioPresent=1;
            NumAssets=1;
        TESTELSE_SB_END();
        Element_Begin1("Sizes");
        for (int8u Pos=0; Pos<NumAssets; Pos++)
        {
            int32u Size;
            Get_S3 (16+ExtraSize, Size,                         "AssetFsize"); Size++; Param_Info2(Size, " bytes");
            Asset_Sizes.push_back(Size);
        }
        Element_End0();
        for (int8u Asset_Pos=0; Asset_Pos<NumAssets; Asset_Pos++)
        {
            Element_Begin1("AssetDescriptor");
            int16u AssetSize;
            Get_S2 (9, AssetSize,                               "AssetDescriptFsize");
            AssetSize++;  Param_Info2(AssetSize, " bytes?");
            Skip_S1(3,                                          "AssetIndex");
            if (StaticFieldsPresent)
            {
                TEST_SB_SKIP(                                   "AssetTypeDescrPresent");
                    Info_S1( 4, TypeDescriptor,                 "AssetTypeDescriptor"); Param_Info1(DTS_HD_TypeDescriptor[TypeDescriptor]);
                TEST_SB_END();
                TEST_SB_SKIP(                                   "LanguageDescrPresent");
                    Info_S3(24, LanguageDescriptor,             "LanguageDescriptor"); Param_Info1(Ztring().From_CC3(LanguageDescriptor));
                TEST_SB_END();
                TEST_SB_SKIP(                                   "InfoTextPresent");
                    int16u InfoTextByteSize;
                    Get_S2(10, InfoTextByteSize,                "InfoTextByteSize"); InfoTextByteSize++; Param_Info2(InfoTextByteSize, "bytes");
                    for (int16u Pos=0; Pos<InfoTextByteSize; Pos++)
                        Skip_S1(8,                              "InfoTextString");
                TEST_SB_END();
                Get_S1 (5, HD_BitResolution,                    "BitResolution"); HD_BitResolution++; Param_Info2(HD_BitResolution, " bits");
                Get_S1 (4, HD_MaximumSampleRate,                "MaxSampleRate"); Param_Info2(DTS_HD_MaximumSampleRate[HD_MaximumSampleRate], " Hz");
                Get_S1 (8, HD_TotalNumberChannels,              "TotalNumChs"); HD_TotalNumberChannels++; Param_Info2(HD_TotalNumberChannels, " channels");
                TEST_SB_GET(One2OneMapChannels2Speakers,        "One2OneMapChannels2Speakers");
                    int8u SpeakerActivityMaskBits, SpeakerRemapSetsCount;
                    if (HD_TotalNumberChannels>2)
                        Skip_SB(                                "TotalNumChs"); //else is 0
                    if (HD_TotalNumberChannels>6)
                        Skip_SB(                                "EmbeddedSixChFlag"); //else is 0
                    TESTELSE_SB_SKIP(                           "SpkrMaskEnabled");
                        Get_S1 (2, SpeakerActivityMaskBits,     "NumBits4SAMask"); Param_Info2(4+SpeakerActivityMaskBits*4, " bits");
                        SpeakerActivityMaskBits=4+SpeakerActivityMaskBits*4;
                        Get_S2 (SpeakerActivityMaskBits, HD_SpeakerActivityMask, "SpkrActivityMask"); Param_Info1(DTS_HD_SpeakerActivityMask(HD_SpeakerActivityMask).c_str());
                    TESTELSE_SB_ELSE(                           "SpkrMaskEnabled");
                        SpeakerActivityMaskBits=0;
                    TESTELSE_SB_END();
                    Get_S1 (3, SpeakerRemapSetsCount,           "NumSpkrRemapSets");
                    for (int8u Pos=0; Pos<SpeakerRemapSetsCount; Pos++)
                        Skip_S2(SpeakerActivityMaskBits,        "StndrSpkrLayoutMask");
                    for (int8u Pos=0; Pos<SpeakerRemapSetsCount; Pos++)
                    {
                        int8u NumDecCh4Remap;
                        Get_S1(5, NumDecCh4Remap,               "NumDecCh4Remap");
                        break; //Not finnished!
                    }
                TEST_SB_END();
                Element_End0();
            }
        }
        auto RemainingBits=(ExtSSHeaderSize-6)*8-(EndBS-Data_BS_Remain());
        if (RemainingBits)
        {
            int8u Padding=1;
            if (RemainingBits<8)
                Peek_S1(RemainingBits, Padding);
            Skip_BS(RemainingBits,                              Padding?"(Not parsed)":"Padding");
        }
        BS_End();
        Skip_B2(                                                "CRC16ExtSSHeader");
        Element_End0();
    }
    else
    {
        //Frame
        Element_Name("Frame");
        Element_Info1(Ztring::ToZtring(Frame_Count));
        Element_Code=0;

        int8u  EncoderSoftwareRevision;
        bool   crc_present;
        Skip_B4(                                                    "Sync");
        BS_Begin();
        Info_SB(    FrameType,                                      "Frame Type"); Param_Info1(DTS_FrameType[FrameType]);
        Skip_S1( 5,                                                 "Deficit Sample Count");
        Get_SB (    crc_present,                                    "CRC Present");
        Get_S2 ( 7, Number_Of_PCM_Sample_Blocks,                    "Number of PCM Sample Blocks");
        Number_Of_PCM_Sample_Blocks++;
        Get_S2 (14, Primary_Frame_Byte_Size,                        "Primary Frame Byte Size minus 1");
        Primary_Frame_Byte_Size++;
        if (!Word)
            Primary_Frame_Byte_Size=Primary_Frame_Byte_Size*16/14; //Word is on 14 bits
        Param_Info2(Primary_Frame_Byte_Size, " bytes");
        Get_S1 ( 6, channel_arrangement,                            "Audio Channel Arrangement"); Param_Info2C(channel_arrangement<sizeof(DTS_Channels), DTS_Channels[channel_arrangement], " channels");
        Core_Core_AMODE=channel_arrangement;
        Get_S1 ( 4, sample_frequency,                               "Core Audio Sampling Frequency"); Param_Info2(DTS_SamplingRate[sample_frequency], " Hz");
        Get_S1 ( 5, bit_rate,                                       "Transmission Bit Rate"); Param_Info2(DTS_BitRate[bit_rate], " bps");
        Skip_SB(                                                    "Embedded Down Mix Enabled");
        Skip_SB(                                                    "Embedded Dynamic Range");
        Skip_SB(                                                    "Embedded Time Stamp");
        Get_SB (    AuxiliaryData,                                  "Auxiliary Data");
        Skip_SB(                                                    "HDCD");
        Get_S1 ( 3, ExtensionAudioDescriptor,                       "Extension Audio Descriptor"); Param_Info1(DTS_ExtensionAudioDescriptor[ExtensionAudioDescriptor]);
        Get_SB (    ExtendedCoding,                                 "Extended Coding");
        Skip_SB(                                                    "Audio Sync Word Insertion");
        Get_S1 ( 2, lfe_effects,                                    "Low Frequency Effects");
        Core_Core_LFF=lfe_effects;
        Skip_SB(                                                    "Predictor History");
        if (crc_present)
            Skip_S2(16,                                             "Header CRC Check");
        Skip_SB(                                                    "Multirate Interpolator");
        Get_S1 ( 4, EncoderSoftwareRevision,                        "Encoder Software Revision");
        Skip_S1( 2,                                                 "Copy History");
        Get_S1 ( 2, bits_per_sample,                                "Source PCM Resolution"); Param_Info1(Ztring::ToZtring(DTS_Resolution[bits_per_sample])+__T(" bits"));
        Get_SB (    ES,                                             "ES");
        Skip_SB(                                                    "Front Sum/Difference");
        Skip_SB(                                                    "Surrounds Sum/Difference");
        Skip_S1( 4,                                                 "Dialog Normalisation Parameter");
        switch (EncoderSoftwareRevision)
        {
            case 6 :
                    Skip_S1( 4,                                     "Dialog Normalisation Parameter");
                    break;
            case 7 :
                    Skip_S1( 4,                                     "Dialog Normalisation Parameter");
                    break;
            default :
                    Skip_S1( 4,                                     "Unspecified");
                    break;
        }
        BS_End();
    }

    //PTS
    Element_Info1C((FrameInfo.PTS!=(int64u)-1), __T("PTS ")+Ztring().Duration_From_Milliseconds(float64_int64s(((float64)FrameInfo.PTS)/1000000)));

    //Counting
    if (File_Offset+Buffer_Offset+Element_Size==File_Size)
        Frame_Count_Valid=Frame_Count; //Finish frames in case of there are less than Frame_Count_Valid frames
    if (Element_Code==0 || !Core_Exists)
    {
        if (Frame_Count==0)
            PTS_Begin=FrameInfo.PTS;
        Frame_Count++;
        Frame_Count_InThisBlock++;
        float64 BitRate=BitRate_Get();
        if (BitRate)
        {
            FrameInfo.DUR=float64_int64s(((float64)(Element_Size+Header_Size))*8/BitRate*1000000000);
            if (FrameInfo.DTS!=(int64u)-1)
                FrameInfo.DTS+=FrameInfo.DUR;
            if (FrameInfo.PTS!=(int64u)-1)
                FrameInfo.PTS+=FrameInfo.DUR;
        }
    }

    //If filled
    if (Status[IsFilled])
    {
        //Little Endian and 14-bit streams management
        if (Save_Buffer)
        {
            delete[] Buffer;
            Buffer=Save_Buffer; Save_Buffer=NULL;
            Buffer_Offset=Save_Buffer_Offset;
            Buffer_Size=Save_Buffer_Size;
            File_Offset-=Buffer_Offset;
            if (!Word)
                Element_Size=Element_Size*16/14;
        }

        Skip_XX(Element_Size-Element_Offset,                    "Data");
        return;
    }

    //Parsing
    switch(Element_Code)
    {
        case 0  : Core(); break;
        case 1  : Extensions(); break;
        default : Skip_XX(Element_Size,                         "Data");
    }

    //Little Endian and 14-bit streams management
    if (Save_Buffer)
    {
        delete[] Buffer;
        Buffer=Save_Buffer; Save_Buffer=NULL;
        Buffer_Offset=Save_Buffer_Offset;
        Buffer_Size=Save_Buffer_Size;
        File_Offset-=Buffer_Offset;
        if (!Word)
            Element_Size=Element_Size*16/14;
    }
}

//***************************************************************************
// Elements
//***************************************************************************

//---------------------------------------------------------------------------
void File_Dts::Core()
{
    Element_Name("Frame");

    //It exists (not in XSA streams)
    Core_Exists=true;
    Presence.set(presence_Core_Core);

    //Parsing
    if (AuxiliaryData || ExtendedCoding)
    {
        Extensions_Resynch(true);
        Asset_Sizes.push_back(Element_Size-Element_Offset);
        Extensions();
    }

    //Filling
    FILLING_BEGIN();
        if (!Status[IsAccepted] && Frame_Count>=2 && (Frame_Count>=Frame_Count_Valid || (Frame_Count_Valid && (File_Size-Buffer_TotalBytes_FirstSynched)/Frame_Count_Valid<Element_Size))) //2 frames mini in order to catch HD part. TODO: find a better way to accept stream in short files so with only few frames
        {
            Accept("DTS");
            Fill("DTS");

            //No more need data
            if (!IsSub && Config->ParseSpeed<1.0)
            {
                if (Stream_Offset_Max!=-1)
                    GoTo(Stream_Offset_Max);
                else
                    Finish("DTS");
            }
        }
    FILLING_END();
}

//---------------------------------------------------------------------------
void File_Dts::Extensions()
{
    //Parsing
    for (auto Size : Asset_Sizes)
    {
        if (Element_Code)
            Element_Begin1("Asset");
        if (Size<4)
        {
            Skip_XX(Size,                                       "?");
            continue;
        }
        auto Element_Size_Save=Element_Size;
        Element_Size=Element_Offset+Size;
        while (Element_Size-Element_Offset>=4)
        {
            Element_Begin0();
            int32u SyncWord;
            Get_B4(SyncWord,                                    "Sync Word");
            auto Index=DTS_Extension_Index_Get(SyncWord);
            Element_Name(Index!=(size_t)-1?Ztring().From_UTF8(DTS_Extension_Names[Index]):Ztring::ToZtring(SyncWord, 16));
            switch (Index)
            {
                #define CASE(EXT) case Ext_##EXT : EXT(); break;
                CASE(Padding4);
                CASE(LBR);
                CASE(X96);
                CASE(XLL);
                CASE(XXCH);
                CASE(XCh);
                CASE(XBR);
                CASE(Aux);
                #undef CASE
                default:
                    Extensions_Resynch(false);
            }
            if (Element_Offset>Element_Size)
                Element_Offset=Element_Size; //Prevent unsigned underflow in loop condition if handler left offset past asset boundary
            Element_End0();
        }
        Element_Size=Element_Size_Save;
        if (Element_Code)
            Element_End0();
    }
    Asset_Sizes.clear();

    //Filling
    FILLING_BEGIN();
        if (Count_Get(Stream_Audio)==0 && Frame_Count>=Frame_Count_Valid)
        {
            Accept("DTS");
            Fill("DTS");

            //No more need data
            if (!IsSub && Config->ParseSpeed<1.0)
                Finish("DTS");
        }
    FILLING_END();

    if (Element_Size-Element_Offset>=6)
    {
        int64u SyncWord;
        Peek_B6(SyncWord);
        if (SyncWord==0x3A429B0A0011)
        {
            Element_Begin1("After assets?");
            Element_Begin1("Header");
            Skip_B6(                                            "SyncWord");
            Element_End0();
            Extensions2();
            Element_End0();
        }
    }
    if (Element_Size>Element_Offset)
        Skip_XX(Element_Size-Element_Offset,                    "(Unknown)");
}

//---------------------------------------------------------------------------
void File_Dts::Extensions_Resynch(bool Known)
{
    if (Element_Size-Element_Offset<4)
        return;
    auto Element_Offset_Sav=Element_Offset;
    auto Element_Offset_Max=Element_Size-3;
    auto Base=Buffer+Buffer_Offset;
    auto AlignmentBytes=Element_Offset%4;
    if (AlignmentBytes)
        Element_Offset+=4-AlignmentBytes;
    while (Element_Offset<Element_Offset_Max)
    {
        auto SuncWord=BigEndian2int32u(Base+(size_t)Element_Offset);
        auto Index=DTS_Extension_Index_Get(SuncWord);
        if (Index!=(size_t)-1 && Index>1)
        {
            bool IsNok=false;
            if (Element_Code)
            {
                // HD
                switch (Index)
                {
                    case Ext_X96:
                    case Ext_XLL:
                    case Ext_XXCH:
                        break;
                    default:
                        IsNok=true;
                }
            }
            else
            {
                // Core
                switch (Index)
                {
                    case Ext_XCh:
                        IsNok=!ExtendedCoding || (ExtensionAudioDescriptor!=0 && ExtensionAudioDescriptor!=3);
                        break;
                    case Ext_XXCH:
                        IsNok=!ExtendedCoding || (ExtensionAudioDescriptor!=6);
                        break;
                    case Ext_X96:
                        IsNok=!ExtendedCoding || (ExtensionAudioDescriptor!=2 && ExtensionAudioDescriptor!=3);
                        break;
                    case Ext_Aux:
                        IsNok=!AuxiliaryData;
                        break;
                    default:
                        IsNok=true;
                }
            }
            if (!IsNok)
                break;
        }
        Element_Offset+=4;
    }
    if (Element_Offset!=Element_Offset_Sav)
    {
        swap(Element_Offset, Element_Offset_Sav);
        if (Element_Size-Element_Offset_Sav<=3)
            Element_Offset_Sav=Element_Size;
        Skip_XX(Element_Offset_Sav-Element_Offset,              Known?"(Not parsed)":"(Unknown)");
    }
}

//---------------------------------------------------------------------------
void File_Dts::Extensions_Padding()
{
    auto Begin=Buffer+Buffer_Offset+(size_t)Element_Offset;
    auto Current=Begin;
    auto Size=Element_Size-Element_Offset;
    auto PaddingBytes=4-(Element_Offset%4);
    if (PaddingBytes!=4 && PaddingBytes<=Size)
    {
        int32u Padding;
        switch (PaddingBytes)
        {
            case 1 : Padding=BigEndian2int8u(Current); break;
            case 2 : Padding=BigEndian2int16u(Current); break;
            default: Padding=BigEndian2int24u(Current);
        }
        if (Padding)
        {
            Skip_XX(PaddingBytes,                               "(Unknown)");
            return;
        }
        Current+=PaddingBytes;
    }
    
    auto End=decltype(Begin)((((size_t)Current+Size)>>2)<<2);
    while (Current<End)
    {
        if (BigEndian2int32u(Current))
            break;
        Current+=4;
    }

    Skip_XX(Current-Begin,                                      "Padding");
}

//---------------------------------------------------------------------------
void File_Dts::LBR()
{
    FILLING_BEGIN();
        Presence.set(presence_Extended_LBR);
    FILLING_END();

    Extensions_Resynch(true);
}

//---------------------------------------------------------------------------
void File_Dts::X96()
{
    //Coherency check
    auto MaxSize=Element_Size-Element_Offset+3;
    if (Element_Code)
    {
        // HD
        int8u Begin;
        Peek_B1(Begin);
        int8u HeaderSize=Begin>>2;
        if (HeaderSize<=3)
            return;
        auto CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, HeaderSize-3);
        if (CRC)
        {
            Element_Info1("CRC NOK");
            Extensions_Resynch(false);
            return;
        }
    }
    else
    {
        // Core
        auto MaxSize=Element_Size-Element_Offset+3;
        int16u Begin;
        Peek_B2(Begin);
        int16u Size=Begin>>4;
        if (MaxSize<95 || Size<95 || Size!=MaxSize)
        {
            Extensions_Resynch(false);
            return;
        }
        Skip_XX(Size-3,                                         "(Not parsed)");
    }

    FILLING_BEGIN();
        Presence.set(Element_Code?presence_Extended_X96:presence_Core_X96);
    FILLING_END();

    Extensions_Resynch(true);
}

//---------------------------------------------------------------------------
void File_Dts::AfterAssets()
{
    // Found in DTS:X content when after assets, no idea about what it is
    auto MaxSize=Element_Size-Element_Offset;
    if (MaxSize<2)
        return;
    int16u Begin;
    Peek_B2(Begin);
    if (Begin!=0x0011)
        return;
    Skip_B2(                                                    "?");
}

//---------------------------------------------------------------------------
void File_Dts::XLL()
{
    //Coherency check
    auto MaxSize=Element_Size-Element_Offset+3;
    if (MaxSize<8)
        return;
    int16u Begin;
    Peek_B2(Begin);
    int8u Version=Begin>>12;
    if (Version)
        return;
    int8u HeaderSize=Begin>>4;
    if (HeaderSize<8)
        return;
    auto CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, HeaderSize-3);
    if (CRC)
    {
        Element_Info1("CRC NOK");
        Extensions_Resynch(false);
        return;
    }

    FILLING_BEGIN();
        Presence.set(presence_Extended_XLL);
    FILLING_END();

    // Parsing
    int32u LLFrameSize;
    int8u Bits4FrameFsize, NumChSetsInFrame, SegmentsInFrame, Bits4SSize, Bits4ChMask;
    bool ScalableLSBs;
    Element_Begin1("Header");
    auto Element_Offset_Start=Element_Offset;
    BS_Begin();
    int64u EndBS=Data_BS_Remain();
    Skip_S1(4,                                                  "Version");
    Skip_S1(8,                                                  "HeaderSize"); Param_Info1(HeaderSize);
    Get_S1 (5, Bits4FrameFsize,                                 "Bits4FrameFsize"); Param_Info1(Bits4FrameFsize+1);
    Get_S4 (Bits4FrameFsize+1, LLFrameSize,                     "LLFrameSize minus 1");
    if (LLFrameSize<6)
    {
        BS_End();
        Element_End0();
        Skip_XX(Element_Size-Element_Offset,                    "(Unknown)");
        return;
    }
    auto Element_Size_Save=Element_Size;
    if (Element_Offset_Start-3+LLFrameSize>Element_Size_Save)
    {
        BS_End();
        Element_End0();
        Skip_XX(Element_Size-Element_Offset,                    "(Unknown)");
        return;
    }
    Element_Size=Element_Offset_Start-3+LLFrameSize;
    Get_S1 (4, NumChSetsInFrame,                                "NumChSetsInFrame");
    NumChSetsInFrame++; Param_Info1(NumChSetsInFrame);
    Get_S1 (4, SegmentsInFrame,                                 "SegmentsInFrame"); Param_Info1(1<<SegmentsInFrame);
    Info_S1(4, SmplInSeg,                                       "SmplInSeg"); Param_Info1(1<<SmplInSeg);
    Get_S1 (5, Bits4SSize,                                      "Bits4SSize");
    Bits4SSize++; Param_Info1(Bits4SSize);
    Skip_S1(2,                                                  "BandDataCRCEn");
    Get_SB (   ScalableLSBs,                                    "ScalableLSBs");
    Get_S1 (5, Bits4ChMask,                                     "Bits4ChMask");
    Bits4ChMask++; Param_Info1(Bits4ChMask);
    if (ScalableLSBs)
        Skip_S1(4,                                              "FixedLSBWidth");
    auto RemainingBitHeader=(HeaderSize-5)*8-(EndBS-Data_BS_Remain());
    if (RemainingBitHeader)
    {
        int8u Padding=(int8u)-1;
        if (RemainingBitHeader<8)
            Peek_S1(RemainingBitHeader, Padding);
        Skip_BS(RemainingBitHeader,                             Padding?"(Unknown)":"Padding");
    }
    BS_End();
    Skip_B2 (                                                   "CRC16Header"); Param_Info1(CRC?"NOK":"OK");
    Element_End0();
    for (int8u i=0; i<NumChSetsInFrame; i++)
    {
        int16u Begin;
        Peek_B2(Begin);
        int8u ChSetHeaderSize=Begin>>6;
        auto CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, ChSetHeaderSize+1);
        if (CRC)
        {
            Skip_XX(Element_Size-Element_Offset,                "(Unknown)");
            Element_Size=Element_Size_Save;
            return;
        }
        Element_Begin1("Channel Set");
        BS_Begin();
        int64u EndBS=Data_BS_Remain();
        int8u ChSetLLChannel, BitResolution, FreqIndex, ReplacementSet;
        Skip_S2(10,                                             "ChSetHeaderSize");
        Get_S1 (4, ChSetLLChannel,                              "ChSetLLChannel");
        ChSetLLChannel+=1;
        Skip_BS(ChSetLLChannel,                                 "ResidualChEncode");
        Get_S1 (5, BitResolution,                               "BitResolution"); BitResolution++;
        if (((int8s)HD_BitResolution_Real)<((int8s)BitResolution))
            HD_BitResolution_Real=BitResolution;
        Skip_S1(5,                                              "BitWidth");
        Get_S1 (4, FreqIndex,                                   "FreqIndex"); Param_Info1(DTS_HD_MaximumSampleRate[FreqIndex]);
        if (((int8s)HD_MaximumSampleRate_Real)<((int8s)FreqIndex))
            HD_MaximumSampleRate_Real=FreqIndex;
        Skip_S1(2,                                              "FsInterpolate");
        Get_S1 (2, ReplacementSet,                              "ReplacementSet");
        if (ReplacementSet)
            Skip_SB(                                            "ActiveReplaceSet");
        if (One2OneMapChannels2Speakers)
        {
            bool PrimaryChSet, DownmixCoeffCodeEmbedded, ChMaskEnabled;
            Get_SB (    PrimaryChSet,                           "PrimaryChSet");
            Get_SB (   DownmixCoeffCodeEmbedded,                "DownmixCoeffCodeEmbedded");
            if (DownmixCoeffCodeEmbedded)
                Skip_SB(                                        "DownmixEmbedded");
            if (DownmixCoeffCodeEmbedded && PrimaryChSet)
                Skip_S1(3,                                      "LLDownmixType");
            Skip_SB(                                            "HierChSet");
            if (DownmixCoeffCodeEmbedded)
            {
                //TODO
            }
            else
            {
                Get_SB (    ChMaskEnabled,                      "ChMaskEnabled");
                if (ChMaskEnabled)
                {
                    Info_S4(Bits4ChMask, ChMask,                "Mask"); Param_Info1(DTS_HD_SpeakerActivityMask(ChMask).c_str());
                }
            }
        }
        Skip_BS((ChSetHeaderSize-1)*8-(EndBS-Data_BS_Remain()), "(Not parsed)");
        BS_End();
        Skip_B2 (                                               "CRC16SubHeader"); Param_Info1("OK");
        Element_End0();
    }
    size_t Count=(1<<SegmentsInFrame)*NumChSetsInFrame;
    if (DTS_HD_MaximumSampleRate[HD_MaximumSampleRate_Real]>DTS_SamplingRate[sample_frequency])
    {
        Count*=2;
        if (DTS_HD_MaximumSampleRate[HD_MaximumSampleRate_Real]>DTS_SamplingRate[sample_frequency]*2)
            Count*=2;
    }
    size_t NaviByteCount=(Count*Bits4SSize+7)/8+2;
    CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, NaviByteCount);
    if (CRC)
    {
        auto Buffer_Temp=Buffer+Buffer_Offset+Element_Offset+NaviByteCount;
        auto Buffer_End=Buffer+Buffer_Offset+(size_t)Element_Size;
        while (CRC && Buffer_Temp<Buffer_End)
            CRC=(CRC>>8)^CRC_CCIT_Table[((uint8_t)CRC)^*Buffer_Temp++];
        if (CRC)
        {
            Skip_XX(Element_Size-Element_Offset,                "(Unknown)");
            Element_Size=Element_Size_Save;
            return;
        }
        Count=(Buffer_Temp-(Buffer+Buffer_Offset+Element_Offset)-2)*8/Bits4SSize;
    }
    auto SegmentSize_Size=2+(Count*Bits4SSize+7)/8;
    if (Element_Size-Element_Offset<SegmentSize_Size)
    {
        Skip_XX(Element_Size-Element_Offset,                    "(Unknown)");
        Element_Size=Element_Size_Save;
        return;
    }
    CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, SegmentSize_Size);
    if (CRC)
    {
        Skip_XX(Element_Size-Element_Offset,                    "(Unknown)");
        Element_Size=Element_Size_Save;
        return;
    }
    Element_Begin1("NAVI");
    size_t Sizes_Total=0;
    BS_Begin();
    for (size_t j=0; j<Count; j++)
    {
        int32u SegmentSize;
        Get_BS (Bits4SSize, SegmentSize,                        "SegmentSize");
        Sizes_Total+=SegmentSize;
        //TODO: Better management of HD sampling rate > base sampling rate
    }
    BS_End();
    Skip_B2(                                                    "CRC16"); Param_Info1("OK");
    Element_End0();

    if (Element_Size-Element_Offset<Sizes_Total+Count)
    {
        Skip_XX(Element_Size-Element_Offset,                    "(Unknown)");
        Element_Size=Element_Size_Save;
        return;
    }
    Skip_XX(Sizes_Total,                                        "Segments");
    Skip_XX(Count,                                              "1 byte per segment?");

    Extensions_Padding();
    if (Element_Offset<Element_Size)
        Extensions2();
    Element_Size=Element_Size_Save;
}

//---------------------------------------------------------------------------
void File_Dts::XXCH()
{
    //Coherency check
    auto MaxSize=Element_Size-Element_Offset+3;
    if (MaxSize<8)
        return;
    int8u Begin;
    Peek_B1(Begin);
    int8u HeaderSize=Begin>>2;
    if (HeaderSize<8)
        return;
    auto CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, HeaderSize-3);
    if (CRC)
    {
        Element_Info1("CRC NOK");
        Extensions_Resynch(false);
        return;
    }

    FILLING_BEGIN();
        Presence.set(Element_Code?presence_Extended_XXCH:presence_Core_XXCH);
    FILLING_END();

    // Parsing
    Element_Begin1("Header");
    vector<int16u> ChSetFsizeXXCHs;
    int16u CoreSpkrActivityMask;
    int8u Bits4SpkrMaskXXCH, NumChSetsInXXCH;
    bool bCRCPresent4ChSetHeaderXXCH;
    auto Element_Offset_Start=Element_Offset;
    BS_Begin();
    int64u EndBS=Data_BS_Remain();
    Skip_S1(6,                                                  "HeaderSizeXXCH minus 1");
    Get_SB (   bCRCPresent4ChSetHeaderXXCH,                     "CRCPresent4ChSetHeaderXXCH");
    Get_S1 (5, Bits4SpkrMaskXXCH,                               "Bits4SpkrMaskXXCH minus 1");
    Bits4SpkrMaskXXCH++;
    Get_S1 (2, NumChSetsInXXCH,                                 "NumChSetsInXXCH minus 1");
    for (int8u nChSet=0; nChSet<=NumChSetsInXXCH; nChSet++)
    {
        int16u ChSetFsizeXXCH;
        Get_S2 (14, ChSetFsizeXXCH,                             "ChSetFsizeXXCH minus 1");
        if (bCRCPresent4ChSetHeaderXXCH && ChSetFsizeXXCH<=1)
        {
            BS_End();
            Element_End0();
            Skip_XX(Element_Size-Element_Offset,                "(Unknown)");
            return;
        }
        ChSetFsizeXXCHs.push_back(ChSetFsizeXXCH);
    }
    Get_S2 (Bits4SpkrMaskXXCH, CoreSpkrActivityMask,            "CoreSpkrActivityMask");
    auto RemainingBits=(HeaderSize-5)*8-(EndBS-Data_BS_Remain());
    if (RemainingBits)
    {
        int8u Padding=(int8u)-1;
        if (RemainingBits<8)
            Peek_S1(RemainingBits, Padding);
        Skip_BS(RemainingBits,                                  Padding?"(Unknown)":"Padding");;
    }
    BS_End();
    Skip_B2 (                                                   "CRC16Header"); Param_Info1("OK");
    Element_End0();
    int32u ChSetFsizeXXCHs_Total=0;
    for (auto ChSetFsizeXXCH : ChSetFsizeXXCHs)
        ChSetFsizeXXCHs_Total+=ChSetFsizeXXCH;
    if (Element_Size-Element_Offset<ChSetFsizeXXCHs_Total+ChSetFsizeXXCHs.size())
    {
        Element_Offset=Element_Offset_Start;
        return;
    }
    for (int8u i=0; i<ChSetFsizeXXCHs.size(); i++)
    {
        int8u Begin;
        Peek_B1(Begin);
        int8u XXCHChSetHeaderSize=Begin>>1;
        if (bCRCPresent4ChSetHeaderXXCH)
        {
            auto CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, XXCHChSetHeaderSize+1);
            if (CRC)
            {
                Skip_XX(Element_Size-Element_Offset,            "(Unknown)");
                return;
            }
            XXCHChSetHeaderSize-=2;
        }
        Element_Begin1("Channel Set");
        Element_Begin1("Header");
            BS_Begin();
            Skip_S1(7,                                          "XXCHChSetHeaderSize minus 1");
            Skip_BS(XXCHChSetHeaderSize*8+1,                    "(Not parsed)");
            BS_End();
            if (bCRCPresent4ChSetHeaderXXCH)
            {
                Skip_B2 (                                       "CRC16HeaderChSet"); Param_Info1("OK");
                XXCHChSetHeaderSize+=2;
            }
        Element_End0();
        Skip_XX(ChSetFsizeXXCHs[i]-XXCHChSetHeaderSize,         "(Not parsed)");
        Element_End0();
    }

    Extensions_Padding();
}

//---------------------------------------------------------------------------
void File_Dts::XCh()
{
    //Coherency check
    auto MaxSize=Element_Size-Element_Offset+3;
    if (MaxSize<95)
        return;
    int16u Begin;
    Peek_B2(Begin);
    int16u Size=Begin>>6;
    if (Size<95 || (Size!=MaxSize && Size-1!=MaxSize)) // Is last item and manage legacy bitstreams
        return;

    FILLING_BEGIN();
        Presence.set(Element_Code?presence_Extended_XCh:presence_Core_XCh);
    FILLING_END();

    //Parsing
    int16u XChFSIZE;
    int8u  AMODE;
    BS_Begin();
    Get_S2 (10, XChFSIZE,                                       "Primary Frame Byte Size");
    Get_S1 ( 4, AMODE,                                          "Extension Channel Arrangement");
    Skip_BS(Data_BS_Remain(),                                   "(Not parsed)");
    BS_End();

    FILLING_BEGIN();
        if (!Element_Code)
            Core_XCh_AMODE=AMODE;
    FILLING_END();

    Extensions_Padding();
}

//---------------------------------------------------------------------------
void File_Dts::XBR()
{
    //Coherency check
    auto MaxSize=Element_Size-Element_Offset+3;
    if (MaxSize<8)
        return;
    int8u Begin;
    Peek_B1(Begin);
    int8u HeaderSize=Begin>>2;
    if (HeaderSize<8)
        return;
    auto CRC=Dts_CRC_CCIT_Compute(Buffer+Buffer_Offset+Element_Offset, HeaderSize-3);
    if (CRC)
    {
        Element_Info1("CRC NOK");
        Extensions_Resynch(false);
        return;
    }

    FILLING_BEGIN();
        Presence.set(presence_Extended_XBR);
    FILLING_END();

    // Parsing
    Element_Begin1("Header");
    vector<int16u> ChSetFsizes;
    int8u NumChSetsInXBR;
    auto Element_Offset_Start=Element_Offset;
    BS_Begin();
    int64u EndBS=Data_BS_Remain();
    Skip_S1(6,                                                  "HeaderSizeXBR minus 1");
    Get_S1 (2, NumChSetsInXBR,                                  "NumChSetsInXBR minus 1");
    for (int8u nChSet=0; nChSet<=NumChSetsInXBR; nChSet++)
    {
        int16u ChSetFsize;
        Get_S2 (14, ChSetFsize,                                 "ChSetFsize minus 1");
        ChSetFsizes.push_back(ChSetFsize);
    }
    int32u ChSetFsizes_Total=0;
    auto RemainingBits=(HeaderSize-5)*8-(EndBS-Data_BS_Remain());
    if (RemainingBits)
    {
        int8u Padding=(int8u)-1;
        if (RemainingBits<8)
            Peek_S1(RemainingBits, Padding);
        Skip_BS(RemainingBits,                                  Padding?"(Unknown)":"Padding");;
    }
    BS_End();
    Skip_B2 (                                                   "CRC16Header"); Param_Info1("OK");
    Element_End0();
    int32u ChSetFsizeXXCHs_Total=0;
    for (auto ChSetFsize : ChSetFsizes)
        ChSetFsizes_Total+=ChSetFsize;
    if (Element_Size-Element_Offset<ChSetFsizes_Total+ChSetFsizes.size())
    {
        Element_Offset=Element_Offset_Start;
        return;
    }
    for (auto ChSetFsize : ChSetFsizes)
    {
        Element_Begin1("Channel Set");
        Skip_XX(ChSetFsize+1,                                   "(Not parsed)");
        Element_End0();
    }

    Extensions_Padding();
}


//---------------------------------------------------------------------------
void File_Dts::Extensions2()
{
    if (Element_Size-Element_Offset<4)
        return;

    Element_Begin0();
    int32u SyncWord;
    Get_B4(SyncWord,                                            "Sync Word");
    switch (SyncWord)
    {
        case 0x02000850:
        case 0xF14000D1:
        case 0xF14000D2:
        case 0xF14000D3:
        case 0xF14000D4:
            Element_Name("X?");
            Presence.set(presence_Extended_X);
            break;
        case 0xF14000D0:
            Element_Name("X IMAX?");
            Presence.set(presence_Extended_X); // It was reported that IMAX is detected as X on legacy X decoders
            Presence.set(presence_Extended_IMAX);
            break;
        default:
            Element_Name(Ztring::ToZtring(SyncWord, 16));
    }
    Skip_XX(Element_Size-Element_Offset,                        "(Unknown)");
    Element_End0();
}

//***************************************************************************
// Helpers
//***************************************************************************

//---------------------------------------------------------------------------
float64 File_Dts::BitRate_Get(bool WithHD)
{
    if (bit_rate<29 || Presence[presence_Extended_LBR])
    {
        float64 BitRate;
        if (Presence[presence_Extended_LBR])
            BitRate=0; //No core bitrate
        else if (DTS_SamplingRate[sample_frequency])
            BitRate=((float64)Primary_Frame_Byte_Size)*8/(Number_Of_PCM_Sample_Blocks*32)*DTS_SamplingRate[sample_frequency]; //(float64)DTS_BitRate[bit_rate];
        else
            BitRate=0; //Problem
        if (WithHD && HD_ExSSFrameDurationCode!=(int8u)-1)
        {
            int32u SamplesPerFrame_Temp=HD_ExSSFrameDurationCode<<(7+DTS_HD_SamplePerFrames_Factor[HD_MaximumSampleRate]);
            BitRate+=((float64)HD_size)*8*DTS_HD_MaximumSampleRate[HD_MaximumSampleRate]/SamplesPerFrame_Temp;
        }
        //if (Primary_Frame_Byte_Size && Profile==__T("HRA"))
        //    BitRate*=1+((float64)HD_size)/Primary_Frame_Byte_Size; //HD block are not in the nominal bitrate
        return BitRate;
    }
    else
        return 0;
}

//---------------------------------------------------------------------------
bool File_Dts::FrameSynchPoint_Test()
{
    if (Save_Buffer)
        return true; //Test already made by Synchronize()

    int64u Value=CC6(Buffer+Buffer_Offset);
    if ((Value&0xFFFFFFFFFC00LL)==0x7FFE8001FC00LL  //16 bits and big    endian Core
     || (Value&0xFFFFFFFF00FCLL)==0xFE7F018000FCLL  //16 bits and little endian Core
     || (Value&0xFFFFFFFFF7F0LL)==0x1FFFE80007F0LL  //14 bits and big    endian Core
     || (Value&0xFFFFFFFFF0F7LL)==0xFF1F00E8F007LL  //14 bits and little endian Core
     || (Value&0xFFFFFFFF0000LL)==0x645820250000LL) //16 bits and big    endian HD
    {
        //Configuration - 14 bits or Little Endian
        switch (Buffer[Buffer_Offset])
        {
            default   : Word=true;  BigEndian=true;  break; //16 bits and big    endian bitstream
            case 0xFE : Word=true;  BigEndian=false; break; //16 bits and little endian bitstream
            case 0x1F : Word=false; BigEndian=true;  break; //14 bits and big    endian bitstream
            case 0xFF : Word=false; BigEndian=false; break; //14 bits and little endian bitstream
        }

        int32u Size;
        if (Buffer[Buffer_Offset]==0x64) //HD
        {
            if (Buffer_Offset+10>Buffer_Size)
                return false; //Need more data

            bool isBlownUpHeader=(Buffer[Buffer_Offset+5]&0x20)?true:false;
            if (isBlownUpHeader)
                Size=((Buffer[Buffer_Offset+6]&0x01)<<19)
                   | ( Buffer[Buffer_Offset+7]      <<11)
                   | ( Buffer[Buffer_Offset+8]      << 3)
                   | ( Buffer[Buffer_Offset+9]      >> 5);
            else
                Size=((Buffer[Buffer_Offset+6]&0x1F)<<11)
                   | ( Buffer[Buffer_Offset+7]      << 3)
                   | ( Buffer[Buffer_Offset+8]      >> 5);
            Size++;
            Original_Size=Size;
        }
        else
        {
            if (Word)
            {
                if (Buffer_Offset+8>Buffer_Size)
                    return false; //Need more data

                if (BigEndian)
                    Size=((Buffer[Buffer_Offset+5]&0x03)<<12)
                       | ( Buffer[Buffer_Offset+6]      << 4)
                       | ( Buffer[Buffer_Offset+7]      >> 4);
                else
                    Size=((Buffer[Buffer_Offset+4]&0x03)<<12)
                       | ( Buffer[Buffer_Offset+7]      << 4)
                       | ( Buffer[Buffer_Offset+6]      >> 4);
                Size++;
                Original_Size=Size;
            }
            else
            {
                if (Buffer_Offset+10>Buffer_Size)
                    return false; //Need more data

                if (BigEndian)
                    Size=((Buffer[Buffer_Offset+6]&0x03)<<12)
                       | ( Buffer[Buffer_Offset+7]      << 4)
                       | ((Buffer[Buffer_Offset+8]&0x3C)>> 2);
                else
                    Size=((Buffer[Buffer_Offset+7]&0x03)<<12)
                       | ( Buffer[Buffer_Offset+6]      << 4)
                       | ((Buffer[Buffer_Offset+9]&0x3C)>> 2);
                Size++;
                Original_Size=Size*16/14;
            }
        }
        if (Buffer_Offset+Original_Size>Buffer_Size)
            return false; //Need more data

        if (!Word || !BigEndian)
        {
            Save_Buffer=Buffer;
            Save_Buffer_Offset=Buffer_Offset;
            Save_Buffer_Size=Buffer_Size;

            //Preparing new buffer
            if (Word)
            {
                int8u* Buffer_Little=new int8u[Size];
                for (size_t Pos=0; Pos+1<Size; Pos+=2)
                {
                    Buffer_Little[Pos+1]=Save_Buffer[Buffer_Offset+Pos  ];
                    Buffer_Little[Pos  ]=Save_Buffer[Buffer_Offset+Pos+1];
                }
                Save_Buffer=Buffer_Little;
                Save_Buffer_Offset=0;
                Save_Buffer_Size=Size;
            }
            else
            {
                int8u* Dest=new int8u[Size];
                for (size_t Pos=0; Pos+8<=Original_Size; Pos+=8)
                {
                    int64u Value;
                    if (BigEndian)
                        Value =(((int64u)(   BigEndian2int16u(Buffer+Buffer_Offset+Pos  )&0x3FFF))<<42)
                             | (((int64u)(   BigEndian2int16u(Buffer+Buffer_Offset+Pos+2)&0x3FFF))<<28)
                             | (((int64u)(   BigEndian2int16u(Buffer+Buffer_Offset+Pos+4)&0x3FFF))<<14)
                             | (((int64u)(   BigEndian2int16u(Buffer+Buffer_Offset+Pos+6)&0x3FFF))    );
                    else
                        Value =(((int64u)(LittleEndian2int16u(Buffer+Buffer_Offset+Pos  )&0x3FFF))<<42)
                             | (((int64u)(LittleEndian2int16u(Buffer+Buffer_Offset+Pos+2)&0x3FFF))<<28)
                             | (((int64u)(LittleEndian2int16u(Buffer+Buffer_Offset+Pos+4)&0x3FFF))<<14)
                             | (((int64u)(LittleEndian2int16u(Buffer+Buffer_Offset+Pos+6)&0x3FFF))    );
                    int56u2BigEndian(Dest+Pos*14/16, Value);
                }
                Save_Buffer=Dest;
                Save_Buffer_Offset=0;
                Save_Buffer_Size=Size;
            }
        }

        Synched=true;
    }
    else
        Synched=false;

    return true;
}

//---------------------------------------------------------------------------
} //NameSpace

#endif //MEDIAINFO_DTS_YES

