ISXDDataEssenceDescriptor_NamespaceURI UL fixed
[asdcplib.git] / src / asdcp-info.cpp
1 /*
2 Copyright (c) 2003-2014, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    asdcp-info.cpp
28     \version $Id$
29     \brief   AS-DCP file metadata utility
30
31   This program provides metadata information about an AS-DCP file.
32
33   For more information about asdcplib, please refer to the header file AS_DCP.h
34 */
35
36 #include <KM_fileio.h>
37 #include <AS_DCP.h>
38 #include <AS_02.h>
39 #include <MXF.h>
40 #include <Metadata.h>
41
42 using namespace Kumu;
43 using namespace ASDCP;
44
45 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
46
47 const byte_t P_HFR_UL_2K[16] = {
48   0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
49   0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
50 };
51
52 const byte_t P_HFR_UL_4K[16] = {
53   0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
54   0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x04
55 };
56
57 //------------------------------------------------------------------------------------------
58 //
59 // command line option parser class
60
61 static const char* PROGRAM_NAME = "asdcp-info";  // program name for messages
62
63
64 // Increment the iterator, test for an additional non-option command line argument.
65 // Causes the caller to return if there are no remaining arguments or if the next
66 // argument begins with '-'.
67 #define TEST_EXTRA_ARG(i,c)                                             \
68   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
69     fprintf(stderr, "Argument not found for option -%c.\n", (c));       \
70     return;                                                             \
71   }
72
73 //
74 void
75 banner(FILE* stream = stdout)
76 {
77   fprintf(stream, "\n\
78 %s (asdcplib %s)\n\n\
79 Copyright (c) 2003-2015 John Hurst\n\n\
80 asdcplib may be copied only under the terms of the license found at\n\
81 the top of every file in the asdcplib distribution kit.\n\n\
82 Specify the -h (help) option for further information about %s\n\n",
83           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
84 }
85
86 //
87 void
88 usage(FILE* stream = stdout)
89 {
90   fprintf(stream, "\
91 USAGE:%s [-h|-help] [-V]\n\
92 \n\
93        %s [options] <input-file>+\n\
94 \n\
95 Options:\n\
96   -3          - Force stereoscopic interpretation of a JP2K file\n\
97   -c          - Show essence coding UL\n\
98   -d          - Show essence descriptor info\n\
99   -h | -help  - Show help\n\
100   -H          - Show MXF header metadata\n\
101   -i          - Show identity info\n\
102   -n          - Show index\n\
103   -r          - Show bit-rate (Mb/s)\n\
104   -t <int>    - Set high-bitrate threshold (Mb/s)\n\
105   -V          - Show version information\n\
106 \n\
107   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
108          o All option arguments must be separated from the option by whitespace.\n\n",
109           PROGRAM_NAME, PROGRAM_NAME);
110
111 }
112
113 //
114 class CommandOptions
115 {
116   CommandOptions();
117
118 public:
119   bool   error_flag;     // true if the given options are in error or not complete
120   bool   version_flag;   // true if the version display option was selected
121   bool   help_flag;      // true if the help display option was selected
122   bool   verbose_flag;   // true if the verbose option was selected
123   PathList_t filenames;  // list of filenames to be processed
124   bool   showindex_flag; // true if index is to be displayed
125   bool   showheader_flag; // true if MXF file header is to be displayed
126   bool   stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
127   bool   showid_flag;          // if true, show file identity info (the WriterInfo struct)
128   bool   showdescriptor_flag;  // if true, show the essence descriptor
129   bool   showcoding_flag;      // if true, show the coding UL
130   bool   showrate_flag;        // if true and is image file, show bit rate
131   bool   max_bitrate_flag;     // true if -t option given
132   double max_bitrate;          // if true and is image file, max bit rate for rate test
133
134   //
135   CommandOptions(int argc, const char** argv) :
136     error_flag(true), version_flag(false), help_flag(false), verbose_flag(false),
137     showindex_flag(), showheader_flag(), stereo_image_flag(false),
138     showid_flag(false), showdescriptor_flag(false), showcoding_flag(false),
139     showrate_flag(false), max_bitrate_flag(false), max_bitrate(0.0)
140   {
141     for ( int i = 1; i < argc; ++i )
142       {
143
144         if ( (strcmp( argv[i], "-help") == 0) )
145           {
146             help_flag = true;
147             continue;
148           }
149
150         if ( argv[i][0] == '-'
151              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
152              && argv[i][2] == 0 )
153           {
154             switch ( argv[i][1] )
155               {
156               case '3': stereo_image_flag = true; break;
157               case 'c': showcoding_flag = true; break;
158               case 'd': showdescriptor_flag = true; break;
159               case 'H': showheader_flag = true; break;
160               case 'h': help_flag = true; break;
161               case 'i': showid_flag = true; break;
162               case 'n': showindex_flag = true; break;
163               case 'r': showrate_flag = true; break;
164
165               case 't':
166                 TEST_EXTRA_ARG(i, 't');
167                 max_bitrate = Kumu::xabs(strtol(argv[i], 0, 10));
168                 max_bitrate_flag = true;
169                 break;
170
171               case 'V': version_flag = true; break;
172               case 'v': verbose_flag = true; break;
173
174               default:
175                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
176                 return;
177               }
178           }
179         else
180           {
181             if ( argv[i][0] != '-' )
182               {
183                 filenames.push_back(argv[i]);
184               }
185             else
186               {
187                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
188                 return;
189               }
190           }
191       }
192
193     if ( help_flag || version_flag )
194       return;
195
196     if ( filenames.empty() )
197       {
198         fputs("At least one filename argument is required.\n", stderr);
199         return;
200       }
201
202     error_flag = false;
203   }
204 };
205
206 //------------------------------------------------------------------------------------------
207 //
208
209 //
210 // These classes wrap the irregular names in the asdcplib API
211 // so that I can use a template to simplify the implementation
212 // of show_file_info()
213
214 class MyVideoDescriptor : public MPEG2::VideoDescriptor
215 {
216  public:
217   void FillDescriptor(MPEG2::MXFReader& Reader) {
218     Reader.FillVideoDescriptor(*this);
219   }
220
221   void Dump(FILE* stream) {
222     MPEG2::VideoDescriptorDump(*this, stream);
223   }
224 };
225
226 class MyPictureDescriptor : public JP2K::PictureDescriptor
227 {
228  public:
229   void FillDescriptor(JP2K::MXFReader& Reader) {
230     Reader.FillPictureDescriptor(*this);
231   }
232
233   void Dump(FILE* stream) {
234     JP2K::PictureDescriptorDump(*this, stream);
235   }
236 };
237
238 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
239 {
240  public:
241   void FillDescriptor(JP2K::MXFSReader& Reader) {
242     Reader.FillPictureDescriptor(*this);
243   }
244
245   void Dump(FILE* stream) {
246     JP2K::PictureDescriptorDump(*this, stream);
247   }
248 };
249
250 class MyAudioDescriptor : public PCM::AudioDescriptor
251 {
252  public:
253   void FillDescriptor(PCM::MXFReader& Reader) {
254     Reader.FillAudioDescriptor(*this);
255   }
256
257   void Dump(FILE* stream) {
258     PCM::AudioDescriptorDump(*this, stream);
259   }
260 };
261
262 class MyTextDescriptor : public TimedText::TimedTextDescriptor
263 {
264  public:
265   void FillDescriptor(TimedText::MXFReader& Reader) {
266     Reader.FillTimedTextDescriptor(*this);
267   }
268
269   void Dump(FILE* stream) {
270     TimedText::DescriptorDump(*this, stream);
271   }
272 };
273
274 class MyDCDataDescriptor : public DCData::DCDataDescriptor
275 {
276  public:
277   void FillDescriptor(DCData::MXFReader& Reader) {
278     Reader.FillDCDataDescriptor(*this);
279   }
280
281   void Dump(FILE* stream) {
282       DCData::DCDataDescriptorDump(*this, stream);
283   }
284 };
285
286 class MyAtmosDescriptor : public ATMOS::AtmosDescriptor
287 {
288  public:
289   void FillDescriptor(ATMOS::MXFReader& Reader) {
290     Reader.FillAtmosDescriptor(*this);
291   }
292
293   void Dump(FILE* stream) {
294       ATMOS::AtmosDescriptorDump(*this, stream);
295   }
296 };
297
298 //
299 //
300 template<class ReaderT, class DescriptorT>
301 class FileInfoWrapper
302 {
303   ReaderT  m_Reader;
304   DescriptorT m_Desc;
305   WriterInfo m_WriterInfo;
306   double m_MaxBitrate, m_AvgBitrate;
307   UL m_PictureEssenceCoding;
308
309   KM_NO_COPY_CONSTRUCT(FileInfoWrapper);
310
311 public:
312   FileInfoWrapper() : m_MaxBitrate(0.0), m_AvgBitrate(0.0) {}
313   virtual ~FileInfoWrapper() {}
314
315   Result_t
316   file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
317   {
318     assert(type_string);
319     if ( stream == 0 )
320       stream = stdout;
321
322     Result_t result = RESULT_OK;
323     result = m_Reader.OpenRead(Options.filenames.front().c_str());
324
325     if ( ASDCP_SUCCESS(result) )
326       {
327         m_Desc.FillDescriptor(m_Reader);
328         m_Reader.FillWriterInfo(m_WriterInfo);
329
330         fprintf(stdout, "%s file essence type is %s, (%d edit unit%s).\n",
331                 ( m_WriterInfo.LabelSetType == LS_MXF_SMPTE ? "SMPTE 429" : LS_MXF_INTEROP ? "Interop" : "Unknown" ),
332                 type_string, m_Desc.ContainerDuration, (m_Desc.ContainerDuration==1?"":"s"));
333
334         if ( Options.showheader_flag )
335           m_Reader.DumpHeaderMetadata(stream);
336
337         if ( Options.showid_flag )
338           WriterInfoDump(m_WriterInfo, stream);
339
340         if ( Options.showdescriptor_flag )
341           m_Desc.Dump(stream);
342
343         if ( Options.showindex_flag )
344           m_Reader.DumpIndex(stream);
345       }
346     else if ( result == RESULT_FORMAT && Options.showheader_flag )
347       {
348         m_Reader.DumpHeaderMetadata(stream);
349       }
350
351     return result;
352   }
353
354   //
355   void get_PictureEssenceCoding(FILE* stream = 0)
356   {
357     const Dictionary& Dict = DefaultCompositeDict();
358     MXF::RGBAEssenceDescriptor *descriptor = 0;
359
360     Result_t result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
361                                                               reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
362
363     if ( KM_SUCCESS(result) )
364       m_PictureEssenceCoding = descriptor->PictureEssenceCoding;
365   }
366
367
368   //
369   void dump_PictureEssenceCoding(FILE* stream = 0)
370   {
371     char buf[64];
372
373     if ( m_PictureEssenceCoding.HasValue() )
374       {
375         const char *encoding_ul_type = "**UNKNOWN**";
376
377         if ( m_PictureEssenceCoding == UL(P_HFR_UL_2K) )
378           encoding_ul_type = "P-HFR-2K";
379         else if ( m_PictureEssenceCoding == UL(P_HFR_UL_4K) )
380           encoding_ul_type = "**P-HFR-4K**";
381         else if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_2K) )
382           encoding_ul_type = "ST-429-4-2K";
383         else if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_4K) )
384           encoding_ul_type = "ST-429-4-4K";
385
386         fprintf(stream, "PictureEssenceCoding: %s (%s)\n", m_PictureEssenceCoding.EncodeString(buf, 64), encoding_ul_type);
387       }
388   }
389
390   //
391   Result_t
392   test_rates(CommandOptions& Options, FILE* stream = 0)
393   {
394     static const double dci_max_bitrate = 250.0;
395     static const double p_hfr_max_bitrate = 400.0;
396
397     double max_bitrate = Options.max_bitrate_flag ? Options.max_bitrate : dci_max_bitrate;
398     ui32_t errors = 0;
399
400     if ( m_PictureEssenceCoding == UL(P_HFR_UL_2K) )
401       {
402         if ( m_Desc.StoredWidth > 2048 ) // 4k
403           {
404             fprintf(stream, "4k images marked as 2k HFR.\n");
405             ++errors;
406           }
407
408         if ( m_Desc.SampleRate < ASDCP::EditRate_96 )
409           {
410             fprintf(stream, "HFR UL used for fps < 96.\n");
411             ++errors;
412           }
413
414         if ( ! Options.max_bitrate_flag )
415           max_bitrate = p_hfr_max_bitrate;
416       }
417     else if ( m_PictureEssenceCoding == UL(P_HFR_UL_4K) )
418       {
419         fprintf(stream, "4k HFR support undefined.\n");
420         ++errors;
421
422         if ( m_Desc.StoredWidth <= 2048 ) // 2k
423           {
424             fprintf(stream, "2k images marked as 4k HFR.\n");
425             ++errors;
426           }
427       }
428     else if ( m_PictureEssenceCoding != DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_2K)
429               && m_PictureEssenceCoding != DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_4K) )
430       {
431         fprintf(stream, "Unknown PictureEssenceCoding UL value.\n");
432         ++errors;
433       }
434     else
435       {
436         if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_2K) )
437           {
438             if ( m_Desc.StoredWidth > 2048 ) // 4k
439               {
440                 fprintf(stream, "4k images marked as 2k ST 429-4.\n");
441                 ++errors;
442               }
443           }
444         else if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_4K) )
445           {
446             if ( m_Desc.StoredWidth <= 2048 ) // 2k
447               {
448                 fprintf(stream, "2k images marked as 4k ST 429-4.\n");
449                 ++errors;
450               }
451           }
452       }
453
454     if ( m_MaxBitrate > max_bitrate )
455       {
456         fprintf(stream, "Bitrate %0.0f exceeds maximum %0.0f (see option -r).\n", m_MaxBitrate, max_bitrate);
457         ++errors;
458       }
459
460     return errors ? RESULT_FAIL : RESULT_OK;
461   }
462
463   //
464   void
465   calc_Bitrate(FILE* stream = 0)
466   {
467     MXF::OPAtomIndexFooter& footer = m_Reader.OPAtomIndexFooter();
468     ui64_t total_frame_bytes = 0, last_stream_offset = 0;
469     ui32_t largest_frame = 0;
470     Result_t result = RESULT_OK;
471
472     for ( ui32_t i = 0; KM_SUCCESS(result) && i < m_Desc.ContainerDuration; ++i )
473       {
474         MXF::IndexTableSegment::IndexEntry entry;
475         result = footer.Lookup(i, entry);
476
477         if ( KM_SUCCESS(result) )
478           {
479             if ( last_stream_offset != 0 )
480               {
481                 ui64_t this_frame_size = entry.StreamOffset - last_stream_offset - 20; // do not count the bytes that represent the KLV wrapping 
482                 total_frame_bytes += this_frame_size;
483
484                 if ( this_frame_size > largest_frame )
485                   largest_frame = this_frame_size;
486               }
487
488             last_stream_offset = entry.StreamOffset;
489           }
490       }
491
492     if ( KM_SUCCESS(result) )
493       {
494         // scale bytes to megabits
495         static const double mega_const = 1.0 / ( 1000000 / 8.0 );
496
497         // we did not accumulate the first or last frame, so duration -= 2
498         double avg_bytes_frame = total_frame_bytes / ( m_Desc.ContainerDuration - 2 );
499
500         m_MaxBitrate = largest_frame * mega_const * m_Desc.EditRate.Quotient();
501         m_AvgBitrate = avg_bytes_frame * mega_const * m_Desc.EditRate.Quotient();
502       }
503   }
504
505   //
506   void
507   dump_Bitrate(FILE* stream = 0)
508   {
509     fprintf(stream, "Max BitRate: %0.2f Mb/s\n", m_MaxBitrate);
510     fprintf(stream, "Average BitRate: %0.2f Mb/s\n", m_AvgBitrate);
511   }
512
513   //
514   void dump_WaveAudioDescriptor(FILE* stream = 0)
515   {
516     const Dictionary& Dict = DefaultCompositeDict();
517     MXF::WaveAudioDescriptor *descriptor = 0;
518
519     Result_t result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor),
520                                                               reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
521
522     if ( KM_SUCCESS(result) )
523       {
524         char buf[64];
525         fprintf(stream, "ChannelAssignment: %s\n", descriptor->ChannelAssignment.const_get().EncodeString(buf, 64));
526       }
527   }
528
529 };
530
531
532 // Read header metadata from an ASDCP file
533 //
534 Result_t
535 show_file_info(CommandOptions& Options)
536 {
537   EssenceType_t EssenceType;
538   Result_t result = ASDCP::EssenceType(Options.filenames.front().c_str(), EssenceType);
539
540   if ( ASDCP_FAILURE(result) )
541     return result;
542
543   if ( EssenceType == ESS_MPEG2_VES )
544     {
545       FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor> wrapper;
546       result = wrapper.file_info(Options, "MPEG2 video");
547
548       if ( ASDCP_SUCCESS(result) && Options.showrate_flag )
549         wrapper.dump_Bitrate(stdout);
550     }
551   else if ( EssenceType == ESS_PCM_24b_48k || EssenceType == ESS_PCM_24b_96k )
552     {
553       FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor> wrapper;
554       result = wrapper.file_info(Options, "PCM audio");
555
556       if ( ASDCP_SUCCESS(result) && Options.showcoding_flag )
557         wrapper.dump_WaveAudioDescriptor();
558     }
559   else if ( EssenceType == ESS_JPEG_2000 )
560     {
561       if ( Options.stereo_image_flag )
562         {
563           FileInfoWrapper<ASDCP::JP2K::MXFSReader, MyStereoPictureDescriptor> wrapper;
564           result = wrapper.file_info(Options, "JPEG 2000 stereoscopic pictures");
565
566           if ( KM_SUCCESS(result) )
567             {
568               wrapper.get_PictureEssenceCoding();
569               wrapper.calc_Bitrate();
570
571               if ( Options.showcoding_flag )
572                 wrapper.dump_PictureEssenceCoding(stdout);
573
574               if ( Options.showrate_flag )
575                 wrapper.dump_Bitrate(stdout);
576
577               result = wrapper.test_rates(Options, stdout);
578             }
579         }
580       else
581         {
582           FileInfoWrapper<ASDCP::JP2K::MXFReader, MyPictureDescriptor>wrapper;
583           result = wrapper.file_info(Options, "JPEG 2000 pictures");
584
585           if ( KM_SUCCESS(result) )
586             {
587               wrapper.get_PictureEssenceCoding();
588               wrapper.calc_Bitrate();
589
590               if ( Options.showcoding_flag )
591                 wrapper.dump_PictureEssenceCoding(stdout);
592
593               if ( Options.showrate_flag )
594                 wrapper.dump_Bitrate(stdout);
595
596               result = wrapper.test_rates(Options, stdout);
597             }
598         }
599     }
600   else if ( EssenceType == ESS_JPEG_2000_S )
601     {
602       FileInfoWrapper<ASDCP::JP2K::MXFSReader, MyStereoPictureDescriptor>wrapper;
603       result = wrapper.file_info(Options, "JPEG 2000 stereoscopic pictures");
604
605       if ( KM_SUCCESS(result) )
606         {
607           wrapper.get_PictureEssenceCoding();
608           wrapper.calc_Bitrate();
609
610           if ( Options.showcoding_flag )
611             wrapper.dump_PictureEssenceCoding(stdout);
612
613           if ( Options.showrate_flag )
614             wrapper.dump_Bitrate(stdout);
615
616           result = wrapper.test_rates(Options, stdout);
617         }
618     }
619   else if ( EssenceType == ESS_TIMED_TEXT )
620     {
621       FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>wrapper;
622       result = wrapper.file_info(Options, "Timed Text");
623     }
624   else if ( EssenceType == ESS_DCDATA_UNKNOWN )
625     {
626       FileInfoWrapper<ASDCP::DCData::MXFReader, MyDCDataDescriptor> wrapper;
627       result = wrapper.file_info(Options, "D-Cinema Generic Data");
628     }
629   else if ( EssenceType == ESS_DCDATA_DOLBY_ATMOS )
630     {
631       FileInfoWrapper<ASDCP::ATMOS::MXFReader, MyAtmosDescriptor> wrapper;
632       result = wrapper.file_info(Options, "Dolby ATMOS");
633     }
634   else if ( EssenceType == ESS_AS02_PCM_24b_48k
635             || EssenceType == ESS_AS02_PCM_24b_96k
636             || EssenceType == ESS_AS02_JPEG_2000
637             || EssenceType == ESS_AS02_TIMED_TEXT )
638     {
639       fprintf(stderr, "File is AS-02. Inspection in not supported by this command.\n");
640     }
641   else
642     {
643       fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames.front().c_str());
644       Kumu::FileReader   Reader;
645       const Dictionary* Dict = &DefaultCompositeDict();
646       MXF::OP1aHeader TestHeader(Dict);
647
648       result = Reader.OpenRead(Options.filenames.front().c_str());
649
650       if ( ASDCP_SUCCESS(result) )
651         result = TestHeader.InitFromFile(Reader); // test UL and OP
652
653       if ( ASDCP_SUCCESS(result) )
654         {
655           TestHeader.Partition::Dump(stdout);
656
657           if ( MXF::Identification* ID = TestHeader.GetIdentification() )
658             ID->Dump(stdout);
659           else
660             fputs("File contains no Identification object.\n", stdout);
661
662           if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
663             SP->Dump(stdout);
664           else
665             fputs("File contains no SourcePackage object.\n", stdout);
666         }
667       else
668         {
669           fputs("File is not MXF.\n", stdout);
670         }
671     }
672
673   return result;
674 }
675
676 //
677 int
678 main(int argc, const char** argv)
679 {
680   Result_t result = RESULT_OK;
681   char     str_buf[64];
682   CommandOptions Options(argc, argv);
683
684   if ( Options.version_flag )
685     banner();
686
687   if ( Options.help_flag )
688     usage();
689
690   if ( Options.version_flag || Options.help_flag )
691     return 0;
692
693   if ( Options.error_flag )
694     {
695       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
696       return 3;
697     }
698
699   while ( ! Options.filenames.empty() && ASDCP_SUCCESS(result) )
700     {
701       result = show_file_info(Options);
702       Options.filenames.pop_front();
703     }
704
705   if ( ASDCP_FAILURE(result) )
706     {
707       fputs("Program stopped on error.\n", stderr);
708
709       if ( result == RESULT_SFORMAT )
710         {
711           fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
712         }
713       else if ( result != RESULT_FAIL )
714         {
715           fputs(result, stderr);
716           fputc('\n', stderr);
717         }
718
719       return 1;
720     }
721
722   return 0;
723 }
724
725
726 //
727 // end asdcp-info.cpp
728 //