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