IMF MCA labels
[asdcplib.git] / src / as-02-wrap.cpp
1 /*
2 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
10 1. Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16    derived from this software without specific prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29 /*! \file    as-02-wrap.cpp
30     \version $Id$       
31     \brief   AS-02 file manipulation utility
32
33   This program wraps IMF essence (picture or sound) in to an AS-02 MXF file.
34
35   For more information about AS-02, please refer to the header file AS_02.h
36   For more information about asdcplib, please refer to the header file AS_DCP.h
37 */
38
39 #include <KM_fileio.h>
40 #include <KM_prng.h>
41 #include <AS_02.h>
42 #include <PCMParserList.h>
43 #include <Metadata.h>
44
45 using namespace ASDCP;
46
47 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
48 const ASDCP::Dictionary *g_dict = 0;
49
50
51 const char*
52 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
53 {
54   snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
55   return buf;
56 }
57
58
59
60 //------------------------------------------------------------------------------------------
61 //
62 // command line option parser class
63
64 static const char* PROGRAM_NAME = "as-02-wrap";  // program name for messages
65
66 // local program identification info written to file headers
67 class MyInfo : public WriterInfo
68 {
69 public:
70   MyInfo()
71   {
72       static byte_t default_ProductUUID_Data[UUIDlen] =
73       { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
74         0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
75       
76       memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
77       CompanyName = "WidgetCo";
78       ProductName = "as-02-wrap";
79       ProductVersion = ASDCP::Version();
80   }
81 } s_MyInfo;
82
83
84
85 // Increment the iterator, test for an additional non-option command line argument.
86 // Causes the caller to return if there are no remaining arguments or if the next
87 // argument begins with '-'.
88 #define TEST_EXTRA_ARG(i,c)                                             \
89   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
90     fprintf(stderr, "Argument not found for option -%c.\n", (c));       \
91     return;                                                             \
92   }
93
94 //
95 void
96 banner(FILE* stream = stdout)
97 {
98   fprintf(stream, "\n\
99 %s (asdcplib %s)\n\n\
100 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
101 asdcplib may be copied only under the terms of the license found at\n\
102 the top of every file in the asdcplib distribution kit.\n\n\
103 Specify the -h (help) option for further information about %s\n\n",
104           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
105 }
106
107 //
108 void
109 usage(FILE* stream = stdout)
110 {
111   fprintf(stream, "\
112 USAGE: %s [-h|-help] [-V]\n\
113 \n\
114        %s [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
115           [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
116             [-M] [-m <expr>] [-r <n>/<d>] [-s <seconds>] [-v] [-W]\n\
117           [-z|-Z] <input-file>+ <output-file>\n\n",
118           PROGRAM_NAME, PROGRAM_NAME);
119
120   fprintf(stream, "\
121 Options:\n\
122   -C <UL>           - Set ChannelAssignment UL value\n\
123   -h | -help        - Show help\n\
124   -V                - Show version information\n\
125   -e                - Encrypt JP2K headers (default)\n\
126   -E                - Do not encrypt JP2K headers\n\
127   -j <key-id-str>   - Write key ID instead of creating a random value\n\
128   -k <key-string>   - Use key for ciphertext operations\n\
129   -M                - Do not create HMAC values when writing\n\
130   -m <expr>         - Write MCA labels using <expr>.  Example:\n\
131                         51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
132   -a <UUID>         - Specify the Asset ID of the file\n\
133   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
134                       Defaults to 4,194,304 (4MB)\n\
135   -d <duration>     - Number of frames to process, default all\n\
136   -f <start-frame>  - Starting frame number, default 0\n\
137   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
138   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
139   -v                - Verbose, prints informative messages to stderr\n\
140   -W                - Read input file only, do not write source file\n\
141   -z                - Fail if j2c inputs have unequal parameters (default)\n\
142   -Z                - Ignore unequal parameters in j2c inputs\n\
143 \n\
144   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
145          o All option arguments must be separated from the option by whitespace.\n\n");
146 }
147
148 //
149 static ASDCP::Rational
150 decode_rational(const char* str_rat)
151 {
152   assert(str_rat);
153   ui32_t Num = atoi(str_rat);
154   ui32_t Den = 0;
155
156   const char* den_str = strrchr(str_rat, ' ');
157   if ( den_str != 0 )
158     Den = atoi(den_str+1);
159
160   return ASDCP::Rational(Num, Den);
161 }
162 //
163 //
164 class CommandOptions
165 {
166   CommandOptions();
167
168 public:
169   bool   error_flag;     // true if the given options are in error or not complete
170   bool   key_flag;       // true if an encryption key was given
171   bool   asset_id_flag;  // true if an asset ID was given
172   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
173   bool   write_hmac;     // true if HMAC values are to be generated and written
174   bool   verbose_flag;   // true if the verbose option was selected
175   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
176   bool   no_write_flag;  // true if no output files are to be written
177   bool   version_flag;   // true if the version display option was selected
178   bool   help_flag;      // true if the help display option was selected
179   ui32_t start_frame;    // frame number to begin processing
180   ui32_t duration;       // number of frames to be processed
181   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
182   Rational edit_rate;    // edit rate of JP2K sequence
183   ui32_t fb_size;        // size of picture frame buffer
184   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
185   bool   key_id_flag;    // true if a key ID was given
186   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
187   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
188   std::string out_file; //
189   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
190   Kumu::PathList_t filenames;  // list of filenames to be processed
191   UL channel_assignment;
192   ASDCP::MXF::AS02_MCAConfigParser mca_config;
193
194   //new attributes for AS-02 support 
195   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
196   ui32_t partition_space; //Shim parameter partition_spacing
197
198   //
199   CommandOptions(int argc, const char** argv) :
200     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
201     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
202     no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
203     duration(0xffffffff), j2c_pedantic(true), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
204     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
205     mca_config(g_dict)
206   {
207     memset(key_value, 0, KeyLen);
208     memset(key_id_value, 0, UUIDlen);
209
210     for ( int i = 1; i < argc; i++ )
211       {
212
213         if ( (strcmp( argv[i], "-help") == 0) )
214           {
215             help_flag = true;
216             continue;
217           }
218          
219         if ( argv[i][0] == '-'
220              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
221              && argv[i][2] == 0 )
222           {
223             switch ( argv[i][1] )
224               {
225               case 'a':
226                 asset_id_flag = true;
227                 TEST_EXTRA_ARG(i, 'a');
228                 {
229                   ui32_t length;
230                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
231
232                   if ( length != UUIDlen )
233                     {
234                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
235                       return;
236                     }
237                 }
238                 break;
239
240               case 'b':
241                 TEST_EXTRA_ARG(i, 'b');
242                 fb_size = abs(atoi(argv[i]));
243
244                 if ( verbose_flag )
245                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
246
247                 break;
248
249               case 'C':
250                 TEST_EXTRA_ARG(i, 'U');
251                 if ( ! channel_assignment.DecodeHex(argv[i]) )
252                   {
253                     fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
254                     return;
255                   }
256                 break;
257
258               case 'd':
259                 TEST_EXTRA_ARG(i, 'd');
260                 duration = abs(atoi(argv[i]));
261                 break;
262
263               case 'E': encrypt_header_flag = false; break;
264               case 'e': encrypt_header_flag = true; break;
265
266               case 'f':
267                 TEST_EXTRA_ARG(i, 'f');
268                 start_frame = abs(atoi(argv[i]));
269                 break;
270
271               case 'h': help_flag = true; break;
272
273               case 'j': key_id_flag = true;
274                 TEST_EXTRA_ARG(i, 'j');
275                 {
276                   ui32_t length;
277                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
278
279                   if ( length != UUIDlen )
280                     {
281                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
282                       return;
283                     }
284                 }
285                 break;
286
287               case 'k': key_flag = true;
288                 TEST_EXTRA_ARG(i, 'k');
289                 {
290                   ui32_t length;
291                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
292
293                   if ( length != KeyLen )
294                     {
295                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
296                       return;
297                     }
298                 }
299                 break;
300
301               case 'M': write_hmac = false; break;
302
303               case 'm':
304                 TEST_EXTRA_ARG(i, 'm');
305                 if ( ! mca_config.DecodeString(argv[i]) )
306                   {
307                     return;
308                   }
309                 break;
310
311               case 'r':
312                 TEST_EXTRA_ARG(i, 'r');
313                 edit_rate = decode_rational(argv[i]);
314                 break;
315
316               case 's':
317                 TEST_EXTRA_ARG(i, 's');
318                 partition_space = abs(atoi(argv[i]));
319                 break;
320
321               case 'u': show_ul_values_flag = true; break;
322               case 'V': version_flag = true; break;
323               case 'v': verbose_flag = true; break;
324               case 'W': no_write_flag = true; break;
325               case 'Z': j2c_pedantic = false; break;
326               case 'z': j2c_pedantic = true; break;
327
328               default:
329                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
330                 return;
331               }
332           }
333         else
334           {
335
336             if ( argv[i][0] != '-' )
337               {
338                 filenames.push_back(argv[i]);
339               }
340             else
341               {
342                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
343                 return;
344               }
345           }
346       }
347
348     if ( help_flag || version_flag )
349       return;
350     
351     if ( filenames.size() < 2 )
352       {
353         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
354         return;
355       }
356
357     out_file = filenames.back();
358     filenames.pop_back();
359     error_flag = false;
360   }
361 };
362
363
364 //------------------------------------------------------------------------------------------
365 // JPEG 2000 essence
366
367 namespace ASDCP {
368   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
369                             const ASDCP::Dictionary& dict,
370                             ASDCP::MXF::RGBAEssenceDescriptor *EssenceDescriptor,
371                             ASDCP::MXF::JPEG2000PictureSubDescriptor *EssenceSubDescriptor);
372
373   Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
374 }
375
376 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
377 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
378 //
379 Result_t
380 write_JP2K_file(CommandOptions& Options)
381 {
382   AESEncContext*          Context = 0;
383   HMACContext*            HMAC = 0;
384   AS_02::JP2K::MXFWriter  Writer;
385   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
386   JP2K::SequenceParser    Parser;
387   byte_t                  IV_buf[CBC_BLOCK_SIZE];
388   Kumu::FortunaRNG        RNG;
389   ASDCP::MXF::RGBAEssenceDescriptor *essence_descriptor = 0;
390   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
391
392   // set up essence parser
393   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
394
395   // set up MXF writer
396   if ( ASDCP_SUCCESS(result) )
397     {
398       ASDCP::JP2K::PictureDescriptor PDesc;
399       Parser.FillPictureDescriptor(PDesc);
400       PDesc.EditRate = Options.edit_rate;
401
402       if ( Options.verbose_flag )
403         {
404           fprintf(stderr, "JPEG 2000 pictures\n");
405           fputs("PictureDescriptor:\n", stderr);
406           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
407           JP2K::PictureDescriptorDump(PDesc);
408         }
409
410       // TODO: optionally set up CDCIEssenceDescriptor
411       essence_descriptor = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
412       essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
413
414       result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict, essence_descriptor,
415                                        reinterpret_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
416
417       /// TODO: set with magic or some such thing
418       essence_descriptor->ComponentMaxRef = 4095;
419       essence_descriptor->ComponentMinRef = 0;
420     }
421
422   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
423     {
424       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
425       Info.LabelSetType = LS_MXF_SMPTE;
426
427       if ( Options.asset_id_flag )
428         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
429       else
430         Kumu::GenRandomUUID(Info.AssetUUID);
431
432       // configure encryption
433       if( Options.key_flag )
434         {
435           Kumu::GenRandomUUID(Info.ContextID);
436           Info.EncryptedEssence = true;
437
438           if ( Options.key_id_flag )
439             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
440           else
441             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
442
443           Context = new AESEncContext;
444           result = Context->InitKey(Options.key_value);
445
446           if ( ASDCP_SUCCESS(result) )
447             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
448
449           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
450             {
451               Info.UsesHMAC = true;
452               HMAC = new HMACContext;
453               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
454             }
455         }
456
457       if ( ASDCP_SUCCESS(result) )
458         {
459           result = Writer.OpenWrite(Options.out_file, Info,
460                                     static_cast<ASDCP::MXF::FileDescriptor*>(essence_descriptor),
461                                     essence_sub_descriptors,
462                                     Options.edit_rate, 16384, Options.index_strategy, Options.partition_space);
463           // TODO: make 16384 part of CommandOptions
464         }
465     }
466
467   if ( ASDCP_SUCCESS(result) )
468     {
469       ui32_t duration = 0;
470       result = Parser.Reset();
471
472       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
473         {
474           result = Parser.ReadFrame(FrameBuffer);
475           
476           if ( ASDCP_SUCCESS(result) )
477             {
478               if ( Options.verbose_flag )
479                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
480               
481               if ( Options.encrypt_header_flag )
482                 FrameBuffer.PlaintextOffset(0);
483             }
484
485           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
486             {
487               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
488
489               // The Writer class will forward the last block of ciphertext
490               // to the encryption context for use as the IV for the next
491               // frame. If you want to use non-sequitur IV values, un-comment
492               // the following  line of code.
493               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
494               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
495             }
496         }
497
498       if ( result == RESULT_ENDOFFILE )
499         result = RESULT_OK;
500     }
501
502   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
503     result = Writer.Finalize();
504
505   return result;
506 }
507
508 //------------------------------------------------------------------------------------------
509 // PCM essence
510
511
512 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
513 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
514 //
515 Result_t
516 write_PCM_file(CommandOptions& Options)
517 {
518   AESEncContext*    Context = 0;
519   HMACContext*      HMAC = 0;
520   PCMParserList     Parser;
521   AS_02::PCM::MXFWriter    Writer;
522   PCM::FrameBuffer  FrameBuffer;
523   byte_t            IV_buf[CBC_BLOCK_SIZE];
524   Kumu::FortunaRNG  RNG;
525   ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
526
527   // set up essence parser
528   Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
529
530   // set up MXF writer
531   if ( ASDCP_SUCCESS(result) )
532     {
533       ASDCP::PCM::AudioDescriptor ADesc;
534       Parser.FillAudioDescriptor(ADesc);
535
536       ADesc.EditRate = Options.edit_rate;
537       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
538
539       if ( Options.verbose_flag )
540         {
541           char buf[64];
542           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
543                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
544                   RationalToString(Options.edit_rate, buf, 64),
545                   PCM::CalcSamplesPerFrame(ADesc));
546           fputs("AudioDescriptor:\n", stderr);
547           PCM::AudioDescriptorDump(ADesc);
548         }
549
550       essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
551
552       result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
553
554       if ( Options.mca_config.empty() )
555         {
556           essence_descriptor->ChannelAssignment = Options.channel_assignment;
557         }
558       else
559         {
560           if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
561             {
562               fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
563                       Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
564               return RESULT_FAIL;
565             }
566
567           // this is the d-cinema MCA label, what is the one for IMF?
568           essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
569         }
570     }
571
572   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
573     {
574       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
575       Info.LabelSetType = LS_MXF_SMPTE;
576
577       if ( Options.asset_id_flag )
578         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
579       else
580         Kumu::GenRandomUUID(Info.AssetUUID);
581
582       // configure encryption
583       if( Options.key_flag )
584         {
585           Kumu::GenRandomUUID(Info.ContextID);
586           Info.EncryptedEssence = true;
587
588           if ( Options.key_id_flag )
589             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
590           else
591             RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
592
593           Context = new AESEncContext;
594           result = Context->InitKey(Options.key_value);
595
596           if ( ASDCP_SUCCESS(result) )
597             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
598
599           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
600             {
601               Info.UsesHMAC = true;
602               HMAC = new HMACContext;
603               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
604             }
605         }
606
607       if ( ASDCP_SUCCESS(result) )
608         {
609           result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
610                                     Options.mca_config, Options.edit_rate);
611         }
612     }
613
614   if ( ASDCP_SUCCESS(result) )
615     {
616       result = Parser.Reset();
617       ui32_t duration = 0;
618
619       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
620         {
621           result = Parser.ReadFrame(FrameBuffer);
622
623           if ( ASDCP_SUCCESS(result) )
624             {
625               if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
626                 {
627                   fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
628                   fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
629                   result = RESULT_ENDOFFILE;
630                   continue;
631                 }
632
633               if ( Options.verbose_flag )
634                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
635
636               if ( ! Options.no_write_flag )
637                 {
638                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
639
640                   // The Writer class will forward the last block of ciphertext
641                   // to the encryption context for use as the IV for the next
642                   // frame. If you want to use non-sequitur IV values, un-comment
643                   // the following  line of code.
644                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
645                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
646                 }
647             }
648         }
649
650       if ( result == RESULT_ENDOFFILE )
651         result = RESULT_OK;
652     }
653
654   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
655     result = Writer.Finalize();
656
657   return result;
658 }
659
660
661 //
662 int
663 main(int argc, const char** argv)
664 {
665   Result_t result = RESULT_OK;
666   char     str_buf[64];
667   g_dict = &ASDCP::DefaultSMPTEDict();
668
669   CommandOptions Options(argc, argv);
670
671   if ( Options.version_flag )
672     banner();
673
674   if ( Options.help_flag )
675     usage();
676
677   if ( Options.show_ul_values_flag )
678     {
679       g_dict->Dump(stdout);
680     }
681
682   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
683     return 0;
684
685   if ( Options.error_flag )
686     {
687       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
688       return 3;
689     }
690
691   EssenceType_t EssenceType;
692   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
693
694   if ( ASDCP_SUCCESS(result) )
695     {
696       switch ( EssenceType )
697         {
698         case ESS_JPEG_2000:
699           result = write_JP2K_file(Options);
700           break;
701
702         case ESS_PCM_24b_48k:
703         case ESS_PCM_24b_96k:
704           result = write_PCM_file(Options);
705           break;
706
707         default:
708           fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
709                   Options.filenames.front().c_str());
710           return 5;
711         }
712     }
713
714   if ( ASDCP_FAILURE(result) )
715     {
716       fputs("Program stopped on error.\n", stderr);
717
718       if ( result != RESULT_FAIL )
719         {
720           fputs(result, stderr);
721           fputc('\n', stderr);
722         }
723
724       return 1;
725     }
726
727   return 0;
728 }
729
730
731 //
732 // end as-02-wrap.cpp
733 //