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