phdr
[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-2014, 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 static ASDCP::Rational
156 decode_rational(const char* str_rat)
157 {
158   assert(str_rat);
159   ui32_t Num = atoi(str_rat);
160   ui32_t Den = 0;
161
162   const char* den_str = strrchr(str_rat, '/');
163   if ( den_str != 0 )
164     Den = atoi(den_str+1);
165
166   return ASDCP::Rational(Num, Den);
167 }
168
169 //
170 //
171 class CommandOptions
172 {
173   CommandOptions();
174
175 public:
176   bool   error_flag;     // true if the given options are in error or not complete
177   bool   key_flag;       // true if an encryption key was given
178   bool   asset_id_flag;  // true if an asset ID was given
179   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
180   bool   write_hmac;     // true if HMAC values are to be generated and written
181   bool   verbose_flag;   // true if the verbose option was selected
182   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
183   bool   no_write_flag;  // true if no output files are to be written
184   bool   version_flag;   // true if the version display option was selected
185   bool   help_flag;      // true if the help display option was selected
186   ui32_t duration;       // number of frames to be processed
187   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
188   bool use_cdci_descriptor; // 
189   Rational edit_rate;    // edit rate of JP2K sequence
190   ui32_t fb_size;        // size of picture frame buffer
191   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
192   bool   key_id_flag;    // true if a key ID was given
193   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
194   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
195   std::string out_file; //
196   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
197   Kumu::PathList_t filenames;  // list of filenames to be processed
198
199   UL picture_coding;
200   ui32_t rgba_MaxRef;
201   ui32_t rgba_MinRef;
202
203   ui32_t horizontal_subsampling;
204   ui32_t vertical_subsampling;
205   ui32_t component_depth;
206   ui8_t frame_layout;
207   ASDCP::Rational aspect_ratio;
208   ui8_t field_dominance;
209   ui32_t mxf_header_size;
210
211   //new attributes for AS-02 support 
212   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
213   ui32_t partition_space; //Shim parameter partition_spacing
214
215   std::string PHDR_master_metadata; //
216
217   //
218   CommandOptions(int argc, const char** argv) :
219     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
220     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
221     no_write_flag(false), version_flag(false), help_flag(false),
222     duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
223     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
224     rgba_MaxRef(1023), rgba_MinRef(0),
225     horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
226     frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
227     mxf_header_size(16384)
228   {
229     memset(key_value, 0, KeyLen);
230     memset(key_id_value, 0, UUIDlen);
231
232     for ( int i = 1; i < argc; i++ )
233       {
234
235         if ( (strcmp( argv[i], "-help") == 0) )
236           {
237             help_flag = true;
238             continue;
239           }
240          
241         if ( argv[i][0] == '-'
242              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
243              && argv[i][2] == 0 )
244           {
245             switch ( argv[i][1] )
246               {
247               case 'A':
248                 TEST_EXTRA_ARG(i, 'A');
249                 edit_rate = decode_rational(argv[i]);
250                 break;
251
252               case 'a':
253                 asset_id_flag = true;
254                 TEST_EXTRA_ARG(i, 'a');
255                 {
256                   ui32_t length;
257                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
258
259                   if ( length != UUIDlen )
260                     {
261                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
262                       return;
263                     }
264                 }
265                 break;
266
267               case 'b':
268                 TEST_EXTRA_ARG(i, 'b');
269                 fb_size = abs(atoi(argv[i]));
270
271                 if ( verbose_flag )
272                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
273
274                 break;
275
276               case 'D':
277                 TEST_EXTRA_ARG(i, 'D');
278                 component_depth = abs(atoi(argv[i]));
279                 break;
280
281               case 'd':
282                 TEST_EXTRA_ARG(i, 'd');
283                 duration = abs(atoi(argv[i]));
284                 break;
285
286               case 'E': encrypt_header_flag = false; break;
287               case 'e': encrypt_header_flag = true; break;
288
289               case 'F':
290                 TEST_EXTRA_ARG(i, 'F');
291                 field_dominance = abs(atoi(argv[i]));
292                 if ( field_dominance > 1 )
293                   {
294                     fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
295                     return;
296                   }
297                 break;
298
299               case 'h': help_flag = true; break;
300
301               case 'i':
302                 frame_layout = 1;
303                 use_cdci_descriptor = true;
304                 break;
305
306               case 'j':
307                 key_id_flag = true;
308                 TEST_EXTRA_ARG(i, 'j');
309                 {
310                   ui32_t length;
311                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
312
313                   if ( length != UUIDlen )
314                     {
315                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
316                       return;
317                     }
318                 }
319                 break;
320
321               case 'k': key_flag = true;
322                 TEST_EXTRA_ARG(i, 'k');
323                 {
324                   ui32_t length;
325                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
326
327                   if ( length != KeyLen )
328                     {
329                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
330                       return;
331                     }
332                 }
333                 break;
334
335               case 'M': write_hmac = false; break;
336
337               case 'm':
338                 TEST_EXTRA_ARG(i, 'm');
339                 if ( KM_FAILURE(Kumu::ReadFileIntoString(argv[i], PHDR_master_metadata) ) )
340                   {
341                     fprintf(stderr, "Unable to read metadata file %s\n", argv[i]);
342                     return;
343                   }
344                 break;
345
346               case 'p':
347                 TEST_EXTRA_ARG(i, 'p');
348                 if ( ! picture_coding.DecodeHex(argv[i]) )
349                   {
350                     fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
351                     return;
352                   }
353                 break;
354
355               case 'r':
356                 TEST_EXTRA_ARG(i, 'r');
357                 edit_rate = decode_rational(argv[i]);
358                 break;
359
360               case 'R':
361                 use_cdci_descriptor = false;
362                 break;
363
364               case 's':
365                 TEST_EXTRA_ARG(i, 's');
366                 partition_space = abs(atoi(argv[i]));
367                 break;
368
369               case 't':
370                 TEST_EXTRA_ARG(i, 't');
371                 rgba_MinRef = abs(atoi(argv[i]));
372                 break;
373
374               case 'T':
375                 TEST_EXTRA_ARG(i, 'T');
376                 rgba_MaxRef = abs(atoi(argv[i]));
377                 break;
378
379               case 'u': show_ul_values_flag = true; break;
380               case 'V': version_flag = true; break;
381               case 'v': verbose_flag = true; break;
382               case 'W': no_write_flag = true; break;
383
384               case 'x':
385                 TEST_EXTRA_ARG(i, 'x');
386                 horizontal_subsampling = abs(atoi(argv[i]));
387                 break;
388
389               case 'X':
390                 TEST_EXTRA_ARG(i, 'X');
391                 vertical_subsampling = abs(atoi(argv[i]));
392                 break;
393
394               case 'Y':
395                 use_cdci_descriptor = true;
396                 break;
397
398               case 'Z': j2c_pedantic = false; break;
399               case 'z': j2c_pedantic = true; break;
400
401               default:
402                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
403                 return;
404               }
405           }
406         else
407           {
408
409             if ( argv[i][0] != '-' )
410               {
411                 filenames.push_back(argv[i]);
412               }
413             else
414               {
415                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
416                 return;
417               }
418           }
419       }
420
421     if ( help_flag || version_flag )
422       return;
423     
424     if ( filenames.size() < 2 )
425       {
426         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
427         return;
428       }
429
430     out_file = filenames.back();
431     filenames.pop_back();
432
433     if ( ! picture_coding.HasValue() )
434       {
435         picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
436       }
437
438     error_flag = false;
439   }
440 };
441
442
443 //------------------------------------------------------------------------------------------
444 // JPEG 2000 essence
445
446 namespace ASDCP {
447   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
448                             const ASDCP::Dictionary& dict,
449                             ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
450                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
451 }
452
453 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
454 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
455 //
456 Result_t
457 write_JP2K_file(CommandOptions& Options)
458 {
459   AS_02::PHDR::MXFWriter  Writer;
460   AS_02::PHDR::FrameBuffer FrameBuffer(Options.fb_size);
461   AS_02::PHDR::SequenceParser Parser;
462
463   AESEncContext* Context = 0;
464   HMACContext* HMAC = 0;
465   byte_t IV_buf[CBC_BLOCK_SIZE];
466   Kumu::FortunaRNG RNG;
467
468   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
469   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
470
471   // set up essence parser
472   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
473
474   // set up MXF writer
475   if ( ASDCP_SUCCESS(result) )
476     {
477       ASDCP::JP2K::PictureDescriptor PDesc;
478       Parser.FillPictureDescriptor(PDesc);
479       PDesc.EditRate = Options.edit_rate;
480
481       if ( Options.verbose_flag )
482         {
483           fprintf(stderr, "JPEG 2000 P-HDR pictures\n");
484           fputs("PictureDescriptor:\n", stderr);
485           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
486           JP2K::PictureDescriptorDump(PDesc);
487         }
488
489       if ( Options.use_cdci_descriptor )
490         {
491           ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
492           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
493           
494           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
495                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
496                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
497
498           if ( ASDCP_SUCCESS(result) )
499             {
500               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
501               tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
502               tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
503               tmp_dscr->ComponentDepth = Options.component_depth;
504               tmp_dscr->FrameLayout = Options.frame_layout;
505               tmp_dscr->AspectRatio = Options.aspect_ratio;
506               tmp_dscr->FieldDominance = Options.field_dominance;
507               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
508             }
509         }
510       else
511         { // use RGB
512           ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
513           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
514           
515           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
516                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
517                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
518
519           if ( ASDCP_SUCCESS(result) )
520             {
521               tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
522               tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
523               tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
524               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
525             }
526         }
527     }
528
529   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
530     {
531       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
532       Info.LabelSetType = LS_MXF_SMPTE;
533
534       if ( Options.asset_id_flag )
535         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
536       else
537         Kumu::GenRandomUUID(Info.AssetUUID);
538
539       // configure encryption
540       if( Options.key_flag )
541         {
542           Kumu::GenRandomUUID(Info.ContextID);
543           Info.EncryptedEssence = true;
544
545           if ( Options.key_id_flag )
546             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
547           else
548             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
549
550           Context = new AESEncContext;
551           result = Context->InitKey(Options.key_value);
552
553           if ( ASDCP_SUCCESS(result) )
554             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
555
556           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
557             {
558               Info.UsesHMAC = true;
559               HMAC = new HMACContext;
560               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
561             }
562         }
563
564       if ( ASDCP_SUCCESS(result) )
565         {
566           result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
567                                     Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
568         }
569     }
570
571   if ( ASDCP_SUCCESS(result) )
572     {
573       ui32_t duration = 0;
574       result = Parser.Reset();
575
576       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
577         {
578           result = Parser.ReadFrame(FrameBuffer);
579           
580           if ( ASDCP_SUCCESS(result) )
581             {
582               if ( Options.verbose_flag )
583                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
584               
585               if ( Options.encrypt_header_flag )
586                 FrameBuffer.PlaintextOffset(0);
587             }
588
589           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
590             {
591               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
592
593               // The Writer class will forward the last block of ciphertext
594               // to the encryption context for use as the IV for the next
595               // frame. If you want to use non-sequitur IV values, un-comment
596               // the following  line of code.
597               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
598               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
599             }
600         }
601
602       if ( result == RESULT_ENDOFFILE )
603         result = RESULT_OK;
604     }
605
606   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
607     result = Writer.Finalize(Options.PHDR_master_metadata);
608
609   return result;
610 }
611
612 //
613 int
614 main(int argc, const char** argv)
615 {
616   Result_t result = RESULT_OK;
617   char     str_buf[64];
618   g_dict = &ASDCP::DefaultSMPTEDict();
619   assert(g_dict);
620
621   CommandOptions Options(argc, argv);
622
623   if ( Options.version_flag )
624     banner();
625
626   if ( Options.help_flag )
627     usage();
628
629   if ( Options.show_ul_values_flag )
630     {
631       g_dict->Dump(stdout);
632     }
633
634   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
635     return 0;
636
637   if ( Options.error_flag )
638     {
639       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
640       return 3;
641     }
642
643   EssenceType_t EssenceType;
644   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
645
646   if ( ASDCP_SUCCESS(result) )
647     {
648       switch ( EssenceType )
649         {
650         case ESS_JPEG_2000:
651           result = write_JP2K_file(Options);
652           break;
653
654         default:
655           fprintf(stderr, "%s: Unknown file type, not P-HDR-compatible essence.\n",
656                   Options.filenames.front().c_str());
657           return 5;
658         }
659     }
660
661   if ( ASDCP_FAILURE(result) )
662     {
663       fputs("Program stopped on error.\n", stderr);
664
665       if ( result != RESULT_FAIL )
666         {
667           fputs(result, stderr);
668           fputc('\n', stderr);
669         }
670
671       return 1;
672     }
673
674   return 0;
675 }
676
677
678 //
679 // end phdr-wrap.cpp
680 //