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 //---------------------------------------------------------------------------
8 // Pre-compilation
9 #include "MediaInfo/PreComp.h"
10 #ifdef __BORLANDC__
11     #pragma hdrstop
12 #endif
13 //---------------------------------------------------------------------------
14 
15 //---------------------------------------------------------------------------
16 #include "MediaInfo/Setup.h"
17 //---------------------------------------------------------------------------
18 
19 //---------------------------------------------------------------------------
20 #if defined(MEDIAINFO_MIXML_YES)
21 //---------------------------------------------------------------------------
22 
23 //---------------------------------------------------------------------------
24 #include "MediaInfo/Multiple/File_MiXml.h"
25 #include "MediaInfo/MediaInfo_Config.h"
26 #include "MediaInfo/MediaInfo_Config_MediaInfo.h"
27 #include "ZenLib/FileName.h"
28 #include "tinyxml2.h"
29 using namespace tinyxml2;
30 //---------------------------------------------------------------------------
31 
32 namespace MediaInfoLib
33 {
34 
35 //***************************************************************************
36 // Info
37 //***************************************************************************
38 
39 //---------------------------------------------------------------------------
40 extern const char* AC3_Surround[];
41 extern const char* AC3_Mode[];
42 extern const char* AC3_Mode_String[];
43 
44 //---------------------------------------------------------------------------
45 // What should be hidden by default
46 
47 struct extra_string
48 {
49     const char** Names;
50     const char* ToAdd;
51 };
52 struct extra_array
53 {
54     const char** Names;
55     const char* Options;
56 };
57 
58 static const char* Xml_Extra_HideString[] =
59 {
60     "GuardBand_Before",
61     "GuardBand_After",
62     "compr",
63     "compr_Average",
64     "compr_Minimum",
65     "compr_Maximum",
66     "dialnorm",
67     "dialnorm_Average",
68     "dialnorm_Minimum",
69     "dialnorm_Maximum",
70     "dynrng",
71     "dynrng_Average",
72     "dynrng_Minimum",
73     "dynrng_Maximum",
74     NULL,
75 };
76 
77 static const char* Xml_Extra_String_channel[] =
78 {
79     "BedChannelCount",
80     NULL,
81 };
82 
83 static const char* Xml_Extra_String_dB[] =
84 {
85     "compr",
86     "compr_Average",
87     "compr_Minimum",
88     "compr_Maximum",
89     "dialnorm",
90     "dialnorm_Average",
91     "dialnorm_Minimum",
92     "dialnorm_Maximum",
93     "dynrng",
94     "dynrng_Average",
95     "dynrng_Minimum",
96     "dynrng_Maximum",
97     NULL,
98 };
99 
100 static const char* Xml_Extra_String_micro[] =
101 {
102     "GuardBand_Before",
103     "GuardBand_After",
104     NULL,
105 };
106 
107 static const extra_string Xml_Extra_String[] =
108 {
109     { Xml_Extra_String_channel,         " channel"},
110     { Xml_Extra_String_dB,              " dB" },
111     { Xml_Extra_String_micro,           " \xC2xB5s"}, //0xC2 0xB5 = micro sign
112     { NULL, NULL},
113 };
114 
115 static const char* Xml_Extra_N_NIY[] =
116 {
117     "BedChannelCount",
118     "BitRate_Instantaneous",
119     "Pos",
120     "Index",
121     "page_id",
122     "region_depth",
123     "region_height",
124     "region_horizontal_address",
125     "region_id",
126     "region_vertical_address",
127     "region_width",
128     "subtitle_stream_id",
129 };
130 
131 static const char* Xml_Extra_N_NT[] =
132 {
133     "acmod",
134     "bext_Present",
135     "BlockAlignment",
136     "bsid",
137     "CaptionServiceContent_IsPresent",
138     "CaptionServiceDescriptor_IsPresent",
139     "CaptionServiceName",
140     "cdp_length_Max",
141     "cdp_length_Min",
142     "compr",
143     "compr_Average",
144     "compr_Count",
145     "compr_Maximum",
146     "compr_Minimum",
147     "data_partitioned",
148     "Delay_SDTI",
149     "Delay_SDTI",
150     "Delay_SystemScheme1",
151     "Demux_InitBytes",
152     "Density_Unit",
153     "Density_X",
154     "Density_Y",
155     "dialnorm",
156     "dialnorm_Average",
157     "dialnorm_Count",
158     "dialnorm_Maximum",
159     "dialnorm_Minimum",
160     "dsurmod",
161     "dynrng",
162     "dynrng_Average",
163     "dynrng_Count",
164     "dynrng_Maximum",
165     "dynrng_Minimum",
166     "Errors_Stats",
167     "Errors_Stats_03",
168     "Errors_Stats_05",
169     "Errors_Stats_09",
170     "Errors_Stats_10",
171     "Errors_Stats_Begin",
172     "Errors_Stats_End",
173     "Errors_Stats_End_03",
174     "Errors_Stats_End_05",
175     "FrameCount_Speed",
176     "GuardBand_After",
177     "GuardBand_Before",
178     "IBI",
179     "intra_dc_precision",
180     "lfeon",
181     "OverallBitRate_Precision_Max",
182     "OverallBitRate_Precision_Min",
183     "PCR_Distance_Average",
184     "PCR_Distance_Max",
185     "PCR_Distance_Min",
186     "PCR_Invalid_Count",
187     "PrimaryPackage",
188     "reversible_vlc",
189     "SideCar_FilePos",
190     "Source_Delay",
191     "Source_Delay_Source",
192     "Source_List",
193     "UniversalAdID_Registry",
194     "UniversalAdID_Value",
195     "UnsupportedSources",
196     NULL,
197 };
198 
199 static const extra_array Xml_Extra_Array[] =
200 {
201     { Xml_Extra_N_NIY,                  "N NIY" },
202     { Xml_Extra_N_NT,                   "N NT" },
203     { NULL, NULL },
204 };
205 
206 namespace
207 {
208     struct nested
209 {
210     XMLElement* Target;
211     string Name;
212 };
213 }
214 
215 //***************************************************************************
216 // Constructor/Destructor
217 //***************************************************************************
218 
219 //---------------------------------------------------------------------------
File_MiXml()220 File_MiXml::File_MiXml()
221 :File__Analyze()
222 {
223     #if MEDIAINFO_EVENTS
224         ParserIDs[0]=MediaInfo_Parser_MiXml;
225     #endif //MEDIAINFO_EVENTS
226 }
227 
228 //***************************************************************************
229 // Buffer - File header
230 //***************************************************************************
231 
232 //---------------------------------------------------------------------------
FileHeader_Begin()233 bool File_MiXml::FileHeader_Begin()
234 {
235     XMLDocument document;
236     if (!FileHeader_Begin_XML(document))
237        return false;
238 
239     {
240         XMLElement* Root=document.FirstChildElement("MediaInfo");
241         if (Root)
242         {
243             const char* Attribute = Root->Attribute("xmlns");
244             if (Attribute == NULL || Ztring().From_UTF8(Attribute) != __T("https://mediaarea.net/mediainfo"))
245             {
246                 Reject("MiXml");
247                 return false;
248             }
249 
250             Accept("MiXml");
251 
252             XMLElement* Media = Root->FirstChildElement();
253             while (Media)
254             {
255                 if (string(Media->Value()) == "media")
256                 {
257                     Attribute = Media->Attribute("ref");
258                     if (Attribute)
259                     {
260                         File_Name.From_UTF8(Attribute);
261                         Config->File_Names.clear();
262 
263                         Fill(Stream_General, 0, General_CompleteName, File_Name, true); //TODO: merge with generic code
264                         Fill(Stream_General, 0, General_FolderName, FileName::Path_Get(File_Name), true);
265                         Fill(Stream_General, 0, General_FileName, FileName::Name_Get(File_Name), true);
266                         Fill(Stream_General, 0, General_FileExtension, FileName::Extension_Get(File_Name), true);
267                         if (Retrieve(Stream_General, 0, General_FileExtension).empty())
268                             Fill(Stream_General, 0, General_FileNameExtension, Retrieve(Stream_General, 0, General_FileName), true);
269                         else
270                             Fill(Stream_General, 0, General_FileNameExtension, Retrieve(Stream_General, 0, General_FileName)+__T('.')+Retrieve(Stream_General, 0, General_FileExtension), true);
271                     }
272 
273                     XMLElement* Track = Media->FirstChildElement();
274                     while (Track)
275                     {
276                         if (string(Track->Value()) == "track")
277                         {
278                             Attribute = Track->Attribute("type");
279                             if (Attribute)
280                             {
281                                 string StreamKind(Attribute);
282 
283                                 StreamKind_Last = Stream_Max;
284                                 if (StreamKind == "General")
285                                     StreamKind_Last = Stream_General;
286                                 if (StreamKind == "Video")
287                                     Stream_Prepare(Stream_Video);
288                                 if (StreamKind == "Audio")
289                                     Stream_Prepare(Stream_Audio);
290                                 if (StreamKind == "Text")
291                                     Stream_Prepare(Stream_Text);
292                                 if (StreamKind == "Other")
293                                     Stream_Prepare(Stream_Other);
294                                 if (StreamKind == "Image")
295                                     Stream_Prepare(Stream_Image);
296                                 if (StreamKind == "Menu")
297                                     Stream_Prepare(Stream_Menu);
298 
299                                 if (StreamKind_Last != Stream_Max)
300                                 {
301                                     XMLElement* Element = Track->FirstChildElement();
302                                     while (Element)
303                                     {
304                                         string Name(Element->Name());
305                                              if (Name == "Format_Version")
306                                             Fill(StreamKind_Last, StreamPos_Last, Element->Name(), string("Version ")+Element->GetText(), true, true);
307                                         else if (MediaInfoLib::Config.Info_Get(StreamKind_Last).Read(Ztring().From_UTF8(Element->Name()), Info_Measure) == __T(" ms"))
308                                         {
309                                             //Converting seconds to milliseconds while keeping precision
310                                             Ztring N;
311                                             N.From_UTF8(Element->GetText());
312                                             size_t Dot = N.find('.');
313                                             size_t Precision = 0;
314                                             if (Dot != string::npos)
315                                             {
316                                                 size_t End = N.find_first_not_of(__T("0123456789"), Dot + 1);
317                                                 if (End == string::npos)
318                                                     End = N.size();
319                                                 Precision = End - (Dot + 1);
320                                                 if (Precision <= 3)
321                                                     Precision = 0;
322                                                 else
323                                                     Precision -= 3;
324                                             }
325 
326                                             Fill(StreamKind_Last, StreamPos_Last, Element->Name(), N.To_float64()*1000, Precision, true);
327                                         }
328                                         else if (Name != "extra")
329                                         {
330                                             //Special cases
331                                             if (Name == "Channels")
332                                                 Fill(StreamKind_Last, StreamPos_Last, "Channel(s)", Element->GetText());
333                                             //Generic filling
334                                             else
335                                                 Fill(StreamKind_Last, StreamPos_Last, Element->Name(), Element->GetText(), Unlimited, true, true);
336                                         }
337                                         else
338                                         {
339                                             XMLElement* Extra = Element->FirstChildElement();
340                                             map<string, size_t> Nested_Pos;
341                                             vector<nested> Nested;
342                                             Nested.resize(1);
343                                             nested* LastNested = &Nested[0];
344                                             LastNested->Target = Extra;
345 
346                                             while (Extra)
347                                             {
348                                                 // Nested elements
349                                                 if (!Extra->GetText())
350                                                 {
351                                                     LastNested->Target = Extra;
352                                                     Nested.resize(Nested.size() + 1);
353                                                     LastNested = &Nested[Nested.size() - 1];
354                                                     LastNested->Name = Extra->Name();
355                                                     Extra = Extra->FirstChildElement();
356                                                     if (Nested.size() >= 2)
357                                                         LastNested->Name.insert(0, Nested[Nested.size() - 2].Name);
358                                                     if (Extra && !strcmp(Extra->Name(), "Pos") && Extra->GetText())
359                                                         LastNested->Name += Extra->GetText();
360                                                     Fill(StreamKind_Last, StreamPos_Last, LastNested->Name.c_str(), "Yes", Unlimited, true, true);
361                                                     LastNested->Name += ' ';
362                                                     LastNested->Target = NULL;
363                                                     Nested_Pos[LastNested->Name]++;
364                                                     continue;
365                                                 }
366 
367                                                 if (strstr(Extra->Name(), "_String"))
368                                                 {
369                                                     Extra = Extra->NextSiblingElement();
370                                                     continue;
371                                                 }
372 
373                                                 Fill(StreamKind_Last, StreamPos_Last, (LastNested->Name + Extra->Name()).c_str(), Extra->GetText(), Unlimited, true, true);
374 
375                                                 bool HasString=false;
376                                                 for (size_t i=0; Xml_Extra_String[i].Names; i++)
377                                                     for (size_t j=0; Xml_Extra_String[i].Names[j]; j++)
378                                                         if (!strcmp(Extra->Name(), Xml_Extra_String[i].Names[j]))
379                                                         {
380                                                             Fill(StreamKind_Last, StreamPos_Last, (LastNested->Name + Extra->Name() + "/String").c_str(), MediaInfoLib::Config.Language_Get(Extra->GetText(), Ztring().From_UTF8(Xml_Extra_String[i].ToAdd)), true);
381                                                             HasString=true;
382                                                             break;
383                                                         }
384 
385                                                 for (size_t i=0; Xml_Extra_Array[i].Names; i++)
386                                                     for (size_t j=0; Xml_Extra_Array[i].Names[j]; j++)
387                                                         if (!strcmp(Extra->Name(), Xml_Extra_Array[i].Names[j]))
388                                                         {
389                                                             Fill_SetOptions(StreamKind_Last, StreamPos_Last, (LastNested->Name + Extra->Name()).c_str(), Xml_Extra_Array[i].Options);
390                                                             if (HasString)
391                                                             {
392                                                                 string Options(Xml_Extra_Array[i].Options);
393                                                                 if (InfoOption_ShowInXml<=Options.size())
394                                                                     Options.resize(InfoOption_ShowInXml+1, ' ');
395                                                                 char Show='Y';
396                                                                 for (size_t k=0; Xml_Extra_HideString[k]; k++)
397                                                                     if (!strcmp(Extra->Name(), Xml_Extra_HideString[k]))
398                                                                     {
399                                                                         Show='N';
400                                                                         break;
401                                                                     }
402                                                                 Options[InfoOption_ShowInInform]=Show;
403                                                                 Options[InfoOption_ShowInXml]='N';
404                                                                 Fill_SetOptions(StreamKind_Last, StreamPos_Last, (LastNested->Name + Extra->Name() + "/String").c_str(), Options.c_str());
405                                                             }
406                                                             break;
407                                                         }
408 
409                                                 if (!strcmp(Extra->Name(), "dsurmod"))
410                                                 {
411                                                     size_t dsurmod=Ztring().From_UTF8(Extra->GetText()).To_int32u();
412                                                     if (dsurmod<4)
413                                                     {
414                                                         Fill(StreamKind_Last, StreamPos_Last, (LastNested->Name + "dsurmod/String").c_str(), AC3_Surround[dsurmod]);
415                                                         Fill_SetOptions(StreamKind_Last, StreamPos_Last, (LastNested->Name + "dsurmod/String").c_str(), "N NTN");
416                                                     }
417                                                 }
418 
419                                                 if (!strcmp(Extra->Name(), "ServiceKind"))
420                                                 {
421                                                     const char* ServiceKind=Extra->GetText();
422                                                     for (int8u i=0; i<8; i++)
423                                                         if (!strcmp(ServiceKind, AC3_Mode[i]))
424                                                         {
425                                                             Fill(Stream_Audio, 0, (LastNested->Name + "ServiceKind/String").c_str(), AC3_Mode_String[i]);
426                                                             break;
427                                                         }
428                                                 }
429 
430                                                 if (!strncmp(Extra->Name(), "LinkedTo_", 9))
431                                                 {
432                                                     string Name=Extra->Name();
433                                                     if (Name.find("_Pos", Name.size() - 4) == Name.size() - 4)
434                                                     {
435                                                         ZtringList List;
436                                                         List.Separator_Set(0, __T(" + "));
437                                                         List.Write(Ztring().From_UTF8(Extra->GetText()));
438                                                         for (size_t i = 0; i < List.size(); i++)
439                                                             if (!List[i].empty() && List[i].find_first_not_of(__T("0123456789")) == string::npos)
440                                                                 List[i].From_Number(List[i].To_int64u() + 1);
441                                                         Fill_SetOptions(StreamKind_Last, StreamPos_Last, (LastNested->Name + Extra->Name()).c_str(), "N NTY");
442                                                         Fill(StreamKind_Last, StreamPos_Last, (LastNested->Name + Extra->Name() + "/String").c_str(), List.Read());
443                                                         Fill_SetOptions(StreamKind_Last, StreamPos_Last, (LastNested->Name + Extra->Name() + "/String").c_str(), "Y NTN");
444                                                     }
445                                                 }
446 
447                                                 Extra = Extra->NextSiblingElement();
448                                                 if (!Extra && !Nested.empty())
449                                                 {
450                                                     Nested.pop_back();
451                                                     if (!Nested.empty())
452                                                     {
453                                                         LastNested = &Nested[Nested.size() - 1];
454                                                         Extra = LastNested->Target->NextSiblingElement();
455                                                     }
456                                                 }
457                                             }
458                                         }
459 
460                                         // Extra filling (duplicated content) //TODO: better handling of all such fields
461                                              if (Name == "Format_Settings_Endianness")
462                                             Fill(StreamKind_Last, StreamPos_Last, "Format_Settings", Element->GetText());
463                                         else if (Name == "Format_Settings_Packing")
464                                             Fill(StreamKind_Last, StreamPos_Last, "Format_Settings", Element->GetText());
465 
466                                         Element = Element->NextSiblingElement();
467                                     }
468                                 }
469                             }
470                         }
471 
472                         Track = Track->NextSiblingElement();
473                     }
474                 }
475 
476                 Media = Media->NextSiblingElement();
477             }
478         }
479         else
480         {
481             Reject("MiXml");
482             return false;
483         }
484     }
485 
486     //Special cases
487     Ztring UniversalAdID_Value=Retrieve(Stream_General, 0, General_UniversalAdID_Value);
488     Ztring UniversalAdID_Registry=Retrieve(Stream_General, 0, General_UniversalAdID_Registry);
489     if (!UniversalAdID_Value.empty() && !UniversalAdID_Registry.empty())
490     {
491         Fill(Stream_General, 0, General_UniversalAdID_String, UniversalAdID_Value + __T(" (") + UniversalAdID_Registry + __T(")"), true);
492     }
493     Ztring MajorBrand=Retrieve(Stream_General, 0, General_CodecID);
494     if (!UniversalAdID_Value.empty() && !UniversalAdID_Registry.empty())
495     {
496         Ztring CodecID_String=MajorBrand;
497         Ztring Compatible=Retrieve(Stream_General, 0, General_CodecID);
498         if (MajorBrand == __T("qt  "))
499         {
500             CodecID_String += __T(' ');
501             CodecID_String += Retrieve(Stream_General, 0, General_CodecID_Version);
502         }
503         if (!Compatible.empty())
504         {
505             CodecID_String += __T(" (");
506             CodecID_String += Compatible;
507             CodecID_String += __T(')');
508         }
509         Fill(Stream_General, 0, General_CodecID_String, CodecID_String, true);
510     }
511 
512     Element_Offset=File_Size;
513 
514     //All should be OK...
515     return true;
516 }
517 
518 } //NameSpace
519 
520 #endif //MEDIAINFO_MiXml_YES
521 
522