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