rrrrr
[asdcplib.git] / src / asdcp-test.cpp
1 /*
2 Copyright (c) 2003-2010, 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-test.cpp
28     \version $Id$       
29     \brief   AS-DCP file manipulation utility
30
31   This program provides command line access to the major features of the asdcplib
32   library, and serves as a library unit test which provides the functionality of
33   the supported use cases.
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 <PCMParserList.h>
53 #include <WavFileWriter.h>
54 #include <MXF.h>
55 #include <Metadata.h>
56 #include <openssl/sha.h>
57
58 #include <iostream>
59 #include <assert.h>
60
61 using namespace ASDCP;
62
63 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
64
65 //------------------------------------------------------------------------------------------
66 //
67 // command line option parser class
68
69 static const char* PROGRAM_NAME = "asdcp-test";  // program name for messages
70 const ui32_t MAX_IN_FILES = 16;             // maximum number of input files handled by
71                                             //   the command option parser
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-test";
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)    if ( ++i >= argc || argv[(i)][0] == '-' ) \
96                                  { \
97                                    fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
98                                    return; \
99                                  }
100 //
101 void
102 banner(FILE* stream = stdout)
103 {
104   fprintf(stream, "\n\
105 %s (asdcplib %s)\n\n\
106 Copyright (c) 2003-2010 John Hurst\n\n\
107 asdcplib may be copied only under the terms of the license found at\n\
108 the top of every file in the asdcplib distribution kit.\n\n\
109 Specify the -h (help) option for further information about %s\n\n",
110           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
111 }
112
113 //
114 void
115 usage(FILE* stream = stdout)
116 {
117   fprintf(stream, "\
118 USAGE: %s -c <output-file> [-3] [-a <uuid>] [-b <buffer-size>]\n\
119        [-d <duration>] [-e|-E] [-f <start-frame>] [-j <key-id-string>]\n\
120        [-k <key-string>] [-l <label>] [-L] [-M] [-p <frame-rate>] [-R]\n\
121        [-s <num>] [-v] [-W] [-z|-Z] <input-file> [<input-file-2> ...]\n\
122 \n\
123        %s [-h|-help] [-V]\n\
124 \n\
125        %s -i [-H] [-n] [-v] <input-file>\n\
126 \n\
127        %s -g | -u\n\
128 \n\
129        %s -G [-v] <input-file>\n\
130 \n\
131        %s -t <input-file>\n\
132 \n\
133        %s -x <file-prefix> [-3] [-b <buffer-size>] [-d <duration>]\n\
134        [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <num>] [-S|-1]\n\
135        [-v] [-W] [-w] <input-file>\n\
136 \n", PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
137
138   fprintf(stream, "\
139 Major modes:\n\
140   -3                - With -c, create a stereoscopic image file. Expects two\n\
141                       directories of JP2K codestreams (directories must have\n\
142                       an equal number of frames; left eye is first).\n\
143                     - With -x, force stereoscopic interpretation of a JP2K\n\
144                       track file.\n\
145   -c <output-file>  - Create an AS-DCP track file from input(s)\n\
146   -g                - Generate a random 16 byte value to stdout\n\
147   -G                - Perform GOP start lookup test on MXF+Interop MPEG file\n\
148   -h | -help        - Show help\n\
149   -i                - Show file info\n\
150   -t                - Calculate message digest of input file\n\
151   -U                - Dump UL catalog to stdout\n\
152   -u                - Generate a random UUID value to stdout\n\
153   -V                - Show version information\n\
154   -x <root-name>    - Extract essence from AS-DCP file to named file(s)\n\
155 \n");
156
157   fprintf(stream, "\
158 Security Options:\n\
159   -e                - Encrypt MPEG or JP2K headers (default)\n\
160   -E                - Do not encrypt MPEG or JP2K headers\n\
161   -j <key-id-str>   - Write key ID instead of creating a random value\n\
162   -k <key-string>   - Use key for ciphertext operations\n\
163   -m                - verify HMAC values when reading\n\
164   -M                - Do not create HMAC values when writing\n\
165 \n");
166
167   fprintf(stream, "\
168 Read/Write Options:\n\
169   -a <UUID>         - Specify the Asset ID of a file (with -c)\n\
170   -b <buffer-size>  - Specify size in bytes of picture frame buffer.\n\
171                       Defaults to 4,194,304 (4MB)\n\
172   -d <duration>     - Number of frames to process, default all\n\
173   -f <start-frame>  - Starting frame number, default 0\n\
174   -l <label>        - Use given channel format label when writing MXF sound\n\
175                       files. SMPTE 429-2 labels: '5.1', '6.1', '7.1'. Default\n\
176                       is no label (valid for Interop only).\n\
177   -L                - Write SMPTE UL values instead of MXF Interop\n\
178   -p <rate>         - fps of picture when wrapping PCM or JP2K:\n\
179                       Use one of [23|24|48], 24 is default\n\
180   -R                - Repeat the first frame over the entire file (picture\n\
181                       essence only, requires -c, -d)\n\
182   -S                - Split Wave essence to stereo WAV files during extract.\n\
183                       Default is multichannel WAV\n\
184   -1                - Split Wave essence to mono WAV files during extract.\n\
185                       Default is multichannel WAV\n\
186   -W                - Read input file only, do not write source file\n\
187   -w <width>        - Width of numeric element in a series of frame file names\n\
188                       (use with -x, default 6).\n\
189   -z                - Fail if j2c inputs have unequal parameters (default)\n\
190   -Z                - Ignore unequal parameters in j2c inputs\n\
191 \n");
192
193   fprintf(stream, "\
194 Info Options:\n\
195   -H                - Show MXF header metadata, used with option -i\n\
196   -n                - Show index, used with option -i\n\
197 \n\
198 Other Options:\n\
199   -s <num>          - Number of bytes of frame buffer to be dumped as hex to\n\
200                       stderr, used with option -v\n\
201   -v                - Verbose, prints informative messages to stderr\n\
202 \n\
203   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
204          o All option arguments must be separated from the option by whitespace.\n\
205          o An argument of \"23\" to the -p option will be interpreted\n\
206            as 24000/1001 fps.\n\
207 \n");
208 }
209
210 //
211 enum MajorMode_t
212 {
213   MMT_NONE,
214   MMT_INFO,
215   MMT_CREATE,
216   MMT_EXTRACT,
217   MMT_GEN_ID,
218   MMT_GEN_KEY,
219   MMT_GOP_START,
220   MMT_DIGEST,
221   MMT_UL_LIST,
222 };
223
224 //
225 PCM::ChannelFormat_t
226 decode_channel_fmt(const std::string& label_name)
227 {
228   if ( label_name == "5.1" )
229     return PCM::CF_CFG_1;
230
231   else if ( label_name == "6.1" )
232     return PCM::CF_CFG_2;
233   
234   else if ( label_name == "7.1" )
235     return PCM::CF_CFG_3;
236
237   fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
238   fprintf(stderr, "Expecting '5.1', '6.1', or '7.1'\n");
239   return PCM::CF_NONE;
240 }
241
242 //
243 //
244 class CommandOptions
245 {
246   CommandOptions();
247
248 public:
249   MajorMode_t mode;
250   bool   error_flag;     // true if the given options are in error or not complete
251   bool   key_flag;       // true if an encryption key was given
252   bool   key_id_flag;    // true if a key ID was given
253   bool   asset_id_flag;  // true if an asset ID was given
254   bool   encrypt_header_flag; // true if mpeg headers are to be encrypted
255   bool   write_hmac;     // true if HMAC values are to be generated and written
256   bool   read_hmac;      // true if HMAC values are to be validated
257   bool   split_wav;      // true if PCM is to be extracted to stereo WAV files
258   bool   mono_wav;       // true if PCM is to be extracted to mono WAV files
259   bool   verbose_flag;   // true if the verbose option was selected
260   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
261   bool   showindex_flag; // true if index is to be displayed
262   bool   showheader_flag; // true if MXF file header is to be displayed
263   bool   no_write_flag;  // true if no output files are to be written
264   bool   version_flag;   // true if the version display option was selected
265   bool   help_flag;      // true if the help display option was selected
266   bool   stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
267   ui32_t number_width;   // number of digits in a serialized filename (for JPEG extract)
268   ui32_t start_frame;    // frame number to begin processing
269   ui32_t duration;       // number of frames to be processed
270   bool   duration_flag;  // true if duration argument given
271   bool   do_repeat;      // if true and -c -d, repeat first input frame
272   bool   use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
273   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
274   ui32_t picture_rate;   // fps of picture when wrapping PCM
275   ui32_t fb_size;        // size of picture frame buffer
276   ui32_t file_count;     // number of elements in filenames[]
277   const char* file_root; // filename pre for files written by the extract mode
278   const char* out_file;  // name of mxf file created by create mode
279   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
280   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
281   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
282   const char* filenames[MAX_IN_FILES]; // list of filenames to be processed
283   PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
284
285   //
286   Rational PictureRate()
287   {
288     if ( picture_rate == 23 ) return EditRate_23_98;
289     if ( picture_rate == 25 ) return EditRate_25;
290     if ( picture_rate == 30 ) return EditRate_30;
291     if ( picture_rate == 48 ) return EditRate_48;
292     if ( picture_rate == 50 ) return EditRate_50;
293     if ( picture_rate == 60 ) return EditRate_60;
294     return EditRate_24;
295   }
296
297   //
298   const char* szPictureRate()
299   {
300     if ( picture_rate == 23 ) return "23.976";
301     if ( picture_rate == 25 ) return "25";
302     if ( picture_rate == 30 ) return "30";
303     if ( picture_rate == 48 ) return "48";
304     if ( picture_rate == 50 ) return "50";
305     if ( picture_rate == 60 ) return "60";
306     return "24";
307   }
308
309   //
310   CommandOptions(int argc, const char** argv) :
311     mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
312     encrypt_header_flag(true), write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false),
313     verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false),
314     no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
315     number_width(6), start_frame(0),
316     duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false), j2c_pedantic(true),
317     picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0),
318     channel_fmt(PCM::CF_NONE)
319   {
320     memset(key_value, 0, KeyLen);
321     memset(key_id_value, 0, UUIDlen);
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 '1': mono_wav = true; break;
339               case '2': split_wav = true; break;
340               case '3': stereo_image_flag = true; break;
341
342               case 'a':
343                 asset_id_flag = true;
344                 TEST_EXTRA_ARG(i, 'a');
345                 {
346                   ui32_t length;
347                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
348
349                   if ( length != UUIDlen )
350                     {
351                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
352                       return;
353                     }
354                 }
355                 break;
356
357               case 'b':
358                 TEST_EXTRA_ARG(i, 'b');
359                 fb_size = abs(atoi(argv[i]));
360
361                 if ( verbose_flag )
362                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
363
364                 break;
365
366               case 'c':
367                 TEST_EXTRA_ARG(i, 'c');
368                 mode = MMT_CREATE;
369                 out_file = argv[i];
370                 break;
371
372               case 'd':
373                 TEST_EXTRA_ARG(i, 'd');
374                 duration_flag = true;
375                 duration = abs(atoi(argv[i]));
376                 break;
377
378               case 'E': encrypt_header_flag = false; break;
379               case 'e': encrypt_header_flag = true; break;
380
381               case 'f':
382                 TEST_EXTRA_ARG(i, 'f');
383                 start_frame = abs(atoi(argv[i]));
384                 break;
385
386               case 'G': mode = MMT_GOP_START; break;
387               case 'g': mode = MMT_GEN_KEY; break;
388               case 'H': showheader_flag = true; break;
389               case 'h': help_flag = true; break;
390               case 'i': mode = MMT_INFO;        break;
391
392               case 'j': key_id_flag = true;
393                 TEST_EXTRA_ARG(i, 'j');
394                 {
395                   ui32_t length;
396                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
397
398                   if ( length != UUIDlen )
399                     {
400                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
401                       return;
402                     }
403                 }
404                 break;
405
406               case 'k': key_flag = true;
407                 TEST_EXTRA_ARG(i, 'k');
408                 {
409                   ui32_t length;
410                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
411
412                   if ( length != KeyLen )
413                     {
414                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
415                       return;
416                     }
417                 }
418                 break;
419
420               case 'l':
421                 TEST_EXTRA_ARG(i, 'l');
422                 channel_fmt = decode_channel_fmt(argv[i]);
423                 break;
424
425               case 'L': use_smpte_labels = true; break;
426               case 'M': write_hmac = false; break;
427               case 'm': read_hmac = true; break;
428               case 'n': showindex_flag = true; break;
429
430               case 'p':
431                 TEST_EXTRA_ARG(i, 'p');
432                 picture_rate = abs(atoi(argv[i]));
433                 break;
434
435               case 'R': do_repeat = true; break;
436               case 'S': split_wav = true; break;
437
438               case 's':
439                 TEST_EXTRA_ARG(i, 's');
440                 fb_dump_size = abs(atoi(argv[i]));
441                 break;
442
443               case 't': mode = MMT_DIGEST; break;
444               case 'U': mode = MMT_UL_LIST; break;
445               case 'u': mode = MMT_GEN_ID; break;
446               case 'V': version_flag = true; break;
447               case 'v': verbose_flag = true; break;
448               case 'W': no_write_flag = true; break;
449
450               case 'w':
451                 TEST_EXTRA_ARG(i, 'w');
452                 number_width = abs(atoi(argv[i]));
453                 break;
454
455               case 'x':
456                 TEST_EXTRA_ARG(i, 'x');
457                 mode = MMT_EXTRACT;
458                 file_root = argv[i];
459                 break;
460
461               case 'Z': j2c_pedantic = false; break;
462               case 'z': j2c_pedantic = true; break;
463
464               default:
465                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
466                 return;
467               }
468           }
469         else
470           {
471
472             if ( argv[i][0] != '-' )
473               {
474                 filenames[file_count++] = argv[i];
475               }
476             else
477               {
478                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
479                 return;
480               }
481
482             if ( file_count >= MAX_IN_FILES )
483               {
484                 fprintf(stderr, "Filename lists exceeds maximum list size: %u\n", MAX_IN_FILES);
485                 return;
486               }
487           }
488       }
489
490     if ( help_flag || version_flag )
491       return;
492     
493     if ( ( mode == MMT_INFO
494            || mode == MMT_CREATE
495            || mode == MMT_EXTRACT
496            || mode == MMT_GOP_START
497            || mode == MMT_DIGEST ) && file_count == 0 )
498       {
499         fputs("Option requires at least one filename argument.\n", stderr);
500         return;
501       }
502
503     if ( mode == MMT_NONE && ! help_flag && ! version_flag )
504       {
505         fputs("No operation selected (use one of -[gGcitux] or -h for help).\n", stderr);
506         return;
507       }
508
509     error_flag = false;
510   }
511 };
512
513 //------------------------------------------------------------------------------------------
514 // MPEG2 essence
515
516 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
517 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
518 //
519 Result_t
520 write_MPEG2_file(CommandOptions& Options)
521 {
522   AESEncContext*     Context = 0;
523   HMACContext*       HMAC = 0;
524   MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
525   MPEG2::Parser      Parser;
526   MPEG2::MXFWriter   Writer;
527   MPEG2::VideoDescriptor VDesc;
528   byte_t             IV_buf[CBC_BLOCK_SIZE];
529   Kumu::FortunaRNG   RNG;
530
531   // set up essence parser
532   Result_t result = Parser.OpenRead(Options.filenames[0]);
533
534   // set up MXF writer
535   if ( ASDCP_SUCCESS(result) )
536     {
537       Parser.FillVideoDescriptor(VDesc);
538
539       if ( Options.verbose_flag )
540         {
541           fputs("MPEG-2 Pictures\n", stderr);
542           fputs("VideoDescriptor:\n", stderr);
543           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
544           MPEG2::VideoDescriptorDump(VDesc);
545         }
546     }
547
548   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
549     {
550       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
551       if ( Options.asset_id_flag )
552         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
553       else
554         Kumu::GenRandomUUID(Info.AssetUUID);
555
556       if ( Options.use_smpte_labels )
557         {
558           Info.LabelSetType = LS_MXF_SMPTE;
559           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
560         }
561
562       // configure encryption
563       if( Options.key_flag )
564         {
565           Kumu::GenRandomUUID(Info.ContextID);
566           Info.EncryptedEssence = true;
567
568           if ( Options.key_id_flag )
569             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
570           else
571             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
572
573           Context = new AESEncContext;
574           result = Context->InitKey(Options.key_value);
575
576           if ( ASDCP_SUCCESS(result) )
577             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
578
579           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
580             {
581               Info.UsesHMAC = true;
582               HMAC = new HMACContext;
583               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
584             }
585         }
586
587       if ( ASDCP_SUCCESS(result) )
588         result = Writer.OpenWrite(Options.out_file, Info, VDesc);
589     }
590
591   if ( ASDCP_SUCCESS(result) )
592     // loop through the frames
593     {
594       result = Parser.Reset();
595       ui32_t duration = 0;
596
597       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
598         {
599           if ( ! Options.do_repeat || duration == 1 )
600             {
601               result = Parser.ReadFrame(FrameBuffer);
602
603               if ( ASDCP_SUCCESS(result) )
604                 {
605                   if ( Options.verbose_flag )
606                     FrameBuffer.Dump(stderr, Options.fb_dump_size);
607                   
608                   if ( Options.encrypt_header_flag )
609                     FrameBuffer.PlaintextOffset(0);
610                 }
611             }
612
613           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
614             {
615               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
616
617               // The Writer class will forward the last block of ciphertext
618               // to the encryption context for use as the IV for the next
619               // frame. If you want to use non-sequitur IV values, un-comment
620               // the following  line of code.
621               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
622               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
623             }
624         }
625
626       if ( result == RESULT_ENDOFFILE )
627         result = RESULT_OK;
628     }
629
630   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
631     result = Writer.Finalize();
632
633   return result;
634 }
635
636 // Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file
637 // Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
638 // Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
639 //
640 Result_t
641 read_MPEG2_file(CommandOptions& Options)
642 {
643   AESDecContext*     Context = 0;
644   HMACContext*       HMAC = 0;
645   MPEG2::MXFReader   Reader;
646   MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
647   Kumu::FileWriter   OutFile;
648   ui32_t             frame_count = 0;
649
650   Result_t result = Reader.OpenRead(Options.filenames[0]);
651
652   if ( ASDCP_SUCCESS(result) )
653     {
654       MPEG2::VideoDescriptor VDesc;
655       Reader.FillVideoDescriptor(VDesc);
656       frame_count = VDesc.ContainerDuration;
657
658       if ( Options.verbose_flag )
659         {
660           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
661           MPEG2::VideoDescriptorDump(VDesc);
662         }
663     }
664
665   if ( ASDCP_SUCCESS(result) )
666     {
667       char filename[256];
668       snprintf(filename, 256, "%s.ves", Options.file_root);
669       result = OutFile.OpenWrite(filename);
670     }
671
672   if ( ASDCP_SUCCESS(result) && Options.key_flag )
673     {
674       Context = new AESDecContext;
675       result = Context->InitKey(Options.key_value);
676
677       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
678         {
679           WriterInfo Info;
680           Reader.FillWriterInfo(Info);
681
682           if ( Info.UsesHMAC )
683             {
684               HMAC = new HMACContext;
685               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
686             }
687           else
688             {
689               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
690             }
691         }
692     }
693
694   ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
695   if ( last_frame > frame_count )
696     last_frame = frame_count;
697
698   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
699     {
700       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
701
702       if ( ASDCP_SUCCESS(result) )
703         {
704           if ( Options.verbose_flag )
705             FrameBuffer.Dump(stderr, Options.fb_dump_size);
706
707           ui32_t write_count = 0;
708           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
709         }
710     }
711
712   return result;
713 }
714
715
716 //
717 Result_t
718 gop_start_test(CommandOptions& Options)
719 {
720   using namespace ASDCP::MPEG2;
721
722   MXFReader   Reader;
723   MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
724   ui32_t      frame_count = 0;
725
726   Result_t result = Reader.OpenRead(Options.filenames[0]);
727
728   if ( ASDCP_SUCCESS(result) )
729     {
730       MPEG2::VideoDescriptor VDesc;
731       Reader.FillVideoDescriptor(VDesc);
732       frame_count = VDesc.ContainerDuration;
733
734       if ( Options.verbose_flag )
735         {
736           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
737           MPEG2::VideoDescriptorDump(VDesc);
738         }
739     }
740
741   ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
742   if ( last_frame > frame_count )
743     last_frame = frame_count;
744
745   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
746     {
747       result = Reader.ReadFrameGOPStart(i, FrameBuffer);
748
749       if ( ASDCP_SUCCESS(result) )
750         {
751           if ( Options.verbose_flag )
752             FrameBuffer.Dump(stderr, Options.fb_dump_size);
753
754           if ( FrameBuffer.FrameType() != FRAME_I )
755             fprintf(stderr, "Expecting an I frame, got %c\n", FrameTypeChar(FrameBuffer.FrameType()));
756
757           fprintf(stderr, "Requested frame %u, got %u\n", i, FrameBuffer.FrameNumber());
758         }
759     }
760
761   return result;
762 }
763
764 //------------------------------------------------------------------------------------------
765 // JPEG 2000 essence
766
767 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
768 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
769 //
770 Result_t
771 write_JP2K_S_file(CommandOptions& Options)
772 {
773   AESEncContext*          Context = 0;
774   HMACContext*            HMAC = 0;
775   JP2K::MXFSWriter        Writer;
776   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
777   JP2K::PictureDescriptor PDesc;
778   JP2K::SequenceParser    ParserLeft, ParserRight;
779   byte_t                  IV_buf[CBC_BLOCK_SIZE];
780   Kumu::FortunaRNG        RNG;
781
782   if ( Options.file_count != 2 )
783     {
784       fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
785       return RESULT_FAIL;
786     }
787
788   // set up essence parser
789   Result_t result = ParserLeft.OpenRead(Options.filenames[0], Options.j2c_pedantic);
790
791   if ( ASDCP_SUCCESS(result) )
792     result = ParserRight.OpenRead(Options.filenames[1], Options.j2c_pedantic);
793
794   // set up MXF writer
795   if ( ASDCP_SUCCESS(result) )
796     {
797       ParserLeft.FillPictureDescriptor(PDesc);
798       PDesc.EditRate = Options.PictureRate();
799
800       if ( Options.verbose_flag )
801         {
802           fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
803           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
804           JP2K::PictureDescriptorDump(PDesc);
805         }
806     }
807
808   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
809     {
810       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
811       if ( Options.asset_id_flag )
812         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
813       else
814         Kumu::GenRandomUUID(Info.AssetUUID);
815
816       if ( Options.use_smpte_labels )
817         {
818           Info.LabelSetType = LS_MXF_SMPTE;
819           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
820         }
821
822       // configure encryption
823       if( Options.key_flag )
824         {
825           Kumu::GenRandomUUID(Info.ContextID);
826           Info.EncryptedEssence = true;
827
828           if ( Options.key_id_flag )
829             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
830           else
831             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
832
833           Context = new AESEncContext;
834           result = Context->InitKey(Options.key_value);
835
836           if ( ASDCP_SUCCESS(result) )
837             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
838
839           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
840             {
841               Info.UsesHMAC = true;
842               HMAC = new HMACContext;
843               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
844             }
845         }
846
847       if ( ASDCP_SUCCESS(result) )
848         result = Writer.OpenWrite(Options.out_file, Info, PDesc);
849     }
850
851   if ( ASDCP_SUCCESS(result) )
852     {
853       ui32_t duration = 0;
854       result = ParserLeft.Reset();
855       if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
856
857       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
858         {
859           result = ParserLeft.ReadFrame(FrameBuffer);
860
861           if ( ASDCP_SUCCESS(result) )
862             {
863               if ( Options.verbose_flag )
864                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
865                   
866               if ( Options.encrypt_header_flag )
867                 FrameBuffer.PlaintextOffset(0);
868             }
869
870           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
871             result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
872
873           if ( ASDCP_SUCCESS(result) )
874             result = ParserRight.ReadFrame(FrameBuffer);
875
876           if ( ASDCP_SUCCESS(result) )
877             {
878               if ( Options.verbose_flag )
879                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
880                   
881               if ( Options.encrypt_header_flag )
882                 FrameBuffer.PlaintextOffset(0);
883             }
884
885           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
886             result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
887         }
888
889       if ( result == RESULT_ENDOFFILE )
890         result = RESULT_OK;
891     }
892
893   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
894     result = Writer.Finalize();
895
896   return result;
897 }
898
899 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a plaintext ASDCP file
900 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
901 // Read one or more ciphertext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
902 Result_t
903 read_JP2K_S_file(CommandOptions& Options)
904 {
905   AESDecContext*     Context = 0;
906   HMACContext*       HMAC = 0;
907   JP2K::MXFSReader    Reader;
908   JP2K::FrameBuffer  FrameBuffer(Options.fb_size);
909   ui32_t             frame_count = 0;
910
911   Result_t result = Reader.OpenRead(Options.filenames[0]);
912
913   if ( ASDCP_SUCCESS(result) )
914     {
915       JP2K::PictureDescriptor PDesc;
916       Reader.FillPictureDescriptor(PDesc);
917
918       frame_count = PDesc.ContainerDuration;
919
920       if ( Options.verbose_flag )
921         {
922           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
923           JP2K::PictureDescriptorDump(PDesc);
924         }
925     }
926
927   if ( ASDCP_SUCCESS(result) && Options.key_flag )
928     {
929       Context = new AESDecContext;
930       result = Context->InitKey(Options.key_value);
931
932       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
933         {
934           WriterInfo Info;
935           Reader.FillWriterInfo(Info);
936
937           if ( Info.UsesHMAC )
938             {
939               HMAC = new HMACContext;
940               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
941             }
942           else
943             {
944               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
945             }
946         }
947     }
948
949   const int filename_max = 1024;
950   char filename[filename_max];
951   ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
952   if ( last_frame > frame_count )
953     last_frame = frame_count;
954
955   char left_format[64];  char right_format[64];
956   snprintf(left_format,  64, "%%s%%0%duL.j2c", Options.number_width);
957   snprintf(right_format, 64, "%%s%%0%duR.j2c", Options.number_width);
958
959   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
960     {
961       result = Reader.ReadFrame(i, JP2K::SP_LEFT, FrameBuffer, Context, HMAC);
962
963       if ( ASDCP_SUCCESS(result) )
964         {
965           Kumu::FileWriter OutFile;
966           ui32_t write_count;
967           snprintf(filename, filename_max, left_format, Options.file_root, i);
968           result = OutFile.OpenWrite(filename);
969
970           if ( ASDCP_SUCCESS(result) )
971             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
972
973           if ( Options.verbose_flag )
974             FrameBuffer.Dump(stderr, Options.fb_dump_size);
975         }
976
977       if ( ASDCP_SUCCESS(result) )
978         result = Reader.ReadFrame(i, JP2K::SP_RIGHT, FrameBuffer, Context, HMAC);
979
980       if ( ASDCP_SUCCESS(result) )
981         {
982           Kumu::FileWriter OutFile;
983           ui32_t write_count;
984           snprintf(filename, filename_max, right_format, Options.file_root, i);
985           result = OutFile.OpenWrite(filename);
986
987           if ( ASDCP_SUCCESS(result) )
988             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
989         }
990     }
991
992   return result;
993 }
994
995
996
997 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
998 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
999 //
1000 Result_t
1001 write_JP2K_file(CommandOptions& Options)
1002 {
1003   AESEncContext*          Context = 0;
1004   HMACContext*            HMAC = 0;
1005   JP2K::MXFWriter         Writer;
1006   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
1007   JP2K::PictureDescriptor PDesc;
1008   JP2K::SequenceParser    Parser;
1009   byte_t                  IV_buf[CBC_BLOCK_SIZE];
1010   Kumu::FortunaRNG        RNG;
1011
1012   // set up essence parser
1013   Result_t result = Parser.OpenRead(Options.filenames[0], Options.j2c_pedantic);
1014
1015   // set up MXF writer
1016   if ( ASDCP_SUCCESS(result) )
1017     {
1018       Parser.FillPictureDescriptor(PDesc);
1019       PDesc.EditRate = Options.PictureRate();
1020
1021       if ( Options.verbose_flag )
1022         {
1023           fprintf(stderr, "JPEG 2000 pictures\n");
1024           fputs("PictureDescriptor:\n", stderr);
1025           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1026           JP2K::PictureDescriptorDump(PDesc);
1027         }
1028     }
1029
1030   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1031     {
1032       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1033       if ( Options.asset_id_flag )
1034         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1035       else
1036         Kumu::GenRandomUUID(Info.AssetUUID);
1037
1038       if ( Options.use_smpte_labels )
1039         {
1040           Info.LabelSetType = LS_MXF_SMPTE;
1041           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1042         }
1043
1044       // configure encryption
1045       if( Options.key_flag )
1046         {
1047           Kumu::GenRandomUUID(Info.ContextID);
1048           Info.EncryptedEssence = true;
1049
1050           if ( Options.key_id_flag )
1051             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1052           else
1053             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1054
1055           Context = new AESEncContext;
1056           result = Context->InitKey(Options.key_value);
1057
1058           if ( ASDCP_SUCCESS(result) )
1059             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1060
1061           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1062             {
1063               Info.UsesHMAC = true;
1064               HMAC = new HMACContext;
1065               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1066             }
1067         }
1068
1069       if ( ASDCP_SUCCESS(result) )
1070         result = Writer.OpenWrite(Options.out_file, Info, PDesc);
1071     }
1072
1073   if ( ASDCP_SUCCESS(result) )
1074     {
1075       ui32_t duration = 0;
1076       result = Parser.Reset();
1077
1078       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1079         {
1080           if ( ! Options.do_repeat || duration == 1 )
1081             {
1082               result = Parser.ReadFrame(FrameBuffer);
1083
1084               if ( ASDCP_SUCCESS(result) )
1085                 {
1086                   if ( Options.verbose_flag )
1087                     FrameBuffer.Dump(stderr, Options.fb_dump_size);
1088                   
1089                   if ( Options.encrypt_header_flag )
1090                     FrameBuffer.PlaintextOffset(0);
1091                 }
1092             }
1093
1094           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1095             {
1096               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1097
1098               // The Writer class will forward the last block of ciphertext
1099               // to the encryption context for use as the IV for the next
1100               // frame. If you want to use non-sequitur IV values, un-comment
1101               // the following  line of code.
1102               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1103               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1104             }
1105         }
1106
1107       if ( result == RESULT_ENDOFFILE )
1108         result = RESULT_OK;
1109     }
1110
1111   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1112     result = Writer.Finalize();
1113
1114   return result;
1115 }
1116
1117 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
1118 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
1119 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
1120 //
1121 Result_t
1122 read_JP2K_file(CommandOptions& Options)
1123 {
1124   AESDecContext*     Context = 0;
1125   HMACContext*       HMAC = 0;
1126   JP2K::MXFReader    Reader;
1127   JP2K::FrameBuffer  FrameBuffer(Options.fb_size);
1128   ui32_t             frame_count = 0;
1129
1130   Result_t result = Reader.OpenRead(Options.filenames[0]);
1131
1132   if ( ASDCP_SUCCESS(result) )
1133     {
1134       JP2K::PictureDescriptor PDesc;
1135       Reader.FillPictureDescriptor(PDesc);
1136
1137       frame_count = PDesc.ContainerDuration;
1138
1139       if ( Options.verbose_flag )
1140         {
1141           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1142           JP2K::PictureDescriptorDump(PDesc);
1143         }
1144     }
1145
1146   if ( ASDCP_SUCCESS(result) && Options.key_flag )
1147     {
1148       Context = new AESDecContext;
1149       result = Context->InitKey(Options.key_value);
1150
1151       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1152         {
1153           WriterInfo Info;
1154           Reader.FillWriterInfo(Info);
1155
1156           if ( Info.UsesHMAC )
1157             {
1158               HMAC = new HMACContext;
1159               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1160             }
1161           else
1162             {
1163               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1164             }
1165         }
1166     }
1167
1168   ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
1169   if ( last_frame > frame_count )
1170     last_frame = frame_count;
1171
1172   char name_format[64];
1173   snprintf(name_format,  64, "%%s%%0%du.j2c", Options.number_width);
1174
1175   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1176     {
1177       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1178
1179       if ( ASDCP_SUCCESS(result) )
1180         {
1181           Kumu::FileWriter OutFile;
1182           char filename[256];
1183           ui32_t write_count;
1184           snprintf(filename, 256, name_format, Options.file_root, i);
1185           result = OutFile.OpenWrite(filename);
1186
1187           if ( ASDCP_SUCCESS(result) )
1188             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
1189
1190           if ( Options.verbose_flag )
1191             FrameBuffer.Dump(stderr, Options.fb_dump_size);
1192         }
1193     }
1194
1195   return result;
1196 }
1197
1198 //------------------------------------------------------------------------------------------
1199 // PCM essence
1200
1201
1202 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
1203 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
1204 //
1205 Result_t
1206 write_PCM_file(CommandOptions& Options)
1207 {
1208   AESEncContext*    Context = 0;
1209   HMACContext*      HMAC = 0;
1210   PCMParserList     Parser;
1211   PCM::MXFWriter    Writer;
1212   PCM::FrameBuffer  FrameBuffer;
1213   PCM::AudioDescriptor ADesc;
1214   Rational          PictureRate = Options.PictureRate();
1215   byte_t            IV_buf[CBC_BLOCK_SIZE];
1216   Kumu::FortunaRNG  RNG;
1217
1218   // set up essence parser
1219   Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate);
1220
1221   // set up MXF writer
1222   if ( ASDCP_SUCCESS(result) )
1223     {
1224       Parser.FillAudioDescriptor(ADesc);
1225
1226       ADesc.EditRate = PictureRate;
1227       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1228       ADesc.ChannelFormat = Options.channel_fmt;
1229
1230       if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE)
1231         {
1232           fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see option -l)\n");
1233         }
1234
1235       if ( Options.verbose_flag )
1236         {
1237           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1238                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
1239                   Options.szPictureRate(),
1240                   PCM::CalcSamplesPerFrame(ADesc));
1241           fputs("AudioDescriptor:\n", stderr);
1242           PCM::AudioDescriptorDump(ADesc);
1243         }
1244     }
1245
1246   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1247     {
1248       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1249       if ( Options.asset_id_flag )
1250         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1251       else
1252         Kumu::GenRandomUUID(Info.AssetUUID);
1253
1254       if ( Options.use_smpte_labels )
1255         {
1256           Info.LabelSetType = LS_MXF_SMPTE;
1257           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1258         }
1259
1260       // configure encryption
1261       if( Options.key_flag )
1262         {
1263           Kumu::GenRandomUUID(Info.ContextID);
1264           Info.EncryptedEssence = true;
1265
1266           if ( Options.key_id_flag )
1267             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1268           else
1269             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1270
1271           Context = new AESEncContext;
1272           result = Context->InitKey(Options.key_value);
1273
1274           if ( ASDCP_SUCCESS(result) )
1275             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1276
1277           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1278             {
1279               Info.UsesHMAC = true;
1280               HMAC = new HMACContext;
1281               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1282             }
1283         }
1284
1285       if ( ASDCP_SUCCESS(result) )
1286         result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1287     }
1288
1289   if ( ASDCP_SUCCESS(result) )
1290     {
1291       result = Parser.Reset();
1292       ui32_t duration = 0;
1293
1294       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1295         {
1296           result = Parser.ReadFrame(FrameBuffer);
1297
1298           if ( ASDCP_SUCCESS(result) )
1299             {
1300               if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1301                 {
1302                   fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1303                   fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1304                   result = RESULT_ENDOFFILE;
1305                   continue;
1306                 }
1307
1308               if ( Options.verbose_flag )
1309                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1310
1311               if ( ! Options.no_write_flag )
1312                 {
1313                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1314
1315                   // The Writer class will forward the last block of ciphertext
1316                   // to the encryption context for use as the IV for the next
1317                   // frame. If you want to use non-sequitur IV values, un-comment
1318                   // the following  line of code.
1319                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1320                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1321                 }
1322             }
1323         }
1324
1325       if ( result == RESULT_ENDOFFILE )
1326         result = RESULT_OK;
1327     }
1328
1329   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1330     result = Writer.Finalize();
1331
1332   return result;
1333 }
1334
1335 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
1336 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
1337 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
1338 //
1339 Result_t
1340 read_PCM_file(CommandOptions& Options)
1341 {
1342   AESDecContext*     Context = 0;
1343   HMACContext*       HMAC = 0;
1344   PCM::MXFReader     Reader;
1345   PCM::FrameBuffer   FrameBuffer;
1346   WavFileWriter      OutWave;
1347   PCM::AudioDescriptor ADesc;
1348   ui32_t last_frame = 0;
1349
1350   Result_t result = Reader.OpenRead(Options.filenames[0]);
1351
1352   if ( ASDCP_SUCCESS(result) )
1353     {
1354       Reader.FillAudioDescriptor(ADesc);
1355
1356       if ( ADesc.EditRate != EditRate_23_98
1357            && ADesc.EditRate != EditRate_24
1358            && ADesc.EditRate != EditRate_25
1359            && ADesc.EditRate != EditRate_48
1360            && ADesc.EditRate != EditRate_50
1361            && ADesc.EditRate != EditRate_60 )
1362         ADesc.EditRate = Options.PictureRate();
1363
1364       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1365
1366       if ( Options.verbose_flag )
1367         PCM::AudioDescriptorDump(ADesc);
1368     }
1369
1370   if ( ASDCP_SUCCESS(result) )
1371     {
1372       last_frame = ADesc.ContainerDuration;
1373
1374       if ( Options.duration > 0 && Options.duration < last_frame )
1375         last_frame = Options.duration;
1376
1377       if ( Options.start_frame > 0 )
1378         {
1379           if ( Options.start_frame > ADesc.ContainerDuration )
1380             {
1381               fprintf(stderr, "Start value greater than file duration.\n");
1382               return RESULT_FAIL;
1383             }
1384
1385           last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
1386         }
1387
1388       ADesc.ContainerDuration = last_frame - Options.start_frame;
1389       OutWave.OpenWrite(ADesc, Options.file_root,
1390                         ( Options.split_wav ? WavFileWriter::ST_STEREO : 
1391                           ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
1392     }
1393
1394   if ( ASDCP_SUCCESS(result) && Options.key_flag )
1395     {
1396       Context = new AESDecContext;
1397       result = Context->InitKey(Options.key_value);
1398
1399       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1400         {
1401           WriterInfo Info;
1402           Reader.FillWriterInfo(Info);
1403
1404           if ( Info.UsesHMAC )
1405             {
1406               HMAC = new HMACContext;
1407               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1408             }
1409           else
1410             {
1411               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1412             }
1413         }
1414     }
1415
1416   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1417     {
1418       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1419
1420       if ( ASDCP_SUCCESS(result) )
1421         {
1422           if ( Options.verbose_flag )
1423             FrameBuffer.Dump(stderr, Options.fb_dump_size);
1424
1425           result = OutWave.WriteFrame(FrameBuffer);
1426         }
1427     }
1428
1429   return result;
1430 }
1431
1432
1433 //------------------------------------------------------------------------------------------
1434 // TimedText essence
1435
1436
1437 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1438 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1439 //
1440 Result_t
1441 write_timed_text_file(CommandOptions& Options)
1442 {
1443   AESEncContext*    Context = 0;
1444   HMACContext*      HMAC = 0;
1445   TimedText::DCSubtitleParser  Parser;
1446   TimedText::MXFWriter    Writer;
1447   TimedText::FrameBuffer  FrameBuffer;
1448   TimedText::TimedTextDescriptor TDesc;
1449   byte_t            IV_buf[CBC_BLOCK_SIZE];
1450   Kumu::FortunaRNG  RNG;
1451
1452   // set up essence parser
1453   Result_t result = Parser.OpenRead(Options.filenames[0]);
1454
1455   // set up MXF writer
1456   if ( ASDCP_SUCCESS(result) )
1457     {
1458       Parser.FillTimedTextDescriptor(TDesc);
1459       FrameBuffer.Capacity(Options.fb_size);
1460
1461       if ( Options.verbose_flag )
1462         {
1463           fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1464           TimedText::DescriptorDump(TDesc);
1465         }
1466     }
1467
1468   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1469     {
1470       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1471       if ( Options.asset_id_flag )
1472         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1473       else
1474         Kumu::GenRandomUUID(Info.AssetUUID);
1475
1476       if ( Options.use_smpte_labels )
1477         {
1478           Info.LabelSetType = LS_MXF_SMPTE;
1479           fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1480         }
1481
1482       // configure encryption
1483       if( Options.key_flag )
1484         {
1485           Kumu::GenRandomUUID(Info.ContextID);
1486           Info.EncryptedEssence = true;
1487
1488           if ( Options.key_id_flag )
1489             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1490           else
1491             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1492
1493           Context = new AESEncContext;
1494           result = Context->InitKey(Options.key_value);
1495
1496           if ( ASDCP_SUCCESS(result) )
1497             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1498
1499           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1500             {
1501               Info.UsesHMAC = true;
1502               HMAC = new HMACContext;
1503               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1504             }
1505         }
1506
1507       if ( ASDCP_SUCCESS(result) )
1508         result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1509     }
1510
1511   if ( ASDCP_FAILURE(result) )
1512     return result;
1513
1514   std::string XMLDoc;
1515   TimedText::ResourceList_t::const_iterator ri;
1516
1517   result = Parser.ReadTimedTextResource(XMLDoc);
1518
1519   if ( ASDCP_SUCCESS(result) )
1520     result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1521
1522   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1523     {
1524       result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1525
1526       if ( ASDCP_SUCCESS(result) )
1527         {
1528           if ( Options.verbose_flag )
1529             FrameBuffer.Dump(stderr, Options.fb_dump_size);
1530
1531           if ( ! Options.no_write_flag )
1532             {
1533               result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1534               
1535               // The Writer class will forward the last block of ciphertext
1536               // to the encryption context for use as the IV for the next
1537               // frame. If you want to use non-sequitur IV values, un-comment
1538               // the following  line of code.
1539               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1540               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1541             }
1542         }
1543
1544       if ( result == RESULT_ENDOFFILE )
1545         result = RESULT_OK;
1546     }
1547
1548   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1549     result = Writer.Finalize();
1550
1551   return result;
1552 }
1553
1554
1555 // Read one or more timed text streams from a plaintext ASDCP file
1556 // Read one or more timed text streams from a ciphertext ASDCP file
1557 // Read one or more timed text streams from a ciphertext ASDCP file
1558 //
1559 Result_t
1560 read_timed_text_file(CommandOptions& Options)
1561 {
1562   AESDecContext*     Context = 0;
1563   HMACContext*       HMAC = 0;
1564   TimedText::MXFReader     Reader;
1565   TimedText::FrameBuffer   FrameBuffer;
1566   TimedText::TimedTextDescriptor TDesc;
1567
1568   Result_t result = Reader.OpenRead(Options.filenames[0]);
1569
1570   if ( ASDCP_SUCCESS(result) )
1571     {
1572       Reader.FillTimedTextDescriptor(TDesc);
1573       FrameBuffer.Capacity(Options.fb_size);
1574
1575       if ( Options.verbose_flag )
1576         TimedText::DescriptorDump(TDesc);
1577     }
1578
1579   if ( ASDCP_SUCCESS(result) && Options.key_flag )
1580     {
1581       Context = new AESDecContext;
1582       result = Context->InitKey(Options.key_value);
1583
1584       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1585         {
1586           WriterInfo Info;
1587           Reader.FillWriterInfo(Info);
1588
1589           if ( Info.UsesHMAC )
1590             {
1591               HMAC = new HMACContext;
1592               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1593             }
1594           else
1595             {
1596               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1597             }
1598         }
1599     }
1600
1601   if ( ASDCP_FAILURE(result) )
1602     return result;
1603
1604   std::string XMLDoc;
1605   std::string out_path = Kumu::PathDirname(Options.file_root);
1606   ui32_t write_count;
1607   char buf[64];
1608   TimedText::ResourceList_t::const_iterator ri;
1609
1610   result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
1611
1612   if ( ASDCP_SUCCESS(result) )
1613     {
1614       Kumu::FileWriter Writer;
1615       result = Writer.OpenWrite(Options.file_root);
1616
1617       if ( ASDCP_SUCCESS(result) )
1618         result = Writer.Write(reinterpret_cast<const byte_t*>(XMLDoc.c_str()), XMLDoc.size(), &write_count);
1619     }
1620
1621   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1622     {
1623       result = Reader.ReadAncillaryResource(ri->ResourceID, FrameBuffer, Context, HMAC);
1624
1625       if ( ASDCP_SUCCESS(result) )
1626         {
1627           Kumu::FileWriter Writer;
1628           result = Writer.OpenWrite(Kumu::PathJoin(out_path, Kumu::UUID(ri->ResourceID).EncodeHex(buf, 64)).c_str());
1629
1630           if ( ASDCP_SUCCESS(result) )
1631             {
1632               if ( Options.verbose_flag )
1633                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1634
1635               result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count);
1636             }
1637         }
1638     }
1639
1640   return result;
1641 }
1642
1643 //------------------------------------------------------------------------------------------
1644 //
1645
1646 //
1647 // These classes wrap the irregular names in the asdcplib API
1648 // so that I can use a template to simplify the implementation
1649 // of show_file_info()
1650
1651 class MyVideoDescriptor : public MPEG2::VideoDescriptor
1652 {
1653  public:
1654   void FillDescriptor(MPEG2::MXFReader& Reader) {
1655     Reader.FillVideoDescriptor(*this);
1656   }
1657
1658   void Dump(FILE* stream) {
1659     MPEG2::VideoDescriptorDump(*this, stream);
1660   }
1661 };
1662
1663 class MyPictureDescriptor : public JP2K::PictureDescriptor
1664 {
1665  public:
1666   void FillDescriptor(JP2K::MXFReader& Reader) {
1667     Reader.FillPictureDescriptor(*this);
1668   }
1669
1670   void Dump(FILE* stream) {
1671     JP2K::PictureDescriptorDump(*this, stream);
1672   }
1673 };
1674
1675 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
1676 {
1677  public:
1678   void FillDescriptor(JP2K::MXFSReader& Reader) {
1679     Reader.FillPictureDescriptor(*this);
1680   }
1681
1682   void Dump(FILE* stream) {
1683     JP2K::PictureDescriptorDump(*this, stream);
1684   }
1685 };
1686
1687 class MyAudioDescriptor : public PCM::AudioDescriptor
1688 {
1689  public:
1690   void FillDescriptor(PCM::MXFReader& Reader) {
1691     Reader.FillAudioDescriptor(*this);
1692   }
1693
1694   void Dump(FILE* stream) {
1695     PCM::AudioDescriptorDump(*this, stream);
1696   }
1697 };
1698
1699 class MyTextDescriptor : public TimedText::TimedTextDescriptor
1700 {
1701  public:
1702   void FillDescriptor(TimedText::MXFReader& Reader) {
1703     Reader.FillTimedTextDescriptor(*this);
1704   }
1705
1706   void Dump(FILE* stream) {
1707     TimedText::DescriptorDump(*this, stream);
1708   }
1709 };
1710
1711 // MSVC didn't like the function template, so now it's a static class method
1712 template<class ReaderT, class DescriptorT>
1713 class FileInfoWrapper
1714 {
1715 public:
1716   static Result_t
1717   file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
1718   {
1719     assert(type_string);
1720     if ( stream == 0 )
1721       stream = stdout;
1722
1723     Result_t result = RESULT_OK;
1724
1725     if ( Options.verbose_flag || Options.showheader_flag )
1726       {
1727         ReaderT     Reader;
1728         result = Reader.OpenRead(Options.filenames[0]);
1729
1730         if ( ASDCP_SUCCESS(result) )
1731           {
1732             fprintf(stdout, "File essence type is %s.\n", type_string);
1733
1734             if ( Options.showheader_flag )
1735               Reader.DumpHeaderMetadata(stream);
1736
1737             WriterInfo WI;
1738             Reader.FillWriterInfo(WI);
1739             WriterInfoDump(WI, stream);
1740
1741             DescriptorT Desc;
1742             Desc.FillDescriptor(Reader);
1743             Desc.Dump(stream);
1744
1745             if ( Options.showindex_flag )
1746               Reader.DumpIndex(stream);
1747           }
1748         else if ( result == RESULT_FORMAT && Options.showheader_flag )
1749           {
1750             Reader.DumpHeaderMetadata(stream);
1751           }
1752       }
1753
1754     return result;
1755   }
1756 };
1757
1758 // Read header metadata from an ASDCP file
1759 //
1760 Result_t
1761 show_file_info(CommandOptions& Options)
1762 {
1763   EssenceType_t EssenceType;
1764   Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1765
1766   if ( ASDCP_FAILURE(result) )
1767     return result;
1768
1769   if ( EssenceType == ESS_MPEG2_VES )
1770     result = FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options, "MPEG2 video");
1771
1772   else if ( EssenceType == ESS_PCM_24b_48k || EssenceType == ESS_PCM_24b_96k )
1773     result = FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options, "PCM audio");
1774
1775   else if ( EssenceType == ESS_JPEG_2000 )
1776     {
1777       if ( Options.stereo_image_flag )
1778         result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1779         MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1780
1781       else
1782         result = FileInfoWrapper<ASDCP::JP2K::MXFReader,
1783         MyPictureDescriptor>::file_info(Options, "JPEG 2000 pictures");
1784     }
1785   else if ( EssenceType == ESS_JPEG_2000_S )
1786     result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1787     MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1788
1789   else if ( EssenceType == ESS_TIMED_TEXT )
1790     result = FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options, "Timed Text");
1791
1792   else
1793     {
1794       fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]);
1795       Kumu::FileReader   Reader;
1796       const Dictionary* Dict = &DefaultCompositeDict();
1797       MXF::OPAtomHeader TestHeader(Dict);
1798
1799       result = Reader.OpenRead(Options.filenames[0]);
1800
1801       if ( ASDCP_SUCCESS(result) )
1802         result = TestHeader.InitFromFile(Reader); // test UL and OP
1803
1804       if ( ASDCP_SUCCESS(result) )
1805         {
1806           TestHeader.Partition::Dump(stdout);
1807
1808           if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1809             ID->Dump(stdout);
1810           else
1811             fputs("File contains no Identification object.\n", stdout);
1812
1813           if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1814             SP->Dump(stdout);
1815           else
1816             fputs("File contains no SourcePackage object.\n", stdout);
1817         }
1818       else
1819         {
1820           fputs("File is not MXF.\n", stdout);
1821         }
1822     }
1823
1824   return result;
1825 }
1826
1827
1828 //
1829 Result_t
1830 digest_file(const char* filename)
1831 {
1832   using namespace Kumu;
1833
1834   ASDCP_TEST_NULL_STR(filename);
1835   FileReader Reader;
1836   SHA_CTX Ctx;
1837   SHA1_Init(&Ctx);
1838   ByteString Buf(8192);
1839
1840   Result_t result = Reader.OpenRead(filename);
1841
1842   while ( ASDCP_SUCCESS(result) )
1843     {
1844       ui32_t read_count = 0;
1845       result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count);
1846
1847       if ( result == RESULT_ENDOFFILE )
1848         {
1849           result = RESULT_OK;
1850           break;
1851         }
1852
1853       if ( ASDCP_SUCCESS(result) )
1854         SHA1_Update(&Ctx, Buf.Data(), read_count);
1855     }
1856
1857   if ( ASDCP_SUCCESS(result) )
1858     {
1859       const ui32_t sha_len = 20;
1860       byte_t bin_buf[sha_len];
1861       char sha_buf[64];
1862       SHA1_Final(bin_buf, &Ctx);
1863
1864       fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename);
1865     }
1866
1867   return result;
1868 }
1869
1870 //
1871 int
1872 main(int argc, const char** argv)
1873 {
1874   Result_t result = RESULT_OK;
1875   char     str_buf[64];
1876   CommandOptions Options(argc, argv);
1877
1878   if ( Options.version_flag )
1879     banner();
1880
1881   if ( Options.help_flag )
1882     usage();
1883
1884   if ( Options.version_flag || Options.help_flag )
1885     return 0;
1886
1887   if ( Options.error_flag )
1888     {
1889       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1890       return 3;
1891     }
1892
1893   if ( Options.mode == MMT_INFO )
1894     {
1895       result = show_file_info(Options);
1896     }
1897   else if ( Options.mode == MMT_GOP_START )
1898     {
1899       result = gop_start_test(Options);
1900     }
1901   else if ( Options.mode == MMT_GEN_KEY )
1902     {
1903       Kumu::FortunaRNG RNG;
1904       byte_t bin_buf[KeyLen];
1905
1906       RNG.FillRandom(bin_buf, KeyLen);
1907       printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 64));
1908     }
1909   else if ( Options.mode == MMT_GEN_ID )
1910     {
1911       UUID TmpID;
1912       Kumu::GenRandomValue(TmpID);
1913       printf("%s\n", TmpID.EncodeHex(str_buf, 64));
1914     }
1915   else if ( Options.mode == MMT_DIGEST )
1916     {
1917       for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ )
1918         result = digest_file(Options.filenames[i]);
1919     }
1920   else if ( Options.mode == MMT_UL_LIST )
1921     {
1922       if ( Options.use_smpte_labels )
1923         DefaultSMPTEDict().Dump(stdout);
1924       else
1925         DefaultInteropDict().Dump(stdout);
1926     }
1927   else if ( Options.mode == MMT_EXTRACT )
1928     {
1929       EssenceType_t EssenceType;
1930       result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1931
1932       if ( ASDCP_SUCCESS(result) )
1933         {
1934           switch ( EssenceType )
1935             {
1936             case ESS_MPEG2_VES:
1937               result = read_MPEG2_file(Options);
1938               break;
1939
1940             case ESS_JPEG_2000:
1941               if ( Options.stereo_image_flag )
1942                 result = read_JP2K_S_file(Options);
1943               else
1944                 result = read_JP2K_file(Options);
1945               break;
1946
1947             case ESS_JPEG_2000_S:
1948               result = read_JP2K_S_file(Options);
1949               break;
1950
1951             case ESS_PCM_24b_48k:
1952             case ESS_PCM_24b_96k:
1953               result = read_PCM_file(Options);
1954               break;
1955
1956             case ESS_TIMED_TEXT:
1957               result = read_timed_text_file(Options);
1958               break;
1959
1960             default:
1961               fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]);
1962               return 5;
1963             }
1964         }
1965     }
1966   else if ( Options.mode == MMT_CREATE )
1967     {
1968       if ( Options.do_repeat && ! Options.duration_flag )
1969         {
1970           fputs("Option -R requires -d <duration>\n", stderr);
1971           return RESULT_FAIL;
1972         }
1973
1974       EssenceType_t EssenceType;
1975       result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType);
1976
1977       if ( ASDCP_SUCCESS(result) )
1978         {
1979           switch ( EssenceType )
1980             {
1981             case ESS_MPEG2_VES:
1982               result = write_MPEG2_file(Options);
1983               break;
1984
1985             case ESS_JPEG_2000:
1986               if ( Options.stereo_image_flag )
1987                 result = write_JP2K_S_file(Options);
1988
1989               else
1990                 result = write_JP2K_file(Options);
1991
1992               break;
1993
1994             case ESS_PCM_24b_48k:
1995             case ESS_PCM_24b_96k:
1996               result = write_PCM_file(Options);
1997               break;
1998
1999             case ESS_TIMED_TEXT:
2000               result = write_timed_text_file(Options);
2001               break;
2002
2003             default:
2004               fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
2005                       Options.filenames[0]);
2006               return 5;
2007             }
2008         }
2009     }
2010   else
2011     {
2012       fprintf(stderr, "Unhandled mode: %d.\n", Options.mode);
2013       return 6;
2014     }
2015
2016   if ( ASDCP_FAILURE(result) )
2017     {
2018       fputs("Program stopped on error.\n", stderr);
2019
2020       if ( result == RESULT_SFORMAT )
2021         {
2022           fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
2023         }
2024       else if ( result != RESULT_FAIL )
2025         {
2026           fputs(result, stderr);
2027           fputc('\n', stderr);
2028         }
2029
2030       return 1;
2031     }
2032
2033   return 0;
2034 }
2035
2036
2037 //
2038 // end asdcp-test.cpp
2039 //