o Cleared up MXFGCP1FrameWrappedPictureElement / MXFGCP1FrameWrappedPictureElement
[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   -l <first>,<second>\n\
148                     - Integer values that set the VideoLineMap when creating\n\
149                       interlaced YCbCr files\n\
150   -M                - Do not create HMAC values when writing\n\
151   -m <expr>         - Write MCA labels using <expr>.  Example:\n\
152                         51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
153   -p <ul>           - Set broadcast profile\n\
154   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
155   -R                - Indicates RGB image essence (default)\n\
156   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
157   -t <min>          - Set RGB component minimum code value (default: 0)\n\
158   -T <max>          - Set RGB component maximum code value (default: 1023)\n\
159   -u                - Print UL catalog to stderr\n\
160   -v                - Verbose, prints informative messages to stderr\n\
161   -W                - Read input file only, do not write source file\n\
162   -x <int>          - Horizontal subsampling degree (default: 2)\n\
163   -X <int>          - Vertical subsampling degree (default: 2)\n\
164   -Y                - Indicates YCbCr image essence (default: RGB), uses\n\
165                       default values for White Ref, Black Ref and Color Range,\n\
166                        940,64,897, indicating 10 bit standard Video Range\n\
167   -y <white-ref>[,<black-ref>[,<color-range>]]\n\
168                     - Same as -Y but White Ref, Black Ref and Color Range are\n\
169                       set from the given argument\n\
170   -z                - Fail if j2c inputs have unequal parameters (default)\n\
171   -Z                - Ignore unequal parameters in j2c inputs\n\
172 \n\
173   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
174          o All option arguments must be separated from the option by whitespace.\n\n");
175 }
176
177
178 //
179 //
180 class CommandOptions
181 {
182   CommandOptions();
183
184 public:
185   bool   error_flag;     // true if the given options are in error or not complete
186   bool   key_flag;       // true if an encryption key was given
187   bool   asset_id_flag;  // true if an asset ID was given
188   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
189   bool   write_hmac;     // true if HMAC values are to be generated and written
190   bool   verbose_flag;   // true if the verbose option was selected
191   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
192   bool   no_write_flag;  // true if no output files are to be written
193   bool   version_flag;   // true if the version display option was selected
194   bool   help_flag;      // true if the help display option was selected
195   ui32_t duration;       // number of frames to be processed
196   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
197   bool use_cdci_descriptor; // 
198   Rational edit_rate;    // edit rate of JP2K sequence
199   ui32_t fb_size;        // size of picture frame buffer
200   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
201   bool   key_id_flag;    // true if a key ID was given
202   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
203   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
204   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
205   Kumu::PathList_t filenames;  // list of filenames to be processed
206
207   UL channel_assignment;
208   ASDCP::MXF::AS02_MCAConfigParser mca_config;
209
210   UL picture_coding;
211   ui32_t rgba_MaxRef;
212   ui32_t rgba_MinRef;
213
214   ui32_t horizontal_subsampling;
215   ui32_t vertical_subsampling;
216   ui32_t component_depth;
217   ui8_t frame_layout;
218   ASDCP::Rational aspect_ratio;
219   ui8_t field_dominance;
220   ui32_t mxf_header_size;
221   ui32_t cdci_BlackRefLevel; 
222   ui32_t cdci_WhiteRefLevel;
223   ui32_t cdci_ColorRange;
224
225   //new attributes for AS-02 support 
226   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
227   ui32_t partition_space; //Shim parameter partition_spacing
228
229   //
230   MXF::LineMapPair line_map;
231   std::string out_file; //
232
233   //
234   bool set_video_line_map(const std::string& arg)
235   {
236     const char* sep_str = strrchr(arg.c_str(), ',');
237
238     if ( sep_str == 0 )
239       {
240         fprintf(stderr, "Expecting <first>,<second>\n");
241         return false;
242       }
243
244     line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
245     line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
246     return true;
247   }
248
249   //
250   bool set_video_ref(const std::string& arg)
251   {
252     std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
253
254     switch ( ref_tokens.size() )
255       {
256       case 3:
257         cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
258         ref_tokens.pop_back();
259       case 2:
260         cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
261         ref_tokens.pop_back();
262       case 1:
263         cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
264         break;
265
266       default:
267         fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
268         return false;
269       }
270
271     if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
272       {
273         fprintf(stderr, "Unexpected CDCI video referece levels.\n");
274         return false;
275       }
276
277     return true;
278   }
279
280
281   CommandOptions(int argc, const char** argv) :
282     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
283     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
284     no_write_flag(false), version_flag(false), help_flag(false),
285     duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
286     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
287     mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
288     horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
289     frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
290     mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
291     line_map(0,0)
292   {
293     memset(key_value, 0, KeyLen);
294     memset(key_id_value, 0, UUIDlen);
295
296     for ( int i = 1; i < argc; i++ )
297       {
298
299         if ( (strcmp( argv[i], "-help") == 0) )
300           {
301             help_flag = true;
302             continue;
303           }
304          
305         if ( argv[i][0] == '-'
306              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
307              && argv[i][2] == 0 )
308           {
309             switch ( argv[i][1] )
310               {
311               case 'A':
312                 TEST_EXTRA_ARG(i, 'A');
313                 if ( ! DecodeRational(argv[i], aspect_ratio) )
314                   {
315                     fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
316                     return;
317                   }
318                 break;
319
320               case 'a':
321                 asset_id_flag = true;
322                 TEST_EXTRA_ARG(i, 'a');
323                 {
324                   ui32_t length;
325                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
326
327                   if ( length != UUIDlen )
328                     {
329                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
330                       return;
331                     }
332                 }
333                 break;
334
335               case 'b':
336                 TEST_EXTRA_ARG(i, 'b');
337                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
338
339                 if ( verbose_flag )
340                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
341
342                 break;
343               case 'C':
344                 TEST_EXTRA_ARG(i, 'C');
345                 if ( ! channel_assignment.DecodeHex(argv[i]) )
346                   {
347                     fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
348                     return;
349                   }
350                 break;
351
352               case 'D':
353                 TEST_EXTRA_ARG(i, 'D');
354                 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
355                 break;
356
357               case 'd':
358                 TEST_EXTRA_ARG(i, 'd');
359                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
360                 break;
361
362               case 'E': encrypt_header_flag = false; break;
363               case 'e': encrypt_header_flag = true; break;
364
365               case 'F':
366                 TEST_EXTRA_ARG(i, 'F');
367                 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
368                 if ( field_dominance > 1 )
369                   {
370                     fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
371                     return;
372                   }
373                 break;
374
375               case 'h': help_flag = true; break;
376
377               case 'i':
378                 frame_layout = 1;
379                 use_cdci_descriptor = true;
380                 break;
381
382               case 'j':
383                 key_id_flag = true;
384                 TEST_EXTRA_ARG(i, 'j');
385                 {
386                   ui32_t length;
387                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
388
389                   if ( length != UUIDlen )
390                     {
391                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
392                       return;
393                     }
394                 }
395                 break;
396
397               case 'k': key_flag = true;
398                 TEST_EXTRA_ARG(i, 'k');
399                 {
400                   ui32_t length;
401                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
402
403                   if ( length != KeyLen )
404                     {
405                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
406                       return;
407                     }
408                 }
409                 break;
410
411               case 'l':
412                 TEST_EXTRA_ARG(i, 'y');
413                 if ( ! set_video_line_map(argv[i]) )
414                   {
415                     return;
416                   }
417                 break;
418
419               case 'M': write_hmac = false; break;
420
421               case 'm':
422                 TEST_EXTRA_ARG(i, 'm');
423                 if ( ! mca_config.DecodeString(argv[i]) )
424                   {
425                     return;
426                   }
427                 break;
428
429               case 'p':
430                 TEST_EXTRA_ARG(i, 'p');
431                 if ( ! picture_coding.DecodeHex(argv[i]) )
432                   {
433                     fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
434                     return;
435                   }
436                 break;
437
438               case 'r':
439                 TEST_EXTRA_ARG(i, 'r');
440                 if ( ! DecodeRational(argv[i], edit_rate) )
441                   {
442                     fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
443                     return;
444                   }
445                 
446                 break;
447
448               case 'R':
449                 use_cdci_descriptor = false;
450                 break;
451
452               case 's':
453                 TEST_EXTRA_ARG(i, 's');
454                 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
455                 break;
456
457               case 't':
458                 TEST_EXTRA_ARG(i, 't');
459                 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
460                 break;
461
462               case 'T':
463                 TEST_EXTRA_ARG(i, 'T');
464                 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
465                 break;
466
467               case 'u': show_ul_values_flag = true; break;
468
469               case 'V': version_flag = true; break;
470               case 'v': verbose_flag = true; break;
471               case 'W': no_write_flag = true; break;
472
473               case 'x':
474                 TEST_EXTRA_ARG(i, 'x');
475                 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
476                 break;
477
478               case 'X':
479                 TEST_EXTRA_ARG(i, 'X');
480                 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
481                 break;
482
483               case 'Y':
484                 use_cdci_descriptor = true;
485                 // default 10 bit video range YUV, ref levels already set
486                 break;
487
488               case 'y':
489                 // Use values provded as argument, sharp tool, be careful
490                 use_cdci_descriptor = true;
491                 TEST_EXTRA_ARG(i, 'y');
492                 if ( ! set_video_ref(argv[i]) )
493                   {
494                     return;
495                   }
496                 break;
497
498               case 'Z': j2c_pedantic = false; break;
499               case 'z': j2c_pedantic = true; break;
500
501               default:
502                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
503                 return;
504               }
505           }
506         else
507           {
508
509             if ( argv[i][0] != '-' )
510               {
511                 filenames.push_back(argv[i]);
512               }
513             else
514               {
515                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
516                 return;
517               }
518           }
519       }
520
521     if ( help_flag || version_flag )
522       return;
523     
524     if ( filenames.size() < 2 )
525       {
526         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
527         return;
528       }
529
530     out_file = filenames.back();
531     filenames.pop_back();
532
533     if ( ! picture_coding.HasValue() )
534       {
535         picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
536       }
537
538     error_flag = false;
539   }
540 };
541
542
543 //------------------------------------------------------------------------------------------
544 // JPEG 2000 essence
545
546 namespace ASDCP {
547   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
548                             const ASDCP::Dictionary& dict,
549                             ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
550                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
551
552   Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
553 }
554
555 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
556 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
557 //
558 Result_t
559 write_JP2K_file(CommandOptions& Options)
560 {
561   AESEncContext*          Context = 0;
562   HMACContext*            HMAC = 0;
563   AS_02::JP2K::MXFWriter  Writer;
564   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
565   JP2K::SequenceParser    Parser;
566   byte_t                  IV_buf[CBC_BLOCK_SIZE];
567   Kumu::FortunaRNG        RNG;
568   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
569   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
570
571   // set up essence parser
572   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
573
574   // set up MXF writer
575   if ( ASDCP_SUCCESS(result) )
576     {
577       ASDCP::JP2K::PictureDescriptor PDesc;
578       Parser.FillPictureDescriptor(PDesc);
579       PDesc.EditRate = Options.edit_rate;
580
581       if ( Options.verbose_flag )
582         {
583           fprintf(stderr, "JPEG 2000 pictures\n");
584           fputs("PictureDescriptor:\n", stderr);
585           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
586           JP2K::PictureDescriptorDump(PDesc);
587         }
588
589       if ( Options.use_cdci_descriptor )
590         {
591           ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
592           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
593           
594           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
595                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
596                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
597
598           if ( ASDCP_SUCCESS(result) )
599             {
600               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
601               tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
602               tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
603               tmp_dscr->ComponentDepth = Options.component_depth;
604               tmp_dscr->FrameLayout = Options.frame_layout;
605               tmp_dscr->AspectRatio = Options.aspect_ratio;
606               tmp_dscr->FieldDominance = Options.field_dominance;
607               tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
608               tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
609               tmp_dscr->ColorRange = Options.cdci_ColorRange;
610               tmp_dscr->VideoLineMap = Options.line_map;
611               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
612             }
613         }
614       else
615         { // use RGB
616           ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
617           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
618           
619           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
620                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
621                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
622
623           if ( ASDCP_SUCCESS(result) )
624             {
625               tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
626               tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
627               tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
628               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
629             }
630         }
631     }
632
633   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
634     {
635       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
636       Info.LabelSetType = LS_MXF_SMPTE;
637
638       if ( Options.asset_id_flag )
639         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
640       else
641         Kumu::GenRandomUUID(Info.AssetUUID);
642
643       // configure encryption
644       if( Options.key_flag )
645         {
646           Kumu::GenRandomUUID(Info.ContextID);
647           Info.EncryptedEssence = true;
648
649           if ( Options.key_id_flag )
650             {
651               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
652             }
653           else
654             {
655               create_random_uuid(Info.CryptographicKeyID);
656             }
657
658           Context = new AESEncContext;
659           result = Context->InitKey(Options.key_value);
660
661           if ( ASDCP_SUCCESS(result) )
662             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
663
664           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
665             {
666               Info.UsesHMAC = true;
667               HMAC = new HMACContext;
668               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
669             }
670         }
671
672       if ( ASDCP_SUCCESS(result) )
673         {
674           result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
675                                     Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
676         }
677     }
678
679   if ( ASDCP_SUCCESS(result) )
680     {
681       ui32_t duration = 0;
682       result = Parser.Reset();
683
684       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
685         {
686           result = Parser.ReadFrame(FrameBuffer);
687           
688           if ( ASDCP_SUCCESS(result) )
689             {
690               if ( Options.verbose_flag )
691                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
692               
693               if ( Options.encrypt_header_flag )
694                 FrameBuffer.PlaintextOffset(0);
695             }
696
697           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
698             {
699               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
700
701               // The Writer class will forward the last block of ciphertext
702               // to the encryption context for use as the IV for the next
703               // frame. If you want to use non-sequitur IV values, un-comment
704               // the following  line of code.
705               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
706               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
707             }
708         }
709
710       if ( result == RESULT_ENDOFFILE )
711         result = RESULT_OK;
712     }
713
714   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
715     result = Writer.Finalize();
716
717   return result;
718 }
719
720 //------------------------------------------------------------------------------------------
721 // PCM essence
722
723
724 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
725 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
726 //
727 Result_t
728 write_PCM_file(CommandOptions& Options)
729 {
730   AESEncContext*    Context = 0;
731   HMACContext*      HMAC = 0;
732   PCMParserList     Parser;
733   AS_02::PCM::MXFWriter    Writer;
734   PCM::FrameBuffer  FrameBuffer;
735   byte_t            IV_buf[CBC_BLOCK_SIZE];
736   Kumu::FortunaRNG  RNG;
737   ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
738
739   // set up essence parser
740   Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
741
742   // set up MXF writer
743   if ( ASDCP_SUCCESS(result) )
744     {
745       ASDCP::PCM::AudioDescriptor ADesc;
746       Parser.FillAudioDescriptor(ADesc);
747
748       ADesc.EditRate = Options.edit_rate;
749       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
750
751       if ( Options.verbose_flag )
752         {
753           char buf[64];
754           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
755                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
756                   RationalToString(Options.edit_rate, buf, 64),
757                   PCM::CalcSamplesPerFrame(ADesc));
758           fputs("AudioDescriptor:\n", stderr);
759           PCM::AudioDescriptorDump(ADesc);
760         }
761
762       essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
763
764       result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
765
766       if ( Options.mca_config.empty() )
767         {
768           essence_descriptor->ChannelAssignment = Options.channel_assignment;
769         }
770       else
771         {
772           if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
773             {
774               fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
775                       Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
776               return RESULT_FAIL;
777             }
778
779           // this is the d-cinema MCA label, what is the one for IMF?
780           essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
781         }
782     }
783
784   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
785     {
786       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
787       Info.LabelSetType = LS_MXF_SMPTE;
788
789       if ( Options.asset_id_flag )
790         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
791       else
792         Kumu::GenRandomUUID(Info.AssetUUID);
793
794       // configure encryption
795       if( Options.key_flag )
796         {
797           Kumu::GenRandomUUID(Info.ContextID);
798           Info.EncryptedEssence = true;
799
800           if ( Options.key_id_flag )
801             {
802               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
803             }
804           else
805             {
806               create_random_uuid(Info.CryptographicKeyID);
807             }
808
809           Context = new AESEncContext;
810           result = Context->InitKey(Options.key_value);
811
812           if ( ASDCP_SUCCESS(result) )
813             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
814
815           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
816             {
817               Info.UsesHMAC = true;
818               HMAC = new HMACContext;
819               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
820             }
821         }
822
823       if ( ASDCP_SUCCESS(result) )
824         {
825           result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
826                                     Options.mca_config, Options.edit_rate);
827         }
828     }
829
830   if ( ASDCP_SUCCESS(result) )
831     {
832       result = Parser.Reset();
833       ui32_t duration = 0;
834
835       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
836         {
837           result = Parser.ReadFrame(FrameBuffer);
838
839           if ( ASDCP_SUCCESS(result) )
840             {
841               if ( Options.verbose_flag )
842                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
843
844               if ( ! Options.no_write_flag )
845                 {
846                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
847
848                   // The Writer class will forward the last block of ciphertext
849                   // to the encryption context for use as the IV for the next
850                   // frame. If you want to use non-sequitur IV values, un-comment
851                   // the following  line of code.
852                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
853                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
854                 }
855             }
856         }
857
858       if ( result == RESULT_ENDOFFILE )
859         result = RESULT_OK;
860     }
861
862   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
863     result = Writer.Finalize();
864
865   return result;
866 }
867
868
869
870
871 //------------------------------------------------------------------------------------------
872 // TimedText essence
873
874
875 // Write one or more plaintext timed text streams to a plaintext AS-02 file
876 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
877 //
878 Result_t
879 write_timed_text_file(CommandOptions& Options)
880 {
881   AESEncContext*    Context = 0;
882   HMACContext*      HMAC = 0;
883   AS_02::TimedText::ST2052_TextParser  Parser;
884   AS_02::TimedText::MXFWriter    Writer;
885   TimedText::FrameBuffer  FrameBuffer;
886   TimedText::TimedTextDescriptor TDesc;
887   byte_t            IV_buf[CBC_BLOCK_SIZE];
888   Kumu::FortunaRNG  RNG;
889
890   // set up essence parser
891   Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
892
893   // set up MXF writer
894   if ( ASDCP_SUCCESS(result) )
895     {
896       Parser.FillTimedTextDescriptor(TDesc);
897       TDesc.EditRate = Options.edit_rate;
898       TDesc.ContainerDuration = Options.duration;
899       FrameBuffer.Capacity(Options.fb_size);
900
901       if ( Options.verbose_flag )
902         {
903           fputs("IMF Timed-Text Descriptor:\n", stderr);
904           TimedText::DescriptorDump(TDesc);
905         }
906     }
907
908   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
909     {
910       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
911       Info.LabelSetType = LS_MXF_SMPTE;
912
913       if ( Options.asset_id_flag )
914         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
915       else
916         Kumu::GenRandomUUID(Info.AssetUUID);
917
918       // configure encryption
919       if( Options.key_flag )
920         {
921           Kumu::GenRandomUUID(Info.ContextID);
922           Info.EncryptedEssence = true;
923
924           if ( Options.key_id_flag )
925             {
926               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
927             }
928           else
929             {
930               create_random_uuid(Info.CryptographicKeyID);
931             }
932
933           Context = new AESEncContext;
934           result = Context->InitKey(Options.key_value);
935
936           if ( ASDCP_SUCCESS(result) )
937             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
938
939           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
940             {
941               Info.UsesHMAC = true;
942               HMAC = new HMACContext;
943               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
944             }
945         }
946
947       if ( ASDCP_SUCCESS(result) )
948         result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
949     }
950
951   if ( ASDCP_FAILURE(result) )
952     return result;
953
954   std::string XMLDoc;
955   TimedText::ResourceList_t::const_iterator ri;
956
957   result = Parser.ReadTimedTextResource(XMLDoc);
958
959   if ( ASDCP_SUCCESS(result) )
960     result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
961
962   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
963     {
964       result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
965
966       if ( ASDCP_SUCCESS(result) )
967         {
968           if ( Options.verbose_flag )
969             FrameBuffer.Dump(stderr, Options.fb_dump_size);
970
971           if ( ! Options.no_write_flag )
972             {
973               result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
974
975               // The Writer class will forward the last block of ciphertext
976               // to the encryption context for use as the IV for the next
977               // frame. If you want to use non-sequitur IV values, un-comment
978               // the following  line of code.
979               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
980               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
981             }
982         }
983
984       if ( result == RESULT_ENDOFFILE )
985         result = RESULT_OK;
986     }
987
988   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
989     result = Writer.Finalize();
990
991   return result;
992 }
993
994
995 //
996 int
997 main(int argc, const char** argv)
998 {
999   Result_t result = RESULT_OK;
1000   char     str_buf[64];
1001   g_dict = &ASDCP::DefaultSMPTEDict();
1002   assert(g_dict);
1003
1004   CommandOptions Options(argc, argv);
1005
1006   if ( Options.version_flag )
1007     banner();
1008
1009   if ( Options.help_flag )
1010     usage();
1011
1012   if ( Options.show_ul_values_flag )
1013     {
1014       g_dict->Dump(stdout);
1015     }
1016
1017   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1018     return 0;
1019
1020   if ( Options.error_flag )
1021     {
1022       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1023       return 3;
1024     }
1025
1026   EssenceType_t EssenceType;
1027   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1028
1029   if ( ASDCP_SUCCESS(result) )
1030     {
1031       switch ( EssenceType )
1032         {
1033         case ESS_JPEG_2000:
1034           result = write_JP2K_file(Options);
1035           break;
1036
1037         case ESS_PCM_24b_48k:
1038         case ESS_PCM_24b_96k:
1039           result = write_PCM_file(Options);
1040           break;
1041
1042         case ESS_TIMED_TEXT:
1043           result = write_timed_text_file(Options);
1044           break;
1045
1046         default:
1047           fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
1048                   Options.filenames.front().c_str());
1049           return 5;
1050         }
1051     }
1052
1053   if ( ASDCP_FAILURE(result) )
1054     {
1055       fputs("Program stopped on error.\n", stderr);
1056
1057       if ( result != RESULT_FAIL )
1058         {
1059           fputs(result, stderr);
1060           fputc('\n', stderr);
1061         }
1062
1063       return 1;
1064     }
1065
1066   return 0;
1067 }
1068
1069
1070 //
1071 // end as-02-wrap.cpp
1072 //