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