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