1 /*! \file mxfdump.cpp
2 * \brief Simple application to dump an MXF file using MXFLib
3 */
4 /*
5 * Copyright (c) 2003, Matt Beard
6 * Portions Copyright (c) 2003, Metaglue Corporation
7 *
8 * This software is provided 'as-is', without any express or implied warranty.
9 * In no event will the authors be held liable for any damages arising from
10 * the use of this software.
11 *
12 * Permission is granted to anyone to use this software for any purpose,
13 * including commercial applications, and to alter it and redistribute it
14 * freely, subject to the following restrictions:
15 *
16 * 1. The origin of this software must not be misrepresented; you must
17 * not claim that you wrote the original software. If you use this
18 * software in a product, an acknowledgment in the product
19 * documentation would be appreciated but is not required.
20 *
21 * 2. Altered source versions must be plainly marked as such, and must
22 * not be misrepresented as being the original software.
23 *
24 * 3. This notice may not be removed or altered from any source
25 * distribution.
26 */
27
28 #include <mxflib/mxflib.h>
29
30 using namespace mxflib;
31
32 #ifdef COMPILED_DICT
33
34 bool UseCompiledDict = true;
35 // include the autogenerated dictionary
36 #include "mxflib/dict.h"
37
38 #else
39
40 const bool UseCompiledDict = false;
41
42 #endif // COMPILED_DICT
43
44
45 #include <stdio.h>
46 #include <iostream>
47
48 using namespace std;
49
50 //! Debug flag for KLVLib
51 int Verbose = 0;
52
53 //! MXFLib debug flag
54 static bool DebugMode = false;
55
56 //! Flag for dumping entire index table
57 static bool FullIndex = false;
58
59 //! Flag for dumping all body partitions
60 static bool FullBody = false;
61
62 //! Flag for dumping object locations as well as object data
63 static bool DumpLocation = false;
64
65 //! Flag for following global references
66 static bool FollowGlobals = true;
67
68 //! Flag for very simple check summary dump only
69 static bool CheckDump = false;
70
71 static void DumpObject(MDObjectPtr Object, std::string Prefix);
72
73
74 //! Should we pause before exit?
75 bool PauseBeforeExit = false;
76
77 // Declare main process function
78 int main_process(int argc, char *argv[]);
79
80 //! Do the main processing and pause if required
main(int argc,char * argv[])81 int main(int argc, char *argv[])
82 {
83 int Ret = main_process(argc, argv);
84
85 if(PauseBeforeExit) PauseForInput();
86
87 return Ret;
88 }
89
90 //! Do the main processing (less any pause before exit)
main_process(int argc,char * argv[])91 int main_process(int argc, char *argv[])
92 {
93 printf("Dump an MXF file using MXFLib\n");
94
95 std::string DictName = "dict.xml";
96 std::list<std::string> SuppDicts;
97 int num_options = 0;
98 for(int i=1; i<argc; i++)
99 {
100 if(argv[i][0] == '-')
101 {
102 num_options++;
103 if((argv[i][1] == 'v') || (argv[i][1] == 'V'))
104 DebugMode = true;
105 else if((argv[i][1] == 'i') || (argv[i][1] == 'I'))
106 FullIndex = true;
107 else if((argv[i][1] == 'c') || (argv[i][1] == 'C'))
108 CheckDump = true;
109 else if((argv[i][1] == 'b') || (argv[i][1] == 'B'))
110 FullBody = true;
111 // else if((argv[i][1] == 'g') || (argv[i][1] == 'G'))
112 // FollowGlobals = true;
113 else if((argv[i][1] == 'l') || (argv[i][1] == 'L'))
114 DumpLocation = true;
115 else if((argv[i][1] == 'd') || (argv[i][1] == 'D'))
116 {
117 int Start = 2;
118 if((Start == 2) && ((argv[i][Start] == 'd') || (argv[i][Start] == 'D'))) Start++;
119 if((argv[i][Start] == '=') || (argv[i][Start] == ':')) Start++;
120 if(argv[i][Start]) SuppDicts.push_back(&argv[i][Start]);
121 else if(argc > (i+1))
122 {
123 SuppDicts.push_back(argv[++i]);
124 num_options++;
125 }
126 }
127 else if((argv[i][1] == 'u') || (argv[i][1] == 'U'))
128 {
129 MDObject::SetParseDark(true);
130 }
131 else if((argv[i][1] == 'x') || (argv[i][1] == 'X'))
132 {
133 if(argv[i][2] == '0') SetLabelFormat(LabelFormatHex);
134 else if(argv[i][2] == '1') SetLabelFormat(LabelFormatTextHex);
135 else SetLabelFormat(LabelFormatTextHexMask);
136 }
137 else if((argv[i][1] == 'm') || (argv[i][1] == 'M'))
138 {
139 int Start = 2;
140 if((argv[i][Start] == '=') || (argv[i][Start] == ':')) Start++;
141 if(argv[i][Start]) DictName = &argv[i][Start];
142 else if(argc > (i+1))
143 {
144 DictName = argv[++i];
145 num_options++;
146 }
147 #ifdef COMPILED_DICT
148 UseCompiledDict = false;
149 #endif // COMPILED_DICT
150 }
151 else if((argv[i][1] == 'z') || (argv[i][1] == 'Z'))
152 PauseBeforeExit = true;
153 }
154 }
155
156 if (argc - num_options < 2)
157 {
158 printf("\nUsage: %s [options] <filename>\n\n", argv[0]);
159 printf("Options: -b Dump body partitions (rather than just header and footer)\n");
160 printf(" -c Check dump (produce simple counts for automated testing)\n");
161 printf(" -dd <dict> Load supplementary dictionary (also -d for legacy)\n");
162 printf(" -g Follow global references (if linked)\n");
163 printf(" -i Dump full index tables (can be lengthy)\n");
164 printf(" -l Show the location (byte offset) of metadata items dumped\n");
165 #ifdef COMPILED_DICT
166 printf(" -m <dict> Specify main dictionary (instead of compile-time version)\n");
167 #else
168 printf(" -m <dict> Specify main dictionary (instead of dict.xml)\n");
169 #endif // COMPILED_DICT
170 printf(" -u Attempt to parse unknown or 'dark' sets\n");
171 printf(" -v Verbose mode - shows lots of debug info\n");
172 printf(" -x0 Always show labels as hex data\n");
173 printf(" -x1 Append hex data to labels\n");
174 printf(" -x2 or -x Append hex data to labels if 'fuzzy' matching used\n");
175 printf(" -z Pause for input before final exit\n");
176 return 1;
177 }
178
179 #ifdef COMPILED_DICT
180 if( UseCompiledDict )
181 {
182 printf("- using compile-time dictionary\n");
183 LoadDictionary(DictData);
184 }
185 else
186 #endif // COMPILED_DICT
187 {
188 printf("- using dictionary \"%s\"\n", DictName.c_str());
189 LoadDictionary(DictName);
190 }
191
192 std::list<std::string>::iterator DictIt = SuppDicts.begin();
193 while(DictIt != SuppDicts.end())
194 {
195 LoadDictionary(*DictIt);
196 DictIt++;
197 }
198
199 MXFFilePtr TestFile = new MXFFile;
200 if (! TestFile->Open(argv[num_options+1], true))
201 {
202 perror(argv[num_options+1]);
203 return 1;
204 }
205
206 // Get a RIP (however possible)
207 TestFile->GetRIP();
208
209 unsigned int PartitionNumber = 0;
210 RIP::iterator it = TestFile->FileRIP.begin();
211 while(it != TestFile->FileRIP.end())
212 {
213 PartitionNumber++;
214 if(CheckDump)
215 printf("\nPartition for BodySID 0x%04x\n", (*it).second->BodySID);
216 else
217 printf("\nPartition at 0x%s is for BodySID 0x%04x\n", Int64toHexString((*it).second->ByteOffset,8).c_str(), (*it).second->BodySID);
218
219 // Only dump header and footer unless asked for all partitions
220 if(FullBody || (PartitionNumber == 1) || (PartitionNumber == TestFile->FileRIP.size()))
221 {
222 TestFile->Seek((*it).second->ByteOffset);
223 PartitionPtr ThisPartition = TestFile->ReadPartition();
224 if(ThisPartition)
225 {
226 if(CheckDump)
227 {
228 if(ThisPartition->ReadMetadata() == 0)
229 {
230 printf("No header metadata in this partition\n");
231 }
232 else
233 {
234 printf(" Top level count = %d\n", (int)ThisPartition->TopLevelMetadata.size());
235 printf(" Set/Pack count = %d\n", (int)ThisPartition->AllMetadata.size());
236
237 size_t Count = 0;
238 MDObjectList::iterator it = ThisPartition->AllMetadata.begin();
239 while(it != ThisPartition->AllMetadata.end())
240 {
241 Count += (*it)->size();
242 it++;
243 }
244
245 printf(" Sub item count = %d\n", (int)Count);
246 }
247
248 // Read any index table segments!
249 MDObjectListPtr Segments = ThisPartition->ReadIndex();
250 if(Segments->empty())
251 {
252 printf("No index table in this partition\n");
253 }
254 else
255 {
256 IndexTablePtr Table = new IndexTable;
257
258 MDObjectList::iterator it = Segments->begin();
259
260 while(it != Segments->end())
261 {
262 // Summarize this new segment
263
264 UInt32 Streams = 1;
265 MDObjectPtr DeltaEntryArray = (*it)[DeltaEntryArray_UL];
266 if(DeltaEntryArray && DeltaEntryArray->GetType()->size())
267 {
268 Streams = static_cast<UInt32>(DeltaEntryArray->size() / DeltaEntryArray->GetType()->size());
269 if(Streams == 0) Streams = 1; // Fix for bad DeltaEntryArray
270 }
271
272 Position Start = (*it)->GetInt64(IndexStartPosition_UL);
273 Length Duration = (*it)->GetInt64(IndexDuration_UL);
274 UInt32 IndexSID = (*it)->GetUInt(IndexSID_UL);
275 UInt32 BodySID = (*it)->GetUInt(BodySID_UL);
276
277 if(Duration == 0) printf("CBR Index Table Segment (covering whole Essence Container) :\n");
278 else printf("\nIndex Table Segment (first edit unit = %s, duration = %s) :\n", Int64toString(Start).c_str(), Int64toString(Duration).c_str());
279
280 printf(" Indexing BodySID 0x%04x from IndexSID 0x%04x\n", BodySID, IndexSID);
281
282 it++;
283 }
284 }
285 }
286 else
287 {
288 // Don't dump the last partition unless it is a footer or we are dumping all
289 // DRAGONS: What is the PartitionNumber != RIP-Size thing about?
290 if(FullBody || (PartitionNumber == 1) || (PartitionNumber != TestFile->FileRIP.size())
291 || (ThisPartition->IsA(CompleteFooter_UL)) || (ThisPartition->IsA(Footer_UL)) )
292 {
293 DumpObject(ThisPartition->Object,"");
294
295 if(ThisPartition->ReadMetadata() == 0)
296 {
297 printf("No header metadata in this partition\n");
298 }
299 else
300 {
301 printf("\nHeader Metadata:\n");
302
303 MDObjectList::iterator it2 = ThisPartition->TopLevelMetadata.begin();
304 while(it2 != ThisPartition->TopLevelMetadata.end())
305 {
306 DumpObject(*it2," ");
307 it2++;
308 }
309 printf("\n");
310 }
311
312 // Read any index table segments!
313 MDObjectListPtr Segments = ThisPartition->ReadIndex();
314 if(Segments->empty())
315 {
316 printf("No index table in this partition\n");
317 }
318 else
319 {
320 IndexTablePtr Table = new IndexTable;
321
322 MDObjectList::iterator it = Segments->begin();
323
324 while(it != Segments->end())
325 {
326 Table->AddSegment(*it);
327
328 // Demonstrate this new segment
329
330 UInt32 Streams = 1;
331 MDObjectPtr DeltaEntryArray = (*it)[DeltaEntryArray_UL];
332 if(DeltaEntryArray && DeltaEntryArray->GetType()->size())
333 {
334 Streams = static_cast<UInt32>(DeltaEntryArray->size() / DeltaEntryArray->GetType()->size());
335 if(Streams == 0) Streams = 1; // Fix for bad DeltaEntryArray
336 }
337
338 Position Start = (*it)->GetInt64(IndexStartPosition_UL);
339 Length Duration = (*it)->GetInt64(IndexDuration_UL);
340 UInt32 IndexSID = (*it)->GetUInt(IndexSID_UL);
341 UInt32 BodySID = (*it)->GetUInt(BodySID_UL);
342
343 if(Duration == 0) printf("CBR Index Table Segment (covering whole Essence Container) :\n");
344 else printf("\nIndex Table Segment (first edit unit = %s, duration = %s) :\n", Int64toString(Start).c_str(), Int64toString(Duration).c_str());
345
346 printf(" Indexing BodySID 0x%04x from IndexSID 0x%04x\n", BodySID, IndexSID);
347
348 if(Duration < 1) Duration = 6; // Could be CBR
349 if(!FullIndex && Duration > 35) Duration = 35; // Don't go mad!
350
351 int i;
352 printf( "\n Bytestream Order:\n" );
353 for(i=0; i<Duration; i++)
354 {
355 UInt32 j;
356 for(j=0; j<Streams; j++)
357 {
358 IndexPosPtr Pos = Table->Lookup(Start + i,j,false);
359 printf(" EditUnit %3s for stream %d is at 0x%s", Int64toString(Start + i).c_str(), j, Int64toHexString(Pos->Location,8).c_str());
360 printf(", Flags=%02x", Pos->Flags);
361 if(Pos->Exact) printf(" *Exact*\n"); else printf("\n");
362 }
363 }
364
365 printf( "\n Presentation Order:\n" );
366 for(i=0; i<Duration; i++)
367 {
368 UInt32 j;
369 for(j=0; j<Streams; j++)
370 {
371 IndexPosPtr Pos = Table->Lookup(Start + i,j);
372 printf(" EditUnit %3s for stream %d is at 0x%s", Int64toString(Start + i).c_str(), j, Int64toHexString(Pos->Location,8).c_str());
373 printf(", Flags=%02x", Pos->Flags);
374 /// printf(", Keyframe is at 0x%s", Int64toHexString(Pos->KeyLocation,8).c_str() );
375
376 if(Pos->Exact) printf(" *Exact*\n");
377 else if(Pos->OtherPos) printf(" (Location of un-reordered position %s)\n", Int64toString(Pos->ThisPos).c_str());
378 else printf("\n");
379 }
380 }
381
382 it++;
383 }
384 }
385 }
386 }
387 }
388 }
389
390 it++;
391 }
392
393 if(TestFile->ReadRIP())
394 {
395 printf("\nRead RIP\n");
396 PartitionInfoMap::iterator it = TestFile->FileRIP.begin();
397 while(it != TestFile->FileRIP.end())
398 {
399 if(CheckDump)
400 printf(" BodySID 0x%04x", (*it).second->BodySID);
401 else
402 printf(" BodySID 0x%04x is at 0x%s", (*it).second->BodySID, Int64toHexString((*it).second->ByteOffset,8).c_str());
403
404 if((*it).second->ThePartition)
405 printf(" type %s\n", (*it).second->ThePartition->Name().c_str());
406 else
407 printf(" and is not loaded\n");
408
409 it++;
410 }
411 }
412
413 if(TestFile->ScanRIP())
414 {
415 printf("\nScanned RIP\n");
416 PartitionInfoMap::iterator it = TestFile->FileRIP.begin();
417 while(it != TestFile->FileRIP.end())
418 {
419 if(CheckDump)
420 printf(" BodySID 0x%04x", (*it).second->BodySID);
421 else
422 printf(" BodySID 0x%04x is at 0x%s", (*it).second->BodySID, Int64toHexString((*it).second->ByteOffset,8).c_str());
423
424 if((*it).second->ThePartition)
425 printf(" type %s\n", (*it).second->ThePartition->Name().c_str());
426 else
427 printf(" and is not loaded\n");
428
429 it++;
430 }
431 }
432
433 /* if(TestFile->BuildRIP())
434 {
435 printf("\nBuilt RIP\n");
436 PartitionInfoList::iterator it = TestFile->FileRIP.begin();
437 while(it != TestFile->FileRIP.end())
438 {
439 printf(" BodySID 0x%04x is at 0x%s", (*it)->BodySID, Int64toHexString((*it)->ByteOffset,8).c_str());
440
441 if((*it)->ThePartition)
442 printf(" type %s\n", (*it)->ThePartition->Name().c_str());
443 else
444 printf(" and is not loaded\n");
445
446 it++;
447 }
448 }
449 */
450 TestFile->Close();
451
452 /* PrimerPtr NewPrimer = new Primer;
453
454 unsigned char Key[16] = { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x04, 0x04, 0x06, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 };
455
456 Tag ThisTag = NewPrimer->Lookup(new UL(Key));
457 printf("Tag = %s\n", Tag2String(ThisTag).c_str());
458
459 Key[11] = 5;
460 ThisTag = NewPrimer->Lookup(new UL(Key));
461 printf("NewTag = %s\n", Tag2String(ThisTag).c_str());
462
463 ThisTag = NewPrimer->Lookup(new UL(Key));
464 printf("NewTag = %s\n", Tag2String(ThisTag).c_str());
465
466 Key[12] = 1;
467 ThisTag = NewPrimer->Lookup(new UL(Key));
468 printf("NewTag = %s\n", Tag2String(ThisTag).c_str());
469 */
470
471 return 0;
472 }
473
474
475 //! Dump an object and any physical or logical children
DumpObject(MDObjectPtr Object,std::string Prefix)476 void DumpObject(MDObjectPtr Object, std::string Prefix)
477 {
478 if(DumpLocation) printf("0x%s : ", Int64toHexString(Object->GetLocation(),8).c_str());
479
480 if(Object->IsModified()) printf("%s%s is *MODIFIED*\n", Object->FullName().c_str(), Prefix.c_str() );
481
482 if(Object->GetLink())
483 {
484 if(Object->GetRefType() == ClassRefStrong)
485 {
486 printf("%s%s = %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetString().c_str());
487
488 if(DumpLocation) printf("0x%s : ", Int64toHexString(Object->GetLocation(),8).c_str());
489 printf("%s%s -> Strong Reference to %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str());
490
491 DumpObject(Object->GetLink(), Prefix + " ");
492 }
493 else if(Object->GetRefType() == ClassRefGlobal)
494 {
495 if(FollowGlobals)
496 {
497 printf("%s%s = %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetString().c_str());
498
499 if(DumpLocation) printf("0x%s : ", Int64toHexString(Object->GetLocation(),8).c_str());
500 printf("%s%s -> Global Reference to %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str());
501
502 DumpObject(Object->GetLink(), Prefix + " ");
503 }
504 else
505 {
506 printf("%s%s -> Global Reference to %s, %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str(), Object->GetString().c_str());
507 }
508 }
509 else
510 {
511 printf("%s%s -> Weak Reference to %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetLink()->Name().c_str());
512 }
513 }
514 else
515 {
516 if(Object->IsDValue())
517 {
518 printf("%s%s = <Unknown>\n", Prefix.c_str(), Object->Name().c_str());
519 }
520 else
521 {
522 if(Object->Value)
523 //if(Object->Name().find("Unknown") == std::string::npos)
524 printf("%s%s = %s\n", Prefix.c_str(), Object->Name().c_str(), Object->GetString().c_str());
525 //else printf("%s%s\n", Prefix.c_str(), Object->Name().c_str());
526 else
527 printf("%s%s\n", Prefix.c_str(), Object->Name().c_str());
528 }
529
530 MDObjectULList::iterator it = Object->begin();
531 while(it != Object->end())
532 {
533 DumpObject((*it).second, Prefix + " ");
534 it++;
535 }
536 }
537
538 return;
539 }
540
541
542
543
544
545 // Debug and error messages
546 #include <stdarg.h>
547
548 #ifdef MXFLIB_DEBUG
549 //! Display a general debug message
debug(const char * Fmt,...)550 void mxflib::debug(const char *Fmt, ...)
551 {
552 if(!DebugMode) return;
553
554 va_list args;
555
556 va_start(args, Fmt);
557 vprintf(Fmt, args);
558 va_end(args);
559 }
560 #endif // MXFLIB_DEBUG
561
562 //! Display a warning message
warning(const char * Fmt,...)563 void mxflib::warning(const char *Fmt, ...)
564 {
565 va_list args;
566
567 va_start(args, Fmt);
568 printf("Warning: ");
569 vprintf(Fmt, args);
570 va_end(args);
571 }
572
573 //! Display an error message
error(const char * Fmt,...)574 void mxflib::error(const char *Fmt, ...)
575 {
576 va_list args;
577
578 va_start(args, Fmt);
579 printf("ERROR: ");
580 vprintf(Fmt, args);
581 va_end(args);
582 }
583
584