2 Copyright (c) 2011-2018, John Hurst
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15 derived from this software without specific prior written permission.
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /*! \file phdr-unwrap.cpp
30 \brief prototype unwrapping for HDR images in AS-02
32 This program extracts picture (P-HDR picture) from an AS-02 MXF file.
35 #include <KM_fileio.h>
36 #include <AS_02_PHDR.h>
38 using namespace ASDCP;
40 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
42 //------------------------------------------------------------------------------------------
44 // command line option parser class
46 static const char* PROGRAM_NAME = "as-02-unwrap"; // program name for messages
48 // Increment the iterator, test for an additional non-option command line argument.
49 // Causes the caller to return if there are no remaining arguments or if the next
50 // argument begins with '-'.
51 #define TEST_EXTRA_ARG(i,c) \
52 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
53 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
59 banner(FILE* stream = stdout)
63 Copyright (c) 2011-2018, John Hurst\n\n\
64 asdcplib may be copied only under the terms of the license found at\n\
65 the top of every file in the asdcplib distribution kit.\n\n\
66 Specify the -h (help) option for further information about %s\n\n",
67 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
72 usage(FILE* stream = stdout)
75 USAGE: %s [-h|-help] [-V]\n\
77 %s [-b <buffer-size>] [-d <duration>]\n\
78 [-f <starting-frame>] [-m] [-R] [-s <size>] [-v] [-W]\n\
79 [-w] <input-file> [<file-prefix>]\n\n",
80 PROGRAM_NAME, PROGRAM_NAME);
84 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
85 Defaults to 4,194,304 (4MB)\n\
86 -d <duration> - Number of frames to process, default all\n\
87 -e <extension> - Extension to use for aux data files. default \"bin\"\n\
88 -f <start-frame> - Starting frame number, default 0\n\
89 -g <filename> - Extract global metadata to the named file.\n\
90 -h | -help - Show help\n\
91 -k <key-string> - Use key for ciphertext operations\n\
92 -m - verify HMAC values when reading\n\
93 -s <size> - Number of bytes to dump to output when -v is given\n\
94 -V - Show version information\n\
95 -v - Verbose, prints informative messages to stderr\n\
96 -W - Read input file only, do not write destination file\n\
97 -w <width> - Width of numeric element in a series of frame file names\n\
100 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
101 o All option arguments must be separated from the option by whitespace.\n\n");
110 bool error_flag; // true if the given options are in error or not complete
111 bool key_flag; // true if an encryption key was given
112 bool read_hmac; // true if HMAC values are to be validated
113 bool verbose_flag; // true if the verbose option was selected
114 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
115 bool no_write_flag; // true if no output files are to be written
116 bool version_flag; // true if the version display option was selected
117 bool help_flag; // true if the help display option was selected
118 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
119 ui32_t number_width; // number of digits in a serialized filename (for JPEG extract)
120 ui32_t start_frame; // frame number to begin processing
121 ui32_t duration; // number of frames to be processed
122 bool duration_flag; // true if duration argument given
123 ui32_t fb_size; // size of picture frame buffer
124 const char* file_prefix; // filename pre for files written by the extract mode
125 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
126 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
127 const char* input_filename;
128 const char* extension;
129 std::string global_metadata_filename, prefix_buffer;
132 CommandOptions(int argc, const char** argv) :
133 error_flag(true), key_flag(false), read_hmac(false), verbose_flag(false),
134 fb_dump_size(0), no_write_flag(false),
135 version_flag(false), help_flag(false), number_width(6),
136 start_frame(0), duration(0xffffffff), duration_flag(false),
137 fb_size(FRAME_BUFFER_SIZE), file_prefix(0),
138 input_filename(0), extension("bin")
140 memset(key_value, 0, KeyLen);
141 memset(key_id_value, 0, UUIDlen);
143 for ( int i = 1; i < argc; ++i )
146 if ( (strcmp( argv[i], "-help") == 0) )
152 if ( argv[i][0] == '-'
153 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
156 switch ( argv[i][1] )
159 TEST_EXTRA_ARG(i, 'b');
160 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
163 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
168 TEST_EXTRA_ARG(i, 'd');
169 duration_flag = true;
170 duration = Kumu::xabs(strtol(argv[i], 0, 10));
174 TEST_EXTRA_ARG(i, 'f');
175 start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
179 TEST_EXTRA_ARG(i, 'g');
180 global_metadata_filename = argv[i];
183 case 'h': help_flag = true; break;
184 case 'm': read_hmac = true; break;
187 TEST_EXTRA_ARG(i, 's');
188 fb_dump_size = Kumu::xabs(strtol(argv[i], 0, 10));
191 case 'V': version_flag = true; break;
192 case 'v': verbose_flag = true; break;
193 case 'W': no_write_flag = true; break;
196 TEST_EXTRA_ARG(i, 'w');
197 number_width = Kumu::xabs(strtol(argv[i], 0, 10));
201 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
207 if ( argv[i][0] != '-' )
209 if ( input_filename == 0 )
211 input_filename = argv[i];
213 else if ( file_prefix == 0 )
215 file_prefix = argv[i];
220 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
226 if ( help_flag || version_flag )
229 if ( input_filename == 0 )
231 fputs("At least one filename argument is required.\n", stderr);
235 if ( file_prefix == 0 )
237 prefix_buffer = Kumu::PathSetExtension(input_filename, "") + "_";
238 file_prefix = prefix_buffer.c_str();
246 //------------------------------------------------------------------------------------------
250 // Read one or more plaintext JPEG 2000 codestreams from a plaintext P-HDR file
251 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext P-HDR file
252 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext P-HDR file
255 read_JP2K_file(CommandOptions& Options)
257 AESDecContext* Context = 0;
258 HMACContext* HMAC = 0;
259 AS_02::PHDR::MXFReader Reader;
260 AS_02::PHDR::FrameBuffer FrameBuffer(Options.fb_size);
261 ui32_t frame_count = 0;
263 std::string PHDR_master_metadata; // todo: write to a file?
265 Result_t result = Reader.OpenRead(Options.input_filename, PHDR_master_metadata);
266 fprintf(stderr, "PHDR_master_metadata size=%zd\n", PHDR_master_metadata.size());
268 if ( ASDCP_SUCCESS(result) )
270 if ( Options.verbose_flag )
272 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
275 ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
276 ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
278 result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
279 reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
281 if ( KM_SUCCESS(result) )
283 assert(rgba_descriptor);
284 frame_count = rgba_descriptor->ContainerDuration;
286 if ( Options.verbose_flag )
288 rgba_descriptor->Dump();
293 result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
294 reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
296 if ( KM_SUCCESS(result) )
298 assert(cdci_descriptor);
299 frame_count = cdci_descriptor->ContainerDuration;
301 if ( Options.verbose_flag )
303 cdci_descriptor->Dump();
308 fprintf(stderr, "File does not contain an essence descriptor.\n");
309 frame_count = Reader.AS02IndexReader().GetDuration();
313 if ( frame_count == 0 )
315 frame_count = Reader.AS02IndexReader().GetDuration();
318 if ( frame_count == 0 )
320 fprintf(stderr, "Unable to determine file duration.\n");
325 if ( ASDCP_SUCCESS(result) && Options.key_flag )
327 Context = new AESDecContext;
328 result = Context->InitKey(Options.key_value);
330 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
333 Reader.FillWriterInfo(Info);
337 HMAC = new HMACContext;
338 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
342 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
347 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
348 if ( last_frame > frame_count )
349 last_frame = frame_count;
351 char name_format[64];
352 snprintf(name_format, 64, "%%s%%0%du.j2c", Options.number_width);
354 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
356 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
359 snprintf(filename, 1024, name_format, Options.file_prefix, i);
361 if ( ASDCP_SUCCESS(result) && Options.verbose_flag )
363 printf("Frame %d, %d bytes", i, FrameBuffer.Size());
365 if ( ! Options.no_write_flag )
367 printf(" -> %s", filename);
373 if ( ASDCP_SUCCESS(result) && ( ! Options.no_write_flag ) )
375 Kumu::FileWriter OutFile;
377 result = OutFile.OpenWrite(filename);
379 if ( ASDCP_SUCCESS(result) )
380 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
382 if ( ASDCP_SUCCESS(result) && Options.verbose_flag )
384 FrameBuffer.Dump(stderr, Options.fb_dump_size);
394 main(int argc, const char** argv)
397 CommandOptions Options(argc, argv);
399 if ( Options.version_flag )
402 if ( Options.help_flag )
405 if ( Options.version_flag || Options.help_flag )
408 if ( Options.error_flag )
410 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
414 EssenceType_t EssenceType;
415 Result_t result = ASDCP::EssenceType(Options.input_filename, EssenceType);
417 if ( ASDCP_SUCCESS(result) )
419 switch ( EssenceType )
421 case ESS_AS02_JPEG_2000:
422 result = read_JP2K_file(Options);
426 fprintf(stderr, "%s: Unknown file type, not P-HDR essence.\n", Options.input_filename);
431 if ( ASDCP_FAILURE(result) )
433 fputs("Program stopped on error.\n", stderr);
435 if ( result != RESULT_FAIL )
437 fputs(result, stderr);
449 // end phdr-unwrap.cpp