1 /*  Copyright (c) MediaArea.net SARL. All Rights Reserved.
2  *
3  *  Use of this source code is governed by a BSD-style license that can
4  *  be found in the License.html file in the root of the source tree.
5  */
6 
7  // audioProgramme
8  // - audioContent
9  // - - audioObject
10  // - - - audioPackFormat
11  // - - - - audioChannelFormat
12  // - - - audioTrackUID
13  // - - - - audioChannelFormat +
14  // - - - - audioPackFormat
15  // - - - - - audioChannelFormat +
16  // - - - - audioTrackFormat
17  // - - - - - audioStreamFormat
18  // - - - - - - audioChannelFormat +
19  // - - - - - - audioPackFormat +
20  // - - - - - - audioTrackFormat +
21 
22 //---------------------------------------------------------------------------
23 // Pre-compilation
24 #include "MediaInfo/PreComp.h"
25 #ifdef __BORLANDC__
26     #pragma hdrstop
27 #endif
28 //---------------------------------------------------------------------------
29 
30 //---------------------------------------------------------------------------
31 #include "MediaInfo/Setup.h"
32 #include <bitset>
33 //---------------------------------------------------------------------------
34 
35 //---------------------------------------------------------------------------
36 #if defined(MEDIAINFO_ADM_YES)
37 //---------------------------------------------------------------------------
38 
39 //---------------------------------------------------------------------------
40 #include "MediaInfo/Audio/File_Adm.h"
41 #include "ThirdParty/tfsxml/tfsxml.h"
42 using namespace ZenLib;
43 //---------------------------------------------------------------------------
44 
45 namespace MediaInfoLib
46 {
47 //---------------------------------------------------------------------------
48 static const char* profile_names[]=
49 {
50     "profileName",
51     "profileVersion",
52     "profileID",
53     "levelID",
54 };
55 static const int profile_names_size=(int)sizeof(profile_names)/sizeof(const char*);
56 static const char* profile_names_InternalID[profile_names_size]=
57 {
58     "Format",
59     "Version",
60     "Profile",
61     "Level",
62 };
63 struct profile_info
64 {
65     string Strings[4];
profile_info_buildMediaInfoLib::profile_info66     string profile_info_build(size_t Max=profile_names_size)
67     {
68         bool HasParenthsis=false;
69         string ToReturn;
70         for (size_t i=0; i<Max; i++)
71         {
72             if (!Strings[i].empty())
73             {
74                 if (!ToReturn.empty())
75                 {
76                     if (i==1)
77                         ToReturn+=", Version";
78                     if (!HasParenthsis)
79                         ToReturn+=' ';
80                 }
81                 if (i>=2)
82                 {
83                     if (!HasParenthsis)
84                     {
85                         ToReturn+='(';
86                         HasParenthsis=true;
87                     }
88                     else
89                     {
90                         ToReturn+=',';
91                         ToReturn+=' ';
92                     }
93                 }
94                 if (i>=2)
95                 {
96                     ToReturn+=profile_names[i];
97                     ToReturn+='=';
98                 }
99                 ToReturn+=Strings[i];
100             }
101         }
102         if (HasParenthsis)
103             ToReturn+=')';
104         return ToReturn;
105     }
106 };
107 
IsHexaDigit(char Value)108 static bool IsHexaDigit(char Value)
109 {
110     return (Value >= '0' && Value <= '9')
111         || (Value >= 'A' && Value <= 'F')
112         || (Value >= 'a' && Value <= 'f');
113 }
114 
115 enum flags {
116     Version0,
117     Version1,
118     Version2,
119     Count0,
120     Count1,
121     Count2,
122     flags_Max
123 };
124 
125 struct attribute_item {
126     const char*         Name;
127     bitset<flags_Max>   Flags;
128 };
129 
130 struct element_item {
131     const char*         Name;
132     int                 Flags;
133 };
134 
135 struct element {
136     attribute_item*     Attributes;
137     element_item*       Elements;
138     int                 Attributes_Count;
139     int                 Elements_Count;
140 };
141 
142 enum items {
143     item_audioProgramme,
144     item_audioContent,
145     item_audioObject,
146     item_audioPackFormat,
147     item_audioChannelFormat,
148     item_audioTrackUID,
149     item_audioTrackFormat,
150     item_audioStreamFormat,
151     item_Max
152 };
153 
154 enum audioProgramme_String {
155     audioProgramme_audioProgrammeID,
156     audioProgramme_audioProgrammeName,
157     audioProgramme_audioProgrammeLanguage,
158     audioProgramme_start,
159     audioProgramme_end,
160     audioProgramme_typeLabel,
161     audioProgramme_String_Max
162 };
163 
164 enum audioProgramme_StringVector {
165     audioProgramme_audioProgrammeLabel,
166     audioProgramme_audioContentIDRef,
167     audioProgramme_StringVector_Max
168 };
169 
170 enum audioContent_String {
171     audioContent_audioContentID,
172     audioContent_audioContentName,
173     audioContent_audioContentLanguage,
174     audioContent_typeLabel,
175     audioContent_String_Max
176 };
177 
178 enum audioContent_StringVector {
179     audioContent_dialogue,
180     audioContent_audioContentLabel,
181     audioContent_integratedLoudness,
182     audioContent_audioObjectIDRef,
183     audioContent_StringVector_Max
184 };
185 
186 enum audioObject_String {
187     audioObject_audioObjectID,
188     audioObject_audioObjectName,
189     audioObject_startTime,
190     audioObject_duration,
191     audioObject_typeLabel,
192     audioObject_String_Max
193 };
194 
195 enum audioObject_StringVector {
196     audioObject_audioPackFormatIDRef,
197     audioObject_audioTrackUIDRef,
198     audioObject_StringVector_Max
199 };
200 
201 enum audioPackFormat_String {
202     audioPackFormat_audioPackFormatID,
203     audioPackFormat_audioPackFormatName,
204     audioPackFormat_typeDefinition,
205     audioPackFormat_typeLabel,
206     audioPackFormat_String_Max
207 };
208 
209 enum audioPackFormat_StringVector {
210     audioPackFormat_audioChannelFormatIDRef,
211     audioPackFormat_StringVector_Max
212 };
213 
214 enum audioChannelFormat_String {
215     audioChannelFormat_audioChannelFormatID,
216     audioChannelFormat_audioChannelFormatName,
217     audioChannelFormat_typeDefinition,
218     audioChannelFormat_typeLabel,
219     audioChannelFormat_String_Max
220 };
221 
222 enum audioChannelFormat_StringVector {
223     audioChannelFormat_StringVector_Max
224 };
225 
226 enum audioTrackUID_String {
227     audioTrackUID_UID,
228     audioTrackUID_bitDepth,
229     audioTrackUID_sampleRate,
230     audioTrackUID_typeLabel,
231     audioTrackUID_String_Max
232 };
233 
234 enum audioTrackUID_StringVector {
235     audioTrackUID_audioChannelFormatIDRef,
236     audioTrackUID_audioPackFormatIDRef,
237     audioTrackUID_audioTrackFormatIDRef,
238     audioTrackUID_StringVector_Max
239 };
240 
241 enum audioTrackFormat_String {
242     audioTrackFormat_audioTrackFormatID,
243     audioTrackFormat_audioTrackFormatName,
244     audioTrackFormat_formatDefinition,
245     audioTrackFormat_typeDefinition,
246     audioTrackFormat_typeLabel,
247     audioTrackFormat_String_Max
248 };
249 
250 enum audioTrackFormat_StringVector {
251     audioTrackFormat_audioStreamFormatIDRef,
252     audioTrackFormat_StringVector_Max
253 };
254 
255 enum audioStreamFormat_String {
256     audioStreamFormat_audioStreamFormatID,
257     audioStreamFormat_audioStreamFormatName,
258     audioStreamFormat_formatDefinition,
259     audioStreamFormat_formatLabel,
260     audioStreamFormat_typeDefinition,
261     audioStreamFormat_typeLabel,
262     audioStreamFormat_String_Max
263 };
264 
265 enum audioStreamFormat_StringVector {
266     audioStreamFormat_audioChannelFormatIDRef,
267     audioStreamFormat_audioPackFormatIDRef,
268     audioStreamFormat_audioTrackFormatIDRef,
269     audioStreamFormat_StringVector_Max
270 };
271 
272 enum error_Type {
273     Error,
274     Warning,
275     error_Type_Max,
276 };
277 
278 static const char* error_Type_String[] = {
279     "Errors",
280     "Warnings",
281 };
282 
283 static attribute_item audioProgramme_Attributes[] =
284 {
285     { "audioProgrammeID"                , (1 << Version0) | (1 << Version1) | (1 << Version2) | (1 << Count1) },
286     { "audioProgrammeName"              , (1 << Version0) | (1 << Version1) | (1 << Version2) | (1 << Count1) },
287     { "audioProgrammeLanguage"          , (1 << Version0) | (1 << Version1) | (1 << Version2) },
288     { "start"                           , (1 << Version0) | (1 << Version1) | (1 << Version2) },
289     { "end"                             , (1 << Version0) | (1 << Version1) | (1 << Version2) },
290     { "maxDuckingDepth"                 , (1 << Version0) | (1 << Version1) | (1 << Version2) },
291 };
292 
293 static element_item audioProgramme_Elements[] =
294 {
295     { "audioProgrammeLabel"             ,                                     (1 << Version2) },
296     { "audioContentIDRef"               , (1 << Version0) | (1 << Version1) | (1 << Version2) | (1 << Count1) | (1 << Count2) },
297     { "loudnessMetadata"                , (1 << Version0) | (1 << Version1) | (1 << Version2) },
298     { "audioProgrammeReferenceScreen"   , (1 << Version0) | (1 << Version1) | (1 << Version2) | (1 << Count0) | (1 << Count1) },
299     { "authoringInformation"            ,                                     (1 << Version2) | (1 << Count0) | (1 << Count1) },
300     { "alternativeValueSetIDRef"        ,                                     (1 << Version2) | (1 << Count0) | (1 << Count1) },
301 };
302 
303 static element audioProgramme_Element =
304 {
305     audioProgramme_Attributes,
306     audioProgramme_Elements,
307     sizeof(audioProgramme_Attributes) / sizeof(attribute_item),
308     sizeof(audioProgramme_Elements) / sizeof(element_item),
309 };
310 
311 struct Item_Struct {
312     vector<string> Strings;
313     vector<vector<string> > StringVectors;
314     map<string, string> Extra;
315     vector<string> Errors[error_Type_Max];
316 };
317 
318 struct Items_Struct {
InitMediaInfoLib::Items_Struct319     void Init(size_t Strings_Size_, size_t StringVectors_Size_) {
320         Strings_Size = Strings_Size_;
321         StringVectors_Size =StringVectors_Size_;
322     }
323 
NewMediaInfoLib::Items_Struct324     Item_Struct& New()
325     {
326         Items.resize(Items.size() + 1);
327         Item_Struct& Item = Items[Items.size() - 1];
328         Item.Strings.resize(Strings_Size);
329         Item.StringVectors.resize(StringVectors_Size);
330         return Item;
331     }
332 
LastMediaInfoLib::Items_Struct333     Item_Struct& Last()
334     {
335         Item_Struct& Item = Items[Items.size() - 1];
336         return Item;
337     }
338 
339     vector<Item_Struct> Items;
340     size_t Strings_Size;
341     size_t StringVectors_Size;
342 };
343 
Apply_Init(File__Analyze & F,const Char * Name,size_t i,const Items_Struct & audioProgramme_List,Ztring Summary)344 static string Apply_Init(File__Analyze& F, const Char* Name, size_t i, const Items_Struct& audioProgramme_List, Ztring Summary) {
345     const Item_Struct& audioProgramme = audioProgramme_List.Items[i];
346     string P = Ztring(Name + Ztring::ToZtring(i)).To_UTF8();
347     F.Fill(Stream_Audio, 0, P.c_str(), Summary.empty() ? __T("Yes") : Summary);
348     F.Fill(Stream_Audio, 0, (P + " Pos").c_str(), i);
349     F.Fill_SetOptions(Stream_Audio, 0, (P + " Pos").c_str(), "N NIY");
350     return P;
351 }
352 
Apply_SubStreams(File__Analyze & F,const string & P_And_LinkedTo,const Item_Struct & Source,size_t i,const Items_Struct & Dest)353 static void Apply_SubStreams(File__Analyze& F, const string& P_And_LinkedTo, const Item_Struct& Source, size_t i, const Items_Struct& Dest) {
354     ZtringList SubstreamPos, SubstreamNum;
355     for (size_t j = 0; j < Source.StringVectors[i].size(); j++) {
356         const string& ID = Source.StringVectors[i][j];
357         size_t Pos = -1;
358         for (size_t k = 0; k < Dest.Items.size(); k++) {
359             if (Dest.Items[k].Strings[0] == ID) {
360                 Pos = k;
361                 break;
362             }
363         }
364         if (Pos == -1) {
365             continue;
366         }
367         SubstreamPos.push_back(Ztring::ToZtring(Pos));
368         SubstreamNum.push_back(Ztring::ToZtring(Pos + 1));
369     }
370     if (SubstreamPos.empty())
371         return;
372     SubstreamPos.Separator_Set(0, __T(" + "));
373     F.Fill(Stream_Audio, 0, P_And_LinkedTo.c_str(), SubstreamPos.Read());
374     F.Fill_SetOptions(Stream_Audio, 0, P_And_LinkedTo.c_str(), "N NIY");
375     SubstreamNum.Separator_Set(0, __T(" + "));
376     F.Fill(Stream_Audio, 0, (P_And_LinkedTo + "/String").c_str(), SubstreamNum.Read());
377     F.Fill_SetOptions(Stream_Audio, 0, (P_And_LinkedTo + "/String").c_str(), "Y NIN");
378 }
379 
380 class file_adm_private
381 {
382 public:
383     tfsxml_string p;
384     Items_Struct Items[item_Max];
385     int Version;
386     bool DolbyProfileCanNotBeVersion1;
387     vector<profile_info> profileInfos;
388 
389     void parse();
390     void coreMetadata();
391     void format();
392     void audioFormatExtended();
393 
file_adm_private()394     file_adm_private() {
395         Version = 0;
396         DolbyProfileCanNotBeVersion1 = false;
397     };
398 };
399 
parse()400 void file_adm_private::parse()
401 {
402     tfsxml_string b, v;
403 
404     # define STRUCTS(NAME) \
405         Items[item_##NAME].Init(NAME##_String_Max, NAME##_StringVector_Max);
406 
407     STRUCTS(audioProgramme);
408     STRUCTS(audioContent);
409     STRUCTS(audioObject);
410     STRUCTS(audioPackFormat);
411     STRUCTS(audioChannelFormat);
412     STRUCTS(audioTrackUID);
413     STRUCTS(audioTrackFormat);
414     STRUCTS(audioStreamFormat);
415 
416 
417     #define ELEMENT_START(NAME) \
418         if (!tfsxml_cmp_charp(b, #NAME)) \
419         { \
420             Item_Struct& NAME##_Content = Items[item_##NAME].New(); \
421             for (;;) { \
422                 if (tfsxml_attr(&p, &b, &v)) \
423                     break; \
424                 if (false) { \
425                 } \
426 
427     #define ELEMENT_MIDDLE(NAME) \
428                 else if (tfsxml_cmp_charp(b, "typeLabel")) { \
429                     NAME##_Content.Errors[Warning].push_back("Attribute \"" + string(b.buf, b.len) + "\" is out of specs"); \
430                 } \
431             } \
432         tfsxml_enter(&p, &b); \
433         for (;;) { \
434             if (tfsxml_next(&p, &b)) \
435                 break; \
436             if (false) { \
437             } \
438 
439     #define ELEMENT_END(NAME) \
440                 else if (tfsxml_cmp_charp(b, "loudnessMetadata") && tfsxml_cmp_charp(b, "authoringInformation") && tfsxml_cmp_charp(b, "alternativeValueSetIDRef")) { \
441                     NAME##_Content.Errors[Warning].push_back("Element \"" + string(b.buf, b.len) + "\" is out of specs"); \
442                 } \
443             } \
444         } \
445 
446     #define ATTRIBUTE(NAME,ATTR) \
447         else if (!tfsxml_cmp_charp(b, #ATTR)) { \
448             NAME##_Content.Strings[NAME##_##ATTR].assign(v.buf, v.len); \
449         } \
450 
451     #define ATTRIBUTE_I(NAME,ATTR) \
452         else if (!tfsxml_cmp_charp(b, #ATTR)) { \
453         } \
454 
455     #define ELEMENT(NAME,ELEM) \
456         else if (!tfsxml_cmp_charp(b, #ELEM)) { \
457             tfsxml_value(&p, &b); \
458             NAME##_Content.StringVectors[NAME##_##ELEM].push_back(string(b.buf, b.len)); \
459         } \
460 
461     #define ELEMENT_I(NAME,ELEM) \
462         else if (!tfsxml_cmp_charp(b, #ELEM)) { \
463         } \
464 
465     for (;;) {
466         if (tfsxml_next(&p, &b))
467             break;
468         if (!tfsxml_cmp_charp(b, "audioFormatExtended"))
469         {
470             audioFormatExtended();
471         }
472         if (!tfsxml_cmp_charp(b, "ebuCoreMain"))
473         {
474             while (!tfsxml_attr(&p, &b, &v)) {
475                 if (!tfsxml_cmp_charp(b, "xmlns") || !tfsxml_cmp_charp(b, "xsi:schemaLocation")) {
476                     DolbyProfileCanNotBeVersion1 = false;
477                     if (!tfsxml_str_charp(v, "ebuCore_2014").len && !tfsxml_str_charp(v, "ebuCore_2016").len) {
478                         DolbyProfileCanNotBeVersion1 = true;
479                     }
480                     if (!DolbyProfileCanNotBeVersion1)
481                         break;
482                 }
483             }
484             tfsxml_enter(&p, &b);
485             for (;;) {
486                 if (tfsxml_next(&p, &b))
487                     break;
488                 if (!tfsxml_cmp_charp(b, "coreMetadata"))
489                 {
490                     coreMetadata();
491                 }
492             }
493         }
494         if (!tfsxml_cmp_charp(b, "frame"))
495         {
496             format();
497         }
498         if (!tfsxml_cmp_charp(b, "format"))
499         {
500             format();
501         }
502     }
503 }
504 
coreMetadata()505 void file_adm_private::coreMetadata()
506 {
507     tfsxml_string b;
508 
509     tfsxml_enter(&p, &b);
510     for (;;) {
511         if (tfsxml_next(&p, &b))
512             break;
513         if (!tfsxml_cmp_charp(b, "format"))
514         {
515             format();
516         }
517     }
518 }
519 
format()520 void file_adm_private::format()
521 {
522     tfsxml_string b, v;
523 
524     tfsxml_enter(&p, &b);
525     for (;;) {
526         if (tfsxml_next(&p, &b))
527             break;
528         if (!tfsxml_cmp_charp(b, "audioFormatCustom")) {
529             tfsxml_enter(&p, &b);
530             while (!tfsxml_next(&p, &b)) {
531                 if (!tfsxml_cmp_charp(b, "audioFormatCustomSet")) {
532                     tfsxml_enter(&p, &b);
533                     while (!tfsxml_next(&p, &b)) {
534                         if (!tfsxml_cmp_charp(b, "admInformation")) {
535                             tfsxml_enter(&p, &b);
536                             while (!tfsxml_next(&p, &b)) {
537                                 if (!tfsxml_cmp_charp(b, "profile")) {
538                                     profileInfos.resize(profileInfos.size() + 1);
539                                     profile_info& profileInfo = profileInfos[profileInfos.size() - 1];
540                                     while (!tfsxml_attr(&p, &b, &v)) {
541                                         for (size_t i = 0; i < profile_names_size; i++)
542                                         {
543                                             if (!tfsxml_cmp_charp(b, profile_names[i])) {
544                                                 profileInfo.Strings[i] = string(v.buf, v.len);
545                                                 if (!i && profileInfo.Strings[0].size() >= 12 && !profileInfo.Strings[0].compare(profileInfo.Strings[0].size() - 12, 12, " ADM Profile"))
546                                                     profileInfo.Strings[0].resize(profileInfo.Strings[0].size() - 12);
547                                             }
548                                         }
549                                     }
550                                     while (!tfsxml_next(&p, &b)) {
551                                     }
552                                 }
553                             }
554                         }
555                     }
556                 }
557             }
558         }
559         if (!tfsxml_cmp_charp(b, "audioFormatExtended")) {
560             audioFormatExtended();
561         }
562     }
563 }
564 
audioFormatExtended()565 void file_adm_private::audioFormatExtended()
566 {
567     tfsxml_string b, v;
568 
569     while (!tfsxml_attr(&p, &b, &v)) {
570         if (!tfsxml_cmp_charp(b, "version")) {
571             if (!tfsxml_cmp_charp(v, "ITU-R_BS.2076-1")) {
572                 Version = 1;
573             }
574             if (!tfsxml_cmp_charp(v, "ITU-R_BS.2076-2")) {
575                 Version = 2;
576             }
577         }
578     }
579     tfsxml_enter(&p, &b);
580     for (;;) {
581         if (tfsxml_next(&p, &b))
582             break;
583         ELEMENT_START(audioProgramme)
584             ATTRIBUTE(audioProgramme, audioProgrammeID)
585             ATTRIBUTE(audioProgramme, audioProgrammeName)
586             ATTRIBUTE(audioProgramme, audioProgrammeLanguage)
587             ATTRIBUTE(audioProgramme, start)
588             ATTRIBUTE(audioProgramme, end)
589         ELEMENT_MIDDLE(audioProgramme)
590             ELEMENT(audioProgramme, audioContentIDRef)
591             else if (!tfsxml_cmp_charp(b, "audioProgrammeLabel")) {
592                 string Language;
593                 for (;;) {
594                     if (tfsxml_attr(&p, &b, &v))
595                         break;
596                     if (!tfsxml_cmp_charp(b, "language")) {
597                         Language += string(v.buf, v.len);
598                     }
599                 }
600                 tfsxml_value(&p, &b);
601                 string Value = string(b.buf, b.len);
602                 if (!Value.empty() && !Language.empty()) {
603                     Value.insert(0, '(' + Language + ')');
604                 }
605                 audioProgramme_Content.StringVectors[audioProgramme_audioProgrammeLabel].push_back(Value);
606             }
607         ELEMENT_END(audioProgramme)
608         ELEMENT_START(audioContent)
609             ATTRIBUTE(audioContent, audioContentID)
610             ATTRIBUTE(audioContent, audioContentName)
611             ATTRIBUTE(audioContent, audioContentLanguage)
612             ATTRIBUTE(audioContent, typeLabel)
613         ELEMENT_MIDDLE(audioContent)
614             ELEMENT(audioContent, audioObjectIDRef)
615             else if (!tfsxml_cmp_charp(b, "dialogue")) {
616                 string Type;
617                 for (;;) {
618                     if (tfsxml_attr(&p, &b, &v))
619                         break;
620                     if (!tfsxml_cmp_charp(b, "nonDialogueContentKind")
621                         || !tfsxml_cmp_charp(b, "dialogueContentKind")
622                         || !tfsxml_cmp_charp(b, "mixedContentKind")) {
623                         Type += string(v.buf, v.len);
624                     }
625                 }
626                 tfsxml_value(&p, &b);
627                 string Value;
628                 if (!tfsxml_cmp_charp(b, "0")) {
629                     if (Type == "1") {
630                         Value = "Music";
631                     }
632                     else if (Type == "2") {
633                         Value = "Effect";
634                     }
635                     else {
636                         Value = "No Dialogue";
637                         if (!Type.empty() && Type != "0") {
638                             Value += " (" + Type + ')';
639                         }
640                     }
641                 }
642                 else if (!tfsxml_cmp_charp(b, "1")) {
643                     if (Type == "1") {
644                         Value = "Music";
645                     }
646                     else if (Type == "2") {
647                         Value = "Effect";
648                     }
649                     else if (Type == "3") {
650                         Value = "Spoken Subtitle";
651                     }
652                     else if (Type == "4") {
653                         Value = "Visually Impaired";
654                     }
655                     else if (Type == "5") {
656                         Value = "Commentary";
657                     }
658                     else if (Type == "6") {
659                         Value = "Emergency";
660                     }
661                     else {
662                         Value = "Dialogue";
663                         if (!Type.empty() && Type != "0") {
664                             Value += " (" + Type + ')';
665                         }
666                     }
667                 }
668                 else if (!tfsxml_cmp_charp(b, "2")) {
669                     if (Type == "1") {
670                         Value = "Complete Main";
671                     }
672                     else if (Type == "2") {
673                         Value = "Mixed (Mixed)";
674                     }
675                     else if (Type == "3") {
676                         Value = "Hearing Impaired";
677                     }
678                     else {
679                         Value = "Mixed";
680                         if (!Type.empty() && Type != "0") {
681                             Value += " (" + Type + ')';
682                         }
683                     }
684                 }
685                 else {
686                     Value = string(b.buf, b.len);
687                     if (!Type.empty()) {
688                         Value += " (" + Type + ')';
689                     }
690                 }
691                 audioContent_Content.StringVectors[audioContent_dialogue].push_back(Value);
692             }
693             else if (!tfsxml_cmp_charp(b, "audioContentLabel")) {
694                 string Language;
695                 for (;;) {
696                     if (tfsxml_attr(&p, &b, &v))
697                         break;
698                     if (!tfsxml_cmp_charp(b, "language")) {
699                         Language += string(v.buf, v.len);
700                     }
701                 }
702                 tfsxml_value(&p, &b);
703                 string Value = string(b.buf, b.len);
704                 if (!Value.empty() && !Language.empty()) {
705                     Value.insert(0, '(' + Language + ')');
706                 }
707                 audioContent_Content.StringVectors[audioContent_audioContentLabel].push_back(Value);
708             }
709             if (!tfsxml_cmp_charp(b, "loudnessMetadata")) {
710                 tfsxml_enter(&p, &b);
711                 for (;;) {
712                     if (tfsxml_next(&p, &b))
713                         break;
714                     if (!tfsxml_cmp_charp(b, "integratedLoudness")) {
715                         tfsxml_value(&p, &b);
716                         audioContent_Content.StringVectors[audioContent_integratedLoudness].push_back(string(b.buf, b.len));
717                     }
718                 }
719             }
720             ELEMENT(audioContent, dialogue)
721         ELEMENT_END(audioContent)
722         ELEMENT_START(audioObject)
723             ATTRIBUTE(audioObject, audioObjectID)
724             ATTRIBUTE(audioObject, audioObjectName)
725             ATTRIBUTE(audioObject, duration)
726             ATTRIBUTE(audioObject, startTime)
727             ATTRIBUTE(audioObject, typeLabel)
728         ELEMENT_MIDDLE(audioObject)
729             ELEMENT(audioObject, audioPackFormatIDRef)
730             ELEMENT(audioObject, audioTrackUIDRef)
731         ELEMENT_END(audioObject)
732         ELEMENT_START(audioPackFormat)
733             ATTRIBUTE(audioPackFormat, audioPackFormatID)
734             ATTRIBUTE(audioPackFormat, audioPackFormatName)
735             ATTRIBUTE(audioPackFormat, typeDefinition)
736             ATTRIBUTE(audioPackFormat, typeLabel)
737         ELEMENT_MIDDLE(audioPackFormat)
738             ELEMENT(audioPackFormat, audioChannelFormatIDRef)
739         ELEMENT_END(audioPackFormat)
740         ELEMENT_START(audioChannelFormat)
741             ATTRIBUTE(audioChannelFormat, audioChannelFormatID)
742             ATTRIBUTE(audioChannelFormat, audioChannelFormatName)
743             ATTRIBUTE(audioChannelFormat, typeDefinition)
744             ATTRIBUTE(audioChannelFormat, typeLabel)
745         ELEMENT_MIDDLE(audioChannelFormat)
746             else if (!tfsxml_cmp_charp(b, "audioBlockFormat")) {
747                 tfsxml_enter(&p, &b);
748                 for (;;) {
749                     if (tfsxml_next(&p, &b))
750                         break;
751                     if (!tfsxml_cmp_charp(b, "speakerLabel")) {
752                         tfsxml_value(&p, &b);
753                         string SpeakerLabel(b.buf, b.len);
754                         if (SpeakerLabel.rfind("RC_", 0) == 0) {
755                             SpeakerLabel.erase(0, 3);
756                         }
757                         map<string, string>::iterator Extra_SpeakerLabel = audioChannelFormat_Content.Extra.find("ChannelLayout");
758                         map<string, string>::iterator Extra_Type = audioChannelFormat_Content.Extra.find("Type");
759                         if (Extra_SpeakerLabel == audioChannelFormat_Content.Extra.end() || Extra_SpeakerLabel->second == SpeakerLabel) {
760                             audioChannelFormat_Content.Extra["ChannelLayout"]  = SpeakerLabel;
761                             audioChannelFormat_Content.Extra["Type"] = "Static";
762                         }
763                         else if (Extra_Type != audioChannelFormat_Content.Extra.end()) {
764                             audioChannelFormat_Content.Extra.clear();
765                             audioChannelFormat_Content.Extra["Type"] = "Dynamic";
766                             tfsxml_leave(&p, &b); // audioBlockFormat
767                             tfsxml_leave(&p, &b); // audioChannelFormat
768                             break;
769                         }
770                     }
771                 }
772             }
773         ELEMENT_END(audioChannelFormat)
774         ELEMENT_START(audioTrackUID)
775             ATTRIBUTE(audioTrackUID, UID)
776             ATTRIBUTE(audioTrackUID, bitDepth)
777             ATTRIBUTE(audioTrackUID, sampleRate)
778             ATTRIBUTE(audioTrackUID, typeLabel)
779         ELEMENT_MIDDLE(audioTrackUID)
780             ELEMENT(audioTrackUID, audioChannelFormatIDRef)
781             ELEMENT(audioTrackUID, audioPackFormatIDRef)
782             ELEMENT(audioTrackUID, audioTrackFormatIDRef)
783         ELEMENT_END(audioTrackUID)
784         ELEMENT_START(audioTrackFormat)
785             ATTRIBUTE(audioTrackFormat, audioTrackFormatID)
786             ATTRIBUTE(audioTrackFormat, audioTrackFormatName)
787             ATTRIBUTE(audioTrackFormat, formatDefinition)
788             ATTRIBUTE(audioTrackFormat, typeDefinition)
789             ATTRIBUTE(audioTrackFormat, typeLabel)
790         ELEMENT_MIDDLE(audioTrackFormat)
791             ELEMENT(audioTrackFormat, audioStreamFormatIDRef)
792         ELEMENT_END(audioTrackFormat)
793         ELEMENT_START(audioStreamFormat)
794             ATTRIBUTE(audioStreamFormat, audioStreamFormatID)
795             ATTRIBUTE(audioStreamFormat, audioStreamFormatName)
796             ATTRIBUTE(audioStreamFormat, formatDefinition)
797             ATTRIBUTE(audioStreamFormat, formatLabel)
798             ATTRIBUTE(audioStreamFormat, typeDefinition)
799             ATTRIBUTE(audioStreamFormat, typeLabel)
800         ELEMENT_MIDDLE(audioStreamFormat)
801             ELEMENT(audioStreamFormat, audioChannelFormatIDRef)
802             ELEMENT(audioStreamFormat, audioPackFormatIDRef)
803             ELEMENT(audioStreamFormat, audioTrackFormatIDRef)
804         ELEMENT_END(audioStreamFormat)
805     }
806 }
807 
808 
809 //***************************************************************************
810 // Constructor/Destructor
811 //***************************************************************************
812 
813 //---------------------------------------------------------------------------
File_Adm()814 File_Adm::File_Adm()
815 :File__Analyze()
816 {
817     //Configuration
818     Buffer_MaximumSize = 256 * 1024 * 1024;
819 
820     File_Adm_Private = new file_adm_private();
821 }
822 
823 //---------------------------------------------------------------------------
~File_Adm()824 File_Adm::~File_Adm()
825 {
826     delete File_Adm_Private;
827 }
828 
829 //***************************************************************************
830 // Buffer - File header
831 //***************************************************************************
832 
833 //---------------------------------------------------------------------------
FileHeader_Begin()834 bool File_Adm::FileHeader_Begin()
835 {
836     // File must be fully loaded
837     if (!IsSub && Buffer_Size < File_Size)
838     {
839         if (Buffer_Size >= 5 && Buffer[0]!='<' && Buffer[1]!='?' && Buffer[2]!='x' && Buffer[3] != 'm' && Buffer[4]!='l')
840         {
841             Reject();
842             return false;
843         }
844 
845         Element_WaitForMoreData();
846         return false; //Must wait for more data
847     }
848 
849     if (tfsxml_init(&File_Adm_Private->p, (void*)(Buffer), Buffer_Size))
850         return true;
851     File_Adm_Private->parse();
852     if (File_Adm_Private->Items[item_audioContent].Items.empty())
853     {
854         Reject();
855         return false;
856     }
857 
858 
859     #define FILL_COUNT(NAME,FIELD) \
860         if (!File_Adm_Private->Items[item_##NAME].Items.empty()) \
861             Fill(Stream_Audio, 0, "NumberOf" FIELD "s", File_Adm_Private->Items[item_##NAME].Items.size());
862 
863     #define FILL_START(NAME,ATTRIBUTE,FIELD) \
864         for (size_t i = 0; i < File_Adm_Private->Items[item_##NAME].Items.size(); i++) { \
865             Ztring Summary = Ztring().From_UTF8(File_Adm_Private->Items[item_##NAME].Items[i].Strings[NAME##_##ATTRIBUTE]); \
866             string P = Apply_Init(*this, __T(FIELD), i, File_Adm_Private->Items[item_##NAME], Summary); \
867 
868     #define FILL_A(NAME,ATTRIBUTE,FIELD) \
869         Fill(Stream_Audio, StreamPos_Last, (P + ' ' + FIELD).c_str(), File_Adm_Private->Items[item_##NAME].Items[i].Strings[NAME##_##ATTRIBUTE].c_str(), Unlimited, true); \
870 
871     #define FILL_E(NAME,ATTRIBUTE,FIELD) \
872         for (size_t j = 0; j < File_Adm_Private->Items[item_##NAME].Items[i].StringVectors[NAME##_##ATTRIBUTE].size(); j++) { \
873             Fill(Stream_Audio, StreamPos_Last, (P + ' ' + FIELD).c_str(), File_Adm_Private->Items[item_##NAME].Items[i].StringVectors[NAME##_##ATTRIBUTE][j].c_str(), Unlimited, true); \
874         } \
875 
876     #define LINK(NAME,FIELD,VECTOR,TARGET) \
877         Apply_SubStreams(*this, P + " LinkedTo_" FIELD "_Pos", File_Adm_Private->Items[item_##NAME].Items[i], NAME##_##VECTOR, File_Adm_Private->Items[item_##TARGET]); \
878 
879     //Filling
880     Accept("ADM");
881     Stream_Prepare(Stream_Audio);
882     if (!IsSub)
883         Fill(Stream_Audio, StreamPos_Last, Audio_Format, "ADM");
884 
885     Fill(Stream_Audio, StreamPos_Last, "Metadata_Format", "ADM, Version " + Ztring::ToZtring(File_Adm_Private->Version).To_UTF8());
886     if (!MuxingMode.empty())
887         Fill(Stream_Audio, StreamPos_Last, "Metadata_MuxingMode", MuxingMode);
888     if (File_Adm_Private->Items[item_audioProgramme].Items.size() == 1 && File_Adm_Private->Items[item_audioProgramme].Items[0].Strings[audioProgramme_audioProgrammeName] == "Atmos_Master") {
889         if (!File_Adm_Private->DolbyProfileCanNotBeVersion1 && File_Adm_Private->Version>1)
890             File_Adm_Private->DolbyProfileCanNotBeVersion1=true;
891         Fill(Stream_Audio, 0, "AdmProfile", (!File_Adm_Private->DolbyProfileCanNotBeVersion1)?"Dolby Atmos Master, Version 1":"Dolby Atmos Master");
892         Fill(Stream_Audio, 0, "AdmProfile_Format", "Dolby Atmos Master");
893         Fill_SetOptions(Stream_Audio, 0, "AdmProfile_Format", "N NTY");
894         if (!File_Adm_Private->DolbyProfileCanNotBeVersion1)
895         {
896             Fill(Stream_Audio, 0, "AdmProfile_Version", "1");
897             Fill_SetOptions(Stream_Audio, 0, "AdmProfile_Version", "N NTY");
898         }
899     }
900     vector<profile_info>& profileInfos = File_Adm_Private->profileInfos;
901     if (!profileInfos.empty())
902     {
903         // Find what is in common
904         int PosCommon = profile_names_size;
905         for (int i = 0; i < PosCommon; i++)
906             for (size_t j = 1; j < profileInfos.size(); j++)
907                 if (profileInfos[j].Strings[i] != profileInfos[0].Strings[i])
908                     PosCommon = i;
909 
910         Fill(Stream_Audio, 0, "AdmProfile", PosCommon ? profileInfos[0].profile_info_build(PosCommon) : string("Multiple"));
911         if (profileInfos.size() > 1)
912         {
913             for (size_t i = 0; i < profileInfos.size(); i++)
914             {
915                 Fill(Stream_Audio, 0, ("AdmProfile AdmProfile" + Ztring::ToZtring(i).To_UTF8()).c_str(), profileInfos[i].profile_info_build());
916                 for (size_t j = 0; j < profile_names_size; j++)
917                 {
918                     Fill(Stream_Audio, 0, ("AdmProfile AdmProfile" + Ztring::ToZtring(i).To_UTF8() + ' ' + profile_names_InternalID[j]).c_str(), profileInfos[i].Strings[j]);
919                     Fill_SetOptions(Stream_Audio, 0, ("AdmProfile AdmProfile" + Ztring::ToZtring(i).To_UTF8() + ' ' + profile_names_InternalID[j]).c_str(), "N NTY");
920                 }
921             }
922         }
923         for (size_t j = 0; j < (PosCommon == 0 ? 1 : PosCommon); j++)
924         {
925             Fill(Stream_Audio, 0, (string("AdmProfile_") + profile_names_InternalID[j]).c_str(), j < PosCommon ? profileInfos[0].Strings[j] : "Multiple");
926             Fill_SetOptions(Stream_Audio, 0, (string("AdmProfile_") + profile_names_InternalID[j]).c_str(), "N NTY");
927         }
928     }
929     size_t TotalCount = 0;
930     for (size_t i = 0; i < item_Max; i++)
931         TotalCount += File_Adm_Private->Items[i].Items.size();
932     bool Full = TotalCount < 100 ? true : false;
933     FILL_COUNT(audioProgramme, "Programme");
934     FILL_COUNT(audioContent, "Content");
935     FILL_COUNT(audioObject, "Object");
936     FILL_COUNT(audioPackFormat, "PackFormat");
937     FILL_COUNT(audioChannelFormat, "ChannelFormat");
938     if (Full)
939     {
940         FILL_COUNT(audioTrackUID, "TrackUID");
941         FILL_COUNT(audioTrackFormat, "TrackFormat");
942         FILL_COUNT(audioStreamFormat, "StreamFormat");
943     }
944     vector<string> Errors_Field[error_Type_Max];
945     vector<string> Errors_Value[error_Type_Max];
946 
947     FILL_START(audioProgramme, audioProgrammeName, "Programme")
948         if (Full)
949             FILL_A(audioProgramme, audioProgrammeID, "ID");
950         FILL_A(audioProgramme, audioProgrammeName, "Title");
951         FILL_E(audioProgramme, audioProgrammeLabel, "Label");
952         FILL_A(audioProgramme, audioProgrammeLanguage, "Language");
953         FILL_A(audioProgramme, start, "Start");
954         FILL_A(audioProgramme, end, "End");
955         LINK(audioProgramme, "Content", audioContentIDRef, audioContent);
956         const Ztring& Label = Retrieve_Const(StreamKind_Last, StreamPos_Last, (P + " Label").c_str());
957         if (!Label.empty()) {
958             Summary += __T(' ');
959             Summary += __T('(');
960             Summary += Label;
961             Summary += __T(')');
962             Fill(StreamKind_Last, StreamPos_Last, P.c_str(), Summary, true);
963         }
964 
965         // Errors
966         const string& audioProgrammeID = File_Adm_Private->Items[item_audioProgramme].Items[i].Strings[audioProgramme_audioProgrammeID];
967         if (audioProgrammeID.size() != 8
968             || audioProgrammeID[0] != 'A'
969             || audioProgrammeID[1] != 'P'
970             || audioProgrammeID[2] != 'R'
971             || audioProgrammeID[3] != '_'
972             || !IsHexaDigit(audioProgrammeID[4])
973             || !IsHexaDigit(audioProgrammeID[5])
974             || !IsHexaDigit(audioProgrammeID[6])
975             || !IsHexaDigit(audioProgrammeID[7])
976             ) {
977             File_Adm_Private->Items[item_audioProgramme].Items[i].Errors->push_back(audioProgrammeID + " is not a valid form (APR_wwww form, wwww is hexadecimal value)");
978         }
979 
980 
981         for (size_t k = 0; k < error_Type_Max; k++) {
982             if (!File_Adm_Private->Items[item_audioProgramme].Items[i].Errors[k].empty()) {
983                 for (size_t j = 0; j < File_Adm_Private->Items[item_audioProgramme].Items[i].Errors[k].size(); j++) {
984                     Errors_Field[k].push_back("audioProgramme");
985                     Errors_Value[k].push_back(File_Adm_Private->Items[item_audioProgramme].Items[i].Errors[k][j]);
986                 }
987             }
988         }
989     }
990 
991     FILL_START(audioContent, audioContentName, "Content")
992         if (Full)
993             FILL_A(audioContent, audioContentID, "ID");
994         FILL_A(audioContent, audioContentName, "Title");
995         FILL_E(audioContent, audioContentLabel, "Label");
996         FILL_A(audioContent, audioContentLanguage, "Language");
997         FILL_E(audioContent, dialogue, "Mode");
998         FILL_E(audioContent, integratedLoudness, "IntegratedLoudness");
999         LINK(audioContent, "Object", audioObjectIDRef, audioObject);
1000         const Ztring& Label = Retrieve_Const(StreamKind_Last, StreamPos_Last, (P + " Label").c_str());
1001         if (!Label.empty()) {
1002             Summary += __T(' ');
1003             Summary += __T('(');
1004             Summary += Label;
1005             Summary += __T(')');
1006             Fill(StreamKind_Last, StreamPos_Last, P.c_str(), Summary, true);
1007         }
1008 }
1009 
1010     FILL_START(audioObject, audioObjectName, "Object")
1011         if (Full)
1012             FILL_A(audioObject, audioObjectID, "ID");
1013         FILL_A(audioObject, audioObjectName, "Title");
1014         FILL_A(audioObject, startTime, "Start");
1015         FILL_A(audioObject, duration, "Duration");
1016         LINK(audioObject, "PackFormat", audioPackFormatIDRef, audioPackFormat);
1017         if (Full)
1018             LINK(audioObject, "TrackUID", audioTrackUIDRef, audioTrackUID);
1019     }
1020 
1021     FILL_START(audioPackFormat, audioPackFormatName, "PackFormat")
1022         if (Full)
1023             FILL_A(audioPackFormat, audioPackFormatID, "ID");
1024         FILL_A(audioPackFormat, audioPackFormatName, "Title");
1025         FILL_A(audioPackFormat, typeDefinition, "TypeDefinition");
1026         const Item_Struct& Source = File_Adm_Private->Items[item_audioPackFormat].Items[i];
1027         const Items_Struct& Dest = File_Adm_Private->Items[item_audioChannelFormat];
1028         string SpeakerLabel;
1029         for (size_t j = 0; j < Source.StringVectors[audioPackFormat_audioChannelFormatIDRef].size(); j++) {
1030             const string& ID = Source.StringVectors[audioPackFormat_audioChannelFormatIDRef][j];
1031             for (size_t k = 0; k < Dest.Items.size(); k++) {
1032                 if (Dest.Items[k].Strings[audioChannelFormat_audioChannelFormatID] != ID) {
1033                     continue;
1034                 }
1035                 for (map<string, string>::const_iterator Extra = Dest.Items[k].Extra.begin(); Extra != Dest.Items[k].Extra.end(); ++Extra) {
1036                     if (Extra->first == "ChannelLayout") {
1037                         if (!SpeakerLabel.empty()) {
1038                             SpeakerLabel += ' ';
1039                         }
1040                         SpeakerLabel += Extra->second;
1041                     }
1042                 }
1043             }
1044         }
1045         if (!SpeakerLabel.empty()) {
1046             Fill(StreamKind_Last, StreamPos_Last, (P + " ChannelLayout").c_str(), SpeakerLabel, true, true);
1047         }
1048 
1049         LINK(audioPackFormat, "ChannelFormat", audioChannelFormatIDRef, audioChannelFormat);
1050     }
1051 
1052     FILL_START(audioChannelFormat, audioChannelFormatName, "ChannelFormat")
1053         if (Full)
1054             FILL_A(audioChannelFormat, audioChannelFormatID, "ID");
1055         FILL_A(audioChannelFormat, audioChannelFormatName, "Title");
1056         FILL_A(audioChannelFormat, typeDefinition, "TypeDefinition");
1057         for (map<string, string>::iterator Extra = File_Adm_Private->Items[item_audioChannelFormat].Items[i].Extra.begin(); Extra != File_Adm_Private->Items[item_audioChannelFormat].Items[i].Extra.end(); ++Extra) {
1058             Fill(Stream_Audio, StreamPos_Last, (P + ' ' + Extra->first).c_str(), Extra->second);
1059         }
1060     }
1061 
1062     if (Full) {
1063             FILL_START(audioTrackUID, UID, "TrackUID")
1064             FILL_A(audioTrackUID, UID, "ID");
1065             FILL_A(audioTrackUID, bitDepth, "BitDepth");
1066             FILL_A(audioTrackUID, sampleRate, "SamplingRate");
1067             LINK(audioTrackUID, "ChannelFormat", audioChannelFormatIDRef, audioChannelFormat);
1068             LINK(audioTrackUID, "PackFormat", audioPackFormatIDRef, audioPackFormat);
1069             LINK(audioTrackUID, "TrackFormat", audioTrackFormatIDRef, audioTrackFormat);
1070         }
1071 
1072         FILL_START(audioTrackFormat, audioTrackFormatName, "TrackFormat")
1073             FILL_A(audioTrackFormat, audioTrackFormatID, "ID");
1074             FILL_A(audioTrackFormat, audioTrackFormatName, "Title");
1075             FILL_A(audioTrackFormat, formatDefinition, "FormatDefinition");
1076             FILL_A(audioTrackFormat, typeDefinition, "TypeDefinition");
1077             LINK(audioTrackFormat, "StreamFormat", audioStreamFormatIDRef, audioStreamFormat);
1078         }
1079 
1080         FILL_START(audioStreamFormat, audioStreamFormatName, "StreamFormat")
1081             FILL_A(audioStreamFormat, audioStreamFormatID, "ID");
1082             FILL_A(audioStreamFormat, audioStreamFormatName, "Title");
1083             FILL_A(audioStreamFormat, formatDefinition, "Format");
1084             FILL_A(audioStreamFormat, typeDefinition, "TypeDefinition");
1085             LINK(audioStreamFormat, "ChannelFormat", audioChannelFormatIDRef, audioChannelFormat);
1086             LINK(audioStreamFormat, "PackFormat", audioPackFormatIDRef, audioPackFormat);
1087             LINK(audioStreamFormat, "TrackFormat", audioTrackFormatIDRef, audioTrackFormat);
1088         }
1089     }
1090     else
1091         Fill(Stream_Audio, 0, "PartialDisplay", "Yes");
1092 
1093     for (size_t k = 0; k < error_Type_Max; k++) {
1094         if (!Errors_Field[k].empty()) {
1095             Fill(StreamKind_Last, StreamPos_Last, error_Type_String[k], Errors_Field[k].size());
1096             for (size_t i = 0; i < Errors_Field[k].size(); i++) {
1097                 Fill(StreamKind_Last, StreamPos_Last, (string(error_Type_String[k]) + ' ' + Errors_Field[k][i]).c_str(), Errors_Value[k][i]);
1098             }
1099         }
1100     }
1101 
1102     Element_Offset=File_Size;
1103     delete File_Adm_Private; File_Adm_Private = NULL;
1104 
1105 
1106     //All should be OK...
1107     Fill("ADM");
1108     return true;
1109 }
1110 
1111 } //NameSpace
1112 
1113 #endif //MEDIAINFO_ADM_YES
1114 
1115