o Moved original '-g' option in asdc-wrap to '-x' (currently
[asdcplib.git] / src / as-02-wrap.cpp
1 /*
2 Copyright (c) 2011-2018, 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 const char*
51 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
52 {
53   snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
54   return buf;
55 }
56
57
58 //------------------------------------------------------------------------------------------
59 //
60 // command line option parser class
61
62 static const char* PROGRAM_NAME = "as-02-wrap";  // program name for messages
63
64 // local program identification info written to file headers
65 class MyInfo : public WriterInfo
66 {
67 public:
68   MyInfo()
69   {
70       static byte_t default_ProductUUID_Data[UUIDlen] =
71       { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
72         0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
73       
74       memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
75       CompanyName = "WidgetCo";
76       ProductName = "as-02-wrap";
77       ProductVersion = ASDCP::Version();
78   }
79 } s_MyInfo;
80
81
82
83 // Increment the iterator, test for an additional non-option command line argument.
84 // Causes the caller to return if there are no remaining arguments or if the next
85 // argument begins with '-'.
86 #define TEST_EXTRA_ARG(i,c)                                             \
87   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
88     fprintf(stderr, "Argument not found for option -%c.\n", (c));       \
89     return;                                                             \
90   }
91
92
93 //
94 static void
95 create_random_uuid(byte_t* uuidbuf)
96 {
97   Kumu::UUID tmp_id;
98   GenRandomValue(tmp_id);
99   memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
100 }
101
102 //
103 void
104 banner(FILE* stream = stdout)
105 {
106   fprintf(stream, "\n\
107 %s (asdcplib %s)\n\n\
108 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
109 asdcplib may be copied only under the terms of the license found at\n\
110 the top of every file in the asdcplib distribution kit.\n\n\
111 Specify the -h (help) option for further information about %s\n\n",
112           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
113 }
114
115 //
116 void
117 usage(FILE* stream = stdout)
118 {
119   fprintf(stream, "\
120 USAGE: %s [-h|-help] [-V]\n\
121 \n\
122        %s [options] <input-file>+ <output-file>\n\n",
123           PROGRAM_NAME, PROGRAM_NAME);
124
125   fprintf(stream, "\
126 Options:\n\
127   -h | -help        - Show help\n\
128   -V                - Show version information\n\
129   -a <uuid>         - Specify the Asset ID of the file\n\
130   -A <w>/<h>        - Set aspect ratio for image (default 4/3)\n\
131   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
132                       Defaults to 4,194,304 (4MB)\n\
133   -c <num>          - Select the IMF color system to be signaled:\n\
134                       Application 2 (2067-20): 1, 2, or 3\n\
135                       Application 2e (2067-21): 4 or 5\n\
136                       All color system values assume YCbCr; also use -R for RGB\n\
137   -C <ul>           - Set ChannelAssignment UL value\n\
138   -d <duration>     - Number of frames to process, default all\n\
139   -D <depth>        - Component depth for YCbCr images (default: 10)\n\
140   -e                - Encrypt JP2K headers (default)\n\
141   -E                - Do not encrypt JP2K headers\n\
142   -F (0|1)          - Set field dominance for interlaced image (default: 0)\n\
143   -g <rfc-5646-code>\n\
144                     - Create MCA labels having the given RFC 5646 language code\n\
145                       (requires option \"-m\")\n\
146   -i                - Indicates input essence is interlaced fields (forces -Y)\n\
147   -j <key-id-str>   - Write key ID instead of creating a random value\n\
148   -k <key-string>   - Use key for ciphertext operations\n\
149   -l <first>,<second>\n\
150                     - Integer values that set the VideoLineMap when creating\n\
151                       interlaced YCbCr files\n\
152   -m <expr>         - Write MCA labels using <expr>.  Example:\n\
153                         51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
154   -M                - Do not create HMAC values when writing\n\
155   -n <UL>           - Set the TransferCharacteristic UL\n\
156   -o <min>,<max>    - Mastering Display luminance, cd*m*m, e.g., \".05,100\"\n\
157   -O <rx>,<ry>,<gx>,<gy>,<bx>,<by>,<wx>,<wy>\n\
158                     - Mastering Display Color Primaries and white point\n\
159                       e.g., \".64,.33,.3,.6,.15,.06,.3457,.3585\"\n\
160   -p <ul>           - Set broadcast profile\n\
161   -P <string>       - Set NamespaceURI property when creating timed text MXF\n\
162   -q <UL>           - Set the CodingEquations UL\n\
163   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
164   -R                - Indicates RGB image essence (default except with -c)\n\
165   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
166   -t <min>          - Set RGB component minimum code value (default: 0)\n\
167   -T <max>          - Set RGB component maximum code value (default: 1023)\n\
168   -u                - Print UL catalog to stdout\n\
169   -v                - Verbose, prints informative messages to stderr\n\
170   -W                - Read input file only, do not write source file\n\
171   -x <int>          - Horizontal subsampling degree (default: 2)\n\
172   -X <int>          - Vertical subsampling degree (default: 2)\n\
173   -y <white-ref>[,<black-ref>[,<color-range>]]\n\
174                     - Same as -Y but White Ref, Black Ref and Color Range are\n\
175                       set from the given argument\n\
176   -Y                - Indicates YCbCr image essence (default with -c), uses\n\
177                       default values for White Ref, Black Ref and Color Range,\n\
178                        940,64,897, indicating 10 bit standard Video Range\n\
179   -z                - Fail if j2c inputs have unequal parameters (default)\n\
180   -Z                - Ignore unequal parameters in j2c inputs\n\
181 \n\
182   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
183          o All option arguments must be separated from the option by whitespace.\n\n");
184 }
185
186 const float chromaticity_scale = 50000.0;
187 //
188 ui32_t
189 set_primary_from_token(const std::string& token, ui16_t& primary)
190 {
191   float raw_value = strtod(token.c_str(),0);
192
193   if ( raw_value == 0.0 || raw_value > 1.0 )
194     {
195       fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str());
196       return false;
197     }
198
199   primary = floor(0.5 + ( raw_value * chromaticity_scale ));
200   return true;
201 }
202
203 const float luminance_scale = 10000.0;
204 //
205 ui32_t
206 set_luminance_from_token(const std::string& token, ui32_t& luminance)
207 {
208   float raw_value = strtod(token.c_str(),0);
209
210   if ( raw_value == 0.0 || raw_value > 400000.0 )
211     {
212       fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str());
213       return false;
214     }
215
216   luminance = floor(0.5 + ( raw_value * luminance_scale ));
217   return true;
218 }
219
220 #define SET_LUMINANCE(p,t)                      \
221   if ( ! set_luminance_from_token(t, p) ) {     \
222     return false;                               \
223   }
224
225 //
226 class CommandOptions
227 {
228   CommandOptions();
229
230 public:
231   bool   error_flag;     // true if the given options are in error or not complete
232   bool   key_flag;       // true if an encryption key was given
233   bool   asset_id_flag;  // true if an asset ID was given
234   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
235   bool   write_hmac;     // true if HMAC values are to be generated and written
236   bool   verbose_flag;   // true if the verbose option was selected
237   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
238   bool   no_write_flag;  // true if no output files are to be written
239   bool   version_flag;   // true if the version display option was selected
240   bool   help_flag;      // true if the help display option was selected
241   ui32_t duration;       // number of frames to be processed
242   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
243   bool use_cdci_descriptor; // 
244   Rational edit_rate;    // edit rate of JP2K sequence
245   ui32_t fb_size;        // size of picture frame buffer
246   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
247   bool   key_id_flag;    // true if a key ID was given
248   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
249   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
250   bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
251   Kumu::PathList_t filenames;  // list of filenames to be processed
252
253   UL channel_assignment, picture_coding, transfer_characteristic, color_primaries, coding_equations;
254   ASDCP::MXF::AS02_MCAConfigParser mca_config;
255   std::string mca_language;
256
257   ui32_t rgba_MaxRef;
258   ui32_t rgba_MinRef;
259
260   ui32_t horizontal_subsampling;
261   ui32_t vertical_subsampling;
262   ui32_t component_depth;
263   ui8_t frame_layout;
264   ASDCP::Rational aspect_ratio;
265   ui8_t field_dominance;
266   ui32_t mxf_header_size;
267   ui32_t cdci_BlackRefLevel; 
268   ui32_t cdci_WhiteRefLevel;
269   ui32_t cdci_ColorRange;
270
271   ui32_t md_min_luminance, md_max_luminance;
272   ASDCP::MXF::ThreeColorPrimaries md_primaries;
273   ASDCP::MXF::ColorPrimary md_white_point;
274
275   //new attributes for AS-02 support 
276   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
277   ui32_t partition_space; //Shim parameter partition_spacing
278
279   //
280   MXF::LineMapPair line_map;
281   std::string out_file, profile_name; //
282
283   //
284   bool set_video_line_map(const std::string& arg)
285   {
286     const char* sep_str = strrchr(arg.c_str(), ',');
287
288     if ( sep_str == 0 )
289       {
290         fprintf(stderr, "Expecting <first>,<second>\n");
291         return false;
292       }
293
294     line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
295     line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
296     return true;
297   }
298
299   //
300   bool set_video_ref(const std::string& arg)
301   {
302     std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
303
304     switch ( ref_tokens.size() )
305       {
306       case 3:
307         cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
308         ref_tokens.pop_back();
309       case 2:
310         cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
311         ref_tokens.pop_back();
312       case 1:
313         cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
314         break;
315
316       default:
317         fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
318         return false;
319       }
320
321     if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
322       {
323         fprintf(stderr, "Unexpected CDCI video referece levels.\n");
324         return false;
325       }
326
327     return true;
328   }
329
330   //
331   bool set_display_primaries(const std::string& arg)
332   {
333     std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ",");
334     if ( coordinate_tokens.size() != 8 )
335       {
336         fprintf(stderr, "Expecting four coordinate pairs.\n");
337         return false;
338       }
339
340     std::list<std::string>::const_iterator i = coordinate_tokens.begin();
341     if ( ! set_primary_from_token(*(i++), md_primaries.First.X) ) return false;
342     if ( ! set_primary_from_token(*(i++), md_primaries.First.Y) ) return false;
343     if ( ! set_primary_from_token(*(i++), md_primaries.Second.X) ) return false;
344     if ( ! set_primary_from_token(*(i++), md_primaries.Second.Y) ) return false;
345     if ( ! set_primary_from_token(*(i++), md_primaries.Third.X) ) return false;
346     if ( ! set_primary_from_token(*(i++), md_primaries.Third.Y) ) return false;
347     if ( ! set_primary_from_token(*(i++), md_white_point.X) ) return false;
348     if ( ! set_primary_from_token(*i, md_white_point.Y) ) return false;
349
350     return true;
351   }
352
353   //
354   bool set_display_luminance(const std::string& arg)
355   {
356     std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ",");
357     if ( luminance_tokens.size() != 2 )
358       {
359         fprintf(stderr, "Expecting a luminance pair.\n");
360         return false;
361       }
362
363     if ( ! set_luminance_from_token(luminance_tokens.front(), md_min_luminance) ) return false;
364     if ( ! set_luminance_from_token(luminance_tokens.back(), md_max_luminance) ) return false;
365
366     return true;
367   }
368
369   //
370   bool set_color_system_from_arg(const char* arg)
371   {
372     assert(arg);
373
374     switch ( *arg )
375       {
376         // Application 2 (ST 2067-20)
377       case '1':
378         coding_equations = g_dict->ul(MDD_CodingEquations_601);
379         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
380         color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU470_PAL);
381         use_cdci_descriptor = true;
382         break;
383
384       case '2':
385         coding_equations = g_dict->ul(MDD_CodingEquations_601);
386         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
387         color_primaries = g_dict->ul(MDD_ColorPrimaries_SMPTE170M);
388         use_cdci_descriptor = true;
389         break;
390
391       case '3':
392         coding_equations = g_dict->ul(MDD_CodingEquations_709);
393         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
394         color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
395         use_cdci_descriptor = true;
396         break;
397
398         // Application 2e (ST 2067-21)
399       case '4':
400         coding_equations = g_dict->ul(MDD_CodingEquations_709);
401         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_xvYCC);
402         color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
403         use_cdci_descriptor = true;
404         break;
405
406       case '5':
407         coding_equations = g_dict->ul(MDD_CodingEquations_709);
408         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_2020);
409         color_primaries = g_dict->ul(MDD_ColorPrimaries_BT2020);
410         use_cdci_descriptor = true;
411         break;
412
413       default:
414         fprintf(stderr, "Unrecognized color system number, expecting one of 1-5.\n");
415         return false;
416       }
417     
418     return true;
419   }
420
421   CommandOptions(int argc, const char** argv) :
422     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
423     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
424     no_write_flag(false), version_flag(false), help_flag(false),
425     duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false),
426     edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
427     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
428     mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
429     horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
430     frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
431     mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
432     md_min_luminance(0), md_max_luminance(0), line_map(0,0)
433   {
434     memset(key_value, 0, KeyLen);
435     memset(key_id_value, 0, UUIDlen);
436
437     coding_equations = g_dict->ul(MDD_CodingEquations_709);
438     color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
439     transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
440     std::string mca_config_str;
441
442     for ( int i = 1; i < argc; i++ )
443       {
444
445         if ( (strcmp( argv[i], "-help") == 0) )
446           {
447             help_flag = true;
448             continue;
449           }
450          
451         if ( argv[i][0] == '-'
452              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
453              && argv[i][2] == 0 )
454           {
455             switch ( argv[i][1] )
456               {
457               case 'A':
458                 TEST_EXTRA_ARG(i, 'A');
459                 if ( ! DecodeRational(argv[i], aspect_ratio) )
460                   {
461                     fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
462                     return;
463                   }
464                 break;
465
466               case 'a':
467                 asset_id_flag = true;
468                 TEST_EXTRA_ARG(i, 'a');
469                 {
470                   ui32_t length;
471                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
472
473                   if ( length != UUIDlen )
474                     {
475                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
476                       return;
477                     }
478                 }
479                 break;
480
481               case 'b':
482                 TEST_EXTRA_ARG(i, 'b');
483                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
484
485                 if ( verbose_flag )
486                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
487
488                 break;
489
490               case 'c':
491                 TEST_EXTRA_ARG(i, 'c');
492                 if ( ! set_color_system_from_arg(argv[i]) )
493                   {
494                     return;
495                   }
496                 break;
497
498               case 'C':
499                 TEST_EXTRA_ARG(i, 'C');
500                 if ( ! channel_assignment.DecodeHex(argv[i]) )
501                   {
502                     fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
503                     return;
504                   }
505                 break;
506
507               case 'D':
508                 TEST_EXTRA_ARG(i, 'D');
509                 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
510                 break;
511
512               case 'd':
513                 TEST_EXTRA_ARG(i, 'd');
514                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
515                 break;
516
517               case 'E': encrypt_header_flag = false; break;
518               case 'e': encrypt_header_flag = true; break;
519
520               case 'F':
521                 TEST_EXTRA_ARG(i, 'F');
522                 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
523                 if ( field_dominance > 1 )
524                   {
525                     fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
526                     return;
527                   }
528                 break;
529
530               case 'g':
531                 TEST_EXTRA_ARG(i, 'g');
532                 mca_language = argv[i];
533                 break;
534
535               case 'h': help_flag = true; break;
536
537               case 'i':
538                 frame_layout = 1;
539                 use_cdci_descriptor = true;
540                 break;
541
542               case 'j':
543                 key_id_flag = true;
544                 TEST_EXTRA_ARG(i, 'j');
545                 {
546                   ui32_t length;
547                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
548
549                   if ( length != UUIDlen )
550                     {
551                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
552                       return;
553                     }
554                 }
555                 break;
556
557               case 'k': key_flag = true;
558                 TEST_EXTRA_ARG(i, 'k');
559                 {
560                   ui32_t length;
561                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
562
563                   if ( length != KeyLen )
564                     {
565                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
566                       return;
567                     }
568                 }
569                 break;
570
571               case 'l':
572                 TEST_EXTRA_ARG(i, 'y');
573                 if ( ! set_video_line_map(argv[i]) )
574                   {
575                     return;
576                   }
577                 break;
578
579               case 'M': write_hmac = false; break;
580
581               case 'm':
582                 TEST_EXTRA_ARG(i, 'm');
583                 mca_config_str = argv[i];
584                 break;
585
586               case 'n':
587                 TEST_EXTRA_ARG(i, 'n');
588                 if ( ! transfer_characteristic.DecodeHex(argv[i]) )
589                   {
590                     fprintf(stderr, "Error decoding TransferCharacteristic UL value: %s\n", argv[i]);
591                     return;
592                   }
593                 break;
594
595               case 'O':
596                 TEST_EXTRA_ARG(i, 'O');
597                 if ( ! set_display_primaries(argv[i]) )
598                   {
599                     return;
600                   }
601                 break;
602
603               case 'o':
604                 TEST_EXTRA_ARG(i, 'o');
605                 if ( ! set_display_luminance(argv[i]) )
606                   {
607                     return;
608                   }
609                 break;
610
611               case 'P':
612                 TEST_EXTRA_ARG(i, 'P');
613                 profile_name = argv[i];
614                 break;
615
616               case 'p':
617                 TEST_EXTRA_ARG(i, 'p');
618                 if ( ! picture_coding.DecodeHex(argv[i]) )
619                   {
620                     fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
621                     return;
622                   }
623                 break;
624
625               case 'q':
626                 TEST_EXTRA_ARG(i, 'q');
627                 if ( ! coding_equations.DecodeHex(argv[i]) )
628                   {
629                     fprintf(stderr, "Error decoding CodingEquations UL value: %s\n", argv[i]);
630                     return;
631                   }
632                 break;
633
634               case 'r':
635                 TEST_EXTRA_ARG(i, 'r');
636                 if ( ! DecodeRational(argv[i], edit_rate) )
637                   {
638                     fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
639                     return;
640                   }
641                 
642                 break;
643
644               case 'R':
645                 use_cdci_descriptor = false;
646                 break;
647
648               case 's':
649                 TEST_EXTRA_ARG(i, 's');
650                 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
651                 break;
652
653               case 't':
654                 TEST_EXTRA_ARG(i, 't');
655                 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
656                 break;
657
658               case 'T':
659                 TEST_EXTRA_ARG(i, 'T');
660                 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
661                 break;
662
663               case 'u': show_ul_values_flag = true; break;
664
665               case 'V': version_flag = true; break;
666               case 'v': verbose_flag = true; break;
667               case 'W': no_write_flag = true; break;
668
669               case 'x':
670                 TEST_EXTRA_ARG(i, 'x');
671                 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
672                 break;
673
674               case 'X':
675                 TEST_EXTRA_ARG(i, 'X');
676                 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
677                 break;
678
679               case 'Y':
680                 use_cdci_descriptor = true;
681                 // default 10 bit video range YUV, ref levels already set
682                 break;
683
684               case 'y':
685                 // Use values provided as argument, sharp tool, be careful
686                 use_cdci_descriptor = true;
687                 TEST_EXTRA_ARG(i, 'y');
688                 if ( ! set_video_ref(argv[i]) )
689                   {
690                     return;
691                   }
692                 break;
693
694               case 'Z': j2c_pedantic = false; break;
695               case 'z': j2c_pedantic = true; break;
696
697               default:
698                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
699                 return;
700               }
701           }
702         else
703           {
704
705             if ( argv[i][0] != '-' )
706               {
707                 filenames.push_back(argv[i]);
708               }
709             else
710               {
711                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
712                 return;
713               }
714           }
715       }
716
717     if ( ! mca_config_str.empty() )
718       {
719         if ( mca_language.empty() )
720           {
721             if ( ! mca_config.DecodeString(mca_config_str) )
722               {
723                 return;
724               }
725           }
726         else
727           {
728             if ( ! mca_config.DecodeString(mca_config_str, mca_language) )
729               {
730                 return;
731               }
732           }
733       }
734
735     if ( help_flag || version_flag || show_ul_values_flag )
736       {
737         return;
738       }
739
740     if ( filenames.size() < 2 )
741       {
742         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
743         return;
744       }
745
746     out_file = filenames.back();
747     filenames.pop_back();
748
749     if ( ! picture_coding.HasValue() )
750       {
751         picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
752       }
753
754     error_flag = false;
755   }
756 };
757
758
759 //------------------------------------------------------------------------------------------
760 // JPEG 2000 essence
761
762 namespace ASDCP {
763   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
764                             const ASDCP::Dictionary& dict,
765                             ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
766                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
767
768   Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
769 }
770
771 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
772 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
773 //
774 Result_t
775 write_JP2K_file(CommandOptions& Options)
776 {
777   AESEncContext*          Context = 0;
778   HMACContext*            HMAC = 0;
779   AS_02::JP2K::MXFWriter  Writer;
780   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
781   JP2K::SequenceParser    Parser;
782   byte_t                  IV_buf[CBC_BLOCK_SIZE];
783   Kumu::FortunaRNG        RNG;
784   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
785   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
786
787   // set up essence parser
788   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
789
790   // set up MXF writer
791   if ( ASDCP_SUCCESS(result) )
792     {
793       ASDCP::JP2K::PictureDescriptor PDesc;
794       Parser.FillPictureDescriptor(PDesc);
795       PDesc.EditRate = Options.edit_rate;
796
797       if ( Options.verbose_flag )
798         {
799           fprintf(stderr, "JPEG 2000 pictures\n");
800           fputs("PictureDescriptor:\n", stderr);
801           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
802           JP2K::PictureDescriptorDump(PDesc);
803         }
804
805       if ( Options.use_cdci_descriptor )
806         {
807           ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
808           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
809           
810           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
811                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
812                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
813
814           if ( ASDCP_SUCCESS(result) )
815             {
816               tmp_dscr->CodingEquations = Options.coding_equations;
817               tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
818               tmp_dscr->ColorPrimaries = Options.color_primaries;
819               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
820               tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
821               tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
822               tmp_dscr->ComponentDepth = Options.component_depth;
823               tmp_dscr->FrameLayout = Options.frame_layout;
824               tmp_dscr->AspectRatio = Options.aspect_ratio;
825               tmp_dscr->FieldDominance = Options.field_dominance;
826               tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
827               tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
828               tmp_dscr->ColorRange = Options.cdci_ColorRange;
829               tmp_dscr->VideoLineMap = Options.line_map;
830
831               if ( Options.md_min_luminance || Options.md_max_luminance )
832                 {
833                   tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
834                   tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
835                 }
836
837               if ( Options.md_primaries.HasValue() )
838                 {
839                   tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
840                   tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
841                 }
842
843               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
844             }
845         }
846       else
847         { // use RGB
848           ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
849           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
850           
851           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
852                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
853                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
854
855           if ( ASDCP_SUCCESS(result) )
856             {
857               tmp_dscr->CodingEquations = Options.coding_equations;
858               tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
859               tmp_dscr->ColorPrimaries = Options.color_primaries;
860               tmp_dscr->ScanningDirection = 0;
861               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
862               tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
863               tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
864
865               if ( Options.md_min_luminance || Options.md_max_luminance )
866                 {
867                   tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
868                   tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
869                 }
870
871               if ( Options.md_primaries.HasValue() )
872                 {
873                   tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
874                   tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
875                 }
876
877               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
878             }
879         }
880     }
881
882   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
883     {
884       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
885       Info.LabelSetType = LS_MXF_SMPTE;
886
887       if ( Options.asset_id_flag )
888         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
889       else
890         Kumu::GenRandomUUID(Info.AssetUUID);
891
892       // configure encryption
893       if( Options.key_flag )
894         {
895           Kumu::GenRandomUUID(Info.ContextID);
896           Info.EncryptedEssence = true;
897
898           if ( Options.key_id_flag )
899             {
900               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
901             }
902           else
903             {
904               create_random_uuid(Info.CryptographicKeyID);
905             }
906
907           Context = new AESEncContext;
908           result = Context->InitKey(Options.key_value);
909
910           if ( ASDCP_SUCCESS(result) )
911             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
912
913           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
914             {
915               Info.UsesHMAC = true;
916               HMAC = new HMACContext;
917               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
918             }
919         }
920
921       if ( ASDCP_SUCCESS(result) )
922         {
923           result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
924                                     Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
925         }
926     }
927
928   if ( ASDCP_SUCCESS(result) )
929     {
930       ui32_t duration = 0;
931       result = Parser.Reset();
932
933       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
934         {
935           result = Parser.ReadFrame(FrameBuffer);
936           
937           if ( ASDCP_SUCCESS(result) )
938             {
939               if ( Options.verbose_flag )
940                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
941               
942               if ( Options.encrypt_header_flag )
943                 FrameBuffer.PlaintextOffset(0);
944             }
945
946           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
947             {
948               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
949
950               // The Writer class will forward the last block of ciphertext
951               // to the encryption context for use as the IV for the next
952               // frame. If you want to use non-sequitur IV values, un-comment
953               // the following  line of code.
954               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
955               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
956             }
957         }
958
959       if ( result == RESULT_ENDOFFILE )
960         result = RESULT_OK;
961     }
962
963   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
964     result = Writer.Finalize();
965
966   return result;
967 }
968
969 //------------------------------------------------------------------------------------------
970 // PCM essence
971
972
973 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
974 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
975 //
976 Result_t
977 write_PCM_file(CommandOptions& Options)
978 {
979   AESEncContext*    Context = 0;
980   HMACContext*      HMAC = 0;
981   PCMParserList     Parser;
982   AS_02::PCM::MXFWriter    Writer;
983   PCM::FrameBuffer  FrameBuffer;
984   byte_t            IV_buf[CBC_BLOCK_SIZE];
985   Kumu::FortunaRNG  RNG;
986   ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
987
988   // set up essence parser
989   Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
990
991   // set up MXF writer
992   if ( ASDCP_SUCCESS(result) )
993     {
994       ASDCP::PCM::AudioDescriptor ADesc;
995       Parser.FillAudioDescriptor(ADesc);
996
997       ADesc.EditRate = Options.edit_rate;
998       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
999
1000       if ( Options.verbose_flag )
1001         {
1002           char buf[64];
1003           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1004                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
1005                   RationalToString(Options.edit_rate, buf, 64),
1006                   PCM::CalcSamplesPerFrame(ADesc));
1007           fputs("AudioDescriptor:\n", stderr);
1008           PCM::AudioDescriptorDump(ADesc);
1009         }
1010
1011       essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
1012
1013       result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
1014
1015       if ( Options.mca_config.empty() )
1016         {
1017           essence_descriptor->ChannelAssignment = Options.channel_assignment;
1018         }
1019       else
1020         {
1021           if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1022             {
1023               fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1024                       Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1025               return RESULT_FAIL;
1026             }
1027
1028           // this is the d-cinema MCA label, what is the one for IMF?
1029           essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
1030         }
1031     }
1032
1033   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1034     {
1035       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1036       Info.LabelSetType = LS_MXF_SMPTE;
1037
1038       if ( Options.asset_id_flag )
1039         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1040       else
1041         Kumu::GenRandomUUID(Info.AssetUUID);
1042
1043       // configure encryption
1044       if( Options.key_flag )
1045         {
1046           Kumu::GenRandomUUID(Info.ContextID);
1047           Info.EncryptedEssence = true;
1048
1049           if ( Options.key_id_flag )
1050             {
1051               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1052             }
1053           else
1054             {
1055               create_random_uuid(Info.CryptographicKeyID);
1056             }
1057
1058           Context = new AESEncContext;
1059           result = Context->InitKey(Options.key_value);
1060
1061           if ( ASDCP_SUCCESS(result) )
1062             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1063
1064           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1065             {
1066               Info.UsesHMAC = true;
1067               HMAC = new HMACContext;
1068               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1069             }
1070         }
1071
1072       if ( ASDCP_SUCCESS(result) )
1073         {
1074           result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
1075                                     Options.mca_config, Options.edit_rate);
1076         }
1077     }
1078
1079   if ( ASDCP_SUCCESS(result) )
1080     {
1081       result = Parser.Reset();
1082       ui32_t duration = 0;
1083
1084       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1085         {
1086           result = Parser.ReadFrame(FrameBuffer);
1087
1088           if ( ASDCP_SUCCESS(result) )
1089             {
1090               if ( Options.verbose_flag )
1091                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1092
1093               if ( ! Options.no_write_flag )
1094                 {
1095                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1096
1097                   // The Writer class will forward the last block of ciphertext
1098                   // to the encryption context for use as the IV for the next
1099                   // frame. If you want to use non-sequitur IV values, un-comment
1100                   // the following  line of code.
1101                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1102                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1103                 }
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
1118
1119
1120 //------------------------------------------------------------------------------------------
1121 // TimedText essence
1122
1123
1124 // Write one or more plaintext timed text streams to a plaintext AS-02 file
1125 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
1126 //
1127 Result_t
1128 write_timed_text_file(CommandOptions& Options)
1129 {
1130   AESEncContext*    Context = 0;
1131   HMACContext*      HMAC = 0;
1132   AS_02::TimedText::ST2052_TextParser  Parser;
1133   AS_02::TimedText::MXFWriter    Writer;
1134   TimedText::FrameBuffer  FrameBuffer;
1135   TimedText::TimedTextDescriptor TDesc;
1136   byte_t            IV_buf[CBC_BLOCK_SIZE];
1137   Kumu::FortunaRNG  RNG;
1138
1139   // set up essence parser
1140   Result_t result = Parser.OpenRead(Options.filenames.front());
1141
1142   // set up MXF writer
1143   if ( ASDCP_SUCCESS(result) )
1144     {
1145       Parser.FillTimedTextDescriptor(TDesc);
1146       TDesc.EditRate = Options.edit_rate;
1147       TDesc.ContainerDuration = Options.duration;
1148       FrameBuffer.Capacity(Options.fb_size);
1149
1150       if ( ! Options.profile_name.empty() )
1151         {
1152           TDesc.NamespaceName = Options.profile_name;
1153         }
1154
1155       if ( Options.verbose_flag )
1156         {
1157           fputs("IMF Timed-Text Descriptor:\n", stderr);
1158           TimedText::DescriptorDump(TDesc);
1159         }
1160     }
1161
1162   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1163     {
1164       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1165       Info.LabelSetType = LS_MXF_SMPTE;
1166
1167       if ( Options.asset_id_flag )
1168         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1169       else
1170         Kumu::GenRandomUUID(Info.AssetUUID);
1171
1172       // configure encryption
1173       if( Options.key_flag )
1174         {
1175           Kumu::GenRandomUUID(Info.ContextID);
1176           Info.EncryptedEssence = true;
1177
1178           if ( Options.key_id_flag )
1179             {
1180               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1181             }
1182           else
1183             {
1184               create_random_uuid(Info.CryptographicKeyID);
1185             }
1186
1187           Context = new AESEncContext;
1188           result = Context->InitKey(Options.key_value);
1189
1190           if ( ASDCP_SUCCESS(result) )
1191             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1192
1193           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1194             {
1195               Info.UsesHMAC = true;
1196               HMAC = new HMACContext;
1197               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1198             }
1199         }
1200
1201       if ( ASDCP_SUCCESS(result) )
1202         result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1203     }
1204
1205   if ( ASDCP_FAILURE(result) )
1206     return result;
1207
1208   std::string XMLDoc;
1209   TimedText::ResourceList_t::const_iterator ri;
1210
1211   result = Parser.ReadTimedTextResource(XMLDoc);
1212
1213   if ( ASDCP_SUCCESS(result) )
1214     result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1215
1216   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1217     {
1218       result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1219
1220       if ( ASDCP_SUCCESS(result) )
1221         {
1222           if ( Options.verbose_flag )
1223             FrameBuffer.Dump(stderr, Options.fb_dump_size);
1224
1225           if ( ! Options.no_write_flag )
1226             {
1227               result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1228
1229               // The Writer class will forward the last block of ciphertext
1230               // to the encryption context for use as the IV for the next
1231               // frame. If you want to use non-sequitur IV values, un-comment
1232               // the following  line of code.
1233               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1234               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1235             }
1236         }
1237
1238       if ( result == RESULT_ENDOFFILE )
1239         result = RESULT_OK;
1240     }
1241
1242   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1243     result = Writer.Finalize();
1244
1245   return result;
1246 }
1247
1248
1249 //
1250 int
1251 main(int argc, const char** argv)
1252 {
1253   Result_t result = RESULT_OK;
1254   char     str_buf[64];
1255   g_dict = &ASDCP::DefaultSMPTEDict();
1256   assert(g_dict);
1257
1258   CommandOptions Options(argc, argv);
1259
1260   if ( Options.version_flag )
1261     banner();
1262
1263   if ( Options.help_flag )
1264     usage();
1265
1266   if ( Options.show_ul_values_flag )
1267     {
1268       g_dict->Dump(stdout);
1269     }
1270
1271   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1272     return 0;
1273
1274   if ( Options.error_flag )
1275     {
1276       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1277       return 3;
1278     }
1279
1280   EssenceType_t EssenceType;
1281   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1282
1283   if ( ASDCP_SUCCESS(result) )
1284     {
1285       switch ( EssenceType )
1286         {
1287         case ESS_JPEG_2000:
1288           result = write_JP2K_file(Options);
1289           break;
1290
1291         case ESS_PCM_24b_48k:
1292         case ESS_PCM_24b_96k:
1293           result = write_PCM_file(Options);
1294           break;
1295
1296         case ESS_TIMED_TEXT:
1297           result = write_timed_text_file(Options);
1298           break;
1299
1300         default:
1301           fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
1302                   Options.filenames.front().c_str());
1303           return 5;
1304         }
1305     }
1306
1307   if ( ASDCP_FAILURE(result) )
1308     {
1309       fputs("Program stopped on error.\n", stderr);
1310
1311       if ( result != RESULT_FAIL )
1312         {
1313           fputs(result, stderr);
1314           fputc('\n', stderr);
1315         }
1316
1317       return 1;
1318     }
1319
1320   return 0;
1321 }
1322
1323
1324 //
1325 // end as-02-wrap.cpp
1326 //