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