o Added support for IMF Numbered Source Channel labels in MCA
[asdcplib.git] / src / phdr-wrap.cpp
1 /*
2 Copyright (c) 2011-2016, 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 static void
90 create_random_uuid(byte_t* uuidbuf)
91 {
92   Kumu::UUID tmp_id;
93   GenRandomValue(tmp_id);
94   memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
95 }
96
97 //
98 void
99 banner(FILE* stream = stdout)
100 {
101   fprintf(stream, "\n\
102 %s (asdcplib %s)\n\n\
103 Copyright (c) 2011-2015, John Hurst\n\n\
104 asdcplib may be copied only under the terms of the license found at\n\
105 the top of every file in the asdcplib distribution kit.\n\n\
106 Specify the -h (help) option for further information about %s\n\n",
107           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
108 }
109
110 //
111 void
112 usage(FILE* stream = stdout)
113 {
114   fprintf(stream, "\
115 USAGE: %s [-h|-help] [-V]\n\
116 \n\
117        %s [-a <uuid>] [-A <w>/<h>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
118             [-D <depth>] [-e|-E] [-i] [-j <key-id-string>] [-k <key-string>]\n\
119             [-M] [-m <expr>] [-p <ul>] [-r <n>/<d>] [-R] [-s <seconds>]\n\
120             [-t <min>] [-T <max>] [-u] [-v] [-W] [-x <int>] [-X <int>] [-Y]\n\
121             [-z|-Z] <input-file>+ <output-file>\n\n",
122           PROGRAM_NAME, PROGRAM_NAME);
123
124   fprintf(stream, "\
125 Options:\n\
126   -h | -help        - Show help\n\
127   -V                - Show version information\n\
128   -a <uuid>         - Specify the Asset ID of the file\n\
129   -A <w>/<h>        - Set aspect ratio for image (default 4/3)\n\
130   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
131                       Defaults to 4,194,304 (4MB)\n\
132   -C <ul>           - Set ChannelAssignment UL value\n\
133   -d <duration>     - Number of frames to process, default all\n\
134   -D <depth>        - Component depth for YCbCr images (default: 10)\n\
135   -e                - Encrypt JP2K headers (default)\n\
136   -E                - Do not encrypt JP2K headers\n\
137   -F (0|1)          - Set field dominance for interlaced image (default: 0)\n\
138   -g <filename>     - Write global metadata from the named PIDM file.\n\
139   -i                - Indicates input essence is interlaced fields (forces -Y)\n\
140   -j <key-id-str>   - Write key ID instead of creating a random value\n\
141   -k <key-string>   - Use key for ciphertext operations\n\
142   -M                - Do not create HMAC values when writing\n\
143   -m <filename>     - Filename of master metadata instance (the contents of\n\
144                         which will be placed in the MXF wrapper)\n\
145   -p <ul>           - Set broadcast profile\n\
146   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
147   -R                - Indicates RGB image essence (default)\n\
148   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
149   -t <min>          - Set RGB component minimum code value (default: 0)\n\
150   -T <max>          - Set RGB component maximum code value (default: 1023)\n\
151   -u                - Print UL catalog to stderr\n\
152   -U <UL>           - Set DataEssenceCoding UL value in an Aux Data file\n\
153   -v                - Verbose, prints informative messages to stderr\n\
154   -W                - Read input file only, do not write source file\n\
155   -x <int>          - Horizontal subsampling degree (default: 2)\n\
156   -X <int>          - Vertical subsampling degree (default: 2)\n\
157   -Y                - Indicates YCbCr image essence (default: RGB)\n\
158   -z                - Fail if j2c inputs have unequal parameters (default)\n\
159   -Z                - Ignore unequal parameters in j2c inputs\n\
160 \n\
161   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
162          o All option arguments must be separated from the option by whitespace.\n\n");
163 }
164
165 //
166 //
167 class CommandOptions
168 {
169   CommandOptions();
170
171 public:
172   bool   error_flag;     // true if the given options are in error or not complete
173   bool   key_flag;       // true if an encryption key was given
174   bool   asset_id_flag;  // true if an asset ID was given
175   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
176   bool   write_hmac;     // true if HMAC values are to be generated and written
177   bool   verbose_flag;   // true if the verbose option was selected
178   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
179   bool   no_write_flag;  // true if no output files are to be written
180   bool   version_flag;   // true if the version display option was selected
181   bool   help_flag;      // true if the help display option was selected
182   ui32_t duration;       // number of frames to be processed
183   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
184   bool use_cdci_descriptor; // 
185   Rational edit_rate;    // edit rate of JP2K sequence
186   ui32_t fb_size;        // size of picture frame buffer
187   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
188   bool   key_id_flag;    // true if a key ID was given
189   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
190   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
191   std::string out_file; //
192   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
193   Kumu::PathList_t filenames;  // list of filenames to be processed
194
195   UL picture_coding;
196   ui32_t rgba_MaxRef;
197   ui32_t rgba_MinRef;
198
199   ui32_t horizontal_subsampling;
200   ui32_t vertical_subsampling;
201   ui32_t component_depth;
202   ui8_t frame_layout;
203   ASDCP::Rational aspect_ratio;
204   ui8_t field_dominance;
205   ui32_t mxf_header_size;
206
207   //new attributes for AS-02 support 
208   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
209   ui32_t partition_space; //Shim parameter partition_spacing
210
211   std::string PHDR_master_metadata;
212   UL aux_data_coding;
213   std::string global_metadata_filename;
214
215   //
216   CommandOptions(int argc, const char** argv) :
217     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
218     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
219     no_write_flag(false), version_flag(false), help_flag(false),
220     duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
221     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
222     rgba_MaxRef(1023), rgba_MinRef(0),
223     horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
224     frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
225     mxf_header_size(16384)
226   {
227     memset(key_value, 0, KeyLen);
228     memset(key_id_value, 0, UUIDlen);
229
230     for ( int i = 1; i < argc; i++ )
231       {
232
233         if ( (strcmp( argv[i], "-help") == 0) )
234           {
235             help_flag = true;
236             continue;
237           }
238          
239         if ( argv[i][0] == '-'
240              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
241              && argv[i][2] == 0 )
242           {
243             switch ( argv[i][1] )
244               {
245               case 'A':
246                 TEST_EXTRA_ARG(i, 'A');
247                 if ( ! DecodeRational(argv[i], aspect_ratio) )
248                   {
249                     fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
250                     return;
251                   }
252                 break;
253
254               case 'a':
255                 asset_id_flag = true;
256                 TEST_EXTRA_ARG(i, 'a');
257                 {
258                   ui32_t length;
259                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
260
261                   if ( length != UUIDlen )
262                     {
263                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
264                       return;
265                     }
266                 }
267                 break;
268
269               case 'b':
270                 TEST_EXTRA_ARG(i, 'b');
271                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
272
273                 if ( verbose_flag )
274                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
275
276                 break;
277
278               case 'D':
279                 TEST_EXTRA_ARG(i, 'D');
280                 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
281                 break;
282
283               case 'd':
284                 TEST_EXTRA_ARG(i, 'd');
285                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
286                 break;
287
288               case 'E': encrypt_header_flag = false; break;
289               case 'e': encrypt_header_flag = true; break;
290
291               case 'F':
292                 TEST_EXTRA_ARG(i, 'F');
293                 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
294                 if ( field_dominance > 1 )
295                   {
296                     fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
297                     return;
298                   }
299                 break;
300
301               case 'g':
302                 TEST_EXTRA_ARG(i, 'g');
303                 global_metadata_filename = argv[i];
304                 break;
305
306               case 'h': help_flag = true; break;
307
308               case 'i':
309                 frame_layout = 1;
310                 use_cdci_descriptor = true;
311                 break;
312
313               case 'j':
314                 key_id_flag = true;
315                 TEST_EXTRA_ARG(i, 'j');
316                 {
317                   ui32_t length;
318                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
319
320                   if ( length != UUIDlen )
321                     {
322                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
323                       return;
324                     }
325                 }
326                 break;
327
328               case 'k': key_flag = true;
329                 TEST_EXTRA_ARG(i, 'k');
330                 {
331                   ui32_t length;
332                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
333
334                   if ( length != KeyLen )
335                     {
336                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
337                       return;
338                     }
339                 }
340                 break;
341
342               case 'M': write_hmac = false; break;
343
344               case 'm':
345                 TEST_EXTRA_ARG(i, 'm');
346                 if ( KM_FAILURE(Kumu::ReadFileIntoString(argv[i], PHDR_master_metadata) ) )
347                   {
348                     fprintf(stderr, "Unable to read metadata file %s\n", argv[i]);
349                     return;
350                   }
351                 break;
352
353               case 'p':
354                 TEST_EXTRA_ARG(i, 'p');
355                 if ( ! picture_coding.DecodeHex(argv[i]) )
356                   {
357                     fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
358                     return;
359                   }
360                 break;
361
362               case 'r':
363                 TEST_EXTRA_ARG(i, 'r');
364                 if ( ! DecodeRational(argv[i], edit_rate) )
365                   {
366                     fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
367                     return;
368                   }
369
370                 break;
371
372               case 'R':
373                 use_cdci_descriptor = false;
374                 break;
375
376               case 's':
377                 TEST_EXTRA_ARG(i, 's');
378                 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
379                 break;
380
381               case 't':
382                 TEST_EXTRA_ARG(i, 't');
383                 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
384                 break;
385
386               case 'T':
387                 TEST_EXTRA_ARG(i, 'T');
388                 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
389                 break;
390
391               case 'u': show_ul_values_flag = true; break;
392
393               case 'U':
394                 TEST_EXTRA_ARG(i, 'U');
395                 if ( ! aux_data_coding.DecodeHex(argv[i]) )
396                   {
397                     fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
398                     return;
399                   }
400                 break;
401
402               case 'V': version_flag = true; break;
403               case 'v': verbose_flag = true; break;
404               case 'W': no_write_flag = true; break;
405
406               case 'x':
407                 TEST_EXTRA_ARG(i, 'x');
408                 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
409                 break;
410
411               case 'X':
412                 TEST_EXTRA_ARG(i, 'X');
413                 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
414                 break;
415
416               case 'Y':
417                 use_cdci_descriptor = true;
418                 break;
419
420               case 'Z': j2c_pedantic = false; break;
421               case 'z': j2c_pedantic = true; break;
422
423               default:
424                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
425                 return;
426               }
427           }
428         else
429           {
430
431             if ( argv[i][0] != '-' )
432               {
433                 filenames.push_back(argv[i]);
434               }
435             else
436               {
437                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
438                 return;
439               }
440           }
441       }
442
443     if ( help_flag || version_flag )
444       return;
445     
446     if ( filenames.size() < 2 )
447       {
448         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
449         return;
450       }
451
452     out_file = filenames.back();
453     filenames.pop_back();
454
455     if ( ! picture_coding.HasValue() )
456       {
457         picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
458       }
459
460     error_flag = false;
461   }
462 };
463
464
465 //------------------------------------------------------------------------------------------
466 // JPEG 2000 essence
467
468 namespace ASDCP {
469   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
470                             const ASDCP::Dictionary& dict,
471                             ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
472                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
473 }
474
475 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
476 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
477 //
478 Result_t
479 write_JP2K_file(CommandOptions& Options)
480 {
481   AS_02::PHDR::MXFWriter  Writer;
482   AS_02::PHDR::FrameBuffer FrameBuffer(Options.fb_size);
483   AS_02::PHDR::SequenceParser Parser;
484
485   AESEncContext* Context = 0;
486   HMACContext* HMAC = 0;
487   byte_t IV_buf[CBC_BLOCK_SIZE];
488   Kumu::FortunaRNG RNG;
489
490   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
491   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
492
493   // set up essence parser
494   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
495
496   // set up MXF writer
497   if ( ASDCP_SUCCESS(result) )
498     {
499       ASDCP::JP2K::PictureDescriptor PDesc;
500       Parser.FillPictureDescriptor(PDesc);
501       PDesc.EditRate = Options.edit_rate;
502
503       if ( Options.verbose_flag )
504         {
505           fprintf(stderr, "JPEG 2000 P-HDR pictures\n");
506           fputs("PictureDescriptor:\n", stderr);
507           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
508           JP2K::PictureDescriptorDump(PDesc);
509         }
510
511       if ( Options.use_cdci_descriptor )
512         {
513           ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
514           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
515           
516           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
517                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
518                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
519
520           if ( ASDCP_SUCCESS(result) )
521             {
522               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
523               tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
524               tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
525               tmp_dscr->ComponentDepth = Options.component_depth;
526               tmp_dscr->FrameLayout = Options.frame_layout;
527               tmp_dscr->AspectRatio = Options.aspect_ratio;
528               tmp_dscr->FieldDominance = Options.field_dominance;
529               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
530             }
531         }
532       else
533         { // use RGB
534           ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
535           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
536           
537           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
538                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
539                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
540
541           if ( ASDCP_SUCCESS(result) )
542             {
543               tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
544               tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
545               tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
546               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
547             }
548         }
549     }
550
551   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
552     {
553       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
554       Info.LabelSetType = LS_MXF_SMPTE;
555
556       if ( Options.asset_id_flag )
557         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
558       else
559         Kumu::GenRandomUUID(Info.AssetUUID);
560
561       // configure encryption
562       if( Options.key_flag )
563         {
564           Kumu::GenRandomUUID(Info.ContextID);
565           Info.EncryptedEssence = true;
566
567           if ( Options.key_id_flag )
568             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
569           else
570             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
571
572           Context = new AESEncContext;
573           result = Context->InitKey(Options.key_value);
574
575           if ( ASDCP_SUCCESS(result) )
576             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
577
578           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
579             {
580               Info.UsesHMAC = true;
581               HMAC = new HMACContext;
582               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
583             }
584         }
585
586       if ( ASDCP_SUCCESS(result) )
587         {
588           result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
589                                     Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
590         }
591     }
592
593   if ( ASDCP_SUCCESS(result) )
594     {
595       ui32_t duration = 0;
596       result = Parser.Reset();
597
598       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
599         {
600           result = Parser.ReadFrame(FrameBuffer);
601           
602           if ( ASDCP_SUCCESS(result) )
603             {
604               if ( Options.verbose_flag )
605                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
606               
607               if ( Options.encrypt_header_flag )
608                 FrameBuffer.PlaintextOffset(0);
609             }
610
611           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
612             {
613               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
614
615               // The Writer class will forward the last block of ciphertext
616               // to the encryption context for use as the IV for the next
617               // frame. If you want to use non-sequitur IV values, un-comment
618               // the following  line of code.
619               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
620               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
621             }
622         }
623
624       if ( result == RESULT_ENDOFFILE )
625         result = RESULT_OK;
626     }
627
628   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
629     result = Writer.Finalize(Options.PHDR_master_metadata);
630
631   return result;
632 }
633
634
635
636 // Write one or more plaintext Aux Data bytestreams to a plaintext AS-02 file
637 // Write one or more plaintext Aux Data bytestreams to a ciphertext AS-02 file
638 //
639 Result_t
640 write_aux_data_file(CommandOptions& Options)
641 {
642   AESEncContext*          Context = 0;
643   HMACContext*            HMAC = 0;
644   AS_02::PIDM::MXFWriter Writer;
645   DCData::FrameBuffer     FrameBuffer(Options.fb_size);
646   DCData::SequenceParser  Parser;
647   byte_t                  IV_buf[CBC_BLOCK_SIZE];
648   Kumu::FortunaRNG        RNG;
649
650   if ( ! Options.global_metadata_filename.empty() )
651     {
652       if ( ! Kumu::PathIsFile(Options.global_metadata_filename) )
653         {
654           fprintf(stderr, "No such file or filename: \"%s\".\n", Options.global_metadata_filename.c_str());
655           return RESULT_PARAM;
656         }
657     }
658
659   // set up essence parser
660   Result_t result = Parser.OpenRead(Options.filenames.front());
661
662   // set up MXF writer
663   if ( ASDCP_SUCCESS(result) )
664   {
665
666     if ( Options.verbose_flag )
667         {
668           fprintf(stderr, "Aux Data\n");
669           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
670         }
671   }
672
673   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
674   {
675     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
676     if ( Options.asset_id_flag )
677       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
678     else
679       Kumu::GenRandomUUID(Info.AssetUUID);
680
681     Info.LabelSetType = LS_MXF_SMPTE;
682
683       // configure encryption
684     if( Options.key_flag )
685         {
686           Kumu::GenRandomUUID(Info.ContextID);
687           Info.EncryptedEssence = true;
688
689           if ( Options.key_id_flag )
690             {
691               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
692             }
693           else
694             {
695               create_random_uuid(Info.CryptographicKeyID);
696             }
697
698           Context = new AESEncContext;
699           result = Context->InitKey(Options.key_value);
700
701           if ( ASDCP_SUCCESS(result) )
702             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
703
704           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
705       {
706         Info.UsesHMAC = true;
707         HMAC = new HMACContext;
708         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
709       }
710         }
711
712     if ( ASDCP_SUCCESS(result) )
713       {
714         result = Writer.OpenWrite(Options.out_file, Info, Options.aux_data_coding, Options.edit_rate);
715       }
716   }
717
718   if ( ASDCP_SUCCESS(result) )
719   {
720     ui32_t duration = 0;
721     result = Parser.Reset();
722
723     while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
724       {
725         result = Parser.ReadFrame(FrameBuffer);
726
727         if ( ASDCP_SUCCESS(result) )
728           {
729             if ( Options.verbose_flag )
730               FrameBuffer.Dump(stderr, Options.fb_dump_size);
731
732             if ( Options.encrypt_header_flag )
733               FrameBuffer.PlaintextOffset(0);
734           }
735
736         if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
737           {
738             result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
739
740             // The Writer class will forward the last block of ciphertext
741             // to the encryption context for use as the IV for the next
742             // frame. If you want to use non-sequitur IV values, un-comment
743             // the following  line of code.
744             // if ( ASDCP_SUCCESS(result) && Options.key_flag )
745             //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
746           }
747       }
748
749     if ( result == RESULT_ENDOFFILE )
750       result = RESULT_OK;
751   }
752
753   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
754     {
755       if ( Options.global_metadata_filename.empty() )
756         {
757           result = Writer.Finalize();
758         }
759       else
760         {
761           ASDCP::FrameBuffer global_metadata;
762           ui32_t file_size = Kumu::FileSize(Options.global_metadata_filename);
763           result = global_metadata.Capacity(file_size);
764
765           if ( ASDCP_SUCCESS(result) )
766             {
767               ui32_t read_count = 0;
768               Kumu::FileReader Reader;
769
770               result = Reader.OpenRead(Options.global_metadata_filename);
771
772               if ( ASDCP_SUCCESS(result) )
773                 result = Reader.Read(global_metadata.Data(), file_size, &read_count);
774     
775               if ( ASDCP_SUCCESS(result) )
776                 {
777                   if ( file_size != read_count) 
778                     return RESULT_READFAIL;
779
780                   global_metadata.Size(read_count);
781                 }
782             }
783   
784           result = Writer.Finalize(global_metadata);
785         }
786     }
787
788   return result;
789 }
790
791 //
792 int
793 main(int argc, const char** argv)
794 {
795   Result_t result = RESULT_OK;
796   char     str_buf[64];
797   g_dict = &ASDCP::DefaultSMPTEDict();
798   assert(g_dict);
799
800   CommandOptions Options(argc, argv);
801
802   if ( Options.version_flag )
803     banner();
804
805   if ( Options.help_flag )
806     usage();
807
808   if ( Options.show_ul_values_flag )
809     {
810       g_dict->Dump(stdout);
811     }
812
813   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
814     return 0;
815
816   if ( Options.error_flag )
817     {
818       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
819       return 3;
820     }
821
822   EssenceType_t EssenceType;
823   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
824
825   if ( ASDCP_SUCCESS(result) )
826     {
827       switch ( EssenceType )
828         {
829         case ESS_JPEG_2000:
830           result = write_JP2K_file(Options);
831           break;
832
833         case ESS_DCDATA_UNKNOWN:
834           if ( ! Options.aux_data_coding.HasValue() )
835             {
836               fprintf(stderr, "Option \"-U <UL>\" is required for Aux Data essence.\n");
837               return 3;
838             }
839           else
840             {
841               result = write_aux_data_file(Options);
842             }
843           break;
844
845         default:
846           fprintf(stderr, "%s: Unknown file type, not P-HDR-compatible essence.\n",
847                   Options.filenames.front().c_str());
848           return 5;
849         }
850     }
851
852   if ( ASDCP_FAILURE(result) )
853     {
854       fputs("Program stopped on error.\n", stderr);
855
856       if ( result != RESULT_FAIL )
857         {
858           fputs(result, stderr);
859           fputc('\n', stderr);
860         }
861
862       return 1;
863     }
864
865   return 0;
866 }
867
868
869 //
870 // end phdr-wrap.cpp
871 //