Reverting const accessor for class optional_property
[asdcplib.git] / src / as-02-wrap.cpp
1 /*
2 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst, Wolfgang Ruppel
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 <KM_xml.h>
42 #include <AS_02.h>
43 #include "AS_02_ACES.h"
44 #include <PCMParserList.h>
45 #include <Metadata.h>
46
47 using namespace ASDCP;
48
49 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
50 const ASDCP::Dictionary *g_dict = 0;
51  
52 const char*
53 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
54 {
55   snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
56   return buf;
57 }
58
59
60 //------------------------------------------------------------------------------------------
61 //
62 // command line option parser class
63
64 static const char* PROGRAM_NAME = "as-02-wrap";  // program name for messages
65
66 // local program identification info written to file headers
67 class MyInfo : public WriterInfo
68 {
69 public:
70   MyInfo()
71   {
72       static byte_t default_ProductUUID_Data[UUIDlen] =
73       { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
74         0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
75       
76       memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
77       CompanyName = "WidgetCo";
78       ProductName = "as-02-wrap";
79       ProductVersion = ASDCP::Version();
80   }
81 } s_MyInfo;
82
83
84
85 // Increment the iterator, test for an additional non-option command line argument.
86 // Causes the caller to return if there are no remaining arguments or if the next
87 // argument begins with '-'.
88 #define TEST_EXTRA_ARG(i,c)                                             \
89   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
90     fprintf(stderr, "Argument not found for option -%c.\n", (c));       \
91     return;                                                             \
92   }
93
94 #define TEST_EXTRA_ARG_STRING(i,s)                                              \
95   if ( ++i >= argc || argv[(i)][0] == '-' ) {                           \
96     fprintf(stderr, "Argument not found for option -%s.\n", (s));       \
97     return;                                                             \
98   }
99
100
101 //
102 static void
103 create_random_uuid(byte_t* uuidbuf)
104 {
105   Kumu::UUID tmp_id;
106   GenRandomValue(tmp_id);
107   memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
108 }
109
110 //
111 void
112 banner(FILE* stream = stdout)
113 {
114   fprintf(stream, "\n\
115 %s (asdcplib %s)\n\n\
116 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
117 asdcplib may be copied only under the terms of the license found at\n\
118 the top of every file in the asdcplib distribution kit.\n\n\
119 Specify the -h (help) option for further information about %s\n\n",
120           PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
121 }
122
123 //
124 void
125 usage(FILE* stream = stdout)
126 {
127   fprintf(stream, "\
128 USAGE: %s [-h|-help] [-V]\n\
129 \n\
130        %s [options] <input-file>+ <output-file>\n\n",
131           PROGRAM_NAME, PROGRAM_NAME);
132
133   fprintf(stream, "\
134 Options:\n\
135   -h | -help        - Show help\n\
136   -V                - Show version information\n\
137   -a <uuid>         - Specify the Asset ID of the file\n\
138   -A <w>/<h>        - Set aspect ratio for image (default 4/3)\n\
139   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
140                       Defaults to 4,194,304 (4MB)\n\
141   -c <num>          - Select the IMF color system to be signaled:\n\
142                       Application 2 (2067-20): 1, 2, or 3\n\
143                       Application 2e (2067-21): 4 or 5\n\
144                       All color system values assume YCbCr; also use -R for RGB\n\
145   -C <ul>           - Set ChannelAssignment UL value\n\
146   -d <duration>     - Number of frames to process, default all\n\
147   -D <depth>        - Component depth for YCbCr images (default: 10)\n\
148   -e                - Encrypt JP2K headers (default)\n\
149   -E                - Do not encrypt JP2K headers\n\
150   -F (0|1)          - Set field dominance for interlaced image (default: 0)\n\
151   -g <rfc-5646-code>\n\
152                     - Create MCA labels having the given RFC 5646 language code\n\
153                       (requires option \"-m\") -- Also used with -G to set the\n\
154                       value of the TextMIMEMediaType property\n\
155   -G <filename>     - Filename of XML resource to be carried per RP 2057 Generic\n\
156                       Stream. May be issued multiple times.\n\
157   -i                - Indicates input essence is interlaced fields (forces -Y)\n\
158   -j <key-id-str>   - Write key ID instead of creating a random value\n\
159   -k <key-string>   - Use key for ciphertext operations\n\
160   -l <first>,<second>\n\
161                     - Integer values that set the VideoLineMap\n\
162   -m <expr>         - Write MCA labels using <expr>.  Example:\n\
163                         51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
164   -M                - Do not create HMAC values when writing\n\
165   -n <UL>           - Set the TransferCharacteristic UL\n\
166   -o <min>,<max>    - Mastering Display luminance, cd*m*m, e.g., \".05,100\"\n\
167   -O <rx>,<ry>,<gx>,<gy>,<bx>,<by>,<wx>,<wy>\n\
168                     - Mastering Display Color Primaries and white point\n\
169                       e.g., \".64,.33,.3,.6,.15,.06,.3457,.3585\"\n\
170   -p <ul>           - Set broadcast profile\n\
171   -P <string>       - Set NamespaceURI property when creating timed text MXF\n\
172   -q <UL>           - Set the CodingEquations UL\n\
173   -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
174   -R                - Indicates RGB image essence (default except with -c)\n\
175   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
176   -t <min>          - Set RGB component minimum code value (default: 0)\n\
177   -T <max>          - Set RGB component maximum code value (default: 1023)\n\
178   -u                - Print UL catalog to stdout\n\
179   -U <URI>          - ISXD (RDD47) document URI (use 'auto' to read the\n\
180                       namespace name from the first edit unit)\n\
181   -v                - Verbose, prints informative messages to stderr\n\
182   -W                - Read input file only, do not write source file\n\
183   -x <int>          - Horizontal subsampling degree (default: 2)\n\
184   -X <int>          - Vertical subsampling degree (default: 2)\n\
185   -y <white-ref>[,<black-ref>[,<color-range>]]\n\
186                     - Same as -Y but White Ref, Black Ref and Color Range are\n\
187                       set from the given argument\n\
188   -Y                - Indicates YCbCr image essence (default with -c), uses\n\
189                       default values for White Ref, Black Ref and Color Range,\n\
190                        940,64,897, indicating 10 bit standard Video Range\n\
191   -z                - Fail if j2c inputs have unequal parameters (default)\n\
192   -Z                - Ignore unequal parameters in j2c inputs\n\
193 \n\
194   --mca-audio-content-kind <string>\n\
195                     - UL value for MCA descriptor MCAAudioContentKind property\n\
196   --mca-audio-element-kind <string>\n\
197                     - UL value for MCA descriptor MCAAudioElementKind property\n\
198 \n\
199 \n\
200 Options specific to ACES ST2067-50:\n\
201   -suba <string>    - Create ACES Picture SubDescriptor, set <string> as ACESAuthoringInformation,\n\
202                       uses values from -o and -O, if present\n\
203   -subt <directoryPath>  - \n\
204                       Create one Target Frame SubDescriptor per PNG or TIFF file in <directoryPath>,\n\
205                       and  wrap each PNG or TIFF file  as ancillary resource\n\
206                       Requires additional options -tfi, -tft, -tfc, -tfr\n\
207   -tfi <int>[,<int>*]    - \n\
208                       List of TargetFrameIndex values in Target Frame SubDescriptor corresponding to the \n\
209                       list of Target Frame frame files in <directoryPath> as given by option -subt\n\
210   -tft <string>     - Target Frame Transfer Characteristics Symbol, e.g. TransferCharacteristic_ITU709\n\
211   -tfc <string>     - Target Frame Color Primaries Symbol, e.g. ColorPrimaries_ITU709\n\
212   -tfr <min>,<max>  - Target Frame Component Min/Max Ref in Target Frame SubDescriptor\n\
213   -tfv <string>     - Target Frame Viewing Environment Symbol, e.g. HDTVReferenceViewingEnvironment\n\
214 \n\
215 \n\
216   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
217          o All option arguments must be separated from the option by whitespace.\n\n");
218 }
219
220 const float chromaticity_scale = 50000.0;
221 //
222 ui32_t
223 set_primary_from_token(const std::string& token, ui16_t& primary)
224 {
225   float raw_value = strtod(token.c_str(),0);
226
227   if ( raw_value == 0.0 || raw_value > 1.0 )
228     {
229       fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str());
230       return false;
231     }
232
233   primary = floor(0.5 + ( raw_value * chromaticity_scale ));
234   return true;
235 }
236
237 const float luminance_scale = 10000.0;
238 //
239 ui32_t
240 set_luminance_from_token(const std::string& token, ui32_t& luminance)
241 {
242   float raw_value = strtod(token.c_str(),0);
243
244   if ( raw_value == 0.0 || raw_value > 400000.0 )
245     {
246       fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str());
247       return false;
248     }
249
250   luminance = floor(0.5 + ( raw_value * luminance_scale ));
251   return true;
252 }
253
254 #define SET_LUMINANCE(p,t)                      \
255   if ( ! set_luminance_from_token(t, p) ) {     \
256     return false;                               \
257   }
258
259 //
260 class CommandOptions
261 {
262   CommandOptions();
263
264 public:
265   bool   error_flag;     // true if the given options are in error or not complete
266   bool   key_flag;       // true if an encryption key was given
267   bool   asset_id_flag;  // true if an asset ID was given
268   bool   encrypt_header_flag; // true if j2c headers are to be encrypted
269   bool   write_hmac;     // true if HMAC values are to be generated and written
270   bool   verbose_flag;   // true if the verbose option was selected
271   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
272   bool   no_write_flag;  // true if no output files are to be written
273   bool   version_flag;   // true if the version display option was selected
274   bool   help_flag;      // true if the help display option was selected
275   ui32_t duration;       // number of frames to be processed
276   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
277   bool use_cdci_descriptor; // 
278   Rational edit_rate;    // edit rate of JP2K sequence
279   ui32_t fb_size;        // size of picture frame buffer
280   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
281   bool   key_id_flag;    // true if a key ID was given
282   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
283   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
284   bool show_ul_values_flag;    // if true, dump the UL table before going tp work.
285   Kumu::PathList_t filenames;  // list of filenames to be processed
286
287   UL channel_assignment, picture_coding, transfer_characteristic, color_primaries, coding_equations;
288   ASDCP::MXF::AS02_MCAConfigParser mca_config;
289   std::string language;
290
291   ui32_t rgba_MaxRef;
292   ui32_t rgba_MinRef;
293
294   ui32_t horizontal_subsampling;
295   ui32_t vertical_subsampling;
296   ui32_t component_depth;
297   ui8_t frame_layout;
298   ASDCP::Rational aspect_ratio;
299   bool aspect_ratio_flag;
300   ui8_t field_dominance;
301   ui32_t mxf_header_size;
302   ui32_t cdci_BlackRefLevel; 
303   ui32_t cdci_WhiteRefLevel;
304   ui32_t cdci_ColorRange;
305
306   ui32_t md_min_luminance, md_max_luminance;
307   ASDCP::MXF::ThreeColorPrimaries md_primaries;
308   ASDCP::MXF::ColorPrimary md_white_point;
309
310   //new attributes for AS-02 support 
311   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
312   ui32_t partition_space; //Shim parameter partition_spacing
313
314   // ISXD
315   std::string isxd_document_namespace;
316   std::list<std::string> global_isxd_metadata;
317
318   //
319   MXF::LineMapPair line_map;
320   bool line_map_flag;
321   std::string out_file, profile_name; //
322   std::string mca_audio_element_kind, mca_audio_content_kind;
323
324   //ST 2067-50 options
325   bool aces_authoring_information_flag, aces_picture_subdescriptor_flag, target_frame_subdescriptor_flag, target_frame_index_flag;
326   bool target_frame_transfer_characteristics_flag, target_frame_color_primaries_flag, target_frame_min_max_ref_flag;
327   bool target_frame_viewing_environment_flag;
328   std::string aces_authoring_information;
329   std::string target_frame_directory;
330   std::list <ui64_t> target_frame_index_list;
331   UL target_frame_transfer_characteristics, target_frame_color_primaries, target_frame_viewing_environment;
332   ui32_t target_frame_min_ref, target_frame_max_ref;  
333   //
334   bool set_video_line_map(const std::string& arg)
335   {
336     const char* sep_str = strrchr(arg.c_str(), ',');
337
338     if ( sep_str == 0 )
339       {
340         fprintf(stderr, "Expecting <first>,<second>\n");
341         return false;
342       }
343
344     line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
345     line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
346     return true;
347   }
348
349   //
350   bool set_video_ref(const std::string& arg)
351   {
352     std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
353
354     switch ( ref_tokens.size() )
355       {
356       case 3:
357         cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
358         ref_tokens.pop_back();
359       case 2:
360         cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
361         ref_tokens.pop_back();
362       case 1:
363         cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
364         break;
365
366       default:
367         fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
368         return false;
369       }
370
371     if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
372       {
373         fprintf(stderr, "Unexpected CDCI video referece levels.\n");
374         return false;
375       }
376
377     return true;
378   }
379
380   //
381   bool set_display_primaries(const std::string& arg)
382   {
383     std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ",");
384     if ( coordinate_tokens.size() != 8 )
385       {
386         fprintf(stderr, "Expecting four coordinate pairs.\n");
387         return false;
388       }
389
390     std::list<std::string>::const_iterator i = coordinate_tokens.begin();
391     if ( ! set_primary_from_token(*(i++), md_primaries.First.X) ) return false;
392     if ( ! set_primary_from_token(*(i++), md_primaries.First.Y) ) return false;
393     if ( ! set_primary_from_token(*(i++), md_primaries.Second.X) ) return false;
394     if ( ! set_primary_from_token(*(i++), md_primaries.Second.Y) ) return false;
395     if ( ! set_primary_from_token(*(i++), md_primaries.Third.X) ) return false;
396     if ( ! set_primary_from_token(*(i++), md_primaries.Third.Y) ) return false;
397     if ( ! set_primary_from_token(*(i++), md_white_point.X) ) return false;
398     if ( ! set_primary_from_token(*i, md_white_point.Y) ) return false;
399
400     return true;
401   }
402
403   //
404   bool set_display_luminance(const std::string& arg)
405   {
406     std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ",");
407     if ( luminance_tokens.size() != 2 )
408       {
409         fprintf(stderr, "Expecting a luminance pair.\n");
410         return false;
411       }
412
413     if ( ! set_luminance_from_token(luminance_tokens.front(), md_min_luminance) ) return false;
414     if ( ! set_luminance_from_token(luminance_tokens.back(), md_max_luminance) ) return false;
415
416     return true;
417   }
418
419   //
420   bool set_color_system_from_arg(const char* arg)
421   {
422     assert(arg);
423
424     switch ( *arg )
425       {
426         // Application 2 (ST 2067-20)
427       case '1':
428         coding_equations = g_dict->ul(MDD_CodingEquations_601);
429         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
430         color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU470_PAL);
431         use_cdci_descriptor = true;
432         break;
433
434       case '2':
435         coding_equations = g_dict->ul(MDD_CodingEquations_601);
436         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
437         color_primaries = g_dict->ul(MDD_ColorPrimaries_SMPTE170M);
438         use_cdci_descriptor = true;
439         break;
440
441       case '3':
442         coding_equations = g_dict->ul(MDD_CodingEquations_709);
443         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
444         color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU709);
445         use_cdci_descriptor = true;
446         break;
447
448         // Application 2e (ST 2067-21)
449       case '4':
450         coding_equations = g_dict->ul(MDD_CodingEquations_709);
451         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_IEC6196624_xvYCC);
452         color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU709);
453         use_cdci_descriptor = true;
454         break;
455
456       case '5':
457         coding_equations = g_dict->ul(MDD_CodingEquations_709);
458         transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU2020);
459         color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU2020);
460         use_cdci_descriptor = true;
461         break;
462
463       default:
464         fprintf(stderr, "Unrecognized color system number, expecting one of 1-5.\n");
465         return false;
466       }
467     
468     return true;
469   }
470
471   bool set_target_frame_min_max_code_value(const std::string& arg)
472   {
473     std::list<std::string> range_tokens = Kumu::km_token_split(arg, ",");
474     if ( range_tokens.size() != 2 )
475       {
476         fprintf(stderr, "Expecting a code value pair.\n");
477         return false;
478       }
479
480     target_frame_min_ref = strtol(range_tokens.front().c_str(), 0 , 10);
481     target_frame_max_ref = strtol(range_tokens.back().c_str(), 0 , 10);
482
483     return true;
484   }
485
486   bool set_target_frame_index_list(const std::string& arg, std::list<ui64_t>& r_target_frame_index_list)
487   {
488     std::list<std::string> index_tokens = Kumu::km_token_split(arg, ",");
489     if ( index_tokens.size() == 0 )
490       {
491         fprintf(stderr, "Expecting at least one Target Frame Index.\n");
492         return false;
493       }
494
495
496     std::list<std::string>::const_iterator i;
497     for (i = index_tokens.begin(); i != index_tokens.end(); i++) {
498         r_target_frame_index_list.push_back(strtoll(i->c_str(), 0, 10));
499     }
500
501     return true;
502   }
503
504
505   CommandOptions(int argc, const char** argv) :
506     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
507     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
508     no_write_flag(false), version_flag(false), help_flag(false),
509     duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false),
510     edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
511     show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
512     mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
513     horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
514     frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), aspect_ratio_flag(false), field_dominance(0),
515     mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
516     md_min_luminance(0), md_max_luminance(0), line_map(0,0), line_map_flag(false),
517         aces_authoring_information_flag(false), aces_picture_subdescriptor_flag(false), target_frame_subdescriptor_flag(false),
518         target_frame_index_flag(false), target_frame_transfer_characteristics_flag(false), target_frame_color_primaries_flag(false),
519         target_frame_min_max_ref_flag(false), target_frame_viewing_environment_flag(false)
520   {
521     memset(key_value, 0, KeyLen);
522     memset(key_id_value, 0, UUIDlen);
523
524     coding_equations = g_dict->ul(MDD_CodingEquations_709);
525     color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU709);
526     transfer_characteristic = g_dict->ul(MDD_TransferCharacteristic_ITU709);
527     std::string mca_config_str;
528
529     for ( int i = 1; i < argc; i++ )
530       {
531
532         if ( (strcmp( argv[i], "-help") == 0) )
533           {
534             help_flag = true;
535             continue;
536           }
537          
538         if ( (strcmp( argv[i], "-suba") == 0) )
539           {
540             aces_picture_subdescriptor_flag = true;
541             if ((++i < argc) && (argv[i][0] != '-')) {
542                 aces_authoring_information = argv[i];
543                 aces_authoring_information_flag = true;
544             } else i--;
545             continue;
546           }
547
548         if ( (strcmp( argv[i], "-subt") == 0) )
549           {
550             target_frame_subdescriptor_flag = true;
551             TEST_EXTRA_ARG_STRING(i, "subt");
552             target_frame_directory = argv[i];
553             continue;
554           }
555
556         if ( (strcmp( argv[i], "-tfi") == 0) )
557           {
558                 TEST_EXTRA_ARG_STRING(i, "tfi");
559             if (set_target_frame_index_list(argv[i], target_frame_index_list)) {
560                         target_frame_index_flag = true;
561             }
562             continue;
563           }
564
565         if ( (strcmp( argv[i], "-tft") == 0) )
566           {
567                 TEST_EXTRA_ARG_STRING(i, "tft");
568                 //
569                 const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
570                 if (entry) {
571                         target_frame_transfer_characteristics_flag = true;
572                         target_frame_transfer_characteristics = entry->ul;
573                         fprintf(stderr, "target_frame_transfer_characteristic %s\n", entry->name);
574                 }
575             continue;
576           }
577
578         if ( (strcmp( argv[i], "-tfc") == 0) )
579           {
580                 TEST_EXTRA_ARG_STRING(i, "tfc");
581                 //
582                 const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
583                 if (entry) {
584                         target_frame_color_primaries_flag = true;
585                         target_frame_color_primaries = entry->ul;
586                         fprintf(stderr, "target_frame_color_primaries %s\n", entry->name);
587                 }
588             continue;
589           }
590
591         if ( (strcmp( argv[i], "-tfr") == 0) )
592           {
593                 TEST_EXTRA_ARG(i, 'o');
594                 if ( ! set_target_frame_min_max_code_value(argv[i]) )
595                   {
596                     return;
597                   }
598                 target_frame_min_max_ref_flag = true;
599             continue;
600           }
601
602         if ( (strcmp( argv[i], "-tfv") == 0) )
603           {
604                 TEST_EXTRA_ARG_STRING(i, "tfv");
605                 //
606                 const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
607                 if (entry) {
608                         target_frame_viewing_environment_flag = true;
609                         target_frame_viewing_environment = entry->ul;
610                         fprintf(stderr, "target_frame_viewing_environment %s\n", entry->name);
611                 }
612             continue;
613           }
614
615
616         if ( argv[i][0] == '-'
617              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
618              && argv[i][2] == 0 )
619           {
620             switch ( argv[i][1] )
621               {
622               case 'A':
623                 TEST_EXTRA_ARG(i, 'A');
624                 if ( ! DecodeRational(argv[i], aspect_ratio) )
625                   {
626                     fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
627                     return;
628                   }
629                 else
630                 {
631                         aspect_ratio_flag = true;
632                 }
633                 break;
634
635               case 'a':
636                 asset_id_flag = true;
637                 TEST_EXTRA_ARG(i, 'a');
638                 {
639                   ui32_t length;
640                   Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
641
642                   if ( length != UUIDlen )
643                     {
644                       fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
645                       return;
646                     }
647                 }
648                 break;
649
650               case 'b':
651                 TEST_EXTRA_ARG(i, 'b');
652                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
653
654                 if ( verbose_flag )
655                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
656
657                 break;
658
659               case 'c':
660                 TEST_EXTRA_ARG(i, 'c');
661                 if ( ! set_color_system_from_arg(argv[i]) )
662                   {
663                     return;
664                   }
665                 break;
666
667               case 'C':
668                 TEST_EXTRA_ARG(i, 'C');
669                 if ( ! channel_assignment.DecodeHex(argv[i]) )
670                   {
671                     fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
672                     return;
673                   }
674                 break;
675
676               case 'D':
677                 TEST_EXTRA_ARG(i, 'D');
678                 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
679                 break;
680
681               case 'd':
682                 TEST_EXTRA_ARG(i, 'd');
683                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
684                 break;
685
686               case 'E': encrypt_header_flag = false; break;
687               case 'e': encrypt_header_flag = true; break;
688
689               case 'F':
690                 TEST_EXTRA_ARG(i, 'F');
691                 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
692                 if ( field_dominance > 1 )
693                   {
694                     fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
695                     return;
696                   }
697                 break;
698
699               case 'g':
700                 TEST_EXTRA_ARG(i, 'g');
701                 language = argv[i];
702                 break;
703
704               case 'G':
705                 TEST_EXTRA_ARG(i, 'G');
706                 global_isxd_metadata.push_back(argv[i]);
707                 break;
708
709               case 'h': help_flag = true; break;
710
711               case 'i':
712                 frame_layout = 1;
713                 use_cdci_descriptor = true;
714                 break;
715
716               case 'j':
717                 key_id_flag = true;
718                 TEST_EXTRA_ARG(i, 'j');
719                 {
720                   ui32_t length;
721                   Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
722
723                   if ( length != UUIDlen )
724                     {
725                       fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
726                       return;
727                     }
728                 }
729                 break;
730
731               case 'k': key_flag = true;
732                 TEST_EXTRA_ARG(i, 'k');
733                 {
734                   ui32_t length;
735                   Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
736
737                   if ( length != KeyLen )
738                     {
739                       fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
740                       return;
741                     }
742                 }
743                 break;
744
745               case 'l':
746                 TEST_EXTRA_ARG(i, 'y');
747                 if ( ! set_video_line_map(argv[i]) )
748                   {
749                     return;
750                   } else {
751                     line_map_flag = true;
752                   }
753                 break;
754
755               case 'M': write_hmac = false; break;
756
757               case 'm':
758                 TEST_EXTRA_ARG(i, 'm');
759                 mca_config_str = argv[i];
760                 break;
761
762               case 'n':
763                 TEST_EXTRA_ARG(i, 'n');
764                 if ( ! transfer_characteristic.DecodeHex(argv[i]) )
765                   {
766                     fprintf(stderr, "Error decoding TransferCharacteristic UL value: %s\n", argv[i]);
767                     return;
768                   }
769                 break;
770
771               case 'O':
772                 TEST_EXTRA_ARG(i, 'O');
773                 if ( ! set_display_primaries(argv[i]) )
774                   {
775                     return;
776                   }
777                 break;
778
779               case 'o':
780                 TEST_EXTRA_ARG(i, 'o');
781                 if ( ! set_display_luminance(argv[i]) )
782                   {
783                     return;
784                   }
785                 break;
786
787               case 'P':
788                 TEST_EXTRA_ARG(i, 'P');
789                 profile_name = argv[i];
790                 break;
791
792               case 'p':
793                 TEST_EXTRA_ARG(i, 'p');
794                 if ( ! picture_coding.DecodeHex(argv[i]) )
795                   {
796                     fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
797                     return;
798                   }
799                 break;
800
801               case 'q':
802                 TEST_EXTRA_ARG(i, 'q');
803                 if ( ! coding_equations.DecodeHex(argv[i]) )
804                   {
805                     fprintf(stderr, "Error decoding CodingEquations UL value: %s\n", argv[i]);
806                     return;
807                   }
808                 break;
809
810               case 'r':
811                 TEST_EXTRA_ARG(i, 'r');
812                 if ( ! DecodeRational(argv[i], edit_rate) )
813                   {
814                     fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
815                     return;
816                   }
817                 
818                 break;
819
820               case 'R':
821                 use_cdci_descriptor = false;
822                 break;
823
824               case 's':
825                 TEST_EXTRA_ARG(i, 's');
826                 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
827                 break;
828
829               case 't':
830                 TEST_EXTRA_ARG(i, 't');
831                 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
832                 break;
833
834               case 'T':
835                 TEST_EXTRA_ARG(i, 'T');
836                 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
837                 break;
838
839               case 'u': show_ul_values_flag = true; break;
840
841               case 'U':
842                 TEST_EXTRA_ARG(i, 'U');
843                 isxd_document_namespace = argv[i];
844                 break;
845
846               case 'V': version_flag = true; break;
847               case 'v': verbose_flag = true; break;
848               case 'W': no_write_flag = true; break;
849
850               case 'x':
851                 TEST_EXTRA_ARG(i, 'x');
852                 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
853                 break;
854
855               case 'X':
856                 TEST_EXTRA_ARG(i, 'X');
857                 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
858                 break;
859
860               case 'Y':
861                 use_cdci_descriptor = true;
862                 // default 10 bit video range YUV, ref levels already set
863                 break;
864
865               case 'y':
866                 // Use values provided as argument, sharp tool, be careful
867                 use_cdci_descriptor = true;
868                 TEST_EXTRA_ARG(i, 'y');
869                 if ( ! set_video_ref(argv[i]) )
870                   {
871                     return;
872                   }
873                 break;
874
875               case 'Z': j2c_pedantic = false; break;
876               case 'z': j2c_pedantic = true; break;
877
878               default:
879                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
880                 return;
881               }
882           }
883         else if ( argv[i][0] == '-' && argv[i][1] == '-' && isalpha(argv[i][2]) )
884           {
885             if ( strcmp(argv[i]+2, "mca-audio-content-kind") == 0 )
886               {
887                 if ( ++i >= argc || argv[(i)][0] == '-' )
888                   {
889                     fprintf(stderr, "Argument not found for option -mca-audio-content-kind.\n");
890                     return;
891                   }
892                 
893                 mca_audio_content_kind = argv[i];
894               }
895             else if ( strcmp(argv[i]+2, "mca-audio-element-kind") == 0 )
896               {
897                 if ( ++i >= argc || argv[(i)][0] == '-' )
898                   {
899                     fprintf(stderr, "Argument not found for option -mca-audio-element-kind.\n");
900                     return;
901                   }
902
903                 mca_audio_element_kind = argv[i];
904               }
905             else
906               {
907                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
908                 return;
909               }
910           }
911         else
912           {
913             if ( argv[i][0] != '-' )
914               {
915                 filenames.push_back(argv[i]);
916               }
917             else
918               {
919                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
920                 return;
921               }
922           }
923       }
924
925     if ( ! mca_config_str.empty() )
926       {
927         if ( language.empty() )
928           {
929             if ( ! mca_config.DecodeString(mca_config_str) )
930               {
931                 return;
932               }
933           }
934         else
935           {
936             if ( ! mca_config.DecodeString(mca_config_str, language) )
937               {
938                 return;
939               }
940           }
941       }
942
943     if ( help_flag || version_flag || show_ul_values_flag )
944       {
945         return;
946       }
947
948     if ( filenames.size() < 2 )
949       {
950         fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
951         return;
952       }
953
954     out_file = filenames.back();
955     filenames.pop_back();
956
957     if ( ! picture_coding.HasValue() )
958       {
959         picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
960       }
961
962     error_flag = false;
963   }
964 };
965
966
967 //------------------------------------------------------------------------------------------
968 // JPEG 2000 essence
969
970 namespace ASDCP {
971   Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
972                             const ASDCP::Dictionary& dict,
973                             ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
974                             ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
975
976   Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
977 }
978
979 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
980 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
981 //
982 Result_t
983 write_JP2K_file(CommandOptions& Options)
984 {
985   AESEncContext*          Context = 0;
986   HMACContext*            HMAC = 0;
987   AS_02::JP2K::MXFWriter  Writer;
988   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
989   JP2K::SequenceParser    Parser;
990   byte_t                  IV_buf[CBC_BLOCK_SIZE];
991   Kumu::FortunaRNG        RNG;
992   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
993   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
994
995   // set up essence parser
996   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
997
998   // set up MXF writer
999   if ( ASDCP_SUCCESS(result) )
1000     {
1001       ASDCP::JP2K::PictureDescriptor PDesc;
1002       Parser.FillPictureDescriptor(PDesc);
1003       PDesc.EditRate = Options.edit_rate;
1004
1005       if ( Options.verbose_flag )
1006         {
1007           fprintf(stderr, "JPEG 2000 pictures\n");
1008           fputs("PictureDescriptor:\n", stderr);
1009           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1010           JP2K::PictureDescriptorDump(PDesc);
1011         }
1012
1013       if ( Options.use_cdci_descriptor )
1014         {
1015           ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
1016           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
1017           
1018           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
1019                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
1020                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
1021
1022           if ( ASDCP_SUCCESS(result) )
1023             {
1024               tmp_dscr->CodingEquations = Options.coding_equations;
1025               tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
1026               tmp_dscr->ColorPrimaries = Options.color_primaries;
1027               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
1028               tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
1029               tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
1030               tmp_dscr->ComponentDepth = Options.component_depth;
1031               tmp_dscr->FrameLayout = Options.frame_layout;
1032               tmp_dscr->AspectRatio = Options.aspect_ratio;
1033               tmp_dscr->FieldDominance = Options.field_dominance;
1034               tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
1035               tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
1036               tmp_dscr->ColorRange = Options.cdci_ColorRange;
1037               if (Options.line_map_flag)  tmp_dscr->VideoLineMap = Options.line_map;
1038
1039               if ( Options.md_min_luminance || Options.md_max_luminance )
1040                 {
1041                   tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
1042                   tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
1043                 }
1044
1045               if ( Options.md_primaries.HasValue() )
1046                 {
1047                   tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
1048                   tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
1049                 }
1050
1051               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1052             }
1053         }
1054       else
1055         { // use RGB
1056           ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
1057           essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
1058           
1059           result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
1060                                            *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
1061                                            *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
1062
1063           if ( ASDCP_SUCCESS(result) )
1064             {
1065               tmp_dscr->CodingEquations = Options.coding_equations;
1066               tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
1067               tmp_dscr->ColorPrimaries = Options.color_primaries;
1068               tmp_dscr->ScanningDirection = 0;
1069               tmp_dscr->PictureEssenceCoding = Options.picture_coding;
1070               tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
1071               tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
1072               if (Options.line_map_flag)  tmp_dscr->VideoLineMap = Options.line_map;
1073
1074               if ( Options.md_min_luminance || Options.md_max_luminance )
1075                 {
1076                   tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
1077                   tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
1078                 }
1079
1080               if ( Options.md_primaries.HasValue() )
1081                 {
1082                   tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
1083                   tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
1084                 }
1085
1086               essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1087             }
1088         }
1089     }
1090
1091   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1092     {
1093       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1094       Info.LabelSetType = LS_MXF_SMPTE;
1095
1096       if ( Options.asset_id_flag )
1097         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1098       else
1099         Kumu::GenRandomUUID(Info.AssetUUID);
1100
1101       // configure encryption
1102       if( Options.key_flag )
1103         {
1104           Kumu::GenRandomUUID(Info.ContextID);
1105           Info.EncryptedEssence = true;
1106
1107           if ( Options.key_id_flag )
1108             {
1109               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1110             }
1111           else
1112             {
1113               create_random_uuid(Info.CryptographicKeyID);
1114             }
1115
1116           Context = new AESEncContext;
1117           result = Context->InitKey(Options.key_value);
1118
1119           if ( ASDCP_SUCCESS(result) )
1120             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1121
1122           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1123             {
1124               Info.UsesHMAC = true;
1125               HMAC = new HMACContext;
1126               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1127             }
1128         }
1129
1130       if ( ASDCP_SUCCESS(result) )
1131         {
1132           result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
1133                                     Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
1134         }
1135     }
1136
1137   if ( ASDCP_SUCCESS(result) )
1138     {
1139       ui32_t duration = 0;
1140       result = Parser.Reset();
1141
1142       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1143         {
1144           result = Parser.ReadFrame(FrameBuffer);
1145           
1146           if ( ASDCP_SUCCESS(result) )
1147             {
1148               if ( Options.verbose_flag )
1149                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1150               
1151               if ( Options.encrypt_header_flag )
1152                 FrameBuffer.PlaintextOffset(0);
1153             }
1154
1155           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1156             {
1157               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1158
1159               // The Writer class will forward the last block of ciphertext
1160               // to the encryption context for use as the IV for the next
1161               // frame. If you want to use non-sequitur IV values, un-comment
1162               // the following  line of code.
1163               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1164               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1165             }
1166         }
1167
1168       if ( result == RESULT_ENDOFFILE )
1169         result = RESULT_OK;
1170     }
1171
1172   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1173     result = Writer.Finalize();
1174
1175   return result;
1176 }
1177
1178
1179 //------------------------------------------------------------------------------------------
1180 // ACES essence
1181
1182
1183 // Write one or more plaintext ACES codestreams to a plaintext AS-02 file
1184 // Write one or more plaintext ACES codestreams to a ciphertext AS-02 file
1185 //
1186 Result_t
1187 write_ACES_file(CommandOptions& Options)
1188 {
1189   AESEncContext*          Context = 0;
1190   HMACContext*            HMAC = 0;
1191   AS_02::ACES::MXFWriter  Writer;
1192   AS_02::ACES::FrameBuffer       FrameBuffer(Options.fb_size);
1193   AS_02::ACES::SequenceParser    Parser;
1194   byte_t                  IV_buf[CBC_BLOCK_SIZE];
1195   Kumu::FortunaRNG        RNG;
1196   ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
1197   ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
1198   AS_02::ACES::PictureDescriptor PDesc;
1199   AS_02::ACES::ResourceList_t resource_list_t;
1200
1201   // set up essence parser
1202   //Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_aces_pedantic);
1203   // set up essence parser
1204   std::list<std::string> target_frame_file_list;
1205   if (Options.target_frame_subdescriptor_flag)
1206   {
1207     Kumu::DirScannerEx dir_reader;
1208     Kumu::DirectoryEntryType_t ft;
1209     std::string next_item;
1210     Result_t result = dir_reader.Open(Options.target_frame_directory);
1211     if ( KM_SUCCESS(result) )
1212     {
1213       while ( KM_SUCCESS(dir_reader.GetNext(next_item, ft)) )
1214       {
1215           if ( next_item[0] == '.' ) continue; // no hidden files
1216           std::string tmp_path = Kumu::PathJoin(Options.target_frame_directory, next_item);
1217           target_frame_file_list.push_back(tmp_path);
1218       }
1219     }
1220   }
1221   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic, target_frame_file_list);
1222
1223   // set up MXF writer
1224   if (ASDCP_SUCCESS(result))
1225   {
1226     Parser.FillPictureDescriptor(PDesc);
1227     Parser.FillResourceList(resource_list_t);
1228     PDesc.EditRate = Options.edit_rate;
1229
1230     if (Options.verbose_flag)
1231     {
1232       fprintf(stderr, "ACES pictures\n");
1233       fputs("PictureDescriptor:\n", stderr);
1234       fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1235       AS_02::ACES::PictureDescriptorDump(PDesc);
1236     }
1237
1238     ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
1239     Kumu::GenRandomValue(tmp_dscr->InstanceUID);
1240     ASDCP::MXF::ACESPictureSubDescriptor* aces_picture_subdescriptor = new ASDCP::MXF::ACESPictureSubDescriptor(g_dict);
1241     Kumu::GenRandomValue(aces_picture_subdescriptor->InstanceUID);
1242     result = AS_02::ACES::ACES_PDesc_to_MD(PDesc, *g_dict, *tmp_dscr);
1243     ASDCP::MXF::ContainerConstraintsSubDescriptor* gc_subdescriptor = new ASDCP::MXF::ContainerConstraintsSubDescriptor(g_dict);
1244     Kumu::GenRandomValue(gc_subdescriptor->InstanceUID);
1245     essence_sub_descriptors.push_back(gc_subdescriptor);
1246
1247     if (ASDCP_SUCCESS(result))
1248     {
1249       if (Options.aspect_ratio_flag) tmp_dscr->AspectRatio = Options.aspect_ratio;
1250
1251       if (Options.aces_picture_subdescriptor_flag)
1252       {
1253         if (Options.aces_authoring_information_flag) aces_picture_subdescriptor->ACESAuthoringInformation = Options.aces_authoring_information;
1254         if (Options.md_primaries.HasValue())
1255         {
1256           aces_picture_subdescriptor->ACESMasteringDisplayPrimaries = Options.md_primaries;
1257           aces_picture_subdescriptor->ACESMasteringDisplayWhitePointChromaticity = Options.md_white_point;
1258         }
1259         if (Options.md_min_luminance && Options.md_max_luminance)
1260         {
1261           aces_picture_subdescriptor->ACESMasteringDisplayMinimumLuminance = Options.md_min_luminance;
1262           aces_picture_subdescriptor->ACESMasteringDisplayMaximumLuminance = Options.md_max_luminance;
1263         }
1264         essence_sub_descriptors.push_back(aces_picture_subdescriptor);
1265       }
1266
1267       if (Options.target_frame_subdescriptor_flag)
1268       {
1269         AS_02::ACES::ResourceList_t::iterator it;
1270         ui32_t EssenceStreamID = 10;  //start with 10, same value in AncillaryResourceWriter
1271         for (it = resource_list_t.begin(); it != resource_list_t.end(); it++ )
1272         {
1273           ASDCP::MXF::TargetFrameSubDescriptor* target_frame_subdescriptor = new ASDCP::MXF::TargetFrameSubDescriptor(g_dict);
1274           Kumu::GenRandomValue(target_frame_subdescriptor->InstanceUID);
1275           target_frame_subdescriptor->TargetFrameAncillaryResourceID.Set(it->ResourceID);
1276           target_frame_subdescriptor->MediaType.assign(AS_02::ACES::MIME2str(it->Type));
1277           target_frame_subdescriptor->TargetFrameEssenceStreamID = EssenceStreamID++;
1278           if (Options.target_frame_index_flag)
1279           {
1280             if (Options.target_frame_index_list.size() > 0)
1281             {
1282               target_frame_subdescriptor->TargetFrameIndex = Options.target_frame_index_list.front();
1283               Options.target_frame_index_list.pop_front();
1284             } else
1285             {
1286               fprintf(stderr, "Insufficient number of Target Frame Index values provided\n");
1287               fprintf(stderr, "Number of Target Frames (%lu) should match number of Target Frame Index values\n", resource_list_t.size());
1288             }
1289           }
1290           if (Options.target_frame_transfer_characteristics_flag) target_frame_subdescriptor->TargetFrameTransferCharacteristic = Options.target_frame_transfer_characteristics;
1291           if (Options.target_frame_color_primaries_flag) target_frame_subdescriptor->TargetFrameColorPrimaries = Options.target_frame_color_primaries;
1292           if (Options.target_frame_min_max_ref_flag)
1293           {
1294             target_frame_subdescriptor->TargetFrameComponentMinRef = Options.target_frame_min_ref;
1295             target_frame_subdescriptor->TargetFrameComponentMaxRef = Options.target_frame_max_ref;
1296           }
1297           if (Options.aces_picture_subdescriptor_flag) target_frame_subdescriptor->ACESPictureSubDescriptorInstanceID = aces_picture_subdescriptor->InstanceUID;
1298           essence_sub_descriptors.push_back(target_frame_subdescriptor);
1299         }
1300       }
1301
1302       essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1303       if (Options.line_map_flag)  tmp_dscr->VideoLineMap = Options.line_map;
1304     }
1305   }
1306
1307   if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1308   {
1309     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1310     Info.LabelSetType = LS_MXF_SMPTE;
1311
1312     if (Options.asset_id_flag)
1313       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1314     else
1315       Kumu::GenRandomUUID(Info.AssetUUID);
1316
1317     // configure encryption
1318     if (Options.key_flag)
1319     {
1320       Kumu::GenRandomUUID(Info.ContextID);
1321       Info.EncryptedEssence = true;
1322
1323       if (Options.key_id_flag)
1324       {
1325         memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1326       }
1327       else
1328       {
1329         create_random_uuid(Info.CryptographicKeyID);
1330       }
1331
1332       Context = new AESEncContext;
1333       result = Context->InitKey(Options.key_value);
1334
1335       if (ASDCP_SUCCESS(result))
1336         result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1337
1338       if (ASDCP_SUCCESS(result) && Options.write_hmac)
1339       {
1340         Info.UsesHMAC = true;
1341         HMAC = new HMACContext;
1342         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1343       }
1344     }
1345
1346     if (ASDCP_SUCCESS(result))
1347     {
1348       result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
1349         Options.edit_rate, AS_02::ACES::ResourceList_t(), Options.mxf_header_size, Options.index_strategy, Options.partition_space);
1350     }
1351   }
1352
1353   if (ASDCP_SUCCESS(result))
1354   {
1355     ui32_t duration = 0;
1356     result = Parser.Reset();
1357
1358     while (ASDCP_SUCCESS(result) && duration++ < Options.duration)
1359     {
1360       result = Parser.ReadFrame(FrameBuffer);
1361
1362       if (ASDCP_SUCCESS(result))
1363       {
1364         if (Options.verbose_flag)
1365           FrameBuffer.Dump(stderr, Options.fb_dump_size);
1366
1367         if (Options.key_flag && Options.encrypt_header_flag)
1368           FrameBuffer.PlaintextOffset(0);
1369       }
1370
1371       if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1372       {
1373         result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1374
1375         // The Writer class will forward the last block of ciphertext
1376         // to the encryption context for use as the IV for the next
1377         // frame. If you want to use non-sequitur IV values, un-comment
1378         // the following  line of code.
1379         // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1380         //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1381       }
1382     }
1383
1384     if (result == RESULT_ENDOFFILE)
1385       result = RESULT_OK;
1386   }
1387     AS_02::ACES::ResourceList_t::const_iterator ri;
1388     for ( ri = resource_list_t.begin() ; ri != resource_list_t.end() && ASDCP_SUCCESS(result); ri++ )
1389     {
1390       result = Parser.ReadAncillaryResource((*ri).filePath, FrameBuffer);
1391
1392       if ( ASDCP_SUCCESS(result) )
1393       {
1394         if ( Options.verbose_flag )
1395           FrameBuffer.Dump(stderr, Options.fb_dump_size);
1396
1397         if ( ! Options.no_write_flag )
1398         {
1399           result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1400
1401           // The Writer class will forward the last block of ciphertext
1402           // to the encryption context for use as the IV for the next
1403           // frame. If you want to use non-sequitur IV values, un-comment
1404           // the following  line of code.
1405           // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1406           //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1407         }
1408       }
1409
1410       if ( result == RESULT_ENDOFFILE )
1411         result = RESULT_OK;
1412     }
1413
1414
1415
1416   if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1417     result = Writer.Finalize();
1418
1419   return result;
1420 }
1421
1422
1423
1424
1425 //------------------------------------------------------------------------------------------
1426 // PCM essence
1427 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
1428 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
1429 //
1430 Result_t
1431 write_PCM_file(CommandOptions& Options)
1432 {
1433   AESEncContext*    Context = 0;
1434   HMACContext*      HMAC = 0;
1435   PCMParserList     Parser;
1436   AS_02::PCM::MXFWriter    Writer;
1437   PCM::FrameBuffer  FrameBuffer;
1438   byte_t            IV_buf[CBC_BLOCK_SIZE];
1439   Kumu::FortunaRNG  RNG;
1440   ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
1441
1442   // set up essence parser
1443   Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
1444
1445   // set up MXF writer
1446   if ( ASDCP_SUCCESS(result) )
1447     {
1448       ASDCP::PCM::AudioDescriptor ADesc;
1449       Parser.FillAudioDescriptor(ADesc);
1450
1451       ADesc.EditRate = Options.edit_rate;
1452       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1453
1454       if ( Options.verbose_flag )
1455         {
1456           char buf[64];
1457           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1458                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
1459                   RationalToString(Options.edit_rate, buf, 64),
1460                   PCM::CalcSamplesPerFrame(ADesc));
1461           fputs("AudioDescriptor:\n", stderr);
1462           PCM::AudioDescriptorDump(ADesc);
1463         }
1464
1465       essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
1466
1467       result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
1468
1469       if ( Options.mca_config.empty() )
1470         {
1471           essence_descriptor->ChannelAssignment = Options.channel_assignment;
1472         }
1473       else
1474         {
1475           if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1476             {
1477               fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1478                       Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1479               return RESULT_FAIL;
1480             }
1481
1482           // This marks all soundfield groups using the same MCA property values
1483           MXF::InterchangeObject_list_t::iterator i;
1484           for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
1485             {
1486               MXF::SoundfieldGroupLabelSubDescriptor * desc = dynamic_cast<MXF::SoundfieldGroupLabelSubDescriptor*>(*i);
1487               if ( desc != 0 )
1488                 {
1489                   if ( ! Options.mca_audio_content_kind.empty() )
1490                     {
1491                       desc->MCAAudioContentKind = Options.mca_audio_content_kind;
1492                     }
1493                   if ( ! Options.mca_audio_element_kind.empty() )
1494                     {
1495                       desc->MCAAudioElementKind = Options.mca_audio_element_kind;
1496                     }
1497                 }
1498             }
1499
1500           essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
1501         }
1502     }
1503
1504   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1505     {
1506       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1507       Info.LabelSetType = LS_MXF_SMPTE;
1508
1509       if ( Options.asset_id_flag )
1510         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1511       else
1512         Kumu::GenRandomUUID(Info.AssetUUID);
1513
1514       // configure encryption
1515       if( Options.key_flag )
1516         {
1517           Kumu::GenRandomUUID(Info.ContextID);
1518           Info.EncryptedEssence = true;
1519
1520           if ( Options.key_id_flag )
1521             {
1522               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1523             }
1524           else
1525             {
1526               create_random_uuid(Info.CryptographicKeyID);
1527             }
1528
1529           Context = new AESEncContext;
1530           result = Context->InitKey(Options.key_value);
1531
1532           if ( ASDCP_SUCCESS(result) )
1533             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1534
1535           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1536             {
1537               Info.UsesHMAC = true;
1538               HMAC = new HMACContext;
1539               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1540             }
1541         }
1542
1543       if ( ASDCP_SUCCESS(result) )
1544         {
1545           result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
1546                                     Options.mca_config, Options.edit_rate);
1547         }
1548     }
1549
1550   if ( ASDCP_SUCCESS(result) )
1551     {
1552       result = Parser.Reset();
1553       ui32_t duration = 0;
1554
1555       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1556         {
1557           result = Parser.ReadFrame(FrameBuffer);
1558
1559           if ( ASDCP_SUCCESS(result) )
1560             {
1561               if ( Options.verbose_flag )
1562                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1563
1564               if ( ! Options.no_write_flag )
1565                 {
1566                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1567
1568                   // The Writer class will forward the last block of ciphertext
1569                   // to the encryption context for use as the IV for the next
1570                   // frame. If you want to use non-sequitur IV values, un-comment
1571                   // the following  line of code.
1572                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1573                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1574                 }
1575             }
1576         }
1577
1578       if ( result == RESULT_ENDOFFILE )
1579         result = RESULT_OK;
1580     }
1581
1582   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1583     result = Writer.Finalize();
1584
1585   return result;
1586 }
1587
1588
1589
1590
1591 //------------------------------------------------------------------------------------------
1592 // TimedText essence
1593
1594
1595 // Write one or more plaintext timed text streams to a plaintext AS-02 file
1596 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
1597 //
1598 Result_t
1599 write_timed_text_file(CommandOptions& Options)
1600 {
1601   AESEncContext*    Context = 0;
1602   HMACContext*      HMAC = 0;
1603   AS_02::TimedText::ST2052_TextParser  Parser;
1604   AS_02::TimedText::MXFWriter    Writer;
1605   TimedText::FrameBuffer  FrameBuffer;
1606   TimedText::TimedTextDescriptor TDesc;
1607   byte_t            IV_buf[CBC_BLOCK_SIZE];
1608   Kumu::FortunaRNG  RNG;
1609
1610   // set up essence parser
1611   Result_t result = Parser.OpenRead(Options.filenames.front());
1612
1613   // set up MXF writer
1614   if ( ASDCP_SUCCESS(result) )
1615     {
1616       Parser.FillTimedTextDescriptor(TDesc);
1617       TDesc.EditRate = Options.edit_rate;
1618       TDesc.ContainerDuration = Options.duration;
1619       FrameBuffer.Capacity(Options.fb_size);
1620
1621       if ( ! Options.profile_name.empty() )
1622         {
1623           TDesc.NamespaceName = Options.profile_name;
1624         }
1625
1626       if ( Options.verbose_flag )
1627         {
1628           fputs("IMF Timed-Text Descriptor:\n", stderr);
1629           TimedText::DescriptorDump(TDesc);
1630         }
1631     }
1632
1633   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1634     {
1635       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1636       Info.LabelSetType = LS_MXF_SMPTE;
1637
1638       if ( Options.asset_id_flag )
1639         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1640       else
1641         Kumu::GenRandomUUID(Info.AssetUUID);
1642
1643       // configure encryption
1644       if( Options.key_flag )
1645         {
1646           Kumu::GenRandomUUID(Info.ContextID);
1647           Info.EncryptedEssence = true;
1648
1649           if ( Options.key_id_flag )
1650             {
1651               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1652             }
1653           else
1654             {
1655               create_random_uuid(Info.CryptographicKeyID);
1656             }
1657
1658           Context = new AESEncContext;
1659           result = Context->InitKey(Options.key_value);
1660
1661           if ( ASDCP_SUCCESS(result) )
1662             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1663
1664           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1665             {
1666               Info.UsesHMAC = true;
1667               HMAC = new HMACContext;
1668               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1669             }
1670         }
1671
1672       if ( ASDCP_SUCCESS(result) )
1673         result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1674     }
1675
1676   if ( ASDCP_FAILURE(result) )
1677     return result;
1678
1679   std::string XMLDoc;
1680   TimedText::ResourceList_t::const_iterator ri;
1681
1682   result = Parser.ReadTimedTextResource(XMLDoc);
1683
1684   if ( ASDCP_SUCCESS(result) )
1685     result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1686
1687   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1688     {
1689       result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1690
1691       if ( ASDCP_SUCCESS(result) )
1692         {
1693           if ( Options.verbose_flag )
1694             FrameBuffer.Dump(stderr, Options.fb_dump_size);
1695
1696           if ( ! Options.no_write_flag )
1697             {
1698               result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1699
1700               // The Writer class will forward the last block of ciphertext
1701               // to the encryption context for use as the IV for the next
1702               // frame. If you want to use non-sequitur IV values, un-comment
1703               // the following  line of code.
1704               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1705               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1706             }
1707         }
1708
1709       if ( result == RESULT_ENDOFFILE )
1710         result = RESULT_OK;
1711     }
1712
1713   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1714     result = Writer.Finalize();
1715
1716   return result;
1717 }
1718
1719 //
1720 bool
1721 get_current_dms_text_descriptor(AS_02::ISXD::MXFWriter& writer, ASDCP::MXF::GenericStreamTextBasedSet *&text_object)
1722 {
1723   std::list<MXF::InterchangeObject*> object_list;
1724   writer.OP1aHeader().GetMDObjectsByType(DefaultSMPTEDict().ul(MDD_GenericStreamTextBasedSet), object_list);
1725
1726   if ( object_list.empty() )
1727     {
1728       return false;
1729     }
1730
1731   text_object = dynamic_cast<MXF::GenericStreamTextBasedSet*>(object_list.back());
1732   assert(text_object != 0);
1733   return true;
1734 }
1735                       
1736
1737 // Write one or more plaintext Aux Data bytestreams to a plaintext AS-02 file
1738 // Write one or more plaintext Aux Data bytestreams to a ciphertext AS-02 file
1739 //
1740 Result_t
1741 write_isxd_file(CommandOptions& Options)
1742 {
1743   AESEncContext*          Context = 0;
1744   HMACContext*            HMAC = 0;
1745   AS_02::ISXD::MXFWriter Writer;
1746   DCData::FrameBuffer     FrameBuffer(Options.fb_size);
1747   DCData::SequenceParser  Parser;
1748   byte_t                  IV_buf[CBC_BLOCK_SIZE];
1749   Kumu::FortunaRNG        RNG;
1750
1751   // set up essence parser
1752   Result_t result = Parser.OpenRead(Options.filenames.front());
1753
1754   // set up MXF writer
1755   if ( ASDCP_SUCCESS(result) )
1756     {
1757
1758       if ( Options.verbose_flag )
1759         {
1760           fprintf(stderr, "ISXD Data\n");
1761           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1762         }
1763     }
1764
1765   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1766   {
1767     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1768     if ( Options.asset_id_flag )
1769       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1770     else
1771       Kumu::GenRandomUUID(Info.AssetUUID);
1772
1773     Info.LabelSetType = LS_MXF_SMPTE;
1774
1775       // configure encryption
1776     if( Options.key_flag )
1777         {
1778           Kumu::GenRandomUUID(Info.ContextID);
1779           Info.EncryptedEssence = true;
1780
1781           if ( Options.key_id_flag )
1782             {
1783               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1784             }
1785           else
1786             {
1787               create_random_uuid(Info.CryptographicKeyID);
1788             }
1789
1790           Context = new AESEncContext;
1791           result = Context->InitKey(Options.key_value);
1792
1793           if ( ASDCP_SUCCESS(result) )
1794             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1795
1796           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1797       {
1798         Info.UsesHMAC = true;
1799         HMAC = new HMACContext;
1800         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1801       }
1802         }
1803
1804     if ( ASDCP_SUCCESS(result) )
1805       {
1806         if ( Options.isxd_document_namespace == "auto" )
1807           {
1808             // get ns of first item
1809             std::string ns_prefix, type_name, namespace_name;
1810             result = Parser.ReadFrame(FrameBuffer);
1811
1812             if ( ASDCP_SUCCESS(result) )
1813               {
1814                 Kumu::AttributeList doc_attr_list;
1815                 result = GetXMLDocType(FrameBuffer.RoData(), FrameBuffer.Size(), ns_prefix, type_name,
1816                                        namespace_name, doc_attr_list) ? RESULT_OK : RESULT_FAIL;
1817               }
1818
1819             if ( ASDCP_SUCCESS(result) && ! namespace_name.empty() )
1820               {
1821                 Options.isxd_document_namespace = namespace_name;
1822               }
1823             else
1824               {
1825                 fprintf(stderr, "Unable to parse an XML namespace name from the input document.\n");
1826                 return RESULT_FAIL;
1827               }
1828           }
1829
1830         result = Writer.OpenWrite(Options.out_file, Info, Options.isxd_document_namespace, Options.edit_rate);
1831       }
1832   }
1833
1834   if ( ASDCP_SUCCESS(result) )
1835     {
1836       ui32_t duration = 0;
1837       result = Parser.Reset();
1838
1839       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1840         {
1841           result = Parser.ReadFrame(FrameBuffer);
1842
1843           if ( ASDCP_SUCCESS(result) )
1844             {
1845               if ( Options.verbose_flag )
1846                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1847
1848               if ( Options.encrypt_header_flag )
1849                 FrameBuffer.PlaintextOffset(0);
1850             }
1851
1852           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1853             {
1854               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1855
1856               // The Writer class will forward the last block of ciphertext
1857               // to the encryption context for use as the IV for the next
1858               // frame. If you want to use non-sequitur IV values, un-comment
1859               // the following  line of code.
1860               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1861               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1862             }
1863         }
1864
1865       if ( result == RESULT_ENDOFFILE )
1866         {
1867           result = RESULT_OK;
1868         }
1869     }
1870   
1871   if ( KM_SUCCESS(result) && ! Options.no_write_flag )
1872     {
1873       ASDCP::FrameBuffer global_metadata;
1874       std::list<std::string>::iterator i;
1875       
1876       for ( i = Options.global_isxd_metadata.begin(); i != Options.global_isxd_metadata.end(); ++i )
1877         {
1878           ui32_t file_size = Kumu::FileSize(*i);
1879           result = global_metadata.Capacity(file_size);
1880
1881           if ( KM_SUCCESS(result) )
1882             {
1883               ui32_t read_count = 0;
1884               Kumu::FileReader Reader;
1885               std::string namespace_name;
1886
1887               result = Reader.OpenRead(*i);
1888
1889               if ( KM_SUCCESS(result) )
1890                 {
1891                   result = Reader.Read(global_metadata.Data(), file_size, &read_count);
1892                 }
1893
1894               if ( KM_SUCCESS(result) )
1895                 {
1896                   if ( file_size != read_count) 
1897                     return RESULT_READFAIL;
1898
1899                   global_metadata.Size(read_count);
1900
1901                   std::string ns_prefix, type_name;
1902                   Kumu::AttributeList doc_attr_list;
1903                   result = GetXMLDocType(global_metadata.RoData(), global_metadata.Size(), ns_prefix, type_name,
1904                                          namespace_name, doc_attr_list) ? RESULT_OK : RESULT_FAIL;
1905                 }
1906
1907               if ( KM_SUCCESS(result) )
1908                 {
1909                   result = Writer.AddDmsGenericPartUtf8Text(global_metadata, Context, HMAC);
1910                 }
1911
1912               if ( KM_SUCCESS(result) )
1913                 {
1914                   ASDCP::MXF::GenericStreamTextBasedSet *text_object = 0;
1915                   get_current_dms_text_descriptor(Writer, text_object);
1916                   assert(text_object);
1917                   text_object->TextMIMEMediaType = "text/xml";
1918                   text_object->TextDataDescription = namespace_name;
1919
1920                   // this is not really useful when inserting multiple objects because
1921                   // it cannot be set per object without some other CLI syntax for
1922                   // associating language codes with 2057 blobs, e.g., <filename>:<lang>
1923                   text_object->RFC5646TextLanguageCode = Options.language;
1924                 }
1925             }
1926         }
1927
1928       if ( KM_SUCCESS(result) )
1929         {
1930           result = Writer.Finalize();
1931         }
1932     }
1933
1934   return result;
1935 }
1936
1937 //
1938 int
1939 main(int argc, const char** argv)
1940 {
1941   Result_t result = RESULT_OK;
1942   char     str_buf[64];
1943   g_dict = &ASDCP::DefaultSMPTEDict();
1944   assert(g_dict);
1945
1946   CommandOptions Options(argc, argv);
1947
1948   if ( Options.version_flag )
1949     banner();
1950
1951   if ( Options.help_flag )
1952     usage();
1953
1954   if ( Options.show_ul_values_flag )
1955     {
1956       g_dict->Dump(stdout);
1957     }
1958
1959   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1960     return 0;
1961
1962   if ( Options.error_flag )
1963     {
1964       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1965       return 3;
1966     }
1967
1968   EssenceType_t EssenceType;
1969   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1970
1971   if ( ASDCP_SUCCESS(result) )
1972     {
1973       switch ( EssenceType )
1974         {
1975         case ESS_JPEG_2000:
1976           result = write_JP2K_file(Options);
1977           break;
1978          // PB
1979         case ::ESS_AS02_ACES:
1980           result = write_ACES_file(Options);
1981           break;
1982         case ESS_PCM_24b_48k:
1983         case ESS_PCM_24b_96k:
1984           result = write_PCM_file(Options);
1985           break;
1986
1987         case ESS_TIMED_TEXT:
1988           result = write_timed_text_file(Options);
1989           break;
1990
1991         case ESS_DCDATA_UNKNOWN:
1992           if ( ! Options.isxd_document_namespace.empty() )
1993             {
1994               result = write_isxd_file(Options);      
1995             }
1996           else
1997             {
1998               fprintf(stderr, "%s: Unknown synchronous data file type, not AS-02-compatible essence.\n",
1999                       Options.filenames.front().c_str());
2000               return 5;
2001             }
2002           break;
2003
2004         default:
2005           fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
2006                   Options.filenames.front().c_str());
2007           return 5;
2008         }
2009     }
2010
2011   if ( ASDCP_FAILURE(result) )
2012     {
2013       fputs("Program stopped on error.\n", stderr);
2014
2015       if ( result != RESULT_FAIL )
2016         {
2017           fputs(result, stderr);
2018           fputc('\n', stderr);
2019         }
2020
2021       return 1;
2022     }
2023
2024   return 0;
2025 }
2026
2027
2028 //
2029 // end as-02-wrap.cpp
2030 //