compile fixes
[asdcplib.git] / src / asdcp-wrap.cpp
1 /*
2 Copyright (c) 2003-2015, John Hurst
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8 1. Redistributions of source code must retain the above copyright
9    notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11    notice, this list of conditions and the following disclaimer in the
12    documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14    derived from this software without specific prior written permission.
15
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27 /*! \file    asdcp-wrap.cpp
28     \version $Id$
29     \brief   AS-DCP file manipulation utility
30
31   This program wraps d-cinema essence (picture, sound or text) into an AS-DCP
32   MXF file.
33
34   For more information about asdcplib, please refer to the header file AS_DCP.h
35
36   WARNING: While the asdcplib library attempts to provide a complete and secure
37   implementation of the cryptographic features of the AS-DCP file formats, this
38   unit test program is NOT secure and is therefore NOT SUITABLE FOR USE in a
39   production environment without some modification.
40
41   In particular, this program uses weak IV generation and externally generated
42   plaintext keys. These shortcomings exist because cryptographic-quality
43   random number generation and key management are outside the scope of the
44   asdcplib library. Developers using asdcplib for commercial implementations
45   claiming SMPTE conformance are expected to provide proper implementations of
46   these features.
47 */
48
49 #include <KM_fileio.h>
50 #include <KM_prng.h>
51 #include <AtmosSyncChannel_Mixer.h>
52 #include <AS_DCP.h>
53 #include <PCMParserList.h>
54 #include <Metadata.h>
55
56 using namespace ASDCP;
57
58 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
59
60 const byte_t P_HFR_UL_2K[16] = {
61   0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
62   0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
63 };
64
65 const ASDCP::Dictionary *g_dict = 0;
66
67 //------------------------------------------------------------------------------------------
68 //
69 // command line option parser class
70
71 static const char* PROGRAM_NAME = "asdcp-wrap";  // program name for messages
72
73 // local program identification info written to file headers
74 class MyInfo : public WriterInfo
75 {
76 public:
77   MyInfo()
78   {
79       static byte_t default_ProductUUID_Data[UUIDlen] =
80       { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
81         0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
82
83       memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
84       CompanyName = "WidgetCo";
85       ProductName = "asdcp-wrap";
86       ProductVersion = ASDCP::Version();
87   }
88 } s_MyInfo;
89
90
91
92 // Increment the iterator, test for an additional non-option command line argument.
93 // Causes the caller to return if there are no remaining arguments or if the next
94 // argument begins with '-'.
95 #define TEST_EXTRA_ARG(i,c)                                             \
96   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
97     fprintf(stderr, "Argument not found for option -%c.\n", (c));       \
98     return;                                                             \
99   }
100
101 //
102 static void
103 create_random_uuid(byte_t* uuidbuf)
104 {
105   Kumu::UUID tmp_id;
106   GenRandomValue(tmp_id);
107   memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
108 }
109
110 //
111 void
112 banner(FILE* stream = stdout)
113 {
114   fprintf(stream, "\n\
115 %s (asdcplib %s)\n\n\
116 Copyright (c) 2003-2015 John Hurst\n\n\
117 asdcplib may be copied only under the terms of the license found at\n\
118 the top of every file in the asdcplib distribution kit.\n\n\
119 Specify the -h (help) option for further information about %s\n\n",
120           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
121 }
122
123 //
124 void
125 usage(FILE* stream = stdout)
126 {
127   fprintf(stream, "\
128 USAGE: %s [-h|-help] [-V]\n\
129 \n\
130        %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
131           [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
132           [-l <label>] [-L] [-M] [-m <expr>] [-p <frame-rate>] [-s] [-v]\n\
133           [-W] [-z|-Z] <input-file>+ <output-file>\n\n",
134           PROGRAM_NAME, PROGRAM_NAME);
135
136   fprintf(stream, "\
137 Options:\n\
138   -3                - Create a stereoscopic image file. Expects two\n\
139                       directories of JP2K codestreams (directories must have\n\
140                       an equal number of frames; the left eye is first)\n\
141   -A <UL>           - Set DataEssenceCoding UL value in an Aux Data file\n\
142   -C <UL>           - Set ChannelAssignment UL value in a PCM file\n\
143   -h | -help        - Show help\n\
144   -V                - Show version information\n\
145   -e                - Encrypt MPEG or JP2K headers (default)\n\
146   -E                - Do not encrypt MPEG or JP2K headers\n\
147   -j <key-id-str>   - Write key ID instead of creating a random value\n\
148   -k <key-string>   - Use key for ciphertext operations\n\
149   -M                - Do not create HMAC values when writing\n\
150   -m <expr>         - Write MCA labels using <expr>.  Example:\n\
151                         51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
152                         Note: The symbol '-' may be used for an unlabeled\n\
153                               channel, but not within a soundfield.\n\
154   -a <UUID>         - Specify the Asset ID of the file\n\
155   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
156                       Defaults to 4,194,304 (4MB)\n\
157   -d <duration>     - Number of frames to process, default all\n\
158   -f <start-frame>  - Starting frame number, default 0\n\
159   -l <label>        - Use given channel format label when writing MXF sound\n\
160                       files. SMPTE 429-2 labels: '5.1', '6.1', '7.1',\n\
161                       '7.1DS', 'WTF'\n\
162                       Default is no label (valid for Interop only).\n\
163   -L                - Write SMPTE UL values instead of MXF Interop\n\
164   -P <UL>           - Set PictureEssenceCoding UL value in a JP2K file\n\
165   -p <rate>         - fps of picture when wrapping PCM or JP2K:\n\
166                       Use one of [23|24|25|30|48|50|60], 24 is default\n\
167   -s                - Insert a Dolby Atmos synchronization channel when\n\
168                       wrapping PCM. This implies a -L option(SMPTE ULs) and \n\
169                       will overide -C and -l options with Configuration 4 \n\
170                       Channel Assigment and no format label respectively. \n\
171   -v                - Verbose, prints informative messages to stderr\n\
172   -W                - Read input file only, do not write source file\n\
173   -z                - Fail if j2c inputs have unequal parameters (default)\n\
174   -Z                - Ignore unequal parameters in j2c inputs\n\
175 \n\
176   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
177          o All option arguments must be separated from the option by whitespace.\n\
178          o An argument of \"23\" to the -p option will be interpreted\n\
179            as 24000/1001 fps.\n\
180 \n");
181 }
182
183 //
184 PCM::ChannelFormat_t
185 decode_channel_fmt(const std::string& label_name)
186 {
187   if ( label_name == "5.1" )
188     return PCM::CF_CFG_1;
189
190   else if ( label_name == "6.1" )
191     return PCM::CF_CFG_2;
192
193   else if ( label_name == "7.1" )
194     return PCM::CF_CFG_3;
195
196   else if ( label_name == "WTF" )
197     return PCM::CF_CFG_4;
198
199   else if ( label_name == "7.1DS" )
200     return PCM::CF_CFG_5;
201
202   fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
203   fprintf(stderr, "Expecting '5.1', '6.1', '7.1', '7.1DS' or 'WTF'\n");
204   return PCM::CF_NONE;
205 }
206
207 //
208 //
209 class CommandOptions
210 {
211   CommandOptions();
212
213 public:
214   bool   error_flag;     // true if the given options are in error or not complete
215   bool   key_flag;       // true if an encryption key was given
216   bool   asset_id_flag;  // true if an asset ID was given
217   bool   encrypt_header_flag; // true if mpeg headers are to be encrypted
218   bool   write_hmac;     // true if HMAC values are to be generated and written
219   bool   verbose_flag;   // true if the verbose option was selected
220   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
221   bool   no_write_flag;  // true if no output files are to be written
222   bool   version_flag;   // true if the version display option was selected
223   bool   help_flag;      // true if the help display option was selected
224   bool   stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
225   bool   write_partial_pcm_flag; // if true, write the last frame of PCM input even when it is incomplete
226   ui32_t start_frame;    // frame number to begin processing
227   ui32_t duration;       // number of frames to be processed
228   bool   use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
229   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
230   ui32_t picture_rate;   // fps of picture when wrapping PCM
231   ui32_t fb_size;        // size of picture frame buffer
232   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
233   bool   key_id_flag;    // true if a key ID was given
234   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
235   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
236   PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
237   std::string out_file; //
238   bool show_ul_values_flag;    /// if true, dump the UL table before going to work.
239   Kumu::PathList_t filenames;  // list of filenames to be processed
240   UL channel_assignment;
241   UL picture_coding;
242   UL aux_data_coding;
243   bool dolby_atmos_sync_flag;  // if true, insert a Dolby Atmos Synchronization channel.
244   ui32_t ffoa;  /// first frame of action for atmos wrapping
245   ui32_t max_channel_count; /// max channel count for atmos wrapping
246   ui32_t max_object_count; /// max object count for atmos wrapping
247   ASDCP::MXF::ASDCP_MCAConfigParser mca_config;
248
249   //
250   Rational PictureRate()
251   {
252     if ( picture_rate == 16 ) return EditRate_16;
253     if ( picture_rate == 18 ) return EditRate_18;
254     if ( picture_rate == 20 ) return EditRate_20;
255     if ( picture_rate == 22 ) return EditRate_22;
256     if ( picture_rate == 23 ) return EditRate_23_98;
257     if ( picture_rate == 24 ) return EditRate_24;
258     if ( picture_rate == 25 ) return EditRate_25;
259     if ( picture_rate == 30 ) return EditRate_30;
260     if ( picture_rate == 48 ) return EditRate_48;
261     if ( picture_rate == 50 ) return EditRate_50;
262     if ( picture_rate == 60 ) return EditRate_60;
263     if ( picture_rate == 96 ) return EditRate_96;
264     if ( picture_rate == 100 ) return EditRate_100;
265     if ( picture_rate == 120 ) return EditRate_120;
266     return EditRate_24;
267   }
268
269   //
270   const char* szPictureRate()
271   {
272     if ( picture_rate == 16 ) return "16";
273     if ( picture_rate == 18 ) return "18.182";
274     if ( picture_rate == 20 ) return "20";
275     if ( picture_rate == 22 ) return "21.818";
276     if ( picture_rate == 23 ) return "23.976";
277     if ( picture_rate == 24 ) return "24";
278     if ( picture_rate == 25 ) return "25";
279     if ( picture_rate == 30 ) return "30";
280     if ( picture_rate == 48 ) return "48";
281     if ( picture_rate == 50 ) return "50";
282     if ( picture_rate == 60 ) return "60";
283     if ( picture_rate == 96 ) return "96";
284     if ( picture_rate == 100 ) return "100";
285     if ( picture_rate == 120 ) return "120";
286     return "24";
287   }
288
289   //
290   CommandOptions(int argc, const char** argv) :
291     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
292     encrypt_header_flag(true), write_hmac(true),
293     verbose_flag(false), fb_dump_size(0),
294     no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
295     write_partial_pcm_flag(false), start_frame(0),
296     duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
297     fb_size(FRAME_BUFFER_SIZE),
298     channel_fmt(PCM::CF_NONE),
299     ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties
300     dolby_atmos_sync_flag(false),
301     show_ul_values_flag(false),
302     mca_config(g_dict)
303   {
304     memset(key_value, 0, KeyLen);
305     memset(key_id_value, 0, UUIDlen);
306
307     for ( int i = 1; i < argc; i++ )
308       {
309
310         if ( (strcmp( argv[i], "-help") == 0) )
311           {
312             help_flag = true;
313             continue;
314           }
315
316         if ( argv[i][0] == '-'
317              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
318              && argv[i][2] == 0 )
319           {
320             switch ( argv[i][1] )
321               {
322               case '3': stereo_image_flag = true; break;
323
324               case 'A':
325                 TEST_EXTRA_ARG(i, 'A');
326                 if ( ! aux_data_coding.DecodeHex(argv[i]) )
327                   {
328                     fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
329                     return;
330                   }
331                 break;
332
333               case 'a':
334                 asset_id_flag = true;
335                 TEST_EXTRA_ARG(i, 'a');
336                 {
337                   ui32_t length;
338                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
339
340                   if ( length != UUIDlen )
341                     {
342                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
343                       return;
344                     }
345                 }
346                 break;
347
348               case 'b':
349                 TEST_EXTRA_ARG(i, 'b');
350                 fb_size = abs(atoi(argv[i]));
351
352                 if ( verbose_flag )
353                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
354
355                 break;
356
357               case 'C':
358                 TEST_EXTRA_ARG(i, 'C');
359                 if ( ! channel_assignment.DecodeHex(argv[i]) )
360                   {
361                     fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
362                     return;
363                   }
364                 break;
365
366               case 'd':
367                 TEST_EXTRA_ARG(i, 'd');
368                 duration = abs(atoi(argv[i]));
369                 break;
370
371               case 'E': encrypt_header_flag = false; break;
372               case 'e': encrypt_header_flag = true; break;
373
374               case 'f':
375                 TEST_EXTRA_ARG(i, 'f');
376                 start_frame = abs(atoi(argv[i]));
377                 break;
378
379               case 'g': write_partial_pcm_flag = true; break;
380               case 'h': help_flag = true; break;
381
382               case 'j': key_id_flag = true;
383                 TEST_EXTRA_ARG(i, 'j');
384                 {
385                   ui32_t length;
386                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
387
388                   if ( length != UUIDlen )
389                     {
390                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
391                       return;
392                     }
393                 }
394                 break;
395
396               case 'k': key_flag = true;
397                 TEST_EXTRA_ARG(i, 'k');
398                 {
399                   ui32_t length;
400                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
401
402                   if ( length != KeyLen )
403                     {
404                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
405                       return;
406                     }
407                 }
408                 break;
409
410               case 'l':
411                 TEST_EXTRA_ARG(i, 'l');
412                 channel_fmt = decode_channel_fmt(argv[i]);
413                 break;
414
415               case 'L': use_smpte_labels = true; break;
416               case 'M': write_hmac = false; break;
417
418               case 'm':
419                 TEST_EXTRA_ARG(i, 'm');
420                 if ( ! mca_config.DecodeString(argv[i]) )
421                   {
422                     return;
423                   }
424                 break;
425
426               case 'P':
427                 TEST_EXTRA_ARG(i, 'P');
428                 if ( ! picture_coding.DecodeHex(argv[i]) )
429                   {
430                     fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
431                     return;
432                   }
433                 break;
434
435               case 'p':
436                 TEST_EXTRA_ARG(i, 'p');
437                 picture_rate = abs(atoi(argv[i]));
438                 break;
439
440               case 's': dolby_atmos_sync_flag = true; break;
441               case 'u': show_ul_values_flag = true; break;
442               case 'V': version_flag = true; break;
443               case 'v': verbose_flag = true; break;
444               case 'W': no_write_flag = true; break;
445               case 'Z': j2c_pedantic = false; break;
446               case 'z': j2c_pedantic = true; break;
447
448               default:
449                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
450                 return;
451               }
452           }
453         else
454           {
455
456             if ( argv[i][0] != '-' )
457               {
458                 filenames.push_back(argv[i]);
459               }
460             else
461               {
462                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
463                 return;
464               }
465           }
466       }
467
468     if ( help_flag || version_flag )
469       return;
470
471     if ( filenames.size() < 2 )
472       {
473         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
474         return;
475       }
476
477     out_file = filenames.back();
478     filenames.pop_back();
479     error_flag = false;
480   }
481 };
482
483 //------------------------------------------------------------------------------------------
484 // MPEG2 essence
485
486 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
487 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
488 //
489 Result_t
490 write_MPEG2_file(CommandOptions& Options)
491 {
492   AESEncContext*     Context = 0;
493   HMACContext*       HMAC = 0;
494   MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
495   MPEG2::Parser      Parser;
496   MPEG2::MXFWriter   Writer;
497   MPEG2::VideoDescriptor VDesc;
498   byte_t             IV_buf[CBC_BLOCK_SIZE];
499   Kumu::FortunaRNG   RNG;
500
501   // set up essence parser
502   Result_t result = Parser.OpenRead(Options.filenames.front());
503
504   // set up MXF writer
505   if ( ASDCP_SUCCESS(result) )
506     {
507       Parser.FillVideoDescriptor(VDesc);
508
509       if ( Options.verbose_flag )
510         {
511           fputs("MPEG-2 Pictures\n", stderr);
512           fputs("VideoDescriptor:\n", stderr);
513           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
514           MPEG2::VideoDescriptorDump(VDesc);
515         }
516     }
517
518   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
519     {
520       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
521       if ( Options.asset_id_flag )
522         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
523       else
524         Kumu::GenRandomUUID(Info.AssetUUID);
525
526       if ( Options.use_smpte_labels )
527         {
528           Info.LabelSetType = LS_MXF_SMPTE;
529           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
530         }
531
532       // configure encryption
533       if( Options.key_flag )
534         {
535           Kumu::GenRandomUUID(Info.ContextID);
536           Info.EncryptedEssence = true;
537
538           if ( Options.key_id_flag )
539             {
540               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
541             }
542           else
543             {
544               create_random_uuid(Info.CryptographicKeyID);
545             }
546
547           Context = new AESEncContext;
548           result = Context->InitKey(Options.key_value);
549
550           if ( ASDCP_SUCCESS(result) )
551             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
552
553           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
554             {
555               Info.UsesHMAC = true;
556               HMAC = new HMACContext;
557               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
558             }
559         }
560
561       if ( ASDCP_SUCCESS(result) )
562         result = Writer.OpenWrite(Options.out_file, Info, VDesc);
563     }
564
565   if ( ASDCP_SUCCESS(result) )
566     // loop through the frames
567     {
568       result = Parser.Reset();
569       ui32_t duration = 0;
570
571       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
572         {
573               result = Parser.ReadFrame(FrameBuffer);
574
575               if ( ASDCP_SUCCESS(result) )
576                 {
577                   if ( Options.verbose_flag )
578                     FrameBuffer.Dump(stderr, Options.fb_dump_size);
579
580                   if ( Options.encrypt_header_flag )
581                     FrameBuffer.PlaintextOffset(0);
582                 }
583
584           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
585             {
586               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
587
588               // The Writer class will forward the last block of ciphertext
589               // to the encryption context for use as the IV for the next
590               // frame. If you want to use non-sequitur IV values, un-comment
591               // the following  line of code.
592               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
593               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
594             }
595         }
596
597       if ( result == RESULT_ENDOFFILE )
598         result = RESULT_OK;
599     }
600
601   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
602     result = Writer.Finalize();
603
604   return result;
605 }
606
607
608 //------------------------------------------------------------------------------------------
609
610 // return false if an error is discovered
611 bool
612 check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc)
613 {
614   Rational rate = Options.PictureRate();
615   if ( rate != EditRate_96 && rate != EditRate_100 && rate != EditRate_120 )
616     return true;
617
618   if ( PDesc.StoredWidth > 2048 )
619     {
620       fprintf(stderr, "P-HFR files currently limited to 2K.\n");
621       return false;
622     }
623
624   if ( ! Options.use_smpte_labels )
625     {
626       fprintf(stderr, "P-HFR files must be written using SMPTE labels. Use option '-L'.\n");
627       return false;
628     }
629
630   // do not set the label if the user has already done so
631   if ( ! Options.picture_coding.HasValue() )
632     Options.picture_coding = UL(P_HFR_UL_2K);
633
634   return true;
635 }
636
637 //------------------------------------------------------------------------------------------
638 // JPEG 2000 essence
639
640 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
641 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
642 //
643 Result_t
644 write_JP2K_S_file(CommandOptions& Options)
645 {
646   AESEncContext*          Context = 0;
647   HMACContext*            HMAC = 0;
648   JP2K::MXFSWriter        Writer;
649   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
650   JP2K::PictureDescriptor PDesc;
651   JP2K::SequenceParser    ParserLeft, ParserRight;
652   byte_t                  IV_buf[CBC_BLOCK_SIZE];
653   Kumu::FortunaRNG        RNG;
654
655   if ( Options.filenames.size() != 2 )
656     {
657       fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
658       return RESULT_FAIL;
659     }
660
661   // set up essence parser
662   Result_t result = ParserLeft.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
663
664   if ( ASDCP_SUCCESS(result) )
665     {
666       Options.filenames.pop_front();
667       result = ParserRight.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
668     }
669
670   // set up MXF writer
671   if ( ASDCP_SUCCESS(result) )
672     {
673       ParserLeft.FillPictureDescriptor(PDesc);
674       PDesc.EditRate = Options.PictureRate();
675
676       if ( Options.verbose_flag )
677         {
678           fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
679           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
680           JP2K::PictureDescriptorDump(PDesc);
681         }
682     }
683
684   if ( ! check_phfr_params(Options, PDesc) )
685     return RESULT_FAIL;
686
687   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
688     {
689       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
690       if ( Options.asset_id_flag )
691         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
692       else
693         Kumu::GenRandomUUID(Info.AssetUUID);
694
695       if ( Options.use_smpte_labels )
696         {
697           Info.LabelSetType = LS_MXF_SMPTE;
698           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
699         }
700
701       // configure encryption
702       if( Options.key_flag )
703         {
704           Kumu::GenRandomUUID(Info.ContextID);
705           Info.EncryptedEssence = true;
706
707           if ( Options.key_id_flag )
708             {
709               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
710             }
711           else
712             {
713               create_random_uuid(Info.CryptographicKeyID);
714             }
715
716           Context = new AESEncContext;
717           result = Context->InitKey(Options.key_value);
718
719           if ( ASDCP_SUCCESS(result) )
720             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
721
722           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
723             {
724               Info.UsesHMAC = true;
725               HMAC = new HMACContext;
726               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
727             }
728         }
729
730       if ( ASDCP_SUCCESS(result) )
731         result = Writer.OpenWrite(Options.out_file, Info, PDesc);
732
733       if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
734         {
735           MXF::RGBAEssenceDescriptor *descriptor = 0;
736           Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
737                                                 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
738           descriptor->PictureEssenceCoding = Options.picture_coding;
739         }
740     }
741
742   if ( ASDCP_SUCCESS(result) )
743     {
744       ui32_t duration = 0;
745       result = ParserLeft.Reset();
746       if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
747
748       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
749         {
750           result = ParserLeft.ReadFrame(FrameBuffer);
751
752           if ( ASDCP_SUCCESS(result) )
753             {
754               if ( Options.verbose_flag )
755                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
756
757               if ( Options.encrypt_header_flag )
758                 FrameBuffer.PlaintextOffset(0);
759             }
760
761           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
762             result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
763
764           if ( ASDCP_SUCCESS(result) )
765             result = ParserRight.ReadFrame(FrameBuffer);
766
767           if ( ASDCP_SUCCESS(result) )
768             {
769               if ( Options.verbose_flag )
770                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
771
772               if ( Options.encrypt_header_flag )
773                 FrameBuffer.PlaintextOffset(0);
774             }
775
776           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
777             result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
778         }
779
780       if ( result == RESULT_ENDOFFILE )
781         result = RESULT_OK;
782     }
783
784   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
785     result = Writer.Finalize();
786
787   return result;
788 }
789
790 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
791 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
792 //
793 Result_t
794 write_JP2K_file(CommandOptions& Options)
795 {
796   AESEncContext*          Context = 0;
797   HMACContext*            HMAC = 0;
798   JP2K::MXFWriter         Writer;
799   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
800   JP2K::PictureDescriptor PDesc;
801   JP2K::SequenceParser    Parser;
802   byte_t                  IV_buf[CBC_BLOCK_SIZE];
803   Kumu::FortunaRNG        RNG;
804
805   // set up essence parser
806   Result_t result = Parser.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
807
808   // set up MXF writer
809   if ( ASDCP_SUCCESS(result) )
810     {
811       Parser.FillPictureDescriptor(PDesc);
812       PDesc.EditRate = Options.PictureRate();
813
814       if ( Options.verbose_flag )
815         {
816           fprintf(stderr, "JPEG 2000 pictures\n");
817           fputs("PictureDescriptor:\n", stderr);
818           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
819           JP2K::PictureDescriptorDump(PDesc);
820         }
821     }
822
823   if ( ! check_phfr_params(Options, PDesc) )
824     return RESULT_FAIL;
825
826   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
827     {
828       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
829       if ( Options.asset_id_flag )
830         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
831       else
832         Kumu::GenRandomUUID(Info.AssetUUID);
833
834       if ( Options.use_smpte_labels )
835         {
836           Info.LabelSetType = LS_MXF_SMPTE;
837           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
838         }
839
840       // configure encryption
841       if( Options.key_flag )
842         {
843           Kumu::GenRandomUUID(Info.ContextID);
844           Info.EncryptedEssence = true;
845
846           if ( Options.key_id_flag )
847             {
848               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
849             }
850           else
851             {
852               create_random_uuid(Info.CryptographicKeyID);
853             }
854
855           Context = new AESEncContext;
856           result = Context->InitKey(Options.key_value);
857
858           if ( ASDCP_SUCCESS(result) )
859             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
860
861           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
862             {
863               Info.UsesHMAC = true;
864               HMAC = new HMACContext;
865               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
866             }
867         }
868
869       if ( ASDCP_SUCCESS(result) )
870         result = Writer.OpenWrite(Options.out_file, Info, PDesc);
871
872       if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
873         {
874           MXF::RGBAEssenceDescriptor *descriptor = 0;
875           Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
876                                                 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
877           descriptor->PictureEssenceCoding = Options.picture_coding;
878         }
879     }
880
881   if ( ASDCP_SUCCESS(result) )
882     {
883       ui32_t duration = 0;
884       result = Parser.Reset();
885
886       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
887         {
888               result = Parser.ReadFrame(FrameBuffer);
889
890               if ( ASDCP_SUCCESS(result) )
891                 {
892                   if ( Options.verbose_flag )
893                     FrameBuffer.Dump(stderr, Options.fb_dump_size);
894
895                   if ( Options.encrypt_header_flag )
896                     FrameBuffer.PlaintextOffset(0);
897                 }
898
899           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
900             {
901               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
902
903               // The Writer class will forward the last block of ciphertext
904               // to the encryption context for use as the IV for the next
905               // frame. If you want to use non-sequitur IV values, un-comment
906               // the following  line of code.
907               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
908               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
909             }
910         }
911
912       if ( result == RESULT_ENDOFFILE )
913         result = RESULT_OK;
914     }
915
916   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
917     result = Writer.Finalize();
918
919   return result;
920 }
921
922 //------------------------------------------------------------------------------------------
923 // PCM essence
924
925
926 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
927 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
928 //
929 Result_t
930 write_PCM_file(CommandOptions& Options)
931 {
932   AESEncContext*    Context = 0;
933   HMACContext*      HMAC = 0;
934   PCMParserList     Parser;
935   PCM::MXFWriter    Writer;
936   PCM::FrameBuffer  FrameBuffer;
937   PCM::AudioDescriptor ADesc;
938   Rational          PictureRate = Options.PictureRate();
939   byte_t            IV_buf[CBC_BLOCK_SIZE];
940   Kumu::FortunaRNG  RNG;
941
942   // set up essence parser
943   Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
944
945   // set up MXF writer
946   if ( ASDCP_SUCCESS(result) )
947     {
948       Parser.FillAudioDescriptor(ADesc);
949
950       ADesc.EditRate = PictureRate;
951       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
952       ADesc.ChannelFormat = Options.channel_fmt;
953
954       if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE && Options.mca_config.empty() )
955         {
956           fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see options -C, -l and -m)\n");
957         }
958
959       if ( Options.verbose_flag )
960         {
961           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
962                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
963                   Options.szPictureRate(),
964                   PCM::CalcSamplesPerFrame(ADesc));
965           fputs("AudioDescriptor:\n", stderr);
966           PCM::AudioDescriptorDump(ADesc);
967         }
968     }
969
970   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
971     {
972       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
973       if ( Options.asset_id_flag )
974         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
975       else
976         Kumu::GenRandomUUID(Info.AssetUUID);
977
978       if ( Options.use_smpte_labels )
979         {
980           Info.LabelSetType = LS_MXF_SMPTE;
981           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
982         }
983
984       // configure encryption
985       if( Options.key_flag )
986         {
987           Kumu::GenRandomUUID(Info.ContextID);
988           Info.EncryptedEssence = true;
989
990           if ( Options.key_id_flag )
991             {
992               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
993             }
994           else
995             {
996               create_random_uuid(Info.CryptographicKeyID);
997             }
998
999           Context = new AESEncContext;
1000           result = Context->InitKey(Options.key_value);
1001
1002           if ( ASDCP_SUCCESS(result) )
1003             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1004
1005           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1006             {
1007               Info.UsesHMAC = true;
1008               HMAC = new HMACContext;
1009               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1010             }
1011         }
1012
1013       if ( ASDCP_SUCCESS(result) )
1014         result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1015
1016       if ( ASDCP_SUCCESS(result)
1017            && ( Options.channel_assignment.HasValue()
1018                 || ! Options.mca_config.empty() ) )
1019         {
1020           MXF::WaveAudioDescriptor *essence_descriptor = 0;
1021           Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_WaveAudioDescriptor),
1022                                                 reinterpret_cast<MXF::InterchangeObject**>(&essence_descriptor));
1023           assert(essence_descriptor);
1024
1025           if ( Options.mca_config.empty() )
1026             {
1027               essence_descriptor->ChannelAssignment = Options.channel_assignment;
1028             }
1029           else
1030             {
1031               if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1032                 {
1033                   fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1034                           Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1035                   return RESULT_FAIL;
1036                 }
1037
1038               essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
1039
1040               // add descriptors to the essence_descriptor and header
1041               ASDCP::MXF::InterchangeObject_list_t::iterator i;
1042               for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
1043                 {
1044                   if ( (*i)->GetUL() != UL(g_dict->ul(MDD_AudioChannelLabelSubDescriptor))
1045                        && (*i)->GetUL() != UL(g_dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
1046                        && (*i)->GetUL() != UL(g_dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
1047                     {
1048                       fprintf(stderr, "Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
1049                       (*i)->Dump();
1050                     }
1051
1052                   Writer.OP1aHeader().AddChildObject(*i);
1053                   essence_descriptor->SubDescriptors.push_back((*i)->InstanceUID);
1054                   *i = 0; // parent will only free the ones we don't keep
1055                 }
1056             }
1057         }
1058     }
1059
1060   if ( ASDCP_SUCCESS(result) )
1061     {
1062       result = Parser.Reset();
1063       ui32_t duration = 0;
1064
1065       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1066         {
1067           result = Parser.ReadFrame(FrameBuffer);
1068
1069           if ( ASDCP_SUCCESS(result) )
1070             {
1071               if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1072                 {
1073                   fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1074                   fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1075
1076                   if ( Options.write_partial_pcm_flag )
1077                     {
1078                       result = RESULT_ENDOFFILE;
1079                       continue;
1080                     }
1081                 }
1082
1083               if ( Options.verbose_flag )
1084                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1085
1086               if ( ! Options.no_write_flag )
1087                 {
1088                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1089
1090                   // The Writer class will forward the last block of ciphertext
1091                   // to the encryption context for use as the IV for the next
1092                   // frame. If you want to use non-sequitur IV values, un-comment
1093                   // the following  line of code.
1094                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1095                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1096                 }
1097             }
1098         }
1099
1100       if ( result == RESULT_ENDOFFILE )
1101         result = RESULT_OK;
1102     }
1103
1104   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1105     result = Writer.Finalize();
1106
1107   return result;
1108 }
1109
1110 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a plaintext ASDCP file
1111 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a ciphertext ASDCP file
1112 //
1113 Result_t
1114 write_PCM_with_ATMOS_sync_file(CommandOptions& Options)
1115 {
1116   AESEncContext*        Context = 0;
1117   HMACContext*          HMAC = 0;
1118   PCM::MXFWriter        Writer;
1119   PCM::FrameBuffer      FrameBuffer;
1120   PCM::AudioDescriptor  ADesc;
1121   Rational              PictureRate = Options.PictureRate();
1122   byte_t                IV_buf[CBC_BLOCK_SIZE];
1123   Kumu::FortunaRNG      RNG;
1124
1125   WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1126   if ( Options.asset_id_flag )
1127         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1128   else
1129         Kumu::GenRandomUUID(Info.AssetUUID);
1130   AtmosSyncChannelMixer Mixer(Info.AssetUUID);
1131
1132   // set up essence parser
1133   Result_t result = Mixer.OpenRead(Options.filenames, PictureRate);
1134
1135   // set up MXF writer
1136   if ( ASDCP_SUCCESS(result) )
1137   {
1138     Mixer.FillAudioDescriptor(ADesc);
1139
1140     ADesc.EditRate = PictureRate;
1141     FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1142     ADesc.ChannelFormat = PCM::CF_CFG_4;
1143
1144     if ( Options.verbose_flag )
1145         {
1146           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1147               ADesc.AudioSamplingRate.Quotient() / 1000.0,
1148               Options.szPictureRate(),
1149               PCM::CalcSamplesPerFrame(ADesc));
1150           fputs("AudioDescriptor:\n", stderr);
1151           PCM::AudioDescriptorDump(ADesc);
1152         }
1153   }
1154
1155   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1156   {
1157     Info.LabelSetType = LS_MXF_SMPTE;
1158     fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1159
1160     // configure encryption
1161     if( Options.key_flag )
1162         {
1163           Kumu::GenRandomUUID(Info.ContextID);
1164           Info.EncryptedEssence = true;
1165
1166           if ( Options.key_id_flag )
1167             {
1168               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1169             }
1170           else
1171             {
1172               create_random_uuid(Info.CryptographicKeyID);
1173             }
1174
1175           Context = new AESEncContext;
1176           result = Context->InitKey(Options.key_value);
1177
1178           if ( ASDCP_SUCCESS(result) )
1179             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1180
1181           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1182       {
1183         Info.UsesHMAC = true;
1184         HMAC = new HMACContext;
1185         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1186       }
1187         }
1188
1189     if ( ASDCP_SUCCESS(result) )
1190       result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1191   }
1192
1193   if ( ASDCP_SUCCESS(result) )
1194   {
1195     result = Mixer.Reset();
1196     ui32_t duration = 0;
1197
1198     while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1199         {
1200           result = Mixer.ReadFrame(FrameBuffer);
1201
1202           if ( ASDCP_SUCCESS(result) )
1203       {
1204         if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1205                 {
1206                   fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1207                   fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1208
1209                   if ( Options.write_partial_pcm_flag )
1210                     {
1211                       result = RESULT_ENDOFFILE;
1212                       continue;
1213                     }
1214                 }
1215
1216         if ( Options.verbose_flag )
1217           FrameBuffer.Dump(stderr, Options.fb_dump_size);
1218
1219         if ( ! Options.no_write_flag )
1220                 {
1221                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1222
1223                   // The Writer class will forward the last block of ciphertext
1224                   // to the encryption context for use as the IV for the next
1225                   // frame. If you want to use non-sequitur IV values, un-comment
1226                   // the following  line of code.
1227                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1228                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1229                 }
1230       }
1231         }
1232
1233     if ( result == RESULT_ENDOFFILE )
1234       result = RESULT_OK;
1235   }
1236
1237   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1238     result = Writer.Finalize();
1239
1240   return result;
1241 }
1242
1243
1244 //------------------------------------------------------------------------------------------
1245 // TimedText essence
1246
1247
1248 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1249 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1250 //
1251 Result_t
1252 write_timed_text_file(CommandOptions& Options)
1253 {
1254   AESEncContext*    Context = 0;
1255   HMACContext*      HMAC = 0;
1256   TimedText::DCSubtitleParser  Parser;
1257   TimedText::MXFWriter    Writer;
1258   TimedText::FrameBuffer  FrameBuffer;
1259   TimedText::TimedTextDescriptor TDesc;
1260   byte_t            IV_buf[CBC_BLOCK_SIZE];
1261   Kumu::FortunaRNG  RNG;
1262
1263   // set up essence parser
1264   Result_t result = Parser.OpenRead(Options.filenames.front());
1265
1266   // set up MXF writer
1267   if ( ASDCP_SUCCESS(result) )
1268     {
1269       Parser.FillTimedTextDescriptor(TDesc);
1270       FrameBuffer.Capacity(Options.fb_size);
1271
1272       if ( Options.verbose_flag )
1273         {
1274           fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1275           TimedText::DescriptorDump(TDesc);
1276         }
1277     }
1278
1279   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1280     {
1281       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1282       if ( Options.asset_id_flag )
1283         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1284       else
1285         Kumu::GenRandomUUID(Info.AssetUUID);
1286
1287       // 428-7 IN 429-5 always uses SMPTE labels
1288       Info.LabelSetType = LS_MXF_SMPTE;
1289       fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1290
1291       // configure encryption
1292       if( Options.key_flag )
1293         {
1294           Kumu::GenRandomUUID(Info.ContextID);
1295           Info.EncryptedEssence = true;
1296
1297           if ( Options.key_id_flag )
1298             {
1299               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1300             }
1301           else
1302             {
1303               create_random_uuid(Info.CryptographicKeyID);
1304             }
1305
1306           Context = new AESEncContext;
1307           result = Context->InitKey(Options.key_value);
1308
1309           if ( ASDCP_SUCCESS(result) )
1310             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1311
1312           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1313             {
1314               Info.UsesHMAC = true;
1315               HMAC = new HMACContext;
1316               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1317             }
1318         }
1319
1320       if ( ASDCP_SUCCESS(result) )
1321         result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1322     }
1323
1324   if ( ASDCP_FAILURE(result) )
1325     return result;
1326
1327   std::string XMLDoc;
1328   TimedText::ResourceList_t::const_iterator ri;
1329
1330   result = Parser.ReadTimedTextResource(XMLDoc);
1331
1332   if ( ASDCP_SUCCESS(result) )
1333     result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1334
1335   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1336     {
1337       result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1338
1339       if ( ASDCP_SUCCESS(result) )
1340         {
1341           if ( Options.verbose_flag )
1342             FrameBuffer.Dump(stderr, Options.fb_dump_size);
1343
1344           if ( ! Options.no_write_flag )
1345             {
1346               result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1347
1348               // The Writer class will forward the last block of ciphertext
1349               // to the encryption context for use as the IV for the next
1350               // frame. If you want to use non-sequitur IV values, un-comment
1351               // the following  line of code.
1352               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1353               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1354             }
1355         }
1356
1357       if ( result == RESULT_ENDOFFILE )
1358         result = RESULT_OK;
1359     }
1360
1361   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1362     result = Writer.Finalize();
1363
1364   return result;
1365 }
1366
1367 // Write one or more plaintext Dolby ATMOS bytestreams to a plaintext ASDCP file
1368 // Write one or more plaintext Dolby ATMOS bytestreams to a ciphertext ASDCP file
1369 //
1370 Result_t
1371 write_dolby_atmos_file(CommandOptions& Options)
1372 {
1373   AESEncContext*          Context = 0;
1374   HMACContext*            HMAC = 0;
1375   ATMOS::MXFWriter         Writer;
1376   DCData::FrameBuffer       FrameBuffer(Options.fb_size);
1377   ATMOS::AtmosDescriptor ADesc;
1378   DCData::SequenceParser    Parser;
1379   byte_t                  IV_buf[CBC_BLOCK_SIZE];
1380   Kumu::FortunaRNG        RNG;
1381
1382   // set up essence parser
1383   Result_t result = Parser.OpenRead(Options.filenames.front());
1384
1385   // set up MXF writer
1386   if ( ASDCP_SUCCESS(result) )
1387   {
1388     Parser.FillDCDataDescriptor(ADesc);
1389     ADesc.EditRate = Options.PictureRate();
1390     // TODO: fill AtmosDescriptor
1391     ADesc.FirstFrame = Options.ffoa;
1392     ADesc.MaxChannelCount = Options.max_channel_count;
1393     ADesc.MaxObjectCount = Options.max_object_count;
1394     Kumu::GenRandomUUID(ADesc.AtmosID);
1395     ADesc.AtmosVersion = 1;
1396     if ( Options.verbose_flag )
1397         {
1398           fprintf(stderr, "Dolby ATMOS Data\n");
1399           fputs("AtmosDescriptor:\n", stderr);
1400       fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1401       ATMOS::AtmosDescriptorDump(ADesc);
1402         }
1403   }
1404
1405   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1406   {
1407     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1408     if ( Options.asset_id_flag )
1409       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1410     else
1411       Kumu::GenRandomUUID(Info.AssetUUID);
1412
1413     Info.LabelSetType = LS_MXF_SMPTE;
1414
1415       // configure encryption
1416     if( Options.key_flag )
1417         {
1418           Kumu::GenRandomUUID(Info.ContextID);
1419           Info.EncryptedEssence = true;
1420
1421           if ( Options.key_id_flag )
1422             {
1423               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1424             }
1425           else
1426             {
1427               create_random_uuid(Info.CryptographicKeyID);
1428             }
1429
1430           Context = new AESEncContext;
1431           result = Context->InitKey(Options.key_value);
1432
1433           if ( ASDCP_SUCCESS(result) )
1434             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1435
1436           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1437       {
1438         Info.UsesHMAC = true;
1439         HMAC = new HMACContext;
1440         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1441       }
1442         }
1443
1444     if ( ASDCP_SUCCESS(result) )
1445       result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1446   }
1447
1448   if ( ASDCP_SUCCESS(result) )
1449   {
1450     ui32_t duration = 0;
1451     result = Parser.Reset();
1452
1453     while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1454         {
1455       result = Parser.ReadFrame(FrameBuffer);
1456
1457       if ( ASDCP_SUCCESS(result) )
1458       {
1459         if ( Options.verbose_flag )
1460           FrameBuffer.Dump(stderr, Options.fb_dump_size);
1461
1462         if ( Options.encrypt_header_flag )
1463           FrameBuffer.PlaintextOffset(0);
1464       }
1465
1466       if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1467       {
1468         result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1469
1470         // The Writer class will forward the last block of ciphertext
1471         // to the encryption context for use as the IV for the next
1472         // frame. If you want to use non-sequitur IV values, un-comment
1473         // the following  line of code.
1474         // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1475         //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1476       }
1477         }
1478
1479     if ( result == RESULT_ENDOFFILE )
1480       result = RESULT_OK;
1481   }
1482
1483   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1484     result = Writer.Finalize();
1485
1486   return result;
1487 }
1488
1489 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a plaintext ASDCP file
1490 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a ciphertext ASDCP file
1491 //
1492 Result_t
1493 write_aux_data_file(CommandOptions& Options)
1494 {
1495   AESEncContext*          Context = 0;
1496   HMACContext*            HMAC = 0;
1497   DCData::MXFWriter       Writer;
1498   DCData::FrameBuffer     FrameBuffer(Options.fb_size);
1499   DCData::DCDataDescriptor DDesc;
1500   DCData::SequenceParser  Parser;
1501   byte_t                  IV_buf[CBC_BLOCK_SIZE];
1502   Kumu::FortunaRNG        RNG;
1503
1504   // set up essence parser
1505   Result_t result = Parser.OpenRead(Options.filenames.front());
1506
1507   // set up MXF writer
1508   if ( ASDCP_SUCCESS(result) )
1509   {
1510     Parser.FillDCDataDescriptor(DDesc);
1511     memcpy(DDesc.DataEssenceCoding, Options.aux_data_coding.Value(), Options.aux_data_coding.Size());
1512     DDesc.EditRate = Options.PictureRate();
1513
1514     if ( Options.verbose_flag )
1515         {
1516           fprintf(stderr, "Aux Data\n");
1517           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1518           DCData::DCDataDescriptorDump(DDesc);
1519         }
1520   }
1521
1522   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1523   {
1524     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1525     if ( Options.asset_id_flag )
1526       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1527     else
1528       Kumu::GenRandomUUID(Info.AssetUUID);
1529
1530     Info.LabelSetType = LS_MXF_SMPTE;
1531
1532       // configure encryption
1533     if( Options.key_flag )
1534         {
1535           Kumu::GenRandomUUID(Info.ContextID);
1536           Info.EncryptedEssence = true;
1537
1538           if ( Options.key_id_flag )
1539             {
1540               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1541             }
1542           else
1543             {
1544               create_random_uuid(Info.CryptographicKeyID);
1545             }
1546
1547           Context = new AESEncContext;
1548           result = Context->InitKey(Options.key_value);
1549
1550           if ( ASDCP_SUCCESS(result) )
1551             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1552
1553           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1554       {
1555         Info.UsesHMAC = true;
1556         HMAC = new HMACContext;
1557         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1558       }
1559         }
1560
1561     if ( ASDCP_SUCCESS(result) )
1562       result = Writer.OpenWrite(Options.out_file, Info, DDesc);
1563   }
1564
1565   if ( ASDCP_SUCCESS(result) )
1566   {
1567     ui32_t duration = 0;
1568     result = Parser.Reset();
1569
1570     while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1571         {
1572       result = Parser.ReadFrame(FrameBuffer);
1573
1574       if ( ASDCP_SUCCESS(result) )
1575       {
1576         if ( Options.verbose_flag )
1577           FrameBuffer.Dump(stderr, Options.fb_dump_size);
1578
1579         if ( Options.encrypt_header_flag )
1580           FrameBuffer.PlaintextOffset(0);
1581       }
1582
1583       if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1584       {
1585         result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1586
1587         // The Writer class will forward the last block of ciphertext
1588         // to the encryption context for use as the IV for the next
1589         // frame. If you want to use non-sequitur IV values, un-comment
1590         // the following  line of code.
1591         // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1592         //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1593       }
1594         }
1595
1596     if ( result == RESULT_ENDOFFILE )
1597       result = RESULT_OK;
1598   }
1599
1600   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1601     result = Writer.Finalize();
1602
1603   return result;
1604 }
1605
1606 //
1607 int
1608 main(int argc, const char** argv)
1609 {
1610   Result_t result = RESULT_OK;
1611   char     str_buf[64];
1612   g_dict = &ASDCP::DefaultSMPTEDict();
1613
1614   CommandOptions Options(argc, argv);
1615
1616   if ( Options.version_flag )
1617     banner();
1618
1619   if ( Options.help_flag )
1620     usage();
1621
1622   if ( Options.show_ul_values_flag )
1623     {
1624       if ( Options.use_smpte_labels )
1625         DefaultSMPTEDict().Dump(stdout);
1626       else
1627         DefaultInteropDict().Dump(stdout);
1628     }
1629
1630   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1631     return 0;
1632
1633   if ( Options.error_flag )
1634     {
1635       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1636       return 3;
1637     }
1638
1639   EssenceType_t EssenceType;
1640   result = ASDCP::RawEssenceType(Options.filenames.front(), EssenceType);
1641
1642   if ( ASDCP_SUCCESS(result) )
1643     {
1644       switch ( EssenceType )
1645         {
1646         case ESS_MPEG2_VES:
1647           result = write_MPEG2_file(Options);
1648           break;
1649
1650         case ESS_JPEG_2000:
1651           if ( Options.stereo_image_flag )
1652             {
1653               result = write_JP2K_S_file(Options);
1654             }
1655           else
1656             {
1657               result = write_JP2K_file(Options);
1658             }
1659           break;
1660
1661         case ESS_PCM_24b_48k:
1662         case ESS_PCM_24b_96k:
1663           if ( Options.dolby_atmos_sync_flag )
1664             {
1665               result = write_PCM_with_ATMOS_sync_file(Options);
1666             }
1667           else
1668             {
1669               result = write_PCM_file(Options);
1670             }
1671           break;
1672           
1673         case ESS_TIMED_TEXT:
1674           result = write_timed_text_file(Options);
1675           break;
1676
1677         case ESS_DCDATA_DOLBY_ATMOS:
1678           result = write_dolby_atmos_file(Options);
1679           break;
1680
1681         case ESS_DCDATA_UNKNOWN:
1682           if ( ! Options.aux_data_coding.HasValue() )
1683             {
1684               fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n");
1685               return 3;
1686             }
1687           else
1688             {
1689               result = write_aux_data_file(Options);
1690             }
1691           break;
1692
1693         default:
1694           fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1695                   Options.filenames.front().c_str());
1696           return 5;
1697         }
1698     }
1699
1700   if ( ASDCP_FAILURE(result) )
1701     {
1702       fputs("Program stopped on error.\n", stderr);
1703
1704       if ( result != RESULT_FAIL )
1705         {
1706           fputs(result, stderr);
1707           fputc('\n', stderr);
1708         }
1709
1710       return 1;
1711     }
1712
1713   return 0;
1714 }
1715
1716
1717 //
1718 // end asdcp-wrap.cpp
1719 //