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