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