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