2 Copyright (c) 2003-2012, John Hurst
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 asdcp-info.cpp
29 \brief AS-DCP file metadata utility
31 This program provides metadata information about an AS-DCP file.
33 For more information about asdcplib, please refer to the header file AS_DCP.h
36 #include <KM_fileio.h>
40 #include <openssl/sha.h>
43 using namespace ASDCP;
45 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
47 //------------------------------------------------------------------------------------------
49 // command line option parser class
51 static const char* PROGRAM_NAME = "asdcp-info"; // program name for messages
54 // Increment the iterator, test for an additional non-option command line argument.
55 // Causes the caller to return if there are no remaining arguments or if the next
56 // argument begins with '-'.
57 #define TEST_EXTRA_ARG(i,c) \
58 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
59 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
65 banner(FILE* stream = stdout)
69 Copyright (c) 2003-2012 John Hurst\n\n\
70 asdcplib may be copied only under the terms of the license found at\n\
71 the top of every file in the asdcplib distribution kit.\n\n\
72 Specify the -h (help) option for further information about %s\n\n",
73 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
78 usage(FILE* stream = stdout)
81 USAGE:%s [-h|-help] [-V]\n\
83 %s [-3] [-H] [-n] <input-file>+\n\
86 -3 - Force stereoscopic interpretation of a JP2K file\n\
87 -h | -help - Show help\n\
88 -H - Show MXF header metadata\n\
90 -V - Show version information\n\
92 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
93 o All option arguments must be separated from the option by whitespace.\n\n",
94 PROGRAM_NAME, PROGRAM_NAME);
104 bool error_flag; // true if the given options are in error or not complete
105 bool version_flag; // true if the version display option was selected
106 bool help_flag; // true if the help display option was selected
107 PathList_t filenames; // list of filenames to be processed
108 bool showindex_flag; // true if index is to be displayed
109 bool showheader_flag; // true if MXF file header is to be displayed
110 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
113 CommandOptions(int argc, const char** argv) :
114 error_flag(true), version_flag(false), help_flag(false),
115 showindex_flag(), showheader_flag(), stereo_image_flag(false)
117 for ( int i = 1; i < argc; ++i )
120 if ( (strcmp( argv[i], "-help") == 0) )
126 if ( argv[i][0] == '-'
127 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
130 switch ( argv[i][1] )
132 case '3': stereo_image_flag = true; break;
133 case 'H': showheader_flag = true; break;
134 case 'h': help_flag = true; break;
135 case 'n': showindex_flag = true; break;
136 case 'V': version_flag = true; break;
139 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
145 if ( argv[i][0] != '-' )
147 filenames.push_back(argv[i]);
151 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
157 if ( help_flag || version_flag )
160 if ( filenames.empty() )
162 fputs("Option requires at least one filename argument.\n", stderr);
170 //------------------------------------------------------------------------------------------
174 // These classes wrap the irregular names in the asdcplib API
175 // so that I can use a template to simplify the implementation
176 // of show_file_info()
178 class MyVideoDescriptor : public MPEG2::VideoDescriptor
181 void FillDescriptor(MPEG2::MXFReader& Reader) {
182 Reader.FillVideoDescriptor(*this);
185 void Dump(FILE* stream) {
186 MPEG2::VideoDescriptorDump(*this, stream);
190 class MyPictureDescriptor : public JP2K::PictureDescriptor
193 void FillDescriptor(JP2K::MXFReader& Reader) {
194 Reader.FillPictureDescriptor(*this);
197 void Dump(FILE* stream) {
198 JP2K::PictureDescriptorDump(*this, stream);
202 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
205 void FillDescriptor(JP2K::MXFSReader& Reader) {
206 Reader.FillPictureDescriptor(*this);
209 void Dump(FILE* stream) {
210 JP2K::PictureDescriptorDump(*this, stream);
214 class MyAudioDescriptor : public PCM::AudioDescriptor
217 void FillDescriptor(PCM::MXFReader& Reader) {
218 Reader.FillAudioDescriptor(*this);
221 void Dump(FILE* stream) {
222 PCM::AudioDescriptorDump(*this, stream);
226 class MyTextDescriptor : public TimedText::TimedTextDescriptor
229 void FillDescriptor(TimedText::MXFReader& Reader) {
230 Reader.FillTimedTextDescriptor(*this);
233 void Dump(FILE* stream) {
234 TimedText::DescriptorDump(*this, stream);
238 // MSVC didn't like the function template, so now it's a static class method
239 template<class ReaderT, class DescriptorT>
240 class FileInfoWrapper
244 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
250 Result_t result = RESULT_OK;
252 result = Reader.OpenRead(Options.filenames.front().c_str());
254 if ( ASDCP_SUCCESS(result) )
256 fprintf(stdout, "File essence type is %s.\n", type_string);
258 if ( Options.showheader_flag )
259 Reader.DumpHeaderMetadata(stream);
262 Reader.FillWriterInfo(WI);
263 WriterInfoDump(WI, stream);
266 Desc.FillDescriptor(Reader);
269 if ( Options.showindex_flag )
270 Reader.DumpIndex(stream);
272 else if ( result == RESULT_FORMAT && Options.showheader_flag )
274 Reader.DumpHeaderMetadata(stream);
281 // Read header metadata from an ASDCP file
284 show_file_info(CommandOptions& Options)
286 EssenceType_t EssenceType;
287 Result_t result = ASDCP::EssenceType(Options.filenames.front().c_str(), EssenceType);
289 if ( ASDCP_FAILURE(result) )
292 if ( EssenceType == ESS_MPEG2_VES )
294 result = FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options, "MPEG2 video");
296 else if ( EssenceType == ESS_PCM_24b_48k || EssenceType == ESS_PCM_24b_96k )
298 result = FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options, "PCM audio");
300 if ( ASDCP_SUCCESS(result) )
302 const Dictionary* Dict = &DefaultCompositeDict();
303 PCM::MXFReader Reader;
304 MXF::OPAtomHeader OPAtomHeader(Dict);
305 MXF::WaveAudioDescriptor *descriptor = 0;
307 result = Reader.OpenRead(Options.filenames.front().c_str());
309 if ( ASDCP_SUCCESS(result) )
310 result = Reader.OPAtomHeader().GetMDObjectByType(Dict->ul(MDD_WaveAudioDescriptor), reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
312 if ( ASDCP_SUCCESS(result) )
315 fprintf(stdout, " ChannelAssignment: %s\n", descriptor->ChannelAssignment.EncodeString(buf, 64));
319 else if ( EssenceType == ESS_JPEG_2000 )
321 if ( Options.stereo_image_flag )
323 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
324 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
328 result = FileInfoWrapper<ASDCP::JP2K::MXFReader,
329 MyPictureDescriptor>::file_info(Options, "JPEG 2000 pictures");
332 else if ( EssenceType == ESS_JPEG_2000_S )
334 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
335 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
337 else if ( EssenceType == ESS_TIMED_TEXT )
339 result = FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options, "Timed Text");
343 fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames.front().c_str());
344 Kumu::FileReader Reader;
345 const Dictionary* Dict = &DefaultCompositeDict();
346 MXF::OPAtomHeader TestHeader(Dict);
348 result = Reader.OpenRead(Options.filenames.front().c_str());
350 if ( ASDCP_SUCCESS(result) )
351 result = TestHeader.InitFromFile(Reader); // test UL and OP
353 if ( ASDCP_SUCCESS(result) )
355 TestHeader.Partition::Dump(stdout);
357 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
360 fputs("File contains no Identification object.\n", stdout);
362 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
365 fputs("File contains no SourcePackage object.\n", stdout);
369 fputs("File is not MXF.\n", stdout);
378 main(int argc, const char** argv)
380 Result_t result = RESULT_OK;
382 CommandOptions Options(argc, argv);
384 if ( Options.version_flag )
387 if ( Options.help_flag )
390 if ( Options.version_flag || Options.help_flag )
393 if ( Options.error_flag )
395 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
399 while ( ! Options.filenames.empty() && ASDCP_SUCCESS(result) )
401 result = show_file_info(Options);
402 Options.filenames.pop_front();
405 if ( ASDCP_FAILURE(result) )
407 fputs("Program stopped on error.\n", stderr);
409 if ( result == RESULT_SFORMAT )
411 fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
413 else if ( result != RESULT_FAIL )
415 fputs(result, stderr);
427 // end asdcp-info.cpp