1st draft as-02 aux data
[asdcplib.git] / src / as-02-wrap.cpp
1 /*
2 Copyright (c) 2011-2016, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10 1. Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16    derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 /*! \file    as-02-wrap.cpp
30     \version $Id$       
31     \brief   AS-02 file manipulation utility
32
33   This program wraps IMF essence (picture or sound) in to an AS-02 MXF file.
34
35   For more information about AS-02, please refer to the header file AS_02.h
36   For more information about asdcplib, please refer to the header file AS_DCP.h
37 */
38
39 #include <KM_fileio.h>
40 #include <KM_prng.h>
41 #include <AS_02.h>
42 #include <PCMParserList.h>
43 #include <Metadata.h>
44
45 using namespace ASDCP;
46
47 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
48 const ASDCP::Dictionary *g_dict = 0;
49  
50 const char*
51 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
52 {
53   snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
54   return buf;
55 }
56
57
58 //------------------------------------------------------------------------------------------
59 //
60 // command line option parser class
61
62 static const char* PROGRAM_NAME = "as-02-wrap";  // program name for messages
63
64 // local program identification info written to file headers
65 class MyInfo : public WriterInfo
66 {
67 public:
68   MyInfo()
69   {
70       static byte_t default_ProductUUID_Data[UUIDlen] =
71       { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
72         0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
73       
74       memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
75       CompanyName = "WidgetCo";
76       ProductName = "as-02-wrap";
77       ProductVersion = ASDCP::Version();
78   }
79 } s_MyInfo;
80
81
82
83 // Increment the iterator, test for an additional non-option command line argument.
84 // Causes the caller to return if there are no remaining arguments or if the next
85 // argument begins with '-'.
86 #define TEST_EXTRA_ARG(i,c)                                             \
87   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
88     fprintf(stderr, "Argument not found for option -%c.\n", (c));       \
89     return;                                                             \
90   }
91
92
93 //
94 static void
95 create_random_uuid(byte_t* uuidbuf)
96 {
97   Kumu::UUID tmp_id;
98   GenRandomValue(tmp_id);
99   memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
100 }
101
102 //
103 void
104 banner(FILE* stream = stdout)
105 {
106   fprintf(stream, "\n\
107 %s (asdcplib %s)\n\n\
108 Copyright (c) 2011-2016, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
109 asdcplib may be copied only under the terms of the license found at\n\
110 the top of every file in the asdcplib distribution kit.\n\n\
111 Specify the -h (help) option for further information about %s\n\n",
112           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
113 }
114
115 //
116 void
117 usage(FILE* stream = stdout)
118 {
119   fprintf(stream, "\
120 USAGE: %s [-h|-help] [-V]\n\
121 \n\
122        %s [-a <uuid>] [-A <w>/<h>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
123             [-D <depth>] [-e|-E] [-i] [-j <key-id-string>] [-k <key-string>]\n\
124             [-M] [-m <expr>] [-p <ul>] [-r <n>/<d>] [-R] [-s <seconds>]\n\
125             [-t <min>] [-T <max>] [-u] [-v] [-W] [-x <int>] [-X <int>] [-Y]\n\
126             [-y <white-ref>[,<black-ref>[,<color-range>]]]\n\
127             [-z|-Z] <input-file>+ <output-file>\n\n",
128           PROGRAM_NAME, PROGRAM_NAME);
129
130   fprintf(stream, "\
131 Options:\n\
132   -h | -help        - Show help\n\
133   -V                - Show version information\n\
134   -a <uuid>         - Specify the Asset ID of the file\n\
135   -A <w>/<h>        - Set aspect ratio for image (default 4/3)\n\
136   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
137                       Defaults to 4,194,304 (4MB)\n\
138   -C <ul>           - Set ChannelAssignment UL value\n\
139   -d <duration>     - Number of frames to process, default all\n\
140   -D <depth>        - Component depth for YCbCr images (default: 10)\n\
141   -e                - Encrypt JP2K headers (default)\n\
142   -E                - Do not encrypt JP2K headers\n\
143   -F (0|1)          - Set field dominance for interlaced image (default: 0)\n\
144   -i                - Indicates input essence is interlaced fields (forces -Y)\n\
145   -j <key-id-str>   - Write key ID instead of creating a random value\n\
146   -k <key-string>   - Use key for ciphertext operations\n\
147   -M                - Do not create HMAC values when writing\n\
148   -m <expr>         - Write MCA labels using <expr>.  Example:\n\
149                         51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
150   -p <ul>           - Set broadcast profile\n\
151   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
152   -R                - Indicates RGB image essence (default)\n\
153   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
154   -t <min>          - Set RGB component minimum code value (default: 0)\n\
155   -T <max>          - Set RGB component maximum code value (default: 1023)\n\
156   -u                - Print UL catalog to stderr\n\
157   -U <UL>           - Set DataEssenceCoding UL value in an Aux Data file\n\
158   -v                - Verbose, prints informative messages to stderr\n\
159   -W                - Read input file only, do not write source file\n\
160   -x <int>          - Horizontal subsampling degree (default: 2)\n\
161   -X <int>          - Vertical subsampling degree (default: 2)\n\
162   -Y                - Indicates YCbCr image essence (default: RGB), uses\n\
163                       default values for White Ref, Black Ref and Color Range,\n\
164                        940,64,897, indicating 10 bit standard Video Range\n\
165   -y <white-ref>[,<black-ref>[,<color-range>]]\n\
166                     - Same as -Y but White Ref, Black Ref and Color Range are\n\
167                       set from the given argument\n\
168   -z                - Fail if j2c inputs have unequal parameters (default)\n\
169   -Z                - Ignore unequal parameters in j2c inputs\n\
170 \n\
171   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
172          o All option arguments must be separated from the option by whitespace.\n\n");
173 }
174
175
176 //
177 //
178 class CommandOptions
179 {
180   CommandOptions();
181
182 public:
183   bool   error_flag;     // true if the given options are in error or not complete
184   bool   key_flag;       // true if an encryption key was given
185   bool   asset_id_flag;  // true if an asset ID was given
186   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
187   bool   write_hmac;     // true if HMAC values are to be generated and written
188   bool   verbose_flag;   // true if the verbose option was selected
189   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
190   bool   no_write_flag;  // true if no output files are to be written
191   bool   version_flag;   // true if the version display option was selected
192   bool   help_flag;      // true if the help display option was selected
193   ui32_t duration;       // number of frames to be processed
194   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
195   bool use_cdci_descriptor; // 
196   Rational edit_rate;    // edit rate of JP2K sequence
197   ui32_t fb_size;        // size of picture frame buffer
198   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
199   bool   key_id_flag;    // true if a key ID was given
200   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
201   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
202   std::string out_file; //
203   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
204   Kumu::PathList_t filenames;  // list of filenames to be processed
205
206   UL channel_assignment;
207   ASDCP::MXF::AS02_MCAConfigParser mca_config;
208
209   UL picture_coding;
210   ui32_t rgba_MaxRef;
211   ui32_t rgba_MinRef;
212
213   ui32_t horizontal_subsampling;
214   ui32_t vertical_subsampling;
215   ui32_t component_depth;
216   ui8_t frame_layout;
217   ASDCP::Rational aspect_ratio;
218   ui8_t field_dominance;
219   ui32_t mxf_header_size;
220   ui32_t cdci_BlackRefLevel; 
221   ui32_t cdci_WhiteRefLevel;
222   ui32_t cdci_ColorRange;
223
224   //new attributes for AS-02 support 
225   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
226   ui32_t partition_space; //Shim parameter partition_spacing
227
228   //
229   UL aux_data_coding;
230
231   //
232   bool set_video_ref(const std::string& arg)
233   {
234     std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
235
236     switch ( ref_tokens.size() )
237       {
238       case 3:
239         cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
240         ref_tokens.pop_back();
241       case 2:
242         cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
243         ref_tokens.pop_back();
244       case 1:
245         cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
246         break;
247
248       default:
249         fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
250         return false;
251       }
252
253     if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
254       {
255         fprintf(stderr, "Unexpected CDCI video referece levels.\n");
256         return false;
257       }
258
259     return true;
260   }
261
262
263   CommandOptions(int argc, const char** argv) :
264     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
265     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
266     no_write_flag(false), version_flag(false), help_flag(false),
267     duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
268     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
269     mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
270     horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
271     frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
272     mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897)
273   {
274     memset(key_value, 0, KeyLen);
275     memset(key_id_value, 0, UUIDlen);
276
277     for ( int i = 1; i < argc; i++ )
278       {
279
280         if ( (strcmp( argv[i], "-help") == 0) )
281           {
282             help_flag = true;
283             continue;
284           }
285          
286         if ( argv[i][0] == '-'
287              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
288              && argv[i][2] == 0 )
289           {
290             switch ( argv[i][1] )
291               {
292               case 'A':
293                 TEST_EXTRA_ARG(i, 'A');
294                 if ( ! DecodeRational(argv[i], aspect_ratio) )
295                   {
296                     fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
297                     return;
298                   }
299                 break;
300
301               case 'a':
302                 asset_id_flag = true;
303                 TEST_EXTRA_ARG(i, 'a');
304                 {
305                   ui32_t length;
306                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
307
308                   if ( length != UUIDlen )
309                     {
310                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
311                       return;
312                     }
313                 }
314                 break;
315
316               case 'b':
317                 TEST_EXTRA_ARG(i, 'b');
318                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
319
320                 if ( verbose_flag )
321                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
322
323                 break;
324               case 'C':
325                 TEST_EXTRA_ARG(i, 'C');
326                 if ( ! channel_assignment.DecodeHex(argv[i]) )
327                   {
328                     fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
329                     return;
330                   }
331                 break;
332
333               case 'D':
334                 TEST_EXTRA_ARG(i, 'D');
335                 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
336                 break;
337
338               case 'd':
339                 TEST_EXTRA_ARG(i, 'd');
340                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
341                 break;
342
343               case 'E': encrypt_header_flag = false; break;
344               case 'e': encrypt_header_flag = true; break;
345
346               case 'F':
347                 TEST_EXTRA_ARG(i, 'F');
348                 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
349                 if ( field_dominance > 1 )
350                   {
351                     fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
352                     return;
353                   }
354                 break;
355
356               case 'h': help_flag = true; break;
357
358               case 'i':
359                 frame_layout = 1;
360                 use_cdci_descriptor = true;
361                 break;
362
363               case 'j':
364                 key_id_flag = true;
365                 TEST_EXTRA_ARG(i, 'j');
366                 {
367                   ui32_t length;
368                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
369
370                   if ( length != UUIDlen )
371                     {
372                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
373                       return;
374                     }
375                 }
376                 break;
377
378               case 'k': key_flag = true;
379                 TEST_EXTRA_ARG(i, 'k');
380                 {
381                   ui32_t length;
382                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
383
384                   if ( length != KeyLen )
385                     {
386                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
387                       return;
388                     }
389                 }
390                 break;
391
392               case 'M': write_hmac = false; break;
393
394               case 'm':
395                 TEST_EXTRA_ARG(i, 'm');
396                 if ( ! mca_config.DecodeString(argv[i]) )
397                   {
398                     return;
399                   }
400                 break;
401
402               case 'p':
403                 TEST_EXTRA_ARG(i, 'p');
404                 if ( ! picture_coding.DecodeHex(argv[i]) )
405                   {
406                     fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
407                     return;
408                   }
409                 break;
410
411               case 'r':
412                 TEST_EXTRA_ARG(i, 'r');
413                 if ( ! DecodeRational(argv[i], edit_rate) )
414                   {
415                     fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
416                     return;
417                   }
418                 
419                 break;
420
421               case 'R':
422                 use_cdci_descriptor = false;
423                 break;
424
425               case 's':
426                 TEST_EXTRA_ARG(i, 's');
427                 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
428                 break;
429
430               case 't':
431                 TEST_EXTRA_ARG(i, 't');
432                 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
433                 break;
434
435               case 'T':
436                 TEST_EXTRA_ARG(i, 'T');
437                 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
438                 break;
439
440               case 'u': show_ul_values_flag = true; break;
441
442               case 'U':
443                 TEST_EXTRA_ARG(i, 'U');
444                 if ( ! aux_data_coding.DecodeHex(argv[i]) )
445                   {
446                     fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
447                     return;
448                   }
449                 break;
450
451               case 'V': version_flag = true; break;
452               case 'v': verbose_flag = true; break;
453               case 'W': no_write_flag = true; break;
454
455               case 'x':
456                 TEST_EXTRA_ARG(i, 'x');
457                 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
458                 break;
459
460               case 'X':
461                 TEST_EXTRA_ARG(i, 'X');
462                 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
463                 break;
464
465               case 'Y':
466                 use_cdci_descriptor = true;
467                 // default 10 bit video range YUV, ref levels already set
468                 break;
469
470               case 'y':
471                 // Use values provded as argument, sharp tool, be careful
472                 use_cdci_descriptor = true;
473                 TEST_EXTRA_ARG(i, 'y');
474                 if ( ! set_video_ref(argv[i]) )
475                   {
476                     return;
477                   }
478                 break;
479
480               case 'Z': j2c_pedantic = false; break;
481               case 'z': j2c_pedantic = true; break;
482
483               default:
484                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
485                 return;
486               }
487           }
488         else
489           {
490
491             if ( argv[i][0] != '-' )
492               {
493                 filenames.push_back(argv[i]);
494               }
495             else
496               {
497                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
498                 return;
499               }
500           }
501       }
502
503     if ( help_flag || version_flag )
504       return;
505     
506     if ( filenames.size() < 2 )
507       {
508         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
509         return;
510       }
511
512     out_file = filenames.back();
513     filenames.pop_back();
514
515     if ( ! picture_coding.HasValue() )
516       {
517         picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
518       }
519
520     error_flag = false;
521   }
522 };
523
524
525 //------------------------------------------------------------------------------------------
526 // JPEG 2000 essence
527
528 namespace ASDCP {
529   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
530                             const ASDCP::Dictionary& dict,
531                             ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
532                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
533
534   Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
535 }
536
537 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
538 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
539 //
540 Result_t
541 write_JP2K_file(CommandOptions& Options)
542 {
543   AESEncContext*          Context = 0;
544   HMACContext*            HMAC = 0;
545   AS_02::JP2K::MXFWriter  Writer;
546   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
547   JP2K::SequenceParser    Parser;
548   byte_t                  IV_buf[CBC_BLOCK_SIZE];
549   Kumu::FortunaRNG        RNG;
550   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
551   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
552
553   // set up essence parser
554   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
555
556   // set up MXF writer
557   if ( ASDCP_SUCCESS(result) )
558     {
559       ASDCP::JP2K::PictureDescriptor PDesc;
560       Parser.FillPictureDescriptor(PDesc);
561       PDesc.EditRate = Options.edit_rate;
562
563       if ( Options.verbose_flag )
564         {
565           fprintf(stderr, "JPEG 2000 pictures\n");
566           fputs("PictureDescriptor:\n", stderr);
567           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
568           JP2K::PictureDescriptorDump(PDesc);
569         }
570
571       if ( Options.use_cdci_descriptor )
572         {
573           ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
574           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
575           
576           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
577                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
578                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
579
580           if ( ASDCP_SUCCESS(result) )
581             {
582               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
583               tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
584               tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
585               tmp_dscr->ComponentDepth = Options.component_depth;
586               tmp_dscr->FrameLayout = Options.frame_layout;
587               tmp_dscr->AspectRatio = Options.aspect_ratio;
588               tmp_dscr->FieldDominance = Options.field_dominance;
589               tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
590               tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
591               tmp_dscr->ColorRange = Options.cdci_ColorRange;
592               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
593             }
594         }
595       else
596         { // use RGB
597           ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
598           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
599           
600           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
601                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
602                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
603
604           if ( ASDCP_SUCCESS(result) )
605             {
606               tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
607               tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
608               tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
609               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
610             }
611         }
612     }
613
614   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
615     {
616       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
617       Info.LabelSetType = LS_MXF_SMPTE;
618
619       if ( Options.asset_id_flag )
620         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
621       else
622         Kumu::GenRandomUUID(Info.AssetUUID);
623
624       // configure encryption
625       if( Options.key_flag )
626         {
627           Kumu::GenRandomUUID(Info.ContextID);
628           Info.EncryptedEssence = true;
629
630           if ( Options.key_id_flag )
631             {
632               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
633             }
634           else
635             {
636               create_random_uuid(Info.CryptographicKeyID);
637             }
638
639           Context = new AESEncContext;
640           result = Context->InitKey(Options.key_value);
641
642           if ( ASDCP_SUCCESS(result) )
643             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
644
645           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
646             {
647               Info.UsesHMAC = true;
648               HMAC = new HMACContext;
649               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
650             }
651         }
652
653       if ( ASDCP_SUCCESS(result) )
654         {
655           result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
656                                     Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
657         }
658     }
659
660   if ( ASDCP_SUCCESS(result) )
661     {
662       ui32_t duration = 0;
663       result = Parser.Reset();
664
665       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
666         {
667           result = Parser.ReadFrame(FrameBuffer);
668           
669           if ( ASDCP_SUCCESS(result) )
670             {
671               if ( Options.verbose_flag )
672                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
673               
674               if ( Options.encrypt_header_flag )
675                 FrameBuffer.PlaintextOffset(0);
676             }
677
678           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
679             {
680               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
681
682               // The Writer class will forward the last block of ciphertext
683               // to the encryption context for use as the IV for the next
684               // frame. If you want to use non-sequitur IV values, un-comment
685               // the following  line of code.
686               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
687               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
688             }
689         }
690
691       if ( result == RESULT_ENDOFFILE )
692         result = RESULT_OK;
693     }
694
695   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
696     result = Writer.Finalize();
697
698   return result;
699 }
700
701 //------------------------------------------------------------------------------------------
702 // PCM essence
703
704
705 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
706 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
707 //
708 Result_t
709 write_PCM_file(CommandOptions& Options)
710 {
711   AESEncContext*    Context = 0;
712   HMACContext*      HMAC = 0;
713   PCMParserList     Parser;
714   AS_02::PCM::MXFWriter    Writer;
715   PCM::FrameBuffer  FrameBuffer;
716   byte_t            IV_buf[CBC_BLOCK_SIZE];
717   Kumu::FortunaRNG  RNG;
718   ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
719
720   // set up essence parser
721   Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
722
723   // set up MXF writer
724   if ( ASDCP_SUCCESS(result) )
725     {
726       ASDCP::PCM::AudioDescriptor ADesc;
727       Parser.FillAudioDescriptor(ADesc);
728
729       ADesc.EditRate = Options.edit_rate;
730       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
731
732       if ( Options.verbose_flag )
733         {
734           char buf[64];
735           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
736                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
737                   RationalToString(Options.edit_rate, buf, 64),
738                   PCM::CalcSamplesPerFrame(ADesc));
739           fputs("AudioDescriptor:\n", stderr);
740           PCM::AudioDescriptorDump(ADesc);
741         }
742
743       essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
744
745       result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
746
747       if ( Options.mca_config.empty() )
748         {
749           essence_descriptor->ChannelAssignment = Options.channel_assignment;
750         }
751       else
752         {
753           if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
754             {
755               fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
756                       Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
757               return RESULT_FAIL;
758             }
759
760           // this is the d-cinema MCA label, what is the one for IMF?
761           essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
762         }
763     }
764
765   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
766     {
767       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
768       Info.LabelSetType = LS_MXF_SMPTE;
769
770       if ( Options.asset_id_flag )
771         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
772       else
773         Kumu::GenRandomUUID(Info.AssetUUID);
774
775       // configure encryption
776       if( Options.key_flag )
777         {
778           Kumu::GenRandomUUID(Info.ContextID);
779           Info.EncryptedEssence = true;
780
781           if ( Options.key_id_flag )
782             {
783               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
784             }
785           else
786             {
787               create_random_uuid(Info.CryptographicKeyID);
788             }
789
790           Context = new AESEncContext;
791           result = Context->InitKey(Options.key_value);
792
793           if ( ASDCP_SUCCESS(result) )
794             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
795
796           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
797             {
798               Info.UsesHMAC = true;
799               HMAC = new HMACContext;
800               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
801             }
802         }
803
804       if ( ASDCP_SUCCESS(result) )
805         {
806           result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
807                                     Options.mca_config, Options.edit_rate);
808         }
809     }
810
811   if ( ASDCP_SUCCESS(result) )
812     {
813       result = Parser.Reset();
814       ui32_t duration = 0;
815
816       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
817         {
818           result = Parser.ReadFrame(FrameBuffer);
819
820           if ( ASDCP_SUCCESS(result) )
821             {
822               if ( Options.verbose_flag )
823                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
824
825               if ( ! Options.no_write_flag )
826                 {
827                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
828
829                   // The Writer class will forward the last block of ciphertext
830                   // to the encryption context for use as the IV for the next
831                   // frame. If you want to use non-sequitur IV values, un-comment
832                   // the following  line of code.
833                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
834                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
835                 }
836             }
837         }
838
839       if ( result == RESULT_ENDOFFILE )
840         result = RESULT_OK;
841     }
842
843   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
844     result = Writer.Finalize();
845
846   return result;
847 }
848
849
850
851
852 //------------------------------------------------------------------------------------------
853 // TimedText essence
854
855
856 // Write one or more plaintext timed text streams to a plaintext AS-02 file
857 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
858 //
859 Result_t
860 write_timed_text_file(CommandOptions& Options)
861 {
862   AESEncContext*    Context = 0;
863   HMACContext*      HMAC = 0;
864   AS_02::TimedText::ST2052_TextParser  Parser;
865   AS_02::TimedText::MXFWriter    Writer;
866   TimedText::FrameBuffer  FrameBuffer;
867   TimedText::TimedTextDescriptor TDesc;
868   byte_t            IV_buf[CBC_BLOCK_SIZE];
869   Kumu::FortunaRNG  RNG;
870
871   // set up essence parser
872   Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
873
874   // set up MXF writer
875   if ( ASDCP_SUCCESS(result) )
876     {
877       Parser.FillTimedTextDescriptor(TDesc);
878       TDesc.EditRate = Options.edit_rate;
879       TDesc.ContainerDuration = Options.duration;
880       FrameBuffer.Capacity(Options.fb_size);
881
882       if ( Options.verbose_flag )
883         {
884           fputs("IMF Timed-Text Descriptor:\n", stderr);
885           TimedText::DescriptorDump(TDesc);
886         }
887     }
888
889   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
890     {
891       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
892       Info.LabelSetType = LS_MXF_SMPTE;
893
894       if ( Options.asset_id_flag )
895         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
896       else
897         Kumu::GenRandomUUID(Info.AssetUUID);
898
899       // configure encryption
900       if( Options.key_flag )
901         {
902           Kumu::GenRandomUUID(Info.ContextID);
903           Info.EncryptedEssence = true;
904
905           if ( Options.key_id_flag )
906             {
907               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
908             }
909           else
910             {
911               create_random_uuid(Info.CryptographicKeyID);
912             }
913
914           Context = new AESEncContext;
915           result = Context->InitKey(Options.key_value);
916
917           if ( ASDCP_SUCCESS(result) )
918             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
919
920           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
921             {
922               Info.UsesHMAC = true;
923               HMAC = new HMACContext;
924               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
925             }
926         }
927
928       if ( ASDCP_SUCCESS(result) )
929         result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
930     }
931
932   if ( ASDCP_FAILURE(result) )
933     return result;
934
935   std::string XMLDoc;
936   TimedText::ResourceList_t::const_iterator ri;
937
938   result = Parser.ReadTimedTextResource(XMLDoc);
939
940   if ( ASDCP_SUCCESS(result) )
941     result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
942
943   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
944     {
945       result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
946
947       if ( ASDCP_SUCCESS(result) )
948         {
949           if ( Options.verbose_flag )
950             FrameBuffer.Dump(stderr, Options.fb_dump_size);
951
952           if ( ! Options.no_write_flag )
953             {
954               result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
955
956               // The Writer class will forward the last block of ciphertext
957               // to the encryption context for use as the IV for the next
958               // frame. If you want to use non-sequitur IV values, un-comment
959               // the following  line of code.
960               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
961               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
962             }
963         }
964
965       if ( result == RESULT_ENDOFFILE )
966         result = RESULT_OK;
967     }
968
969   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
970     result = Writer.Finalize();
971
972   return result;
973 }
974
975
976 // Write one or more plaintext Aux Data bytestreams to a plaintext AS-02 file
977 // Write one or more plaintext Aux Data bytestreams to a ciphertext AS-02 file
978 //
979 Result_t
980 write_aux_data_file(CommandOptions& Options)
981 {
982   AESEncContext*          Context = 0;
983   HMACContext*            HMAC = 0;
984   AS_02::AuxData::MXFWriter Writer;
985   DCData::FrameBuffer     FrameBuffer(Options.fb_size);
986   DCData::SequenceParser  Parser;
987   byte_t                  IV_buf[CBC_BLOCK_SIZE];
988   Kumu::FortunaRNG        RNG;
989
990   // set up essence parser
991   Result_t result = Parser.OpenRead(Options.filenames.front());
992
993   // set up MXF writer
994   if ( ASDCP_SUCCESS(result) )
995   {
996
997     if ( Options.verbose_flag )
998         {
999           fprintf(stderr, "Aux Data\n");
1000           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1001         }
1002   }
1003
1004   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1005   {
1006     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1007     if ( Options.asset_id_flag )
1008       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1009     else
1010       Kumu::GenRandomUUID(Info.AssetUUID);
1011
1012     Info.LabelSetType = LS_MXF_SMPTE;
1013
1014       // configure encryption
1015     if( Options.key_flag )
1016         {
1017           Kumu::GenRandomUUID(Info.ContextID);
1018           Info.EncryptedEssence = true;
1019
1020           if ( Options.key_id_flag )
1021             {
1022               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1023             }
1024           else
1025             {
1026               create_random_uuid(Info.CryptographicKeyID);
1027             }
1028
1029           Context = new AESEncContext;
1030           result = Context->InitKey(Options.key_value);
1031
1032           if ( ASDCP_SUCCESS(result) )
1033             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1034
1035           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1036       {
1037         Info.UsesHMAC = true;
1038         HMAC = new HMACContext;
1039         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1040       }
1041         }
1042
1043     if ( ASDCP_SUCCESS(result) )
1044       {
1045         ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptor_list; // empty for now
1046         ASDCP::MXF::DCDataDescriptor *essence_descriptor = new ASDCP::MXF::DCDataDescriptor(g_dict);
1047         essence_descriptor->SampleRate = Options.edit_rate;
1048         essence_descriptor->ContainerDuration = 0;
1049         essence_descriptor->DataEssenceCoding = Options.aux_data_coding;
1050         
1051         result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor,
1052                                   essence_sub_descriptor_list, Options.edit_rate);
1053       }
1054   }
1055
1056   if ( ASDCP_SUCCESS(result) )
1057   {
1058     ui32_t duration = 0;
1059     result = Parser.Reset();
1060
1061     while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1062       {
1063         result = Parser.ReadFrame(FrameBuffer);
1064
1065         if ( ASDCP_SUCCESS(result) )
1066           {
1067             if ( Options.verbose_flag )
1068               FrameBuffer.Dump(stderr, Options.fb_dump_size);
1069
1070             if ( Options.encrypt_header_flag )
1071               FrameBuffer.PlaintextOffset(0);
1072           }
1073
1074         if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1075           {
1076             result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1077
1078             // The Writer class will forward the last block of ciphertext
1079             // to the encryption context for use as the IV for the next
1080             // frame. If you want to use non-sequitur IV values, un-comment
1081             // the following  line of code.
1082             // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1083             //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1084           }
1085       }
1086
1087     if ( result == RESULT_ENDOFFILE )
1088       result = RESULT_OK;
1089   }
1090
1091   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1092     result = Writer.Finalize();
1093
1094   return result;
1095 }
1096
1097
1098 //
1099 int
1100 main(int argc, const char** argv)
1101 {
1102   Result_t result = RESULT_OK;
1103   char     str_buf[64];
1104   g_dict = &ASDCP::DefaultSMPTEDict();
1105   assert(g_dict);
1106
1107   CommandOptions Options(argc, argv);
1108
1109   if ( Options.version_flag )
1110     banner();
1111
1112   if ( Options.help_flag )
1113     usage();
1114
1115   if ( Options.show_ul_values_flag )
1116     {
1117       g_dict->Dump(stdout);
1118     }
1119
1120   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1121     return 0;
1122
1123   if ( Options.error_flag )
1124     {
1125       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1126       return 3;
1127     }
1128
1129   EssenceType_t EssenceType;
1130   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1131
1132   if ( ASDCP_SUCCESS(result) )
1133     {
1134       switch ( EssenceType )
1135         {
1136         case ESS_JPEG_2000:
1137           result = write_JP2K_file(Options);
1138           break;
1139
1140         case ESS_PCM_24b_48k:
1141         case ESS_PCM_24b_96k:
1142           result = write_PCM_file(Options);
1143           break;
1144
1145         case ESS_TIMED_TEXT:
1146           result = write_timed_text_file(Options);
1147           break;
1148
1149         case ESS_DCDATA_UNKNOWN:
1150           if ( ! Options.aux_data_coding.HasValue() )
1151             {
1152               fprintf(stderr, "Option \"-U <UL>\" is required for Aux Data essence.\n");
1153               return 3;
1154             }
1155           else
1156             {
1157               result = write_aux_data_file(Options);
1158             }
1159           break;
1160
1161         default:
1162           fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
1163                   Options.filenames.front().c_str());
1164           return 5;
1165         }
1166     }
1167
1168   if ( ASDCP_FAILURE(result) )
1169     {
1170       fputs("Program stopped on error.\n", stderr);
1171
1172       if ( result != RESULT_FAIL )
1173         {
1174           fputs(result, stderr);
1175           fputc('\n', stderr);
1176         }
1177
1178       return 1;
1179     }
1180
1181   return 0;
1182 }
1183
1184
1185 //
1186 // end as-02-wrap.cpp
1187 //