Add call to parent constructor.
[asdcplib-cth.git] / src / phdr-unwrap.cpp
1 /*
2 Copyright (c) 2011-2014, John Hurst
3
4 All rights reserved.
5
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
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.
16
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.
27 */
28 /*! \file    phdr-unwrap.cpp
29     \version $Id: phdr-unwrap.cpp,v 1.6 2015/10/07 16:41:23 jhurst Exp $       
30     \brief   prototype unwrapping for HDR images in AS-02
31
32   This program extracts picture (P-HDR picture) from an AS-02 MXF file.
33 */
34
35 #include <KM_fileio.h>
36 #include <AS_02_PHDR.h>
37
38 using namespace ASDCP;
39
40 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
41
42 //------------------------------------------------------------------------------------------
43 //
44 // command line option parser class
45
46 static const char* PROGRAM_NAME = "as-02-unwrap";  // program name for messages
47
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));       \
54     return;                                                             \
55   }
56
57 //
58 void
59 banner(FILE* stream = stdout)
60 {
61   fprintf(stream, "\n\
62 %s (asdcplib %s)\n\n\
63 Copyright (c) 2011-2015, 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);
68 }
69
70 //
71 void
72 usage(FILE* stream = stdout)
73 {
74   fprintf(stream, "\
75 USAGE: %s [-h|-help] [-V]\n\
76 \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);
81
82   fprintf(stream, "\
83 Options:\n\
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   -f <start-frame>  - Starting frame number, default 0\n\
88   -h | -help        - Show help\n\
89   -k <key-string>   - Use key for ciphertext operations\n\
90   -m                - verify HMAC values when reading\n\
91   -s <size>         - Number of bytes to dump to output when -v is given\n\
92   -V                - Show version information\n\
93   -v                - Verbose, prints informative messages to stderr\n\
94   -W                - Read input file only, do not write destination file\n\
95   -w <width>        - Width of numeric element in a series of frame file names\n\
96                       (default 6)\n\
97 \n\
98   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
99          o All option arguments must be separated from the option by whitespace.\n\n");
100 }
101
102 //
103 class CommandOptions
104 {
105   CommandOptions();
106
107 public:
108   bool   error_flag;     // true if the given options are in error or not complete
109   bool   key_flag;       // true if an encryption key was given
110   bool   read_hmac;      // true if HMAC values are to be validated
111   bool   verbose_flag;   // true if the verbose option was selected
112   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
113   bool   no_write_flag;  // true if no output files are to be written
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   stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
117   ui32_t number_width;   // number of digits in a serialized filename (for JPEG extract)
118   ui32_t start_frame;    // frame number to begin processing
119   ui32_t duration;       // number of frames to be processed
120   bool   duration_flag;  // true if duration argument given
121   ui32_t fb_size;        // size of picture frame buffer
122   const char* file_prefix; // filename pre for files written by the extract mode
123   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
124   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
125   const char* input_filename;
126   std::string prefix_buffer;
127
128   //
129   CommandOptions(int argc, const char** argv) :
130     error_flag(true), key_flag(false), read_hmac(false), verbose_flag(false),
131     fb_dump_size(0), no_write_flag(false),
132     version_flag(false), help_flag(false), number_width(6),
133     start_frame(0), duration(0xffffffff), duration_flag(false),
134     fb_size(FRAME_BUFFER_SIZE), file_prefix(0),
135     input_filename(0)
136   {
137     memset(key_value, 0, KeyLen);
138     memset(key_id_value, 0, UUIDlen);
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 'b':
156                 TEST_EXTRA_ARG(i, 'b');
157                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
158
159                 if ( verbose_flag )
160                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
161
162                 break;
163
164               case 'd':
165                 TEST_EXTRA_ARG(i, 'd');
166                 duration_flag = true;
167                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
168                 break;
169
170               case 'f':
171                 TEST_EXTRA_ARG(i, 'f');
172                 start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
173                 break;
174
175               case 'h': help_flag = true; break;
176               case 'm': read_hmac = true; break;
177
178               case 's':
179                 TEST_EXTRA_ARG(i, 's');
180                 fb_dump_size = Kumu::xabs(strtol(argv[i], 0, 10));
181                 break;
182
183               case 'V': version_flag = true; break;
184               case 'v': verbose_flag = true; break;
185               case 'W': no_write_flag = true; break;
186
187               case 'w':
188                 TEST_EXTRA_ARG(i, 'w');
189                 number_width = Kumu::xabs(strtol(argv[i], 0, 10));
190                 break;
191
192               default:
193                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
194                 return;
195               }
196           }
197         else
198           {
199             if ( argv[i][0] != '-' )
200               {
201                 if ( input_filename == 0 )
202                   {
203                     input_filename = argv[i];
204                   }
205                 else if ( file_prefix == 0 )
206                   {
207                     file_prefix = argv[i];
208                   }
209               }
210             else
211               {
212                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
213                 return;
214               }
215           }
216       }
217
218     if ( help_flag || version_flag )
219       return;
220     
221     if ( input_filename == 0 )
222       {
223         fputs("At least one filename argument is required.\n", stderr);
224         return;
225       }
226
227     if ( file_prefix == 0 )
228       {
229         prefix_buffer = Kumu::PathSetExtension(input_filename, "") + "_";
230         file_prefix = prefix_buffer.c_str();
231       }
232
233     error_flag = false;
234   }
235 };
236
237
238 //------------------------------------------------------------------------------------------
239 // JPEG 2000 essence
240
241
242 // Read one or more plaintext JPEG 2000 codestreams from a plaintext P-HDR file
243 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext P-HDR file
244 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext P-HDR file
245 //
246 Result_t
247 read_JP2K_file(CommandOptions& Options)
248 {
249   AESDecContext*     Context = 0;
250   HMACContext*       HMAC = 0;
251   AS_02::PHDR::MXFReader    Reader;
252   AS_02::PHDR::FrameBuffer  FrameBuffer(Options.fb_size);
253   ui32_t             frame_count = 0;
254
255   std::string PHDR_master_metadata; // todo: write to a file?
256
257   Result_t result = Reader.OpenRead(Options.input_filename, PHDR_master_metadata);
258   fprintf(stderr, "PHDR_master_metadata size=%zd\n", PHDR_master_metadata.size());
259
260   if ( ASDCP_SUCCESS(result) )
261     {
262       if ( Options.verbose_flag )
263         {
264           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
265         }
266
267       ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
268       ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
269
270       result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
271                                                      reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
272
273       if ( KM_SUCCESS(result) )
274         {
275           assert(rgba_descriptor);
276           frame_count = rgba_descriptor->ContainerDuration;
277
278           if ( Options.verbose_flag )
279             {
280               rgba_descriptor->Dump();
281             }
282         }
283       else
284         {
285           result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
286                                                          reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
287
288           if ( KM_SUCCESS(result) )
289             {
290               assert(cdci_descriptor);
291               frame_count = cdci_descriptor->ContainerDuration;
292
293               if ( Options.verbose_flag )
294                 {
295                   cdci_descriptor->Dump();
296                 }
297             }
298           else
299             {
300               fprintf(stderr, "File does not contain an essence descriptor.\n");
301               frame_count = Reader.AS02IndexReader().GetDuration();
302             }
303         }
304
305       if ( frame_count == 0 )
306         {
307           frame_count = Reader.AS02IndexReader().GetDuration();
308         }
309
310       if ( frame_count == 0 )
311         {
312           fprintf(stderr, "Unable to determine file duration.\n");
313           return RESULT_FAIL;
314         }
315     }
316
317   if ( ASDCP_SUCCESS(result) && Options.key_flag )
318     {
319       Context = new AESDecContext;
320       result = Context->InitKey(Options.key_value);
321
322       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
323         {
324           WriterInfo Info;
325           Reader.FillWriterInfo(Info);
326
327           if ( Info.UsesHMAC )
328             {
329               HMAC = new HMACContext;
330               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
331             }
332           else
333             {
334               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
335             }
336         }
337     }
338
339   ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
340   if ( last_frame > frame_count )
341     last_frame = frame_count;
342
343   char name_format[64];
344   snprintf(name_format,  64, "%%s%%0%du.j2c", Options.number_width);
345
346   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
347     {
348       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
349
350       char filename[1024];
351       snprintf(filename, 1024, name_format, Options.file_prefix, i);
352
353       if ( ASDCP_SUCCESS(result) && Options.verbose_flag )
354         {
355           printf("Frame %d, %d bytes", i, FrameBuffer.Size());
356
357           if ( ! Options.no_write_flag )
358             {
359               printf(" -> %s", filename);
360             }
361
362           printf("\n");
363         }
364
365       if ( ASDCP_SUCCESS(result)  && ( ! Options.no_write_flag ) )
366         {
367           Kumu::FileWriter OutFile;
368           ui32_t write_count;
369           result = OutFile.OpenWrite(filename);
370
371           if ( ASDCP_SUCCESS(result) )
372             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
373
374           if ( ASDCP_SUCCESS(result) && Options.verbose_flag )
375             {
376               FrameBuffer.Dump(stderr, Options.fb_dump_size);
377             }
378         }
379     }
380
381   return result;
382 }
383
384 //
385 int
386 main(int argc, const char** argv)
387 {
388   char     str_buf[64];
389   CommandOptions Options(argc, argv);
390
391   if ( Options.version_flag )
392     banner();
393
394   if ( Options.help_flag )
395     usage();
396
397   if ( Options.version_flag || Options.help_flag )
398     return 0;
399
400   if ( Options.error_flag )
401     {
402       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
403       return 3;
404     }
405
406   EssenceType_t EssenceType;
407   Result_t result = ASDCP::EssenceType(Options.input_filename, EssenceType);
408
409   if ( ASDCP_SUCCESS(result) )
410     {
411       switch ( EssenceType )
412         {
413         case ESS_AS02_JPEG_2000:
414           result = read_JP2K_file(Options);
415           break;
416
417         default:
418           fprintf(stderr, "%s: Unknown file type, not P-HDR essence.\n", Options.input_filename);
419           return 5;
420         }
421     }
422
423   if ( ASDCP_FAILURE(result) )
424     {
425       fputs("Program stopped on error.\n", stderr);
426
427       if ( result != RESULT_FAIL )
428         {
429           fputs(result, stderr);
430           fputc('\n', stderr);
431         }
432
433       return 1;
434     }
435
436   return 0;
437 }
438
439
440 //
441 // end phdr-unwrap.cpp
442 //