/* 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. */ // Initial code provided by Xperi Inc. in 2023 // via a Contributor License Agreement //--------------------------------------------------------------------------- // Pre-compilation #include "MediaInfo/PreComp.h" #ifdef __BORLANDC__ #pragma hdrstop #endif //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Setup.h" //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #if defined(MEDIAINFO_DTSUHD_YES) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Audio/File_DtsUhd.h" #include "ZenLib/Utils.h" #include "ZenLib/BitStream.h" #include "MediaInfo/MediaInfo_Config_MediaInfo.h" #if MEDIAINFO_EVENTS #include "MediaInfo/MediaInfo_Events.h" #endif //MEDIAINFO_EVENTS #include using namespace ZenLib; using namespace std; //--------------------------------------------------------------------------- namespace MediaInfoLib { static constexpr int32u DTSUHD_NONSYNCWORD=0x71C442E8; static constexpr int32u DTSUHD_SYNCWORD=0x40411BF2; /* Return codes from dtsuhd_frame */ enum DTSUHDStatus { DTSUHD_OK, DTSUHD_INCOMPLETE, /* Entire frame not in buffer. */ DTSUHD_INVALID_FRAME, /* Error parsing frame. */ DTSUHD_NOSYNC, /* No sync frame prior to non-sync frame. */ DTSUHD_NULL, /* Function parameter may not be NULL. */ }; enum RepType { REP_TYPE_CH_MASK_BASED, REP_TYPE_MTRX2D_CH_MASK_BASED, REP_TYPE_MTRX3D_CH_MASK_BASED, REP_TYPE_BINAURAL, REP_TYPE_AMBISONIC, REP_TYPE_AUDIO_TRACKS, REP_TYPE_3D_OBJECT_SINGLE_SRC_PER_WF, REP_TYPE_3D_MONO_OBJECT_SINGLE_SRC_PER_WF, }; // Section 5.2.2.2: Long Term Loudness Measure Table static constexpr float LongTermLoudnessMeasure[] = { -40.00, -39.00, -38.00, -37.00, -36.00, -35.00, -34.00, -33.00, -32.00, -31.00, -30.00, -29.50, -29.00, -28.50, -28.00, -27.50, -27.00, -26.75, -26.50, -26.25, -26.00, -25.75, -25.50, -25.25, -25.00, -24.75, -24.50, -24.25, -24.00, -23.75, -23.50, -23.25, -23.00, -22.75, -22.50, -22.25, -22.00, -21.75, -21.50, -21.25, -21.00, -20.50, -20.00, -19.50, -19.00, -18.00, -17.00, -16.00, -15.00, -14.00, -13.00, -12.00, -11.00, -10.00, -9.00, -8.00, -7.00, -6.00, -5.00, -4.00, -3.00, -2.00, -1.00, 0.00, }; static int CountBits(int32u Mask) { int Count=0; for (; Mask; Mask>>=1) Count+=Mask&1; return Count; } /** Read a variable number of bits from a buffer. In the ETSI TS 103 491 V1.2.1 specification, the pseudo code defaults the 'add' parameter to true. Table 7-30 shows passing an explicit false, most other calls do not pass the extractAndAdd parameter. This function is based on code in Table 5-2 */ void File_DtsUhd::Get_VR(const uint8_t Table[], int32u& Value, const char* Name) { Element_Begin1(Name?Name:"?"); static const uint8_t BitsUsed[8] = {1, 1, 1, 1, 2, 2, 3, 3}; static const uint8_t IndexTable[8] = { 0, 0, 0, 0, 1, 1, 2, 3 }; uint8_t Code; Peek_S1(3, Code); Skip_S1(BitsUsed[Code], "index (partial)"); auto Index=IndexTable[Code]; Value=0; if (Table[Index]>0) { for (int i=0; iStarted && AudPresParam[Object->PresIndex].Selectable) { if (ObjIndex < 0 || (Object->PresIndex < MD01.Object[ObjIndex].PresIndex)) ObjIndex = i; } } if (ObjIndex>=0) return MD01.Object+ObjIndex; } return nullptr; } /* Save channel mask, count, and rep type to descriptor info. ETSI TS 103 491 Table 7-28 channel activity mask bits mapping and SCTE DVS 243-4 Rev. 0.2 DG X Table 4. Convert activity mask and representation type to channel mask and channel counts. */ void File_DtsUhd::ExtractObjectInfo(MDObject* Object) { if (!Object) return; constexpr struct { int32u ActivityMask, ChannelMask; // ChannelMask is defined by ETSI TS 103 491 } ActivityMap[] = { // act mask | chan mask | ffmpeg channel mask { 0x000001, 0x00000001 }, { 0x000002, 0x00000006 }, { 0x000004, 0x00000018 }, { 0x000008, 0x00000020 }, { 0x000010, 0x00000040 }, { 0x000020, 0x0000A000 }, { 0x000040, 0x00000180 }, { 0x000080, 0x00004000 }, { 0x000100, 0x00080000 }, { 0x000200, 0x00001800 }, { 0x000400, 0x00060000 }, { 0x000800, 0x00000600 }, { 0x001000, 0x00010000 }, { 0x002000, 0x00300000 }, { 0x004000, 0x00400000 }, { 0x008000, 0x01800000 }, { 0x010000, 0x02000000 }, { 0x020000, 0x0C000000 }, { 0x140000, 0x30000000 }, { 0x080000, 0xC0000000 }, { 0 } // Terminator }; for (int i = 0; ActivityMap[i].ActivityMask; i++) if (ActivityMap[i].ActivityMask & Object->ChActivityMask) FrameDescriptor.ChannelMask |= ActivityMap[i].ChannelMask; FrameDescriptor.ChannelCount = CountBits(FrameDescriptor.ChannelMask); FrameDescriptor.RepType = Object->RepType; } /* Assemble information for MP4 Sample Entry box. Sample Size is always 16 bits. The coding name is the name of the SampleEntry sub-box and is 'dtsx' unless the version of the bitstream is > 2. If DecoderProfile == 2, then MaxPayloadCode will be zero. */ void File_DtsUhd::UpdateDescriptor() { FrameDescriptor.ChannelMask=0; FrameDescriptor.RepType=0; ExtractObjectInfo(FindDefaultAudio()); /* 6.3.6.9: audio frame duration may be a fraction of metadata frame duration. */ int Fraction=1; for (const auto& Audio_Chunk : Audio_Chunks) { if (Audio_Chunk.Present) { if (Audio_Chunk.AudioChunkID==3) Fraction=2; else if (Audio_Chunk.AudioChunkID==4) Fraction=4; } } FrameDescriptor.BaseSampleFreqCode=SampleRate==48000; FrameDescriptor.ChannelCount=CountBits(FrameDescriptor.ChannelMask); FrameDescriptor.DecoderProfileCode=StreamMajorVerNum-2; FrameDescriptor.MaxPayloadCode=0+(StreamMajorVerNum>=2); FrameDescriptor.NumPresCode=NumAudioPres-1; FrameDescriptor.SampleCount=(FrameDuration*SampleRate)/(ClockRateInHz*Fraction); } /* Table 6-17 p47 */ int File_DtsUhd::ExtractExplicitObjectsLists(int DepAuPresExplObjListMask, int CurrentAuPresInd) { Element_Begin1("ExtractExplicitObjectsLists"); constexpr int8u Table[4] = {4, 8, 16, 32}; for (int i=0; i>i)&1) { bool UpdFlag=SyncFrameFlag; if (!UpdFlag) Get_SB (UpdFlag, "UpdFlag"); if (UpdFlag) Skip_VR(Table, "ExplObjListMasks"); } Element_End0(); return 0; } /* Table 6-15 p44, Table 6-16 p45 */ int File_DtsUhd::ResolveAudPresParams() { Element_Begin1("ResolveAudPresParams"); constexpr uint8_t Table[4] = {0, 2, 4, 5}; if (SyncFrameFlag) { if (FullChannelBasedMixFlag) NumAudioPres=0; else Get_VR (Table, NumAudioPres, "NumAudioPres"); NumAudioPres++; memset(AudPresParam, 0, sizeof(AudPresParam[0])*NumAudioPres); } for (int AuPresInd=0; AuPresInd>=1) if (DepAuPresMask&1) { bool DepAuPresMask; Get_SB (DepAuPresMask, "DepAuPresExplObjListPresMask"); AudPresParam[AuPresInd].DepAuPresMask|=DepAuPresMask<>8)^CRC_CCIT_Table[((int8u)C)^*Buffer++]; return C; } /* Table 6-12 p 53 */ void File_DtsUhd::DecodeVersion() { bool ShortRevNum; Get_SB (ShortRevNum, "ShortRevNum"); int8u Bits=6>>(unsigned)ShortRevNum; Get_S1 (Bits, StreamMajorVerNum, "StreamMajorVerNum"); StreamMajorVerNum+=2; Skip_S1(Bits, "StreamMinorRevNum"); } /* Table 6-12 p 53 */ int File_DtsUhd::ExtractStreamParams() { Element_Begin1("ExtractStreamParams"); static constexpr uint16_t TableBaseDuration[4] = {512, 480, 384, 0}; static constexpr uint16_t TableClockRate[4] = {32000, 44100, 48000, 0}; if (SyncFrameFlag) Get_SB (FullChannelBasedMixFlag, "FullChannelMixFlag"); if (SyncFrameFlag || !FullChannelBasedMixFlag) if (CheckCRC(Buffer+Buffer_Offset, FTOCPayloadinBytes)) return 1; if (SyncFrameFlag) { if (FullChannelBasedMixFlag) StreamMajorVerNum=2; else DecodeVersion(); int8u BaseDuration, FrameDurationCode, Temp, SampleRateMod; bool TimeStampPresent; Get_S1 (2, BaseDuration, "BaseDuration"); FrameDuration=TableBaseDuration[BaseDuration]; Get_S1 (3, FrameDurationCode, "FrameDurationCode"); FrameDuration*=FrameDurationCode+1; Param_Info2(FrameDuration, " samples"); Get_S1 (2, Temp, "ClockRateInHz"); ClockRateInHz=TableClockRate[Temp]; Param_Info2(ClockRateInHz, " Hz"); if (FrameDuration==0 || ClockRateInHz==0) return 1; /* bitstream error */ Get_SB (TimeStampPresent, "TimeStampPresent"); if (TimeStampPresent) Skip_BS(36, "TimeStamp"); Get_S1 (2, SampleRateMod, "SampleRateMod"); SampleRate=ClockRateInHz*(1<=Audio_Chunks.size()) Audio_Chunks.push_back(audio_chunk()); auto& Navi=Audio_Chunks[Index]; Navi.AudioChunkSize=0; Navi.Present=true; Navi.AudioChunkID=256; Navi.Index=Index; *ListIndex=Index; return 0; } /* Table 6-20 p48 */ int File_DtsUhd::ExtractChunkNaviData() { Element_Begin1("ExtractChunkNaviData"); constexpr int8u Table2468[4] = {2, 4, 6, 8}; constexpr int8u TableAudioChunkSizes[4] = {9, 11, 13, 16}; constexpr int8u TableChunkSizes[4] = {6, 9, 12, 15}; ChunkBytes = 0; if (FullChannelBasedMixFlag) MD_Chunks.resize(SyncFrameFlag); else { int32u Num_MD_Chunks; Get_VR(Table2468, Num_MD_Chunks, "Num_MD_Chunks"); MD_Chunks.resize(Num_MD_Chunks); } for (auto& MD_Chunk : MD_Chunks) { Get_VR(TableChunkSizes, MD_Chunk.MDChunkSize, "MDChunkSize"); ChunkBytes+=MD_Chunk.MDChunkSize; if (FullChannelBasedMixFlag) MD_Chunk.MDChunkCRCFlag=false; else Get_SB (MD_Chunk.MDChunkCRCFlag, "MDChunkCRCFlag"); } int32u Num_Audio_Chunks; if (FullChannelBasedMixFlag) Num_Audio_Chunks=1; else Get_VR (Table2468, Num_Audio_Chunks, "Num_Audio_Chunks"); if (!SyncFrameFlag) { for (auto& Navi : Audio_Chunks) Navi.Present=false; } else Audio_Chunks.clear(); for (int32u i=0; iNumObjects=1; MD01->ObjectList[0]=256; } else { constexpr int8u Table[4] = {3, 4, 6, 8}; Get_VR (Table, MD01->NumObjects, "NumObjects"); for (int i=0; iNumObjects; i++) { bool NumBitsforObjID_b; Get_SB (NumBitsforObjID_b, "NumBitsforObjID"); int8u NumBitsforObjID=4<<((unsigned)NumBitsforObjID_b); Get_S2 (NumBitsforObjID, MD01->ObjectList[i], "ObjectIDList"); } } Element_End0(); return 0; } /* Table 7-9 */ void File_DtsUhd::ExtractLTLMParamSet(MD01* MD01, bool NominalLD_DescriptionFlag) { Element_Begin1("ExtractLTLMParamSet"); Get_S1 (6, LongTermLoudnessIndex, "LongTermLoudnessMeasureIndex"); /* rLoudness */ Param_Info2(LongTermLoudnessMeasure[LongTermLoudnessIndex], " LKFS"); if (!NominalLD_DescriptionFlag) Skip_S1(5, "AssociatedAssetType"); Skip_S1(NominalLD_DescriptionFlag?2:4, "BitWidth"); Element_End0(); } /* Table 7-8 */ int File_DtsUhd::ParseStaticMDParams(MD01* MD01, bool OnlyFirst) { bool NominalLD_DescriptionFlag=true; int8u NumLongTermLoudnessMsrmSets=1; if (FullChannelBasedMixFlag==false) Get_SB (NominalLD_DescriptionFlag, "NominalLD_DescriptionFlag"); if (NominalLD_DescriptionFlag) { if (!FullChannelBasedMixFlag) { Get_S1 (1, NumLongTermLoudnessMsrmSets, "NumLongTermLoudnessMsrmSets"); NumLongTermLoudnessMsrmSets=1+(NumLongTermLoudnessMsrmSets<<1); //?3:1 Param_Info2(NumLongTermLoudnessMsrmSets, " sets"); } } else { Get_S1 (4, NumLongTermLoudnessMsrmSets, "NumLongTermLoudnessMsrmSets"); NumLongTermLoudnessMsrmSets++; Param_Info2(NumLongTermLoudnessMsrmSets, " sets"); } for (int i=0; iStaticMDParamsExtracted=true; return 0; } /* Table 7-7 */ int File_DtsUhd::ExtractMultiFrameDistribStaticMD(MD01* MD01) { Element_Begin1("ExtractMultiFrameDistribStaticMD"); static const uint8_t Table1[4] = {0, 6, 9, 12}; static const uint8_t Table2[4] = {5, 7, 9, 11}; if (SyncFrameFlag) { MD01->PacketsAcquired=0; if (FullChannelBasedMixFlag) { MD01->NumStaticMDPackets=1; MD01->StaticMDPacketByteSize=0/*2*/; //TEMP, only with p2.mp4 } else { Get_VR (Table1, MD01->NumStaticMDPackets, "NumStaticMDPackets"); MD01->NumStaticMDPackets++; Get_VR (Table2, MD01->StaticMDPacketByteSize, "StaticMDPacketByteSize"); MD01->StaticMDPacketByteSize+=3; } MD01->Buffer.resize(MD01->NumStaticMDPackets*MD01->StaticMDPacketByteSize); if (MD01->NumStaticMDPackets>1) Get_SB (MD01->StaticMetadataUpdtFlag, "StaticMetadataUpdtFlag"); else MD01->StaticMetadataUpdtFlag=true; } if (MD01->PacketsAcquiredNumStaticMDPackets) { int n=MD01->PacketsAcquired*MD01->StaticMDPacketByteSize; for (int32u i = 0; i < MD01->StaticMDPacketByteSize; i++) Get_S1 (8/*i==1?6:8*/, MD01->Buffer[n+i], ("MetadataPacketPayload[" + std::to_string(n+i) + "]").c_str()); //TEMP, only with p2.mp4 //MD01->Buffer[1]<<=2; //TEMP, only with p2.mp4 //MD01->Buffer.resize(3); //TEMP, only with p2.mp4 MD01->PacketsAcquired++; if (MD01->PacketsAcquired==MD01->NumStaticMDPackets || MD01->PacketsAcquired==1) { if (MD01->StaticMetadataUpdtFlag||!MD01->StaticMDParamsExtracted) { const int8u* Save_Buffer=nullptr; size_t Save_Buffer_Offset=0; size_t Save_Buffer_Size=0; int64u Save_Element_Offset=0; int64u Save_Element_Size=0; BitStream_Fast Save_BS; #if MEDIAINFO_TRACE size_t Save_BS_Size=0; #endif if (!MD01->Buffer.empty()) { //Use the buffered content Save_Buffer=Buffer; Save_Buffer_Offset=Buffer_Offset; Save_Buffer_Size=Buffer_Size; Save_Element_Offset=Element_Offset; Save_Element_Size=Element_Size; Save_BS=*BS; #if MEDIAINFO_TRACE Save_BS_Size=BS_Size; #endif File_Offset+=Buffer_Offset+Element_Size-(Data_BS_Remain()+7)/8-MD01->StaticMDPacketByteSize; Buffer=MD01->Buffer.data(); Buffer_Offset=0; Buffer_Size=MD01->Buffer.size(); Element_Offset=0; Element_Size=Buffer_Size; BS_Begin(); } auto Result=ParseStaticMDParams(MD01, MD01->PacketsAcquired!=MD01->NumStaticMDPackets); if (!MD01->Buffer.empty()) { //Back to normal buffer if (Data_BS_Remain()) Skip_BS(Data_BS_Remain(), "Padding"); BS_End(); Buffer=Save_Buffer; Buffer_Offset=Save_Buffer_Offset; Buffer_Size=Save_Buffer_Size; Element_Offset=Save_Element_Offset; Element_Size=Save_Element_Size; *BS=Save_BS; #if MEDIAINFO_TRACE BS_Size=Save_BS_Size; #endif File_Offset-=Buffer_Offset+Element_Size-(Data_BS_Remain()+7)/8-MD01->StaticMDPacketByteSize; } if (Result) { Element_End0(); return 1; } } } } Element_End0(); return 0; } /* Return 1 if suitable, 0 if not. Table 7-18. OBJGROUPIDSTART=224 Sec 7.8.7 p75 */ bool File_DtsUhd::CheckIfMDIsSuitableforImplObjRenderer(MD01* MD01, int ObjectId) { if (ObjectId>=224) return true; Element_Begin1("CheckIfMDIsSuitableforImplObjRenderer"); bool MDUsedByAllRenderersFlag; Get_SB (MDUsedByAllRenderersFlag, "MDUsedByAllRenderersFlag"); { Element_End0(); return true; } /* Reject the render and skip the render data. */ int32u NumBits2Skip; constexpr int8u Table[4] = {8, 10, 12, 14}; Skip_SB( "RequiredRendererType"); Get_VR (Table, NumBits2Skip, "NumBits2Skip"); Skip_BS (1+NumBits2Skip, "data"); Element_End0(); return false; } /* Table 7-26 */ void File_DtsUhd::ExtractChMaskParams(MD01* MD01, MDObject* Object) { Element_Begin1("ExtractChMaskParams"); constexpr int32u MaskTable[14] = /* Table 7-27 */ { 0x000001, 0x000002, 0x000006, 0x00000F, 0x00001F, 0x00084B, 0x00002F, 0x00802F, 0x00486B, 0x00886B, 0x03FBFB, 0x000003, 0x000007, 0x000843, }; int8u ChLayoutIndex; if (Object->RepType==REP_TYPE_BINAURAL) ChLayoutIndex=1; else Get_S1 (4, ChLayoutIndex, "ChLayoutIndex"); if (ChLayoutIndex>=14) // Index=14->read 16 bits, Index=15->read 32 bits Get_S4 (16<<(ChLayoutIndex-14), Object->ChActivityMask, "ChActivityMask"); else Object->ChActivityMask=MaskTable[ChLayoutIndex]; Element_End0(); IsType1CertifiedContent=ChLayoutIndex==14 && (Object->ChActivityMask==0xF || Object->ChActivityMask==0x2F || Object->ChActivityMask==0x802F); } /* Table 7-22 */ int File_DtsUhd::ExtractObjectMetadata(MD01* MD01, MDObject* Object, bool ObjStartFrameFlag, int ObjectId) { Element_Begin1("ExtractObjectMetadata"); if (ObjectId!=256) Skip_SB( "ObjActiveFlag"); if (ObjStartFrameFlag) { bool ChMaskObjectFlag=false, Object3DMetaDataPresent=false; Get_S1 (3, Object->RepType, "ObjRepresTypeIndex"); switch (Object->RepType) { case REP_TYPE_BINAURAL: case REP_TYPE_CH_MASK_BASED: case REP_TYPE_MTRX2D_CH_MASK_BASED: case REP_TYPE_MTRX3D_CH_MASK_BASED: ChMaskObjectFlag=true; break; case REP_TYPE_3D_OBJECT_SINGLE_SRC_PER_WF: case REP_TYPE_3D_MONO_OBJECT_SINGLE_SRC_PER_WF: Object3DMetaDataPresent=true; break; } if (ChMaskObjectFlag) { if (ObjectId!=256) { bool ObjTypeDescrPresent; Skip_S1(3, "ObjectImportanceLevel"); Get_SB (ObjTypeDescrPresent, "ObjTypeDescrPresent"); if (ObjTypeDescrPresent) { bool TypeBitWidth; Get_SB (TypeBitWidth, "TypeBitWidth"); Skip_S1(TypeBitWidth?3:5, "ObjTypeDescrIndex"); } constexpr int8u Table1[4]={1, 4, 4, 8}; constexpr int8u Table2[4]={3, 3, 4, 8}; Skip_VR(Table1, "ObjAudioChunkIndex"); Skip_VR(Table2, "ObjNaviWithinACIndex"); /* Skip optional Loudness block. */ bool PerObjLTLoudnessMDPresent; Get_SB (PerObjLTLoudnessMDPresent, "PerObjLTLoudnessMDPresent"); if (PerObjLTLoudnessMDPresent) Skip_S1(8, "PerObjLTLoudnessMD"); /* Skip optional Object Interactive MD (Table 7-25). */ bool ObjInteractiveFlag; Get_SB (ObjInteractiveFlag, "ObjInteractiveFlag"); if (ObjInteractiveFlag && InteractObjLimitsPresent) { bool ObjInterLimitsFlag; Get_SB (ObjInterLimitsFlag, "ObjInterLimitsFlag"); if (ObjInterLimitsFlag) Skip_S2(5+6*Object3DMetaDataPresent, "ObjectInteractMD"); } } ExtractChMaskParams(MD01, Object); } } /* Skip rest of object */ Element_End0(); return 0; } /* Table 7-4 */ int File_DtsUhd::ParseMD01(MD01 *MD01, int AuPresInd) { if (AudPresParam[AuPresInd].Selectable) { Element_Begin1("ExtractPresScalingParams"); for (int i = 0; i<4; i++) /* Table 7-5. Scaling data. */ { bool OutScalePresent; Get_SB (OutScalePresent, "OutScalePresent"); if (OutScalePresent) Skip_S1(5, "OutScale"); } Element_End0(); bool MFDistrStaticMDPresent; Get_SB (MFDistrStaticMDPresent, "MFDistrStaticMDPresent"); if (MFDistrStaticMDPresent && ExtractMultiFrameDistribStaticMD(MD01)) return 1; } /* Table 7-16: Object metadata. */ memset(MD01->Object, 0, sizeof(MD01->Object)); if (!FullChannelBasedMixFlag) { bool MixStudioParamsPresent; Get_SB (MixStudioParamsPresent, "MixStudioParamsPresent"); if (MixStudioParamsPresent) Skip_S2(11, "MixStudioParams"); } for (int i=0; iNumObjects; i++) { int32u Id = MD01->ObjectList[i]; if (!CheckIfMDIsSuitableforImplObjRenderer(MD01, Id)) continue; MD01->Object[Id].PresIndex = AuPresInd; bool StartFlag=false; if (!MD01->Object[Id].Started) { if (Id!=256) Skip_SB( "ObjStaticFlag"); StartFlag=MD01->Object[Id].Started=true; } if ((Id<224||Id>255) && ExtractObjectMetadata(MD01, MD01->Object+Id, StartFlag, Id)) return 1; break; } return 0; } /* Table 6-2 */ int File_DtsUhd::UnpackMDFrame() { Element_Begin1("UnpackMDFrame"); for (auto& MDChunk : MD_Chunks) { if (!MDChunk.MDChunkSize) continue; if (MDChunk.MDChunkCRCFlag && CheckCRC(Buffer+Buffer_Offset, MDChunk.MDChunkSize)) { Element_End0(); return 1; } Element_Begin1("MDChunk"); int64u End=Element_Offset+MDChunk.MDChunkSize; int8u MDChunkID; Get_B1 (MDChunkID, "MDChunkID"); switch (MDChunkID) { case 1 : UnpackMDFrame_1(MDChunkID); break; } if (End>Element_Offset) Skip_XX(End-Element_Offset, "(Unknown)"); Element_End0(); } Element_End0(); return 0; } /* Table 6-2 */ void File_DtsUhd::UnpackMDFrame_1(int8u MDChunkID) { BS_Begin(); static constexpr int8u Table[4] = { 0, 2, 4, 4 }; int32u AudPresIndex; Get_VR (Table, AudPresIndex, "AudPresIndex"); if (AudPresIndex>255) { BS_End(); return; } MD01* MD01=ChunkFindMD01(MDChunkID); if (MD01==nullptr) MD01=ChunkAppendMD01(MDChunkID); if (!MD01) { BS_End(); return; } if (ExtractMDChunkObjIDList(MD01)) { BS_End(); return; } if (ParseMD01(MD01, AudPresIndex)) { BS_End(); return; } BS_End(); } /** Parse a single DTS:X Profile 2 frame. The frame must start at the first byte of the data buffer, and enough of the frame must be present to decode the majority of the FTOC. From Table 6-11 p52. A sync frame must be provided. */ int File_DtsUhd::Frame() { int32u SyncWord; Get_B4 (SyncWord, "SyncWord"); SyncFrameFlag=SyncWord==DTSUHD_SYNCWORD; if (SyncFrameFlag) Element_Info1("Key frame"); BS_Begin(); static constexpr int8u Table[4]={5, 8, 10, 12}; Get_VR (Table, FTOCPayloadinBytes, "FTOCPayloadinBytes"); FTOCPayloadinBytes++; if (FTOCPayloadinBytes<=4 || FTOCPayloadinBytes>=FrameSize) return DTSUHD_INCOMPLETE; //Data buffer does not contain entire FTOC if (ExtractStreamParams()) return DTSUHD_INVALID_FRAME; if (ResolveAudPresParams()) return DTSUHD_INVALID_FRAME; if (ExtractChunkNaviData()) //AudioChunkTypes and payload sizes. return DTSUHD_INVALID_FRAME; size_t Remain8=Data_BS_Remain()%8; if (Remain8) Skip_S1(Remain8, "Padding"); BS_End(); if (SyncFrameFlag || !FullChannelBasedMixFlag) Skip_B2( "CRC16"); if (Element_Offset!=FTOCPayloadinBytes) return DTSUHD_INVALID_FRAME; //At this point in the parsing, we can calculate the size of the frame. if (FrameSizeBuffer_Size-Buffer_Offset) return false; bool HasCRC=SyncFrame||!FullChannelBasedMixFlag; return !HasCRC || CheckCRC(Buffer+Buffer_Offset, FtocSize)==0; } //*************************************************************************** // Constructor/Destructor //*************************************************************************** //--------------------------------------------------------------------------- File_DtsUhd::File_DtsUhd() :File_Dts_Common() { //Configuration ParserName="DtsUhd"; #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 IsType1CertifiedContent=false; LongTermLoudnessIndex=(int8u)-1; MustSynchronize=true; Buffer_TotalBytes_FirstSynched_Max=64*1024; PTS_DTS_Needed=true; StreamSource=IsStream; } //--------------------------------------------------------------------------- void File_DtsUhd::Streams_Fill() { DTSUHD_ChannelMaskInfo ChannelMaskInfo = DTSUHD_DecodeChannelMask(FrameDescriptor.ChannelMask); float32 BitRate_Max=0; if (FrameDuration && Retrieve_Const(Stream_Audio, 0, Audio_BitRate_Maximum).empty()) // Only if not provided by header { int32u MaxPayload=2048< 0; CommercialString.back()+=FrameDescriptor.DecoderProfileCode; if (IsType1CertifiedContent) CommercialString+=" with IMAX Enhanced"; Fill(Stream_General, 0, General_Format, "DTS-UHD"); Fill(Stream_General, 0, General_OverallBitRate_Mode, "VBR"); Stream_Prepare(Stream_Audio); if (BitRate_Max) Fill(Stream_Audio, 0, Audio_BitRate_Maximum, BitRate_Max, 0, true); Fill(Stream_Audio, 0, Audio_BitRate_Mode, "VBR", Unlimited, true, true); Fill(Stream_Audio, 0, Audio_Codec, AudioCodec); Fill(Stream_Audio, 0, Audio_Format, "DTS-UHD"); Fill(Stream_Audio, 0, Audio_Format_Commercial_IfAny, CommercialString); Fill(Stream_Audio, 0, Audio_Format_Profile, FrameDescriptor.DecoderProfileCode + 2); Fill(Stream_Audio, 0, Audio_Format_Settings, RepresentationTypeTable[FrameDescriptor.RepType]); if (IsType1CertifiedContent) Fill(Stream_Audio, 0, Audio_Format_Settings, "T1-CC"); Fill(Stream_Audio, 0, Audio_SamplesPerFrame, FrameDescriptor.SampleCount, 10, true); if (SampleRate) Fill(Stream_Audio, 0, Audio_SamplingRate, SampleRate); if (LongTermLoudnessIndex<64) { Fill(Stream_Audio, 0, "Loudness", "Yes", Unlimited, true, true); Fill_Measure(Stream_Audio, 0, "Loudness LongTermLoudness", Ztring::ToZtring(LongTermLoudnessMeasure[LongTermLoudnessIndex], 2), __T(" LKFS")); } if (FrameDescriptor.ChannelMask) { Fill(Stream_Audio, 0, Audio_Channel_s_, ChannelMaskInfo.ChannelCount); Fill(Stream_Audio, 0, Audio_ChannelLayout, ChannelMaskInfo.ChannelLayoutText); Fill(Stream_Audio, 0, Audio_ChannelPositions, ChannelMaskInfo.ChannelPositionsText); Fill(Stream_Audio, 0, Audio_ChannelPositions_String2, ChannelMaskInfo.ChannelPositions2Text); } } //--------------------------------------------------------------------------- bool File_DtsUhd::Synchronize() { while (Buffer_Offset+4<=Buffer_Size) { if (!FrameSynchPoint_Test(false)) return false; //Need more data if (Synched) break; Buffer_Offset++; } return true; } //--------------------------------------------------------------------------- bool File_DtsUhd::Synched_Test() { //Quick test of synchro if (!FrameSynchPoint_Test(true)) 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_DtsUhd::Read_Buffer_Unsynched() { FrameInfo=frame_info(); } //--------------------------------------------------------------------------- void File_DtsUhd::Header_Parse() { Header_Fill_Size(FrameSize); } //--------------------------------------------------------------------------- void File_DtsUhd::Data_Parse() { Element_Name("Frame"); Element_Info1(Frame_Count); if (Frame()!=DTSUHD_OK) Trusted_IsNot("Parsing issue"); for (const auto& Audio_Chunk : Audio_Chunks) Skip_XX(Audio_Chunk.AudioChunkSize, "AudioChunk"); Skip_XX(Element_Size-Element_Offset, "(Unknown)"); FILLING_BEGIN(); if (!Status[IsAccepted]) Accept("DTS-UHD"); Frame_Count++; if (Frame_Count>=Frame_Count_Valid) { Fill("DTS-UHD"); //No more need data if (!IsSub && Config->ParseSpeed<1.0) { if (Stream_Offset_Max!=-1) GoTo(Stream_Offset_Max); else Finish("DTS"); } } FILLING_END(); } //--------------------------------------------------------------------------- bool File_DtsUhd::FrameSynchPoint_Test(bool AcceptNonSync) { if (Buffer_Offset+16>Buffer_Size) return false; //Must wait for more data int32u SyncWord=CC4(Buffer+Buffer_Offset); if (SyncWord!=DTSUHD_SYNCWORD && (!AcceptNonSync || SyncWord!=DTSUHD_NONSYNCWORD)) { Synched=false; return true; //Not a syncpoint, done } Synched=CheckCurrentFrame(); if (Synched) { FrameSize=4; if (IsSub) { FrameSize+=Element_Size; return true; // We trust the container enough } while (Buffer_Offset+FrameSize+4<=Buffer_Size) { int32u SyncWord=CC4(Buffer+Buffer_Offset+FrameSize); if (SyncWord==DTSUHD_SYNCWORD||SyncWord==DTSUHD_NONSYNCWORD) { Buffer_Offset+=FrameSize; bool IsOk=CheckCurrentFrame(); Buffer_Offset-=FrameSize; if (IsOk) return true; } FrameSize++; } } return false; //Must wait for more data } //--------------------------------------------------------------------------- } //NameSpace #endif //MEDIAINFO_DTSUHD_YES