2 Copyright (c) 2003-2016, John Hurst, Wolfgang Ruppel
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /*! \file as-02-info.cpp
31 \brief AS-02 file metadata utility
33 This program provides metadata information about an AS-02 file.
35 For more information about asdcplib, please refer to the header file AS_DCP.h
38 #include <KM_fileio.h>
42 #include <AS_02_IAB.h>
43 #include <AS_02_JXS.h>
45 #include <AS_02_ACES.h>
52 using namespace ASDCP;
54 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
56 //------------------------------------------------------------------------------------------
58 // command line option parser class
60 static const char* PROGRAM_NAME = "as-02-info"; // program name for messages
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)); \
74 banner(FILE* stream = stdout)
78 Copyright (c) 2003-2015 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);
87 usage(FILE* stream = stdout)
90 USAGE:%s [-h|-help] [-V]\n\
92 %s [options] <input-file>+\n\
95 -c - Show essence coding UL\n\
96 -d - Show essence descriptor info\n\
97 -h | -help - Show help\n\
98 -H - Show MXF header metadata\n\
99 -i - Show identity info\n\
101 -r - Show bit-rate (Mb/s)\n\
102 -t <int> - Set high-bitrate threshold (Mb/s)\n\
103 -V - Show version information\n\
105 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
106 o All option arguments must be separated from the option by whitespace.\n\n",
107 PROGRAM_NAME, PROGRAM_NAME);
117 bool error_flag; // true if the given options are in error or not complete
118 bool version_flag; // true if the version display option was selected
119 bool help_flag; // true if the help display option was selected
120 bool verbose_flag; // true if the verbose option was selected
121 PathList_t filenames; // list of filenames to be processed
122 bool showindex_flag; // true if index is to be displayed
123 bool showheader_flag; // true if MXF file header is to be displayed
124 bool showid_flag; // if true, show file identity info (the WriterInfo struct)
125 bool showdescriptor_flag; // if true, show the essence descriptor
126 bool showcoding_flag; // if true, show the coding UL
127 bool showrate_flag; // if true and is image file, show bit rate
128 bool max_bitrate_flag; // true if -t option given
129 double max_bitrate; // if true and is image file, max bit rate for rate test
132 CommandOptions(int argc, const char** argv) :
133 error_flag(true), version_flag(false), help_flag(false), verbose_flag(false),
134 showindex_flag(false), showheader_flag(false),
135 showid_flag(false), showdescriptor_flag(false), showcoding_flag(false),
136 showrate_flag(false), max_bitrate_flag(false), max_bitrate(0.0)
138 for ( int i = 1; i < argc; ++i )
141 if ( (strcmp( argv[i], "-help") == 0) )
147 if ( argv[i][0] == '-'
148 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
151 switch ( argv[i][1] )
153 case 'c': showcoding_flag = true; break;
154 case 'd': showdescriptor_flag = true; break;
155 case 'H': showheader_flag = true; break;
156 case 'h': help_flag = true; break;
157 case 'i': showid_flag = true; break;
158 case 'n': showindex_flag = true; break;
159 case 'r': showrate_flag = true; break;
162 TEST_EXTRA_ARG(i, 't');
163 max_bitrate = Kumu::xabs(strtol(argv[i], 0, 10));
164 max_bitrate_flag = true;
167 case 'V': version_flag = true; break;
168 case 'v': verbose_flag = true; break;
171 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
177 if ( argv[i][0] != '-' )
179 filenames.push_back(argv[i]);
183 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
189 if ( help_flag || version_flag )
192 if ( filenames.empty() )
194 fputs("At least one filename argument is required.\n", stderr);
202 //------------------------------------------------------------------------------------------
206 // These classes wrap the irregular names in the asdcplib API
207 // so that I can use a template to simplify the implementation
208 // of show_file_info()
210 static int s_exp_lookup[16] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,2048, 4096, 8192, 16384, 32768 };
212 using namespace ASDCP::MXF;
214 template <class ReaderType, class DescriptorType>
215 DescriptorType *get_descriptor_by_type(ReaderType& reader, const UL& type_ul)
217 InterchangeObject *obj = 0;
218 reader.OP1aHeader().GetMDObjectByType(type_ul.Value(), &obj);
219 return dynamic_cast<DescriptorType*>(obj);
222 class MyPictureDescriptor : public JP2K::PictureDescriptor
224 RGBAEssenceDescriptor *m_RGBADescriptor;
225 CDCIEssenceDescriptor *m_CDCIDescriptor;
226 JPEG2000PictureSubDescriptor *m_JP2KSubDescriptor;
229 MyPictureDescriptor() :
232 m_JP2KSubDescriptor(0) {}
234 void FillDescriptor(AS_02::JP2K::MXFReader& Reader)
236 m_CDCIDescriptor = get_descriptor_by_type<AS_02::JP2K::MXFReader, CDCIEssenceDescriptor>
237 (Reader, DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor));
239 m_RGBADescriptor = get_descriptor_by_type<AS_02::JP2K::MXFReader, RGBAEssenceDescriptor>
240 (Reader, DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor));
242 if ( m_RGBADescriptor != 0 )
244 SampleRate = m_RGBADescriptor->SampleRate;
245 if ( ! m_RGBADescriptor->ContainerDuration.empty() )
247 ContainerDuration = m_RGBADescriptor->ContainerDuration;
250 else if ( m_CDCIDescriptor != 0 )
252 SampleRate = m_CDCIDescriptor->SampleRate;
253 if ( ! m_CDCIDescriptor->ContainerDuration.empty() )
255 ContainerDuration = m_CDCIDescriptor->ContainerDuration;
260 DefaultLogSink().Error("Picture descriptor not found.\n");
263 m_JP2KSubDescriptor = get_descriptor_by_type<AS_02::JP2K::MXFReader, JPEG2000PictureSubDescriptor>
264 (Reader, DefaultCompositeDict().ul(MDD_JPEG2000PictureSubDescriptor));
266 if ( m_JP2KSubDescriptor == 0 )
268 DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
271 std::list<InterchangeObject*> ObjectList;
272 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), ObjectList);
274 if ( ObjectList.empty() )
276 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
279 EditRate = ((Track*)ObjectList.front())->EditRate;
282 void MyDump(FILE* stream) {
288 if ( m_CDCIDescriptor != 0 )
290 m_CDCIDescriptor->Dump(stream);
292 else if ( m_RGBADescriptor != 0 )
294 m_RGBADescriptor->Dump(stream);
301 if ( m_JP2KSubDescriptor != 0 )
303 m_JP2KSubDescriptor->Dump(stream);
305 fprintf(stream, " ImageComponents: (max=%d)\n", JP2K::MaxComponents);
308 ui32_t component_sizing = m_JP2KSubDescriptor->PictureComponentSizing.const_get().Length();
309 JP2K::ImageComponent_t image_components[JP2K::MaxComponents];
311 if ( component_sizing == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
313 memcpy(&image_components,
314 m_JP2KSubDescriptor->PictureComponentSizing.const_get().RoData() + 8,
315 component_sizing - 8);
319 DefaultLogSink().Warn("Unexpected PictureComponentSizing size: %u, should be 17.\n", component_sizing);
322 fprintf(stream, " bits h-sep v-sep\n");
324 for ( int i = 0; i < m_JP2KSubDescriptor->Csize && i < JP2K::MaxComponents; i++ )
326 fprintf(stream, " %4d %5d %5d\n",
327 image_components[i].Ssize + 1, // See ISO 15444-1, Table A11, for the origin of '+1'
328 image_components[i].XRsize,
329 image_components[i].YRsize
334 JP2K::CodingStyleDefault_t coding_style_default;
336 memcpy(&coding_style_default,
337 m_JP2KSubDescriptor->CodingStyleDefault.const_get().RoData(),
338 m_JP2KSubDescriptor->CodingStyleDefault.const_get().Length());
340 fprintf(stream, " Scod: %hhu\n", coding_style_default.Scod);
341 fprintf(stream, " ProgressionOrder: %hhu\n", coding_style_default.SGcod.ProgressionOrder);
342 fprintf(stream, " NumberOfLayers: %hd\n",
343 KM_i16_BE(Kumu::cp2i<ui16_t>(coding_style_default.SGcod.NumberOfLayers)));
345 fprintf(stream, " MultiCompTransform: %hhu\n", coding_style_default.SGcod.MultiCompTransform);
346 fprintf(stream, "DecompositionLevels: %hhu\n", coding_style_default.SPcod.DecompositionLevels);
347 fprintf(stream, " CodeblockWidth: %hhu\n", coding_style_default.SPcod.CodeblockWidth);
348 fprintf(stream, " CodeblockHeight: %hhu\n", coding_style_default.SPcod.CodeblockHeight);
349 fprintf(stream, " CodeblockStyle: %hhu\n", coding_style_default.SPcod.CodeblockStyle);
350 fprintf(stream, " Transformation: %hhu\n", coding_style_default.SPcod.Transformation);
352 ui32_t precinct_set_size = 0;
354 for ( int i = 0; coding_style_default.SPcod.PrecinctSize[i] != 0 && i < JP2K::MaxPrecincts; ++i )
359 fprintf(stream, " Precincts: %u\n", precinct_set_size);
360 fprintf(stream, "precinct dimensions:\n");
362 for ( unsigned int i = 0; i < precinct_set_size && i < JP2K::MaxPrecincts; i++ )
363 fprintf(stream, " %d: %d x %d\n", i + 1,
364 s_exp_lookup[coding_style_default.SPcod.PrecinctSize[i]&0x0f],
365 s_exp_lookup[(coding_style_default.SPcod.PrecinctSize[i]>>4)&0x0f]
371 class MyACESPictureDescriptor : public AS_02::ACES::PictureDescriptor
373 RGBAEssenceDescriptor *m_RGBADescriptor;
374 std::list<ACESPictureSubDescriptor*> m_ACESPictureSubDescriptorList;
375 std::list<TargetFrameSubDescriptor*> m_TargetFrameSubDescriptorList;
378 MyACESPictureDescriptor() :
379 m_RGBADescriptor(0) {}
381 void FillDescriptor(AS_02::ACES::MXFReader& Reader)
383 m_RGBADescriptor = get_descriptor_by_type<AS_02::ACES::MXFReader, RGBAEssenceDescriptor>
384 (Reader, DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor));
386 if ( m_RGBADescriptor != 0 )
388 SampleRate = m_RGBADescriptor->SampleRate;
389 if ( ! m_RGBADescriptor->ContainerDuration.empty() )
391 ContainerDuration = m_RGBADescriptor->ContainerDuration;
396 DefaultLogSink().Error("Picture descriptor not found.\n");
399 std::list<InterchangeObject*> object_list;
400 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_ACESPictureSubDescriptor), object_list);
402 std::list<InterchangeObject*>::iterator i = object_list.begin();
403 for ( ; i != object_list.end(); ++i )
405 ACESPictureSubDescriptor *p = dynamic_cast<ACESPictureSubDescriptor*>(*i);
409 m_ACESPictureSubDescriptorList.push_back(p);
414 DefaultLogSink().Error("ACESPictureSubDescriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
420 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_TargetFrameSubDescriptor), object_list);
422 i = object_list.begin();
423 for ( ; i != object_list.end(); ++i )
425 TargetFrameSubDescriptor *p = dynamic_cast<TargetFrameSubDescriptor*>(*i);
429 m_TargetFrameSubDescriptorList.push_back(p);
434 DefaultLogSink().Error("TargetFrameSubDescriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
440 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), object_list);
442 if ( object_list.empty() )
444 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
447 EditRate = ((Track*)object_list.front())->EditRate;
450 void MyDump(FILE* stream) {
456 if ( m_RGBADescriptor != 0 )
458 m_RGBADescriptor->Dump(stream);
465 for ( std::list<ACESPictureSubDescriptor*>::iterator i = m_ACESPictureSubDescriptorList.begin(); i != m_ACESPictureSubDescriptorList.end(); ++i )
469 for ( std::list<TargetFrameSubDescriptor*>::iterator i = m_TargetFrameSubDescriptorList.begin(); i != m_TargetFrameSubDescriptorList.end(); ++i )
476 class MyAudioDescriptor : public PCM::AudioDescriptor
478 WaveAudioDescriptor *m_WaveAudioDescriptor;
479 std::list<MCALabelSubDescriptor*> m_ChannelDescriptorList;
482 MyAudioDescriptor() : m_WaveAudioDescriptor(0) {}
483 void FillDescriptor(AS_02::PCM::MXFReader& Reader)
485 m_WaveAudioDescriptor = get_descriptor_by_type<AS_02::PCM::MXFReader, WaveAudioDescriptor>
486 (Reader, DefaultCompositeDict().ul(MDD_WaveAudioDescriptor));
488 if ( m_WaveAudioDescriptor != 0 )
490 AudioSamplingRate = m_WaveAudioDescriptor->SampleRate;
491 if ( ! m_WaveAudioDescriptor->ContainerDuration.empty() )
493 ContainerDuration = m_WaveAudioDescriptor->ContainerDuration;
498 DefaultLogSink().Error("Audio descriptor not found.\n");
501 std::list<InterchangeObject*> object_list;
502 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_AudioChannelLabelSubDescriptor), object_list);
503 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SoundfieldGroupLabelSubDescriptor), object_list);
504 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor), object_list);
506 std::list<InterchangeObject*>::iterator i = object_list.begin();
507 for ( ; i != object_list.end(); ++i )
509 MCALabelSubDescriptor *p = dynamic_cast<MCALabelSubDescriptor*>(*i);
513 m_ChannelDescriptorList.push_back(p);
518 DefaultLogSink().Error("Audio sub-descriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
523 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), object_list);
525 if ( object_list.empty() )
527 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
530 EditRate = ((Track*)object_list.front())->EditRate;
533 void MyDump(FILE* stream) {
539 if ( m_WaveAudioDescriptor != 0 )
541 m_WaveAudioDescriptor->Dump(stream);
544 if ( ! m_ChannelDescriptorList.empty() )
546 fprintf(stream, "Audio Channel Subdescriptors:\n");
548 std::list<MCALabelSubDescriptor*>::const_iterator i = m_ChannelDescriptorList.begin();
549 for ( ; i != m_ChannelDescriptorList.end(); ++i )
559 int ContainerDuration;
560 IabDescriptor() : ContainerDuration(0) {}
563 class MyIabDescriptor : public IabDescriptor
566 void FillDescriptor(AS_02::IAB::MXFReader& reader) {
567 const Dictionary& Dict = DefaultCompositeDict();
568 IABEssenceDescriptor *essence_descriptor_mxf = 0;
570 std::list<MXF::InterchangeObject*> object_list;
571 reader.OP1aHeader().GetMDObjectsByType(DefaultSMPTEDict().ul(MDD_IABEssenceDescriptor), object_list);
573 if ( ! object_list.empty() )
575 essence_descriptor_mxf = dynamic_cast<MXF::IABEssenceDescriptor*>(object_list.back());
576 assert(essence_descriptor_mxf);
577 ContainerDuration = (int)essence_descriptor_mxf->ContainerDuration.get();
580 void Dump(FILE* stream) {}
581 void MyDump(FILE* stream) {}
584 class MyTextDescriptor : public TimedText::TimedTextDescriptor
587 void FillDescriptor(TimedText::MXFReader& Reader) {
588 Reader.FillTimedTextDescriptor(*this);
591 void Dump(FILE* stream) {
592 TimedText::DescriptorDump(*this, stream);
596 class MyJXSDescriptor
598 RGBAEssenceDescriptor *m_RGBADescriptor;
599 CDCIEssenceDescriptor *m_CDCIDescriptor;
600 JPEGXSPictureSubDescriptor *m_JPEGXSSubDescriptor;
602 ui64_t ContainerDuration;
603 ASDCP::MXF::Rational m_SampleRate;
604 ASDCP::MXF::Rational m_EditRate;
610 m_JPEGXSSubDescriptor(0),
614 void FillDescriptor(AS_02::JXS::MXFReader& Reader)
616 m_CDCIDescriptor = get_descriptor_by_type<AS_02::JXS::MXFReader, CDCIEssenceDescriptor>
617 (Reader, DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor));
619 m_RGBADescriptor = get_descriptor_by_type<AS_02::JXS::MXFReader, RGBAEssenceDescriptor>
620 (Reader, DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor));
622 if ( m_RGBADescriptor != 0 )
624 m_SampleRate = m_RGBADescriptor->SampleRate;
625 if ( ! m_RGBADescriptor->ContainerDuration.empty() )
627 ContainerDuration = m_RGBADescriptor->ContainerDuration;
630 else if ( m_CDCIDescriptor != 0 )
632 m_SampleRate = m_CDCIDescriptor->SampleRate;
633 if ( ! m_CDCIDescriptor->ContainerDuration.empty() )
635 ContainerDuration = m_CDCIDescriptor->ContainerDuration;
640 DefaultLogSink().Error("Picture descriptor not found.\n");
643 m_JPEGXSSubDescriptor = get_descriptor_by_type<AS_02::JXS::MXFReader, JPEGXSPictureSubDescriptor>
644 (Reader, DefaultCompositeDict().ul(MDD_JPEGXSPictureSubDescriptor));
646 if ( m_JPEGXSSubDescriptor == 0 )
648 DefaultLogSink().Error("JPEGXSPictureSubDescriptor not found.\n");
651 std::list<InterchangeObject*> ObjectList;
652 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), ObjectList);
654 if ( ObjectList.empty() )
656 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
659 m_EditRate = ((Track*)ObjectList.front())->EditRate;
662 void MyDump(FILE* stream) {
668 if ( m_CDCIDescriptor != 0 )
670 m_CDCIDescriptor->Dump(stream);
672 else if ( m_RGBADescriptor != 0 )
674 m_RGBADescriptor->Dump(stream);
681 if ( m_JPEGXSSubDescriptor != 0 )
683 m_JPEGXSSubDescriptor->Dump(stream);
694 RateInfo(const UL& u, const double& b, const std::string& l) {
695 ul = u; bitrate = b; label = l;
700 static const double dci_max_bitrate = 250.0;
701 static const double p_hfr_max_bitrate = 400.0;
702 typedef std::map<const UL, const RateInfo> rate_info_map;
703 static rate_info_map g_rate_info;
709 UL rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_1);
710 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 200.0, "ISO/IEC 15444-1 Amendment 3 Level 1")));
712 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_2);
713 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 200.0, "ISO/IEC 15444-1 Amendment 3 Level 2")));
715 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_3);
716 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 200.0, "ISO/IEC 15444-1 Amendment 3 Level 3")));
718 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_4);
719 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 400.0, "ISO/IEC 15444-1 Amendment 3 Level 4")));
721 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_5);
722 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 800.0, "ISO/IEC 15444-1 Amendment 3 Level 5")));
724 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_6);
725 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 1600.0, "ISO/IEC 15444-1 Amendment 3 Level 6")));
727 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_7);
728 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ISO/IEC 15444-1 Amendment 3 Level 7")));
730 rate_ul = DefaultCompositeDict().ul(MDD_ACESUncompressedMonoscopicWithoutAlpha);
731 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ST 2065-5")));
733 rate_ul = DefaultCompositeDict().ul(MDD_ACESUncompressedMonoscopicWithAlpha);
734 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ST 2065-5")));
740 template<class ReaderT, class DescriptorT>
741 class FileInfoWrapper
745 WriterInfo m_WriterInfo;
746 double m_MaxBitrate, m_AvgBitrate;
747 UL m_PictureEssenceCoding;
749 KM_NO_COPY_CONSTRUCT(FileInfoWrapper);
752 Result_t OpenRead(const T& m, const CommandOptions& Options)
754 return m.OpenRead(Options.filenames.front().c_str());
757 Result_t OpenRead(AS_02::IAB::MXFReader& m, const CommandOptions& Options)
759 // OpenRead method is not const
760 return m.OpenRead(Options.filenames.front().c_str());
763 Result_t OpenRead(const AS_02::PCM::MXFReader& m, const CommandOptions& Options)
765 return m.OpenRead(Options.filenames.front().c_str(), EditRate_24);
766 //Result_t OpenRead(const std::string& filename, const ASDCP::Rational& EditRate);
770 FileInfoWrapper(const IFileReaderFactory& fileReaderFactory) : m_MaxBitrate(0.0), m_AvgBitrate(0.0), m_Reader(fileReaderFactory) {}
771 virtual ~FileInfoWrapper() {}
774 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
782 Result_t result = RESULT_OK;
783 result = OpenRead(m_Reader, Options);
785 if ( ASDCP_SUCCESS(result) )
787 m_Desc.FillDescriptor(m_Reader);
788 m_Reader.FillWriterInfo(m_WriterInfo);
789 ui64_t container_duration = m_Desc.ContainerDuration;
790 fprintf(stdout, "%s file essence type is %s, (%llu edit unit%s).\n",
791 ( m_WriterInfo.LabelSetType == LS_MXF_SMPTE ? "SMPTE 2067-5" : "Unknown" ),
794 (container_duration == ui64_C(1) ? "":"s"));
796 if ( Options.showheader_flag )
798 m_Reader.DumpHeaderMetadata(stream);
801 if ( Options.showid_flag )
803 WriterInfoDump(m_WriterInfo, stream);
806 if ( Options.showdescriptor_flag )
808 m_Desc.MyDump(stream);
811 if ( Options.showindex_flag )
813 m_Reader.DumpIndex(stream);
816 else if ( result == RESULT_FORMAT && Options.showheader_flag )
818 m_Reader.DumpHeaderMetadata(stream);
825 void get_PictureEssenceCoding(FILE* stream = 0)
827 const Dictionary& Dict = DefaultCompositeDict();
828 MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
829 MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
831 Result_t result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
832 reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
834 if ( KM_SUCCESS(result) && rgba_descriptor)
835 m_PictureEssenceCoding = rgba_descriptor->PictureEssenceCoding;
837 result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
838 reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
839 if ( KM_SUCCESS(result) && cdci_descriptor)
840 m_PictureEssenceCoding = cdci_descriptor->PictureEssenceCoding;
846 void dump_PictureEssenceCoding(FILE* stream = 0)
850 if ( m_PictureEssenceCoding.HasValue() )
852 std::string encoding_ul_type = "**UNKNOWN**";
854 rate_info_map::const_iterator rate_i = g_rate_info.find(m_PictureEssenceCoding);
855 if ( rate_i == g_rate_info.end() )
857 fprintf(stderr, "Unknown PictureEssenceCoding UL: %s\n", m_PictureEssenceCoding.EncodeString(buf, 64));
861 encoding_ul_type = rate_i->second.label;
864 fprintf(stream, "PictureEssenceCoding: %s (%s)\n",
865 m_PictureEssenceCoding.EncodeString(buf, 64),
866 encoding_ul_type.c_str());
872 test_rates(CommandOptions& Options, FILE* stream = 0)
874 double max_bitrate = 0; //Options.max_bitrate_flag ? Options.max_bitrate : dci_max_bitrate;
878 rate_info_map::const_iterator rate_i = g_rate_info.find(m_PictureEssenceCoding);
879 if ( rate_i == g_rate_info.end() )
881 fprintf(stderr, "Unknown PictureEssenceCoding UL: %s\n", m_PictureEssenceCoding.EncodeString(buf, 64));
885 max_bitrate = rate_i->second.bitrate;
888 max_bitrate = Options.max_bitrate_flag ? Options.max_bitrate : max_bitrate;
890 if ( m_MaxBitrate > max_bitrate )
892 fprintf(stream, "Bitrate %0.0f Mb/s exceeds maximum %0.0f Mb/s\n", m_MaxBitrate, max_bitrate);
896 return errors ? RESULT_FAIL : RESULT_OK;
901 calc_Bitrate(FILE* stream = 0)
903 //MXF::OP1aHeader& footer = m_Reader.OP1aHeader();
904 AS_02::MXF::AS02IndexReader& footer = m_Reader.AS02IndexReader();
905 ui64_t total_frame_bytes = 0, last_stream_offset = 0;
906 ui32_t largest_frame = 0;
907 Result_t result = RESULT_OK;
910 if ( m_Desc.EditRate.Numerator == 0 || m_Desc.EditRate.Denominator == 0 )
912 fprintf(stderr, "Broken edit rate, unable to calculate essence bitrate.\n");
916 duration = m_Desc.ContainerDuration;
919 fprintf(stderr, "ContainerDuration not set in file descriptor, attempting to use index duration.\n");
920 duration = m_Reader.AS02IndexReader().GetDuration();
923 for ( ui32_t i = 0; KM_SUCCESS(result) && i < duration; ++i )
925 MXF::IndexTableSegment::IndexEntry entry;
926 result = footer.Lookup(i, entry);
928 if ( KM_SUCCESS(result) )
930 if ( last_stream_offset != 0 )
932 ui64_t this_frame_size = entry.StreamOffset - last_stream_offset - 20; // do not count the bytes that represent the KLV wrapping
933 total_frame_bytes += this_frame_size;
935 if ( this_frame_size > largest_frame )
936 largest_frame = (ui32_t)this_frame_size;
939 last_stream_offset = entry.StreamOffset;
943 if ( KM_SUCCESS(result) )
945 // scale bytes to megabits
946 static const double mega_const = 1.0 / ( 1000000 / 8.0 );
948 // we did not accumulate the last, so duration -= 1
949 double avg_bytes_frame = (double)(total_frame_bytes / ( duration - 1 ));
951 m_MaxBitrate = largest_frame * mega_const * m_Desc.EditRate.Quotient();
952 m_AvgBitrate = avg_bytes_frame * mega_const * m_Desc.EditRate.Quotient();
958 dump_Bitrate(FILE* stream = 0)
960 fprintf(stream, "Max BitRate: %0.2f Mb/s\n", m_MaxBitrate);
961 fprintf(stream, "Average BitRate: %0.2f Mb/s\n", m_AvgBitrate);
965 void dump_WaveAudioDescriptor(FILE* stream = 0)
967 const Dictionary& Dict = DefaultCompositeDict();
968 MXF::WaveAudioDescriptor *descriptor = 0;
970 Result_t result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor),
971 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
973 if ( KM_SUCCESS(result) )
976 fprintf(stream, "ChannelAssignment: %s\n", descriptor->ChannelAssignment.const_get().EncodeString(buf, 64));
983 // Read header metadata from an ASDCP file
986 show_file_info(CommandOptions& Options, const Kumu::IFileReaderFactory& fileReaderFactory)
988 EssenceType_t EssenceType;
989 Result_t result = ASDCP::EssenceType(Options.filenames.front().c_str(), EssenceType, fileReaderFactory);
991 if ( ASDCP_FAILURE(result) )
994 if ( EssenceType == ESS_AS02_JPEG_2000 )
996 FileInfoWrapper<AS_02::JP2K::MXFReader, MyPictureDescriptor> wrapper(fileReaderFactory);
997 result = wrapper.file_info(Options, "JPEG 2000 pictures");
999 if ( KM_SUCCESS(result) )
1001 wrapper.get_PictureEssenceCoding();
1002 wrapper.calc_Bitrate(stdout);
1004 if ( Options.showcoding_flag )
1006 wrapper.dump_PictureEssenceCoding(stdout);
1009 if ( Options.showrate_flag )
1011 wrapper.dump_Bitrate(stdout);
1014 result = wrapper.test_rates(Options, stdout);
1018 else if ( EssenceType == ESS_AS02_ACES )
1020 FileInfoWrapper<AS_02::ACES::MXFReader, MyACESPictureDescriptor> wrapper(fileReaderFactory);
1021 result = wrapper.file_info(Options, "ACES pictures");
1023 if ( KM_SUCCESS(result) )
1025 wrapper.get_PictureEssenceCoding();
1026 wrapper.calc_Bitrate(stdout);
1028 if ( Options.showcoding_flag )
1030 wrapper.dump_PictureEssenceCoding(stdout);
1033 if ( Options.showrate_flag )
1035 wrapper.dump_Bitrate(stdout);
1038 result = wrapper.test_rates(Options, stdout);
1042 else if ( EssenceType == ESS_AS02_PCM_24b_48k || EssenceType == ESS_AS02_PCM_24b_96k )
1044 FileInfoWrapper<AS_02::PCM::MXFReader, MyAudioDescriptor> wrapper(fileReaderFactory);
1045 result = wrapper.file_info(Options, "PCM audio");
1047 if ( ASDCP_SUCCESS(result) && Options.showcoding_flag )
1048 wrapper.dump_WaveAudioDescriptor(stdout);
1050 else if ( EssenceType == ESS_AS02_JPEG_XS )
1052 FileInfoWrapper<AS_02::JXS::MXFReader, MyJXSDescriptor> wrapper(fileReaderFactory);
1053 result = wrapper.file_info(Options, "JPEG XS");
1055 else if ( EssenceType == ESS_AS02_IAB )
1057 FileInfoWrapper<AS_02::IAB::MXFReader, MyIabDescriptor> wrapper(fileReaderFactory);
1058 result = wrapper.file_info(Options, "IAB audio");
1062 fprintf(stderr, "Unknown/unsupported essence type: %s\n", Options.filenames.front().c_str());
1063 ASDCP::mem_ptr<Kumu::IFileReader> Reader(fileReaderFactory.CreateFileReader());
1064 const Dictionary* Dict = &DefaultCompositeDict();
1065 MXF::OP1aHeader TestHeader(Dict);
1067 result = Reader->OpenRead(Options.filenames.front().c_str());
1069 if ( ASDCP_SUCCESS(result) )
1070 result = TestHeader.InitFromFile(*Reader); // test UL and OP
1072 if ( ASDCP_SUCCESS(result) )
1074 TestHeader.Partition::Dump(stdout);
1076 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1079 fputs("File contains no Identification object.\n", stdout);
1081 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1084 fputs("File contains no SourcePackage object.\n", stdout);
1088 fputs("File is not MXF.\n", stdout);
1096 main(int argc, const char** argv)
1098 Result_t result = RESULT_OK;
1099 CommandOptions Options(argc, argv);
1101 if ( Options.version_flag )
1104 if ( Options.help_flag )
1107 if ( Options.version_flag || Options.help_flag )
1110 if ( Options.error_flag )
1112 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1117 Kumu::FileReaderFactory defaultFactory;
1118 while ( ! Options.filenames.empty() && ASDCP_SUCCESS(result) )
1120 result = show_file_info(Options, defaultFactory);
1121 Options.filenames.pop_front();
1124 if ( ASDCP_FAILURE(result) )
1126 fputs("Program stopped on error.\n", stderr);
1128 if ( result != RESULT_FAIL )
1130 fputs(result, stderr);
1131 fputc('\n', stderr);
1142 // end as-02-info.cpp