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