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