/* 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. */ //--------------------------------------------------------------------------- // Digital Audio Tape // https://nixdoc.net/man-pages/IRIX/man4/datframe.4.html //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Pre-compilation #include "MediaInfo/PreComp.h" #ifdef __BORLANDC__ #pragma hdrstop #endif //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Setup.h" //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #if defined(MEDIAINFO_DAT_YES) //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- #include "MediaInfo/Audio/File_Dat.h" #include "MediaInfo/MediaInfo_Config_MediaInfo.h" #include "MediaInfo/TimeCode.h" using namespace ZenLib; using namespace std; //--------------------------------------------------------------------------- namespace MediaInfoLib { //*************************************************************************** // Constants //*************************************************************************** //--------------------------------------------------------------------------- static const char* Dat_pack_id[] = { "Program Time", "Absolute Time", "Running Time", "Table of Contents", "Date", "Catalog Number", "International Standard Recording Code", "Pro Binary", }; const auto Dat_pack_id_size = sizeof(Dat_pack_id) / sizeof(*Dat_pack_id); //--------------------------------------------------------------------------- static const char* Dat_fmtid[4] = { "for audio use", nullptr, nullptr, nullptr, }; //--------------------------------------------------------------------------- static const char* Dat_emphasis[4] = { "off", "50/15 ms", nullptr, nullptr, }; //--------------------------------------------------------------------------- static const int16u Dat_sampfreq[4] = { 48000, 44100, 32000, 0, }; //--------------------------------------------------------------------------- static const int8u Dat_numchans[4] = { 2, 4, 0, 0, }; //--------------------------------------------------------------------------- static const int8u Dat_quantization[4] = { 16, 12, 0, 0, }; //--------------------------------------------------------------------------- static const char* Dat_trackpitch[4] = { "Normal", "Wide", nullptr, nullptr, }; //--------------------------------------------------------------------------- static const char* Dat_copy[4] = { "Yes", nullptr, "No", nullptr, }; //--------------------------------------------------------------------------- static const char* Dat_sid[4] = { "SMPTE", "Local Pro DIO", "Time-of-day Pro DIO", nullptr, }; //--------------------------------------------------------------------------- static const float Dat_xrate[8] = { 30, 30 / 1.001, // NDF 30 / 1.001, // DF 25, 24, 0, 0, 0, }; //--------------------------------------------------------------------------- enum items { item_emphasis, item_sampfreq, item_numchans, item_quantization, item_Max, }; //--------------------------------------------------------------------------- class file_dat_frame { public: TimeCode TCs[7] = {}; int8u TC_IDs[7] = {}; int16u dtsubid_pno = (int16u)-1; }; class file_dat_priv { public: file_dat_frame Frame_First; file_dat_frame Frame_Last; int8u Items[item_Max][4] = {}; int8u Frame_Count_Valid = 32; // Mini 31 frames for catching wrong pro time codes }; //*************************************************************************** // Constructor/Destructor //*************************************************************************** //--------------------------------------------------------------------------- File_Dat::File_Dat() : File__Analyze() { //Configuration #if MEDIAINFO_TRACE Trace_Layers_Update(8); //Stream #endif //MEDIAINFO_TRACE Frame_Count_NotParsedIncluded=0; } //--------------------------------------------------------------------------- File_Dat::~File_Dat() { delete Priv; } //*************************************************************************** // Streams management //*************************************************************************** //--------------------------------------------------------------------------- void File_Dat::Streams_Accept() { auto Conditional = [&](int8u* Values) { int Max_Pos = 0; int Max_Value = 0; for (size_t i = 0; i < 4; i++) { if (Max_Value < Values[i]) { Max_Value = Values[i]; Max_Pos = i; } } Max_Value >>= 2; for (size_t i = 0; i < 4; i++) { if (Max_Value < Values[i] && i != Max_Pos) { return -1; } } return Max_Pos; }; auto Conditional_Int8 = [&](audio Field, items Item, const int8u Array[]) { auto Index = Conditional(Priv->Items[Item]); if (Index >= 0) { if (Array[Index]) { Fill(Stream_Audio, 0, Field, Array[Index]); } else { Fill(Stream_Audio, 0, Field, "Value" + to_string(Index)); } } }; auto Conditional_Int16 = [&](audio Field, items Item, const int16u Array[]) { auto Index = Conditional(Priv->Items[Item]); if (Index >= 0) { if (Array[Index]) { Fill(Stream_Audio, 0, Field, Array[Index]); } else { Fill(Stream_Audio, 0, Field, "Value" + to_string(Index)); } } }; auto Conditional_Char = [&](audio Field, items Item, const char* Array[]) { auto Index = Conditional(Priv->Items[Item]); if (Index >= 0) { if (Array[Index]) { Fill(Stream_Audio, 0, Field, Array[Index]); } else { Fill(Stream_Audio, 0, Field, "Value" + to_string(Index)); } } }; auto Conditional_TimeCode = [&](size_t i) { if (!Priv->Frame_First.TC_IDs[i]) { return; } Stream_Prepare(Stream_Other); TimeCode& First = Priv->Frame_First.TCs[i]; if (First.IsValid()) { TimeCode& Last = Priv->Frame_Last.TCs[i]; if (Last.IsValid()) { First.SetFramesMax(Last.GetFramesMax()); First.SetDropFrame(Last.IsDropFrame()); First.Set1001fps(Last.Is1001fps()); } Fill(Stream_Other, StreamPos_Last, Other_Type, "Time code"); Fill(Stream_Other, StreamPos_Last, Other_Format, "SMPTE TC"); Fill(Stream_Other, StreamPos_Last, Other_MuxingMode, Dat_pack_id[Priv->Frame_First.TC_IDs[i] - 1]); Fill(Stream_Other, StreamPos_Last, Other_BitRate, 0); Fill(Stream_Other, StreamPos_Last, Other_TimeCode_FirstFrame, First.ToString()); Fill(Stream_Other, StreamPos_Last, Other_FrameRate, First.GetFramesMax() == 33 ? (100.0 / 3.0) : First.GetFrameRate()); } }; Fill(Stream_General, 0, General_Format, "DAT"); Stream_Prepare(Stream_Audio); Fill(Stream_Audio, 0, Audio_Format, "PCM"); Fill(Stream_Audio, 0, Audio_BitRate, float32_int64s(1536000 * 441 / 480)); if (File_Size != (int64u)-1) { Fill(Stream_Audio, 0, Audio_StreamSize, float32_int64s((((float)File_Size / 5822) * 5760) * 441 / 480), 0); } Conditional_Char(Audio_Format_Settings_Emphasis, item_emphasis, Dat_emphasis); Conditional_Int16(Audio_SamplingRate, item_sampfreq, Dat_sampfreq); Conditional_Int8(Audio_Channel_s_, item_numchans, Dat_numchans); Conditional_Int8(Audio_BitDepth, item_quantization, Dat_quantization); for (int i = 0; i < 7; i++) { Conditional_TimeCode(i); } } //--------------------------------------------------------------------------- void File_Dat::Streams_Finish() { auto Conditional_TimeCode = [&](size_t i, size_t& StreamPos) { if (!Priv->Frame_First.TC_IDs[i]) { return; } TimeCode& Last = Priv->Frame_Last.TCs[i]; if (Last.IsValid()) { TimeCode& First = Priv->Frame_First.TCs[i]; if (First.IsValid()) { Last.SetFramesMax(First.GetFramesMax()); Last.SetDropFrame(First.IsDropFrame()); Last.Set1001fps(First.Is1001fps()); } Fill(Stream_Other, StreamPos, Other_TimeCode_LastFrame, Last.ToString()); } StreamPos++; }; size_t StreamPos = 0; for (int i = 0; i < 7; i++) { Conditional_TimeCode(i, StreamPos); } } //*************************************************************************** // Buffer - Global //*************************************************************************** //--------------------------------------------------------------------------- void File_Dat::Read_Buffer_Unsynched() { if (Priv) { Priv->Frame_Last = {}; } FrameInfo = frame_info(); } //*************************************************************************** // Buffer - Per element //*************************************************************************** //--------------------------------------------------------------------------- void File_Dat::Header_Parse() { Header_Fill_Size(5822); Header_Fill_Code(0, "dtframe"); } //*************************************************************************** // Elements //*************************************************************************** //--------------------------------------------------------------------------- void File_Dat::Data_Parse() { Skip_XX(5760, "audio"); Element_Begin1("dtsubcode"); BS_Begin(); size_t numpacks_Min = 0; auto Frame = Priv ? Priv->Frame_Last : file_dat_frame(); for (int i = 0; i < 7; i++) { auto ParityCheck_Cur = Buffer + (size_t)(Buffer_Offset + Element_Offset); auto ParityCheck_End = ParityCheck_Cur + 8; int8u Parity = 0; for (; ParityCheck_Cur < ParityCheck_End; ParityCheck_Cur++) { Parity ^= *ParityCheck_Cur; } Element_Begin1("dtsubcodepack"); int8u id; Get_S1(4, id, "id"); Element_Info1C(id && id <= Dat_pack_id_size, Dat_pack_id[id - 1]); if (id) { numpacks_Min++; } switch (id) { case 0: Skip_BS(52, "padding"); break; case 2: case 3: Frame.TC_IDs[i] = id; dttimepack(Frame.TCs[i]); break; default: Skip_BS(52, "(Unknown)"); } Skip_S1( 8, "parity"); if (Parity) { Trusted_IsNot("parity"); } Element_End0(); } Element_Begin1("dtsubid"); int8u dataid, pno1, numpacks, pno2, pno3; Skip_SB( "ctrlid - Priority ID"); Skip_SB( "ctrlid - Start ID"); Skip_SB( "ctrlid - Shortening ID"); Skip_SB( "ctrlid - TOC ID"); Get_S1 ( 4, dataid, "dataid"); Get_S1 ( 4, pno1, "pno (program number) - 1"); Get_S1 ( 4, numpacks, "numpacks"); Get_S1 ( 4, pno2, "pno (program number) - 2"); Get_S1 ( 4, pno3, "pno (program number) - 3"); Info_S1( 8, ipf, "ipf (interpolation flags)"); Skip_Flags(ipf, 6, "left ipf"); Skip_Flags(ipf, 7, "right ipf"); int16u pno = ((int16u)pno1 << 8) | (pno2 << 4) | pno3; auto pno_IsSpecial_Check = [](int8u pno) { return pno == 0xAA || pno == 0xBB || pno == 0xEE; }; bool pno_CurrentIsSpecial = pno_IsSpecial_Check(pno); bool pno_IsValid = false; if ((pno1 < 8 && pno2 < 10 && pno3 < 10) || pno_CurrentIsSpecial) { // pno is BCD or 0x0AA meaning unknown or 0x0BB meaning lead-in or 0x0EE meaning lead-out bool pno_PreviousIsSpecial = pno_IsSpecial_Check(Frame.dtsubid_pno); if (pno_CurrentIsSpecial || pno_PreviousIsSpecial || pno == Frame.dtsubid_pno + 1) { pno_IsValid = true; } Frame.dtsubid_pno = pno; } if (dataid || numpacks >= 8 || numpacks < numpacks_Min || !pno_IsValid) { Trusted_IsNot("dtsubid"); } Element_End0(); Element_Begin1("dtmainid"); int8u fmtid, emphasis, sampfreq, numchans, quantization; Get_S1 ( 2, fmtid, "fmtid"); Param_Info1C(Dat_fmtid[fmtid], Dat_fmtid[fmtid]); Get_S1 ( 2, emphasis, "emphasis"); Param_Info1C(Dat_emphasis[emphasis], Dat_emphasis[emphasis]); Get_S1 ( 2, sampfreq, "sampfreq"); Param_Info2C(Dat_sampfreq[sampfreq], Dat_sampfreq[sampfreq], " Hz"); Get_S1 ( 2, numchans, "numchans"); Param_Info2C(Dat_numchans[numchans], Dat_numchans[numchans], " channels"); Get_S1 ( 2, quantization, "quantization"); Param_Info2C(Dat_quantization[quantization], Dat_quantization[quantization], "bits"); Info_S1( 2, trackpitch, "trackpitch"); Param_Info1C(Dat_trackpitch[trackpitch], Dat_trackpitch[trackpitch]); Info_S1( 2, copy, "copy"); Param_Info1C(Dat_copy[copy], Dat_copy[copy]); Skip_S1( 2, "pack"); if (fmtid) { Trusted_IsNot("dtmainid"); } Element_End0(); BS_End(); Element_End0(); FILLING_BEGIN(); Element_Info1C(Frame_Count_NotParsedIncluded != (int64u)-1, __T("Frame ") + Ztring::ToZtring(Frame_Count_NotParsedIncluded)); Element_Info1C((FrameInfo.PTS!=(int64u)-1), __T("PTS ")+Ztring().Duration_From_Milliseconds(float64_int64s(((float64)FrameInfo.PTS)/1000000))); if (Frame_Count_NotParsedIncluded != (int64u)-1) { Frame_Count_NotParsedIncluded++; } if (!Status[IsAccepted]) { if (!Priv) { Priv = new file_dat_priv; } if (!Frame_Count) { Priv->Frame_First = Frame; } Priv->Items[item_emphasis][emphasis]++; Priv->Items[item_sampfreq][sampfreq]++; Priv->Items[item_numchans][numchans]++; Priv->Items[item_quantization][quantization]++; Frame_Count++; if (Frame_Count >= Priv->Frame_Count_Valid) { Accept(); Fill(); if (!IsSub && Config->ParseSpeed < 1.0 && File_Offset + Buffer_Offset < File_Size / 2) { if (File_Size != (int64u)-1) { Open_Buffer_Unsynch(); GoTo((((File_Size) / 5822) - 1) * 5822); } else { Finish(); } } } } Priv->Frame_Last = Frame; FILLING_ELSE(); if (Priv) { if (!Frame_Count) { Priv->Frame_First = {}; } Priv->Frame_Last = {}; } FILLING_END(); } //*************************************************************************** // C++ //*************************************************************************** //--------------------------------------------------------------------------- void File_Dat::dttimepack(TimeCode& TC) { // Parsing int16u sample; int8u pno1, pno23, index, h, m, s, f; bool pro; Get_SB ( pro, "pro"); if (pro) { Skip_SB( "fill"); Get_S1 ( 2, pno1, "sid"); Param_Info1C(Dat_sid[pno1], Dat_sid[pno1]); Get_S1 ( 2, pno23, "freq"); Param_Info2C(Dat_sampfreq[pno23], Dat_sampfreq[pno23], " Hz"); Get_S1 ( 3, index, "xrate"); Param_Info2C(pno23 && Dat_xrate[index], Dat_xrate[index], " fps"); Get_S2 (11, sample, "sample"); } else { Get_S1 (3, pno1, "pno1"); Get_BCD( pno23, "pno2", "pno3"); Get_BCD( index, "index1", "index2"); } Element_Begin1("dttimecode"); Get_BCD( h, "h1", "h2"); Get_BCD( m, "m1", "m2"); Get_BCD( s, "s1", "s2"); Get_BCD( f, "f1", "f2"); auto FramesMax = TC.GetFramesMax(); auto FrameMas_Theory = pro ? (index <= 2 ? 29 : (((int32u)Dat_xrate[index]) - 1)) : 33; if (f > FrameMas_Theory && FramesMax <= FrameMas_Theory) { //Fill_Conformance("dttimepack", "dttimepack is indicated as pro time code but contains frame numbers as if it is non pro time code"); FramesMax = 33; } else if (FramesMax < FrameMas_Theory) { FramesMax = FrameMas_Theory; } bool IS1001fps = FramesMax != 33 && pro && (index == 1 || index == 2); TC = TimeCode(h, m, s, f, FramesMax, TimeCode::FPS1001(IS1001fps).DropFrame(IS1001fps && index == 2)); Element_Info1(TC.ToString()); Element_End0(); Element_Info1(TC.ToString()); Element_Level -= 2; Element_Info1(TC.ToString()); Element_Level += 2; if ((!pro && (pno1 >= 10 || pno23 == (int8u)-1 || index == (int8u)-1)) || (pro && (!Dat_sampfreq[pno23] || (pno23 && !Dat_xrate[index]) || sample >= 1440)) || !TC.IsValid()) { Trusted_IsNot("dtsubcode dttimecode"); } } void File_Dat::Get_BCD(int8u& Value, const char* Name0, const char* Name1) { int8u Value0; Get_S1 (4, Value0, Name0); Get_S1 (4, Value, Name1); int8u ValueHex = (Value0 << 4) | Value; if (ValueHex == 0xAA || ValueHex == 0xBB || ValueHex == 0xEE) { Value = ValueHex; } else if (Value0 >= 10 || Value >= 10) { Value = (int8u)-1; } else { Value += Value0 * 10; } } //*************************************************************************** // C++ //*************************************************************************** } //NameSpace #endif //MEDIAINFO_DAT_YES