2 Copyright (c) 2003-2016, John Hurst, Wolfgang Ruppel
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
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.
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.
27 /*! \file as-02-info.cpp
29 \brief AS-02 file metadata utility
31 This program provides metadata information about an AS-02 file.
33 For more information about asdcplib, please refer to the header file AS_DCP.h
36 #include <KM_fileio.h>
41 #include <AS_02_ACES.h>
48 using namespace ASDCP;
50 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
52 //------------------------------------------------------------------------------------------
54 // command line option parser class
56 static const char* PROGRAM_NAME = "as-02-info"; // program name for messages
59 // Increment the iterator, test for an additional non-option command line argument.
60 // Causes the caller to return if there are no remaining arguments or if the next
61 // argument begins with '-'.
62 #define TEST_EXTRA_ARG(i,c) \
63 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
64 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
70 banner(FILE* stream = stdout)
74 Copyright (c) 2003-2015 John Hurst\n\n\
75 asdcplib may be copied only under the terms of the license found at\n\
76 the top of every file in the asdcplib distribution kit.\n\n\
77 Specify the -h (help) option for further information about %s\n\n",
78 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
83 usage(FILE* stream = stdout)
86 USAGE:%s [-h|-help] [-V]\n\
88 %s [options] <input-file>+\n\
91 -c - Show essence coding UL\n\
92 -d - Show essence descriptor info\n\
93 -h | -help - Show help\n\
94 -H - Show MXF header metadata\n\
95 -i - Show identity info\n\
97 -r - Show bit-rate (Mb/s)\n\
98 -t <int> - Set high-bitrate threshold (Mb/s)\n\
99 -V - Show version information\n\
101 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
102 o All option arguments must be separated from the option by whitespace.\n\n",
103 PROGRAM_NAME, PROGRAM_NAME);
113 bool error_flag; // true if the given options are in error or not complete
114 bool version_flag; // true if the version display option was selected
115 bool help_flag; // true if the help display option was selected
116 bool verbose_flag; // true if the verbose option was selected
117 PathList_t filenames; // list of filenames to be processed
118 bool showindex_flag; // true if index is to be displayed
119 bool showheader_flag; // true if MXF file header is to be displayed
120 bool showid_flag; // if true, show file identity info (the WriterInfo struct)
121 bool showdescriptor_flag; // if true, show the essence descriptor
122 bool showcoding_flag; // if true, show the coding UL
123 bool showrate_flag; // if true and is image file, show bit rate
124 bool max_bitrate_flag; // true if -t option given
125 double max_bitrate; // if true and is image file, max bit rate for rate test
128 CommandOptions(int argc, const char** argv) :
129 error_flag(true), version_flag(false), help_flag(false), verbose_flag(false),
130 showindex_flag(false), showheader_flag(false),
131 showid_flag(false), showdescriptor_flag(false), showcoding_flag(false),
132 showrate_flag(false), max_bitrate_flag(false), max_bitrate(0.0)
134 for ( int i = 1; i < argc; ++i )
137 if ( (strcmp( argv[i], "-help") == 0) )
143 if ( argv[i][0] == '-'
144 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
147 switch ( argv[i][1] )
149 case 'c': showcoding_flag = true; break;
150 case 'd': showdescriptor_flag = true; break;
151 case 'H': showheader_flag = true; break;
152 case 'h': help_flag = true; break;
153 case 'i': showid_flag = true; break;
154 case 'n': showindex_flag = true; break;
155 case 'r': showrate_flag = true; break;
158 TEST_EXTRA_ARG(i, 't');
159 max_bitrate = Kumu::xabs(strtol(argv[i], 0, 10));
160 max_bitrate_flag = true;
163 case 'V': version_flag = true; break;
164 case 'v': verbose_flag = true; break;
167 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
173 if ( argv[i][0] != '-' )
175 filenames.push_back(argv[i]);
179 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
185 if ( help_flag || version_flag )
188 if ( filenames.empty() )
190 fputs("At least one filename argument is required.\n", stderr);
198 //------------------------------------------------------------------------------------------
202 // These classes wrap the irregular names in the asdcplib API
203 // so that I can use a template to simplify the implementation
204 // of show_file_info()
206 static int s_exp_lookup[16] = { 0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024,2048, 4096, 8192, 16384, 32768 };
208 using namespace ASDCP::MXF;
210 template <class ReaderType, class DescriptorType>
211 DescriptorType *get_descriptor_by_type(ReaderType& reader, const UL& type_ul)
213 InterchangeObject *obj = 0;
214 reader.OP1aHeader().GetMDObjectByType(type_ul.Value(), &obj);
215 return dynamic_cast<DescriptorType*>(obj);
218 class MyPictureDescriptor : public JP2K::PictureDescriptor
220 RGBAEssenceDescriptor *m_RGBADescriptor;
221 CDCIEssenceDescriptor *m_CDCIDescriptor;
222 JPEG2000PictureSubDescriptor *m_JP2KSubDescriptor;
225 MyPictureDescriptor() :
228 m_JP2KSubDescriptor(0) {}
230 void FillDescriptor(AS_02::JP2K::MXFReader& Reader)
232 m_CDCIDescriptor = get_descriptor_by_type<AS_02::JP2K::MXFReader, CDCIEssenceDescriptor>
233 (Reader, DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor));
235 m_RGBADescriptor = get_descriptor_by_type<AS_02::JP2K::MXFReader, RGBAEssenceDescriptor>
236 (Reader, DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor));
238 if ( m_RGBADescriptor != 0 )
240 SampleRate = m_RGBADescriptor->SampleRate;
241 ContainerDuration = m_RGBADescriptor->ContainerDuration;
243 else if ( m_CDCIDescriptor != 0 )
245 SampleRate = m_CDCIDescriptor->SampleRate;
246 ContainerDuration = m_CDCIDescriptor->ContainerDuration;
250 DefaultLogSink().Error("Picture descriptor not found.\n");
253 m_JP2KSubDescriptor = get_descriptor_by_type<AS_02::JP2K::MXFReader, JPEG2000PictureSubDescriptor>
254 (Reader, DefaultCompositeDict().ul(MDD_JPEG2000PictureSubDescriptor));
256 if ( m_JP2KSubDescriptor == 0 )
258 DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
261 std::list<InterchangeObject*> ObjectList;
262 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), ObjectList);
264 if ( ObjectList.empty() )
266 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
269 EditRate = ((Track*)ObjectList.front())->EditRate;
272 void MyDump(FILE* stream) {
278 if ( m_CDCIDescriptor != 0 )
280 m_CDCIDescriptor->Dump(stream);
282 else if ( m_RGBADescriptor != 0 )
284 m_RGBADescriptor->Dump(stream);
291 if ( m_JP2KSubDescriptor != 0 )
293 m_JP2KSubDescriptor->Dump(stream);
295 fprintf(stream, " ImageComponents: (max=%d)\n", JP2K::MaxComponents);
298 ui32_t component_sizing = m_JP2KSubDescriptor->PictureComponentSizing.const_get().Length();
299 JP2K::ImageComponent_t image_components[JP2K::MaxComponents];
301 if ( component_sizing == 17 ) // ( 2 * sizeof(ui32_t) ) + 3 components * 3 byte each
303 memcpy(&image_components,
304 m_JP2KSubDescriptor->PictureComponentSizing.const_get().RoData() + 8,
305 component_sizing - 8);
309 DefaultLogSink().Warn("Unexpected PictureComponentSizing size: %u, should be 17.\n", component_sizing);
312 fprintf(stream, " bits h-sep v-sep\n");
314 for ( int i = 0; i < m_JP2KSubDescriptor->Csize && i < JP2K::MaxComponents; i++ )
316 fprintf(stream, " %4d %5d %5d\n",
317 image_components[i].Ssize + 1, // See ISO 15444-1, Table A11, for the origin of '+1'
318 image_components[i].XRsize,
319 image_components[i].YRsize
324 JP2K::CodingStyleDefault_t coding_style_default;
326 memcpy(&coding_style_default,
327 m_JP2KSubDescriptor->CodingStyleDefault.const_get().RoData(),
328 m_JP2KSubDescriptor->CodingStyleDefault.const_get().Length());
330 fprintf(stream, " Scod: %hhu\n", coding_style_default.Scod);
331 fprintf(stream, " ProgressionOrder: %hhu\n", coding_style_default.SGcod.ProgressionOrder);
332 fprintf(stream, " NumberOfLayers: %hd\n",
333 KM_i16_BE(Kumu::cp2i<ui16_t>(coding_style_default.SGcod.NumberOfLayers)));
335 fprintf(stream, " MultiCompTransform: %hhu\n", coding_style_default.SGcod.MultiCompTransform);
336 fprintf(stream, "DecompositionLevels: %hhu\n", coding_style_default.SPcod.DecompositionLevels);
337 fprintf(stream, " CodeblockWidth: %hhu\n", coding_style_default.SPcod.CodeblockWidth);
338 fprintf(stream, " CodeblockHeight: %hhu\n", coding_style_default.SPcod.CodeblockHeight);
339 fprintf(stream, " CodeblockStyle: %hhu\n", coding_style_default.SPcod.CodeblockStyle);
340 fprintf(stream, " Transformation: %hhu\n", coding_style_default.SPcod.Transformation);
342 ui32_t precinct_set_size = 0;
344 for ( int i = 0; coding_style_default.SPcod.PrecinctSize[i] != 0 && i < JP2K::MaxPrecincts; ++i )
349 fprintf(stream, " Precincts: %u\n", precinct_set_size);
350 fprintf(stream, "precinct dimensions:\n");
352 for ( unsigned int i = 0; i < precinct_set_size && i < JP2K::MaxPrecincts; i++ )
353 fprintf(stream, " %d: %d x %d\n", i + 1,
354 s_exp_lookup[coding_style_default.SPcod.PrecinctSize[i]&0x0f],
355 s_exp_lookup[(coding_style_default.SPcod.PrecinctSize[i]>>4)&0x0f]
361 class MyACESPictureDescriptor : public AS_02::ACES::PictureDescriptor
363 RGBAEssenceDescriptor *m_RGBADescriptor;
364 std::list<ACESPictureSubDescriptor*> m_ACESPictureSubDescriptorList;
365 std::list<TargetFrameSubDescriptor*> m_TargetFrameSubDescriptorList;
368 MyACESPictureDescriptor() :
369 m_RGBADescriptor(0) {}
371 void FillDescriptor(AS_02::ACES::MXFReader& Reader)
373 m_RGBADescriptor = get_descriptor_by_type<AS_02::ACES::MXFReader, RGBAEssenceDescriptor>
374 (Reader, DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor));
376 if ( m_RGBADescriptor != 0 )
378 SampleRate = m_RGBADescriptor->SampleRate;
379 ContainerDuration = m_RGBADescriptor->ContainerDuration;
383 DefaultLogSink().Error("Picture descriptor not found.\n");
386 std::list<InterchangeObject*> object_list;
387 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_ACESPictureSubDescriptor), object_list);
389 std::list<InterchangeObject*>::iterator i = object_list.begin();
390 for ( ; i != object_list.end(); ++i )
392 ACESPictureSubDescriptor *p = dynamic_cast<ACESPictureSubDescriptor*>(*i);
396 m_ACESPictureSubDescriptorList.push_back(p);
401 DefaultLogSink().Error("ACESPictureSubDescriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
407 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_TargetFrameSubDescriptor), object_list);
409 i = object_list.begin();
410 for ( ; i != object_list.end(); ++i )
412 TargetFrameSubDescriptor *p = dynamic_cast<TargetFrameSubDescriptor*>(*i);
416 m_TargetFrameSubDescriptorList.push_back(p);
421 DefaultLogSink().Error("TargetFrameSubDescriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
427 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), object_list);
429 if ( object_list.empty() )
431 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
434 EditRate = ((Track*)object_list.front())->EditRate;
437 void MyDump(FILE* stream) {
443 if ( m_RGBADescriptor != 0 )
445 m_RGBADescriptor->Dump(stream);
452 for ( std::list<ACESPictureSubDescriptor*>::iterator i = m_ACESPictureSubDescriptorList.begin(); i != m_ACESPictureSubDescriptorList.end(); ++i )
456 for ( std::list<TargetFrameSubDescriptor*>::iterator i = m_TargetFrameSubDescriptorList.begin(); i != m_TargetFrameSubDescriptorList.end(); ++i )
463 class MyAudioDescriptor : public PCM::AudioDescriptor
465 WaveAudioDescriptor *m_WaveAudioDescriptor;
466 std::list<MCALabelSubDescriptor*> m_ChannelDescriptorList;
469 MyAudioDescriptor() : m_WaveAudioDescriptor(0) {}
470 void FillDescriptor(AS_02::PCM::MXFReader& Reader)
472 m_WaveAudioDescriptor = get_descriptor_by_type<AS_02::PCM::MXFReader, WaveAudioDescriptor>
473 (Reader, DefaultCompositeDict().ul(MDD_WaveAudioDescriptor));
475 if ( m_WaveAudioDescriptor != 0 )
477 AudioSamplingRate = m_WaveAudioDescriptor->SampleRate;
478 ContainerDuration = m_WaveAudioDescriptor->ContainerDuration;
482 DefaultLogSink().Error("Audio descriptor not found.\n");
485 std::list<InterchangeObject*> object_list;
486 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_AudioChannelLabelSubDescriptor), object_list);
487 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SoundfieldGroupLabelSubDescriptor), object_list);
488 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor), object_list);
490 std::list<InterchangeObject*>::iterator i = object_list.begin();
491 for ( ; i != object_list.end(); ++i )
493 MCALabelSubDescriptor *p = dynamic_cast<MCALabelSubDescriptor*>(*i);
497 m_ChannelDescriptorList.push_back(p);
502 DefaultLogSink().Error("Audio sub-descriptor type error.\n", (**i).InstanceUID.EncodeHex(buf, 64));
507 Reader.OP1aHeader().GetMDObjectsByType(DefaultCompositeDict().ul(MDD_Track), object_list);
509 if ( object_list.empty() )
511 DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
514 EditRate = ((Track*)object_list.front())->EditRate;
517 void MyDump(FILE* stream) {
523 if ( m_WaveAudioDescriptor != 0 )
525 m_WaveAudioDescriptor->Dump(stream);
528 if ( ! m_ChannelDescriptorList.empty() )
530 fprintf(stream, "Audio Channel Subdescriptors:\n");
532 std::list<MCALabelSubDescriptor*>::const_iterator i = m_ChannelDescriptorList.begin();
533 for ( ; i != m_ChannelDescriptorList.end(); ++i )
541 class MyTextDescriptor : public TimedText::TimedTextDescriptor
544 void FillDescriptor(TimedText::MXFReader& Reader) {
545 Reader.FillTimedTextDescriptor(*this);
548 void Dump(FILE* stream) {
549 TimedText::DescriptorDump(*this, stream);
559 RateInfo(const UL& u, const double& b, const std::string& l) {
560 ul = u; bitrate = b; label = l;
565 static const double dci_max_bitrate = 250.0;
566 static const double p_hfr_max_bitrate = 400.0;
567 typedef std::map<const UL, const RateInfo> rate_info_map;
568 static rate_info_map g_rate_info;
574 UL rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_1);
575 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 200.0, "ISO/IEC 15444-1 Amendment 3 Level 1")));
577 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_2);
578 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 200.0, "ISO/IEC 15444-1 Amendment 3 Level 2")));
580 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_3);
581 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 200.0, "ISO/IEC 15444-1 Amendment 3 Level 3")));
583 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_4);
584 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 400.0, "ISO/IEC 15444-1 Amendment 3 Level 4")));
586 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_5);
587 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 800.0, "ISO/IEC 15444-1 Amendment 3 Level 5")));
589 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_6);
590 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, 1600.0, "ISO/IEC 15444-1 Amendment 3 Level 6")));
592 rate_ul = DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_BroadcastProfile_7);
593 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ISO/IEC 15444-1 Amendment 3 Level 7")));
595 rate_ul = DefaultCompositeDict().ul(MDD_ACESUncompressedMonoscopicWithoutAlpha);
596 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ST 2065-5")));
598 rate_ul = DefaultCompositeDict().ul(MDD_ACESUncompressedMonoscopicWithAlpha);
599 g_rate_info.insert(rate_info_map::value_type(rate_ul, RateInfo(rate_ul, DBL_MAX, "ST 2065-5")));
605 template<class ReaderT, class DescriptorT>
606 class FileInfoWrapper
610 WriterInfo m_WriterInfo;
611 double m_MaxBitrate, m_AvgBitrate;
612 UL m_PictureEssenceCoding;
614 KM_NO_COPY_CONSTRUCT(FileInfoWrapper);
617 Result_t OpenRead(const T& m, const CommandOptions& Options)
619 return m.OpenRead(Options.filenames.front().c_str());
621 Result_t OpenRead(const AS_02::PCM::MXFReader& m, const CommandOptions& Options)
623 return m.OpenRead(Options.filenames.front().c_str(), EditRate_24);
624 //Result_t OpenRead(const std::string& filename, const ASDCP::Rational& EditRate);
628 FileInfoWrapper() : m_MaxBitrate(0.0), m_AvgBitrate(0.0) {}
629 virtual ~FileInfoWrapper() {}
632 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
640 Result_t result = RESULT_OK;
641 result = OpenRead(m_Reader, Options);
643 if ( ASDCP_SUCCESS(result) )
645 m_Desc.FillDescriptor(m_Reader);
646 m_Reader.FillWriterInfo(m_WriterInfo);
648 fprintf(stdout, "%s file essence type is %s, (%d edit unit%s).\n",
649 ( m_WriterInfo.LabelSetType == LS_MXF_SMPTE ? "SMPTE 2067-5" : "Unknown" ),
651 (m_Desc.ContainerDuration != 0 ? m_Desc.ContainerDuration : m_Reader.AS02IndexReader().GetDuration()),
652 (m_Desc.ContainerDuration == (ui64_t)1 ? "":"s"));
654 if ( Options.showheader_flag )
656 m_Reader.DumpHeaderMetadata(stream);
659 if ( Options.showid_flag )
661 WriterInfoDump(m_WriterInfo, stream);
664 if ( Options.showdescriptor_flag )
666 m_Desc.MyDump(stream);
669 if ( Options.showindex_flag )
671 m_Reader.DumpIndex(stream);
674 else if ( result == RESULT_FORMAT && Options.showheader_flag )
676 m_Reader.DumpHeaderMetadata(stream);
683 void get_PictureEssenceCoding(FILE* stream = 0)
685 const Dictionary& Dict = DefaultCompositeDict();
686 MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
687 MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
689 Result_t result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
690 reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
692 if ( KM_SUCCESS(result) && rgba_descriptor)
693 m_PictureEssenceCoding = rgba_descriptor->PictureEssenceCoding;
695 result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
696 reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
697 if ( KM_SUCCESS(result) && cdci_descriptor)
698 m_PictureEssenceCoding = cdci_descriptor->PictureEssenceCoding;
704 void dump_PictureEssenceCoding(FILE* stream = 0)
708 if ( m_PictureEssenceCoding.HasValue() )
710 std::string encoding_ul_type = "**UNKNOWN**";
712 rate_info_map::const_iterator rate_i = g_rate_info.find(m_PictureEssenceCoding);
713 if ( rate_i == g_rate_info.end() )
715 fprintf(stderr, "Unknown PictureEssenceCoding UL: %s\n", m_PictureEssenceCoding.EncodeString(buf, 64));
719 encoding_ul_type = rate_i->second.label;
722 fprintf(stream, "PictureEssenceCoding: %s (%s)\n",
723 m_PictureEssenceCoding.EncodeString(buf, 64),
724 encoding_ul_type.c_str());
730 test_rates(CommandOptions& Options, FILE* stream = 0)
732 double max_bitrate = 0; //Options.max_bitrate_flag ? Options.max_bitrate : dci_max_bitrate;
736 rate_info_map::const_iterator rate_i = g_rate_info.find(m_PictureEssenceCoding);
737 if ( rate_i == g_rate_info.end() )
739 fprintf(stderr, "Unknown PictureEssenceCoding UL: %s\n", m_PictureEssenceCoding.EncodeString(buf, 64));
743 max_bitrate = rate_i->second.bitrate;
746 max_bitrate = Options.max_bitrate_flag ? Options.max_bitrate : max_bitrate;
748 if ( m_MaxBitrate > max_bitrate )
750 fprintf(stream, "Bitrate %0.0f Mb/s exceeds maximum %0.0f Mb/s\n", m_MaxBitrate, max_bitrate);
754 return errors ? RESULT_FAIL : RESULT_OK;
759 calc_Bitrate(FILE* stream = 0)
761 //MXF::OP1aHeader& footer = m_Reader.OP1aHeader();
762 AS_02::MXF::AS02IndexReader& footer = m_Reader.AS02IndexReader();
763 ui64_t total_frame_bytes = 0, last_stream_offset = 0;
764 ui32_t largest_frame = 0;
765 Result_t result = RESULT_OK;
768 if ( m_Desc.EditRate.Numerator == 0 || m_Desc.EditRate.Denominator == 0 )
770 fprintf(stderr, "Broken edit rate, unable to calculate essence bitrate.\n");
774 duration = m_Desc.ContainerDuration;
777 fprintf(stderr, "ContainerDuration not set in file descriptor, attempting to use index duration.\n");
778 duration = m_Reader.AS02IndexReader().GetDuration();
781 for ( ui32_t i = 0; KM_SUCCESS(result) && i < duration; ++i )
783 MXF::IndexTableSegment::IndexEntry entry;
784 result = footer.Lookup(i, entry);
786 if ( KM_SUCCESS(result) )
788 if ( last_stream_offset != 0 )
790 ui64_t this_frame_size = entry.StreamOffset - last_stream_offset - 20; // do not count the bytes that represent the KLV wrapping
791 total_frame_bytes += this_frame_size;
793 if ( this_frame_size > largest_frame )
794 largest_frame = (ui32_t)this_frame_size;
797 last_stream_offset = entry.StreamOffset;
801 if ( KM_SUCCESS(result) )
803 // scale bytes to megabits
804 static const double mega_const = 1.0 / ( 1000000 / 8.0 );
806 // we did not accumulate the last, so duration -= 1
807 double avg_bytes_frame = (double)(total_frame_bytes / ( duration - 1 ));
809 m_MaxBitrate = largest_frame * mega_const * m_Desc.EditRate.Quotient();
810 m_AvgBitrate = avg_bytes_frame * mega_const * m_Desc.EditRate.Quotient();
816 dump_Bitrate(FILE* stream = 0)
818 fprintf(stream, "Max BitRate: %0.2f Mb/s\n", m_MaxBitrate);
819 fprintf(stream, "Average BitRate: %0.2f Mb/s\n", m_AvgBitrate);
823 void dump_WaveAudioDescriptor(FILE* stream = 0)
825 const Dictionary& Dict = DefaultCompositeDict();
826 MXF::WaveAudioDescriptor *descriptor = 0;
828 Result_t result = m_Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor),
829 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
831 if ( KM_SUCCESS(result) )
834 fprintf(stream, "ChannelAssignment: %s\n", descriptor->ChannelAssignment.const_get().EncodeString(buf, 64));
841 // Read header metadata from an ASDCP file
844 show_file_info(CommandOptions& Options)
846 EssenceType_t EssenceType;
847 Result_t result = ASDCP::EssenceType(Options.filenames.front().c_str(), EssenceType);
849 if ( ASDCP_FAILURE(result) )
852 if ( EssenceType == ESS_AS02_JPEG_2000 )
854 FileInfoWrapper<AS_02::JP2K::MXFReader, MyPictureDescriptor> wrapper;
855 result = wrapper.file_info(Options, "JPEG 2000 pictures");
857 if ( KM_SUCCESS(result) )
859 wrapper.get_PictureEssenceCoding();
860 wrapper.calc_Bitrate(stdout);
862 if ( Options.showcoding_flag )
864 wrapper.dump_PictureEssenceCoding(stdout);
867 if ( Options.showrate_flag )
869 wrapper.dump_Bitrate(stdout);
872 result = wrapper.test_rates(Options, stdout);
876 else if ( EssenceType == ESS_AS02_ACES )
878 FileInfoWrapper<AS_02::ACES::MXFReader, MyACESPictureDescriptor> wrapper;
879 result = wrapper.file_info(Options, "ACES pictures");
881 if ( KM_SUCCESS(result) )
883 wrapper.get_PictureEssenceCoding();
884 wrapper.calc_Bitrate(stdout);
886 if ( Options.showcoding_flag )
888 wrapper.dump_PictureEssenceCoding(stdout);
891 if ( Options.showrate_flag )
893 wrapper.dump_Bitrate(stdout);
896 result = wrapper.test_rates(Options, stdout);
900 else if ( EssenceType == ESS_AS02_PCM_24b_48k || EssenceType == ESS_AS02_PCM_24b_96k )
902 FileInfoWrapper<AS_02::PCM::MXFReader, MyAudioDescriptor> wrapper;
903 result = wrapper.file_info(Options, "PCM audio");
905 if ( ASDCP_SUCCESS(result) && Options.showcoding_flag )
906 wrapper.dump_WaveAudioDescriptor(stdout);
910 fprintf(stderr, "Unknown/unsupported essence type: %s\n", Options.filenames.front().c_str());
911 Kumu::FileReader Reader;
912 const Dictionary* Dict = &DefaultCompositeDict();
913 MXF::OP1aHeader TestHeader(Dict);
915 result = Reader.OpenRead(Options.filenames.front().c_str());
917 if ( ASDCP_SUCCESS(result) )
918 result = TestHeader.InitFromFile(Reader); // test UL and OP
920 if ( ASDCP_SUCCESS(result) )
922 TestHeader.Partition::Dump(stdout);
924 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
927 fputs("File contains no Identification object.\n", stdout);
929 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
932 fputs("File contains no SourcePackage object.\n", stdout);
936 fputs("File is not MXF.\n", stdout);
945 main(int argc, const char** argv)
947 Result_t result = RESULT_OK;
948 CommandOptions Options(argc, argv);
950 if ( Options.version_flag )
953 if ( Options.help_flag )
956 if ( Options.version_flag || Options.help_flag )
959 if ( Options.error_flag )
961 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
967 while ( ! Options.filenames.empty() && ASDCP_SUCCESS(result) )
969 result = show_file_info(Options);
970 Options.filenames.pop_front();
973 if ( ASDCP_FAILURE(result) )
975 fputs("Program stopped on error.\n", stderr);
977 if ( result != RESULT_FAIL )
979 fputs(result, stderr);
991 // end as-02-info.cpp