release
[asdcplib.git] / src / phdr-wrap.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-wrap.cpp
29     \version $Id$       
30     \brief   prototype wrapping for HDR images in AS-02
31
32   This program wraps IMF essence (P-HDR picture) in to an AS-02 MXF file.
33 */
34
35 #include <KM_fileio.h>
36 #include <KM_prng.h>
37 #include <AS_02_PHDR.h>
38
39 using namespace ASDCP;
40
41 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
42 const ASDCP::Dictionary *g_dict = 0;
43
44
45 const char*
46 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
47 {
48   snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
49   return buf;
50 }
51
52
53
54 //------------------------------------------------------------------------------------------
55 //
56 // command line option parser class
57
58 static const char* PROGRAM_NAME = "as-02-wrap";  // program name for messages
59
60 // local program identification info written to file headers
61 class MyInfo : public WriterInfo
62 {
63 public:
64   MyInfo()
65   {
66       static byte_t default_ProductUUID_Data[UUIDlen] =
67       { 0xef, 0xe4, 0x59, 0xab, 0xbe, 0x0f, 0x4c, 0x7d,
68         0xb3, 0xa2, 0xb8, 0x96, 0x79, 0xe2, 0x3e, 0x8e };
69       
70       memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
71       CompanyName = "WidgetCo";
72       ProductName = "phdr-wrap";
73       ProductVersion = ASDCP::Version();
74   }
75 } s_MyInfo;
76
77
78
79 // Increment the iterator, test for an additional non-option command line argument.
80 // Causes the caller to return if there are no remaining arguments or if the next
81 // argument begins with '-'.
82 #define TEST_EXTRA_ARG(i,c)                                             \
83   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
84     fprintf(stderr, "Argument not found for option -%c.\n", (c));       \
85     return;                                                             \
86   }
87
88 //
89 void
90 banner(FILE* stream = stdout)
91 {
92   fprintf(stream, "\n\
93 %s (asdcplib %s)\n\n\
94 Copyright (c) 2011-2015, John Hurst\n\n\
95 asdcplib may be copied only under the terms of the license found at\n\
96 the top of every file in the asdcplib distribution kit.\n\n\
97 Specify the -h (help) option for further information about %s\n\n",
98           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
99 }
100
101 //
102 void
103 usage(FILE* stream = stdout)
104 {
105   fprintf(stream, "\
106 USAGE: %s [-h|-help] [-V]\n\
107 \n\
108        %s [-a <uuid>] [-A <w>/<h>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
109             [-D <depth>] [-e|-E] [-i] [-j <key-id-string>] [-k <key-string>]\n\
110             [-M] [-m <expr>] [-p <ul>] [-r <n>/<d>] [-R] [-s <seconds>]\n\
111             [-t <min>] [-T <max>] [-u] [-v] [-W] [-x <int>] [-X <int>] [-Y]\n\
112             [-z|-Z] <input-file>+ <output-file>\n\n",
113           PROGRAM_NAME, PROGRAM_NAME);
114
115   fprintf(stream, "\
116 Options:\n\
117   -h | -help        - Show help\n\
118   -V                - Show version information\n\
119   -a <uuid>         - Specify the Asset ID of the file\n\
120   -A <w>/<h>        - Set aspect ratio for image (default 4/3)\n\
121   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
122                       Defaults to 4,194,304 (4MB)\n\
123   -C <ul>           - Set ChannelAssignment UL value\n\
124   -d <duration>     - Number of frames to process, default all\n\
125   -D <depth>        - Component depth for YCbCr images (default: 10)\n\
126   -e                - Encrypt JP2K headers (default)\n\
127   -E                - Do not encrypt JP2K headers\n\
128   -F (0|1)          - Set field dominance for interlaced image (default: 0)\n\
129   -i                - Indicates input essence is interlaced fields (forces -Y)\n\
130   -j <key-id-str>   - Write key ID instead of creating a random value\n\
131   -k <key-string>   - Use key for ciphertext operations\n\
132   -M                - Do not create HMAC values when writing\n\
133   -m <filename>     - Filename of master metadata instance (the contents of\n\
134                         which will be placed in the MXF wrapper)\n\
135   -p <ul>           - Set broadcast profile\n\
136   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
137   -R                - Indicates RGB image essence (default)\n\
138   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
139   -t <min>          - Set RGB component minimum code value (default: 0)\n\
140   -T <max>          - Set RGB component maximum code value (default: 1023)\n\
141   -u                - Print UL catalog to stderr\n\
142   -v                - Verbose, prints informative messages to stderr\n\
143   -W                - Read input file only, do not write source file\n\
144   -x <int>          - Horizontal subsampling degree (default: 2)\n\
145   -X <int>          - Vertical subsampling degree (default: 2)\n\
146   -Y                - Indicates YCbCr image essence (default: RGB)\n\
147   -z                - Fail if j2c inputs have unequal parameters (default)\n\
148   -Z                - Ignore unequal parameters in j2c inputs\n\
149 \n\
150   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
151          o All option arguments must be separated from the option by whitespace.\n\n");
152 }
153
154 //
155 //
156 class CommandOptions
157 {
158   CommandOptions();
159
160 public:
161   bool   error_flag;     // true if the given options are in error or not complete
162   bool   key_flag;       // true if an encryption key was given
163   bool   asset_id_flag;  // true if an asset ID was given
164   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
165   bool   write_hmac;     // true if HMAC values are to be generated and written
166   bool   verbose_flag;   // true if the verbose option was selected
167   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
168   bool   no_write_flag;  // true if no output files are to be written
169   bool   version_flag;   // true if the version display option was selected
170   bool   help_flag;      // true if the help display option was selected
171   ui32_t duration;       // number of frames to be processed
172   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
173   bool use_cdci_descriptor; // 
174   Rational edit_rate;    // edit rate of JP2K sequence
175   ui32_t fb_size;        // size of picture frame buffer
176   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
177   bool   key_id_flag;    // true if a key ID was given
178   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
179   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
180   std::string out_file; //
181   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
182   Kumu::PathList_t filenames;  // list of filenames to be processed
183
184   UL picture_coding;
185   ui32_t rgba_MaxRef;
186   ui32_t rgba_MinRef;
187
188   ui32_t horizontal_subsampling;
189   ui32_t vertical_subsampling;
190   ui32_t component_depth;
191   ui8_t frame_layout;
192   ASDCP::Rational aspect_ratio;
193   ui8_t field_dominance;
194   ui32_t mxf_header_size;
195
196   //new attributes for AS-02 support 
197   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
198   ui32_t partition_space; //Shim parameter partition_spacing
199
200   std::string PHDR_master_metadata; //
201
202   //
203   CommandOptions(int argc, const char** argv) :
204     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
205     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
206     no_write_flag(false), version_flag(false), help_flag(false),
207     duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
208     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
209     rgba_MaxRef(1023), rgba_MinRef(0),
210     horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
211     frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
212     mxf_header_size(16384)
213   {
214     memset(key_value, 0, KeyLen);
215     memset(key_id_value, 0, UUIDlen);
216
217     for ( int i = 1; i < argc; i++ )
218       {
219
220         if ( (strcmp( argv[i], "-help") == 0) )
221           {
222             help_flag = true;
223             continue;
224           }
225          
226         if ( argv[i][0] == '-'
227              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
228              && argv[i][2] == 0 )
229           {
230             switch ( argv[i][1] )
231               {
232               case 'A':
233                 TEST_EXTRA_ARG(i, 'A');
234                 if ( ! DecodeRational(argv[i], aspect_ratio) )
235                   {
236                     fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
237                     return;
238                   }
239                 break;
240
241               case 'a':
242                 asset_id_flag = true;
243                 TEST_EXTRA_ARG(i, 'a');
244                 {
245                   ui32_t length;
246                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
247
248                   if ( length != UUIDlen )
249                     {
250                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
251                       return;
252                     }
253                 }
254                 break;
255
256               case 'b':
257                 TEST_EXTRA_ARG(i, 'b');
258                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
259
260                 if ( verbose_flag )
261                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
262
263                 break;
264
265               case 'D':
266                 TEST_EXTRA_ARG(i, 'D');
267                 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
268                 break;
269
270               case 'd':
271                 TEST_EXTRA_ARG(i, 'd');
272                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
273                 break;
274
275               case 'E': encrypt_header_flag = false; break;
276               case 'e': encrypt_header_flag = true; break;
277
278               case 'F':
279                 TEST_EXTRA_ARG(i, 'F');
280                 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
281                 if ( field_dominance > 1 )
282                   {
283                     fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
284                     return;
285                   }
286                 break;
287
288               case 'h': help_flag = true; break;
289
290               case 'i':
291                 frame_layout = 1;
292                 use_cdci_descriptor = true;
293                 break;
294
295               case 'j':
296                 key_id_flag = true;
297                 TEST_EXTRA_ARG(i, 'j');
298                 {
299                   ui32_t length;
300                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
301
302                   if ( length != UUIDlen )
303                     {
304                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
305                       return;
306                     }
307                 }
308                 break;
309
310               case 'k': key_flag = true;
311                 TEST_EXTRA_ARG(i, 'k');
312                 {
313                   ui32_t length;
314                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
315
316                   if ( length != KeyLen )
317                     {
318                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
319                       return;
320                     }
321                 }
322                 break;
323
324               case 'M': write_hmac = false; break;
325
326               case 'm':
327                 TEST_EXTRA_ARG(i, 'm');
328                 if ( KM_FAILURE(Kumu::ReadFileIntoString(argv[i], PHDR_master_metadata) ) )
329                   {
330                     fprintf(stderr, "Unable to read metadata file %s\n", argv[i]);
331                     return;
332                   }
333                 break;
334
335               case 'p':
336                 TEST_EXTRA_ARG(i, 'p');
337                 if ( ! picture_coding.DecodeHex(argv[i]) )
338                   {
339                     fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
340                     return;
341                   }
342                 break;
343
344               case 'r':
345                 TEST_EXTRA_ARG(i, 'r');
346                 if ( ! DecodeRational(argv[i], edit_rate) )
347                   {
348                     fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
349                     return;
350                   }
351
352                 break;
353
354               case 'R':
355                 use_cdci_descriptor = false;
356                 break;
357
358               case 's':
359                 TEST_EXTRA_ARG(i, 's');
360                 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
361                 break;
362
363               case 't':
364                 TEST_EXTRA_ARG(i, 't');
365                 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
366                 break;
367
368               case 'T':
369                 TEST_EXTRA_ARG(i, 'T');
370                 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
371                 break;
372
373               case 'u': show_ul_values_flag = true; break;
374               case 'V': version_flag = true; break;
375               case 'v': verbose_flag = true; break;
376               case 'W': no_write_flag = true; break;
377
378               case 'x':
379                 TEST_EXTRA_ARG(i, 'x');
380                 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
381                 break;
382
383               case 'X':
384                 TEST_EXTRA_ARG(i, 'X');
385                 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
386                 break;
387
388               case 'Y':
389                 use_cdci_descriptor = true;
390                 break;
391
392               case 'Z': j2c_pedantic = false; break;
393               case 'z': j2c_pedantic = true; break;
394
395               default:
396                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
397                 return;
398               }
399           }
400         else
401           {
402
403             if ( argv[i][0] != '-' )
404               {
405                 filenames.push_back(argv[i]);
406               }
407             else
408               {
409                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
410                 return;
411               }
412           }
413       }
414
415     if ( help_flag || version_flag )
416       return;
417     
418     if ( filenames.size() < 2 )
419       {
420         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
421         return;
422       }
423
424     out_file = filenames.back();
425     filenames.pop_back();
426
427     if ( ! picture_coding.HasValue() )
428       {
429         picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
430       }
431
432     error_flag = false;
433   }
434 };
435
436
437 //------------------------------------------------------------------------------------------
438 // JPEG 2000 essence
439
440 namespace ASDCP {
441   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
442                             const ASDCP::Dictionary& dict,
443                             ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
444                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
445 }
446
447 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
448 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
449 //
450 Result_t
451 write_JP2K_file(CommandOptions& Options)
452 {
453   AS_02::PHDR::MXFWriter  Writer;
454   AS_02::PHDR::FrameBuffer FrameBuffer(Options.fb_size);
455   AS_02::PHDR::SequenceParser Parser;
456
457   AESEncContext* Context = 0;
458   HMACContext* HMAC = 0;
459   byte_t IV_buf[CBC_BLOCK_SIZE];
460   Kumu::FortunaRNG RNG;
461
462   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
463   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
464
465   // set up essence parser
466   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
467
468   // set up MXF writer
469   if ( ASDCP_SUCCESS(result) )
470     {
471       ASDCP::JP2K::PictureDescriptor PDesc;
472       Parser.FillPictureDescriptor(PDesc);
473       PDesc.EditRate = Options.edit_rate;
474
475       if ( Options.verbose_flag )
476         {
477           fprintf(stderr, "JPEG 2000 P-HDR pictures\n");
478           fputs("PictureDescriptor:\n", stderr);
479           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
480           JP2K::PictureDescriptorDump(PDesc);
481         }
482
483       if ( Options.use_cdci_descriptor )
484         {
485           ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
486           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
487           
488           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
489                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
490                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
491
492           if ( ASDCP_SUCCESS(result) )
493             {
494               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
495               tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
496               tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
497               tmp_dscr->ComponentDepth = Options.component_depth;
498               tmp_dscr->FrameLayout = Options.frame_layout;
499               tmp_dscr->AspectRatio = Options.aspect_ratio;
500               tmp_dscr->FieldDominance = Options.field_dominance;
501               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
502             }
503         }
504       else
505         { // use RGB
506           ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
507           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
508           
509           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
510                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
511                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
512
513           if ( ASDCP_SUCCESS(result) )
514             {
515               tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
516               tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
517               tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
518               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
519             }
520         }
521     }
522
523   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
524     {
525       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
526       Info.LabelSetType = LS_MXF_SMPTE;
527
528       if ( Options.asset_id_flag )
529         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
530       else
531         Kumu::GenRandomUUID(Info.AssetUUID);
532
533       // configure encryption
534       if( Options.key_flag )
535         {
536           Kumu::GenRandomUUID(Info.ContextID);
537           Info.EncryptedEssence = true;
538
539           if ( Options.key_id_flag )
540             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
541           else
542             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
543
544           Context = new AESEncContext;
545           result = Context->InitKey(Options.key_value);
546
547           if ( ASDCP_SUCCESS(result) )
548             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
549
550           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
551             {
552               Info.UsesHMAC = true;
553               HMAC = new HMACContext;
554               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
555             }
556         }
557
558       if ( ASDCP_SUCCESS(result) )
559         {
560           result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
561                                     Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
562         }
563     }
564
565   if ( ASDCP_SUCCESS(result) )
566     {
567       ui32_t duration = 0;
568       result = Parser.Reset();
569
570       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
571         {
572           result = Parser.ReadFrame(FrameBuffer);
573           
574           if ( ASDCP_SUCCESS(result) )
575             {
576               if ( Options.verbose_flag )
577                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
578               
579               if ( Options.encrypt_header_flag )
580                 FrameBuffer.PlaintextOffset(0);
581             }
582
583           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
584             {
585               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
586
587               // The Writer class will forward the last block of ciphertext
588               // to the encryption context for use as the IV for the next
589               // frame. If you want to use non-sequitur IV values, un-comment
590               // the following  line of code.
591               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
592               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
593             }
594         }
595
596       if ( result == RESULT_ENDOFFILE )
597         result = RESULT_OK;
598     }
599
600   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
601     result = Writer.Finalize(Options.PHDR_master_metadata);
602
603   return result;
604 }
605
606 //
607 int
608 main(int argc, const char** argv)
609 {
610   Result_t result = RESULT_OK;
611   char     str_buf[64];
612   g_dict = &ASDCP::DefaultSMPTEDict();
613   assert(g_dict);
614
615   CommandOptions Options(argc, argv);
616
617   if ( Options.version_flag )
618     banner();
619
620   if ( Options.help_flag )
621     usage();
622
623   if ( Options.show_ul_values_flag )
624     {
625       g_dict->Dump(stdout);
626     }
627
628   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
629     return 0;
630
631   if ( Options.error_flag )
632     {
633       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
634       return 3;
635     }
636
637   EssenceType_t EssenceType;
638   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
639
640   if ( ASDCP_SUCCESS(result) )
641     {
642       switch ( EssenceType )
643         {
644         case ESS_JPEG_2000:
645           result = write_JP2K_file(Options);
646           break;
647
648         default:
649           fprintf(stderr, "%s: Unknown file type, not P-HDR-compatible essence.\n",
650                   Options.filenames.front().c_str());
651           return 5;
652         }
653     }
654
655   if ( ASDCP_FAILURE(result) )
656     {
657       fputs("Program stopped on error.\n", stderr);
658
659       if ( result != RESULT_FAIL )
660         {
661           fputs(result, stderr);
662           fputc('\n', stderr);
663         }
664
665       return 1;
666     }
667
668   return 0;
669 }
670
671
672 //
673 // end phdr-wrap.cpp
674 //