o removed waywars #endif
[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 luminance pair.\n");
477         return false;
478       }
479
480     if ( ! set_luminance_from_token(range_tokens.front(), target_frame_min_ref) ) return false;
481     if ( ! set_luminance_from_token(range_tokens.back(), target_frame_max_ref) ) return false;
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
1244     if (ASDCP_SUCCESS(result))
1245     {
1246       if (Options.aspect_ratio_flag) tmp_dscr->AspectRatio = Options.aspect_ratio;
1247
1248       if (Options.aces_picture_subdescriptor_flag)
1249       {
1250         if (Options.aces_authoring_information_flag) aces_picture_subdescriptor->ACESAuthoringInformation = Options.aces_authoring_information;
1251         if (Options.md_primaries.HasValue())
1252         {
1253           aces_picture_subdescriptor->ACESMasteringDisplayPrimaries = Options.md_primaries;
1254           aces_picture_subdescriptor->ACESMasteringDisplayWhitePointChromaticity = Options.md_white_point;
1255         }
1256         if (Options.md_min_luminance && Options.md_max_luminance)
1257         {
1258           aces_picture_subdescriptor->ACESMasteringDisplayMinimumLuminance = Options.md_min_luminance;
1259           aces_picture_subdescriptor->ACESMasteringDisplayMaximumLuminance = Options.md_max_luminance;
1260         }
1261         essence_sub_descriptors.push_back(aces_picture_subdescriptor);
1262       }
1263
1264       if (Options.target_frame_subdescriptor_flag)
1265       {
1266         AS_02::ACES::ResourceList_t::iterator it;
1267         ui32_t EssenceStreamID = 10;  //start with 10, same value in AncillaryResourceWriter
1268         for (it = resource_list_t.begin(); it != resource_list_t.end(); it++ )
1269         {
1270           ASDCP::MXF::TargetFrameSubDescriptor* target_frame_subdescriptor = new ASDCP::MXF::TargetFrameSubDescriptor(g_dict);
1271           Kumu::GenRandomValue(target_frame_subdescriptor->InstanceUID);
1272           target_frame_subdescriptor->TargetFrameAncillaryResourceID.Set(it->ResourceID);
1273           target_frame_subdescriptor->MediaType.assign(AS_02::ACES::MIME2str(it->Type));
1274           target_frame_subdescriptor->TargetFrameEssenceStreamID = EssenceStreamID++;
1275           if (Options.target_frame_index_flag)
1276           {
1277             if (Options.target_frame_index_list.size() > 0)
1278             {
1279               target_frame_subdescriptor->TargetFrameIndex = Options.target_frame_index_list.front();
1280               Options.target_frame_index_list.pop_front();
1281             } else
1282             {
1283               fprintf(stderr, "Insufficient number of Target Frame Index values provided\n");
1284               fprintf(stderr, "Number of Target Frames (%lu) should match number of Target Frame Index values\n", resource_list_t.size());
1285             }
1286           }
1287           if (Options.target_frame_transfer_characteristics_flag) target_frame_subdescriptor->TargetFrameTransferCharacteristic = Options.target_frame_transfer_characteristics;
1288           if (Options.target_frame_color_primaries_flag) target_frame_subdescriptor->TargetFrameColorPrimaries = Options.target_frame_color_primaries;
1289           if (Options.target_frame_min_max_ref_flag)
1290           {
1291             target_frame_subdescriptor->TargetFrameComponentMinRef = Options.target_frame_min_ref;
1292             target_frame_subdescriptor->TargetFrameComponentMaxRef = Options.target_frame_max_ref;
1293           }
1294           if (Options.aces_picture_subdescriptor_flag) target_frame_subdescriptor->ACESPictureSubDescriptorInstanceID = aces_picture_subdescriptor->InstanceUID;
1295           essence_sub_descriptors.push_back(target_frame_subdescriptor);
1296         }
1297       }
1298
1299       essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1300       if (Options.line_map_flag)  tmp_dscr->VideoLineMap = Options.line_map;
1301     }
1302   }
1303
1304   if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1305   {
1306     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1307     Info.LabelSetType = LS_MXF_SMPTE;
1308
1309     if (Options.asset_id_flag)
1310       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1311     else
1312       Kumu::GenRandomUUID(Info.AssetUUID);
1313
1314     // configure encryption
1315     if (Options.key_flag)
1316     {
1317       Kumu::GenRandomUUID(Info.ContextID);
1318       Info.EncryptedEssence = true;
1319
1320       if (Options.key_id_flag)
1321       {
1322         memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1323       }
1324       else
1325       {
1326         create_random_uuid(Info.CryptographicKeyID);
1327       }
1328
1329       Context = new AESEncContext;
1330       result = Context->InitKey(Options.key_value);
1331
1332       if (ASDCP_SUCCESS(result))
1333         result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1334
1335       if (ASDCP_SUCCESS(result) && Options.write_hmac)
1336       {
1337         Info.UsesHMAC = true;
1338         HMAC = new HMACContext;
1339         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1340       }
1341     }
1342
1343     if (ASDCP_SUCCESS(result))
1344     {
1345       result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
1346         Options.edit_rate, AS_02::ACES::ResourceList_t(), Options.mxf_header_size, Options.index_strategy, Options.partition_space);
1347     }
1348   }
1349
1350   if (ASDCP_SUCCESS(result))
1351   {
1352     ui32_t duration = 0;
1353     result = Parser.Reset();
1354
1355     while (ASDCP_SUCCESS(result) && duration++ < Options.duration)
1356     {
1357       result = Parser.ReadFrame(FrameBuffer);
1358
1359       if (ASDCP_SUCCESS(result))
1360       {
1361         if (Options.verbose_flag)
1362           FrameBuffer.Dump(stderr, Options.fb_dump_size);
1363
1364         if (Options.key_flag && Options.encrypt_header_flag)
1365           FrameBuffer.PlaintextOffset(0);
1366       }
1367
1368       if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1369       {
1370         result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1371
1372         // The Writer class will forward the last block of ciphertext
1373         // to the encryption context for use as the IV for the next
1374         // frame. If you want to use non-sequitur IV values, un-comment
1375         // the following  line of code.
1376         // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1377         //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1378       }
1379     }
1380
1381     if (result == RESULT_ENDOFFILE)
1382       result = RESULT_OK;
1383   }
1384     AS_02::ACES::ResourceList_t::const_iterator ri;
1385     for ( ri = resource_list_t.begin() ; ri != resource_list_t.end() && ASDCP_SUCCESS(result); ri++ )
1386     {
1387       result = Parser.ReadAncillaryResource((*ri).filePath, FrameBuffer);
1388
1389       if ( ASDCP_SUCCESS(result) )
1390       {
1391         if ( Options.verbose_flag )
1392           FrameBuffer.Dump(stderr, Options.fb_dump_size);
1393
1394         if ( ! Options.no_write_flag )
1395         {
1396           result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1397
1398           // The Writer class will forward the last block of ciphertext
1399           // to the encryption context for use as the IV for the next
1400           // frame. If you want to use non-sequitur IV values, un-comment
1401           // the following  line of code.
1402           // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1403           //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1404         }
1405       }
1406
1407       if ( result == RESULT_ENDOFFILE )
1408         result = RESULT_OK;
1409     }
1410
1411
1412
1413   if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1414     result = Writer.Finalize();
1415
1416   return result;
1417 }
1418
1419
1420
1421
1422 //------------------------------------------------------------------------------------------
1423 // PCM essence
1424 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
1425 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
1426 //
1427 Result_t
1428 write_PCM_file(CommandOptions& Options)
1429 {
1430   AESEncContext*    Context = 0;
1431   HMACContext*      HMAC = 0;
1432   PCMParserList     Parser;
1433   AS_02::PCM::MXFWriter    Writer;
1434   PCM::FrameBuffer  FrameBuffer;
1435   byte_t            IV_buf[CBC_BLOCK_SIZE];
1436   Kumu::FortunaRNG  RNG;
1437   ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
1438
1439   // set up essence parser
1440   Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
1441
1442   // set up MXF writer
1443   if ( ASDCP_SUCCESS(result) )
1444     {
1445       ASDCP::PCM::AudioDescriptor ADesc;
1446       Parser.FillAudioDescriptor(ADesc);
1447
1448       ADesc.EditRate = Options.edit_rate;
1449       FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1450
1451       if ( Options.verbose_flag )
1452         {
1453           char buf[64];
1454           fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1455                   ADesc.AudioSamplingRate.Quotient() / 1000.0,
1456                   RationalToString(Options.edit_rate, buf, 64),
1457                   PCM::CalcSamplesPerFrame(ADesc));
1458           fputs("AudioDescriptor:\n", stderr);
1459           PCM::AudioDescriptorDump(ADesc);
1460         }
1461
1462       essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
1463
1464       result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
1465
1466       if ( Options.mca_config.empty() )
1467         {
1468           essence_descriptor->ChannelAssignment = Options.channel_assignment;
1469         }
1470       else
1471         {
1472           if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1473             {
1474               fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1475                       Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1476               return RESULT_FAIL;
1477             }
1478
1479           // This marks all soundfield groups using the same MCA property values
1480           MXF::InterchangeObject_list_t::iterator i;
1481           for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
1482             {
1483               MXF::SoundfieldGroupLabelSubDescriptor * desc = dynamic_cast<MXF::SoundfieldGroupLabelSubDescriptor*>(*i);
1484               if ( desc != 0 )
1485                 {
1486                   if ( ! Options.mca_audio_content_kind.empty() )
1487                     {
1488                       desc->MCAAudioContentKind = Options.mca_audio_content_kind;
1489                     }
1490                   if ( ! Options.mca_audio_element_kind.empty() )
1491                     {
1492                       desc->MCAAudioElementKind = Options.mca_audio_element_kind;
1493                     }
1494                 }
1495             }
1496
1497           essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
1498         }
1499     }
1500
1501   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1502     {
1503       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1504       Info.LabelSetType = LS_MXF_SMPTE;
1505
1506       if ( Options.asset_id_flag )
1507         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1508       else
1509         Kumu::GenRandomUUID(Info.AssetUUID);
1510
1511       // configure encryption
1512       if( Options.key_flag )
1513         {
1514           Kumu::GenRandomUUID(Info.ContextID);
1515           Info.EncryptedEssence = true;
1516
1517           if ( Options.key_id_flag )
1518             {
1519               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1520             }
1521           else
1522             {
1523               create_random_uuid(Info.CryptographicKeyID);
1524             }
1525
1526           Context = new AESEncContext;
1527           result = Context->InitKey(Options.key_value);
1528
1529           if ( ASDCP_SUCCESS(result) )
1530             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1531
1532           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1533             {
1534               Info.UsesHMAC = true;
1535               HMAC = new HMACContext;
1536               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1537             }
1538         }
1539
1540       if ( ASDCP_SUCCESS(result) )
1541         {
1542           result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
1543                                     Options.mca_config, Options.edit_rate);
1544         }
1545     }
1546
1547   if ( ASDCP_SUCCESS(result) )
1548     {
1549       result = Parser.Reset();
1550       ui32_t duration = 0;
1551
1552       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1553         {
1554           result = Parser.ReadFrame(FrameBuffer);
1555
1556           if ( ASDCP_SUCCESS(result) )
1557             {
1558               if ( Options.verbose_flag )
1559                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1560
1561               if ( ! Options.no_write_flag )
1562                 {
1563                   result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1564
1565                   // The Writer class will forward the last block of ciphertext
1566                   // to the encryption context for use as the IV for the next
1567                   // frame. If you want to use non-sequitur IV values, un-comment
1568                   // the following  line of code.
1569                   // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1570                   //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1571                 }
1572             }
1573         }
1574
1575       if ( result == RESULT_ENDOFFILE )
1576         result = RESULT_OK;
1577     }
1578
1579   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1580     result = Writer.Finalize();
1581
1582   return result;
1583 }
1584
1585
1586
1587
1588 //------------------------------------------------------------------------------------------
1589 // TimedText essence
1590
1591
1592 // Write one or more plaintext timed text streams to a plaintext AS-02 file
1593 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
1594 //
1595 Result_t
1596 write_timed_text_file(CommandOptions& Options)
1597 {
1598   AESEncContext*    Context = 0;
1599   HMACContext*      HMAC = 0;
1600   AS_02::TimedText::ST2052_TextParser  Parser;
1601   AS_02::TimedText::MXFWriter    Writer;
1602   TimedText::FrameBuffer  FrameBuffer;
1603   TimedText::TimedTextDescriptor TDesc;
1604   byte_t            IV_buf[CBC_BLOCK_SIZE];
1605   Kumu::FortunaRNG  RNG;
1606
1607   // set up essence parser
1608   Result_t result = Parser.OpenRead(Options.filenames.front());
1609
1610   // set up MXF writer
1611   if ( ASDCP_SUCCESS(result) )
1612     {
1613       Parser.FillTimedTextDescriptor(TDesc);
1614       TDesc.EditRate = Options.edit_rate;
1615       TDesc.ContainerDuration = Options.duration;
1616       FrameBuffer.Capacity(Options.fb_size);
1617
1618       if ( ! Options.profile_name.empty() )
1619         {
1620           TDesc.NamespaceName = Options.profile_name;
1621         }
1622
1623       if ( Options.verbose_flag )
1624         {
1625           fputs("IMF Timed-Text Descriptor:\n", stderr);
1626           TimedText::DescriptorDump(TDesc);
1627         }
1628     }
1629
1630   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1631     {
1632       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1633       Info.LabelSetType = LS_MXF_SMPTE;
1634
1635       if ( Options.asset_id_flag )
1636         memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1637       else
1638         Kumu::GenRandomUUID(Info.AssetUUID);
1639
1640       // configure encryption
1641       if( Options.key_flag )
1642         {
1643           Kumu::GenRandomUUID(Info.ContextID);
1644           Info.EncryptedEssence = true;
1645
1646           if ( Options.key_id_flag )
1647             {
1648               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1649             }
1650           else
1651             {
1652               create_random_uuid(Info.CryptographicKeyID);
1653             }
1654
1655           Context = new AESEncContext;
1656           result = Context->InitKey(Options.key_value);
1657
1658           if ( ASDCP_SUCCESS(result) )
1659             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1660
1661           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1662             {
1663               Info.UsesHMAC = true;
1664               HMAC = new HMACContext;
1665               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1666             }
1667         }
1668
1669       if ( ASDCP_SUCCESS(result) )
1670         result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1671     }
1672
1673   if ( ASDCP_FAILURE(result) )
1674     return result;
1675
1676   std::string XMLDoc;
1677   TimedText::ResourceList_t::const_iterator ri;
1678
1679   result = Parser.ReadTimedTextResource(XMLDoc);
1680
1681   if ( ASDCP_SUCCESS(result) )
1682     result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1683
1684   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1685     {
1686       result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1687
1688       if ( ASDCP_SUCCESS(result) )
1689         {
1690           if ( Options.verbose_flag )
1691             FrameBuffer.Dump(stderr, Options.fb_dump_size);
1692
1693           if ( ! Options.no_write_flag )
1694             {
1695               result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1696
1697               // The Writer class will forward the last block of ciphertext
1698               // to the encryption context for use as the IV for the next
1699               // frame. If you want to use non-sequitur IV values, un-comment
1700               // the following  line of code.
1701               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1702               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1703             }
1704         }
1705
1706       if ( result == RESULT_ENDOFFILE )
1707         result = RESULT_OK;
1708     }
1709
1710   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1711     result = Writer.Finalize();
1712
1713   return result;
1714 }
1715
1716 //
1717 bool
1718 get_current_dms_text_descriptor(AS_02::ISXD::MXFWriter& writer, ASDCP::MXF::GenericStreamTextBasedSet *&text_object)
1719 {
1720   std::list<MXF::InterchangeObject*> object_list;
1721   writer.OP1aHeader().GetMDObjectsByType(DefaultSMPTEDict().ul(MDD_GenericStreamTextBasedSet), object_list);
1722
1723   if ( object_list.empty() )
1724     {
1725       return false;
1726     }
1727
1728   text_object = dynamic_cast<MXF::GenericStreamTextBasedSet*>(object_list.back());
1729   assert(text_object != 0);
1730   return true;
1731 }
1732                       
1733
1734 // Write one or more plaintext Aux Data bytestreams to a plaintext AS-02 file
1735 // Write one or more plaintext Aux Data bytestreams to a ciphertext AS-02 file
1736 //
1737 Result_t
1738 write_isxd_file(CommandOptions& Options)
1739 {
1740   AESEncContext*          Context = 0;
1741   HMACContext*            HMAC = 0;
1742   AS_02::ISXD::MXFWriter Writer;
1743   DCData::FrameBuffer     FrameBuffer(Options.fb_size);
1744   DCData::SequenceParser  Parser;
1745   byte_t                  IV_buf[CBC_BLOCK_SIZE];
1746   Kumu::FortunaRNG        RNG;
1747
1748   // set up essence parser
1749   Result_t result = Parser.OpenRead(Options.filenames.front());
1750
1751   // set up MXF writer
1752   if ( ASDCP_SUCCESS(result) )
1753     {
1754
1755       if ( Options.verbose_flag )
1756         {
1757           fprintf(stderr, "ISXD Data\n");
1758           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1759         }
1760     }
1761
1762   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1763   {
1764     WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
1765     if ( Options.asset_id_flag )
1766       memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1767     else
1768       Kumu::GenRandomUUID(Info.AssetUUID);
1769
1770     Info.LabelSetType = LS_MXF_SMPTE;
1771
1772       // configure encryption
1773     if( Options.key_flag )
1774         {
1775           Kumu::GenRandomUUID(Info.ContextID);
1776           Info.EncryptedEssence = true;
1777
1778           if ( Options.key_id_flag )
1779             {
1780               memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1781             }
1782           else
1783             {
1784               create_random_uuid(Info.CryptographicKeyID);
1785             }
1786
1787           Context = new AESEncContext;
1788           result = Context->InitKey(Options.key_value);
1789
1790           if ( ASDCP_SUCCESS(result) )
1791             result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1792
1793           if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1794       {
1795         Info.UsesHMAC = true;
1796         HMAC = new HMACContext;
1797         result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1798       }
1799         }
1800
1801     if ( ASDCP_SUCCESS(result) )
1802       {
1803         if ( Options.isxd_document_namespace == "auto" )
1804           {
1805             // get ns of first item
1806             std::string ns_prefix, type_name, namespace_name;
1807             result = Parser.ReadFrame(FrameBuffer);
1808
1809             if ( ASDCP_SUCCESS(result) )
1810               {
1811                 Kumu::AttributeList doc_attr_list;
1812                 result = GetXMLDocType(FrameBuffer.RoData(), FrameBuffer.Size(), ns_prefix, type_name,
1813                                        namespace_name, doc_attr_list) ? RESULT_OK : RESULT_FAIL;
1814               }
1815
1816             if ( ASDCP_SUCCESS(result) && ! namespace_name.empty() )
1817               {
1818                 Options.isxd_document_namespace = namespace_name;
1819               }
1820             else
1821               {
1822                 fprintf(stderr, "Unable to parse an XML namespace name from the input document.\n");
1823                 return RESULT_FAIL;
1824               }
1825           }
1826
1827         result = Writer.OpenWrite(Options.out_file, Info, Options.isxd_document_namespace, Options.edit_rate);
1828       }
1829   }
1830
1831   if ( ASDCP_SUCCESS(result) )
1832     {
1833       ui32_t duration = 0;
1834       result = Parser.Reset();
1835
1836       while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1837         {
1838           result = Parser.ReadFrame(FrameBuffer);
1839
1840           if ( ASDCP_SUCCESS(result) )
1841             {
1842               if ( Options.verbose_flag )
1843                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1844
1845               if ( Options.encrypt_header_flag )
1846                 FrameBuffer.PlaintextOffset(0);
1847             }
1848
1849           if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1850             {
1851               result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1852
1853               // The Writer class will forward the last block of ciphertext
1854               // to the encryption context for use as the IV for the next
1855               // frame. If you want to use non-sequitur IV values, un-comment
1856               // the following  line of code.
1857               // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1858               //   Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1859             }
1860         }
1861
1862       if ( result == RESULT_ENDOFFILE )
1863         {
1864           result = RESULT_OK;
1865         }
1866     }
1867   
1868   if ( KM_SUCCESS(result) && ! Options.no_write_flag )
1869     {
1870       ASDCP::FrameBuffer global_metadata;
1871       std::list<std::string>::iterator i;
1872       
1873       for ( i = Options.global_isxd_metadata.begin(); i != Options.global_isxd_metadata.end(); ++i )
1874         {
1875           ui32_t file_size = Kumu::FileSize(*i);
1876           result = global_metadata.Capacity(file_size);
1877
1878           if ( KM_SUCCESS(result) )
1879             {
1880               ui32_t read_count = 0;
1881               Kumu::FileReader Reader;
1882               std::string namespace_name;
1883
1884               result = Reader.OpenRead(*i);
1885
1886               if ( KM_SUCCESS(result) )
1887                 {
1888                   result = Reader.Read(global_metadata.Data(), file_size, &read_count);
1889                 }
1890
1891               if ( KM_SUCCESS(result) )
1892                 {
1893                   if ( file_size != read_count) 
1894                     return RESULT_READFAIL;
1895
1896                   global_metadata.Size(read_count);
1897
1898                   std::string ns_prefix, type_name;
1899                   Kumu::AttributeList doc_attr_list;
1900                   result = GetXMLDocType(global_metadata.RoData(), global_metadata.Size(), ns_prefix, type_name,
1901                                          namespace_name, doc_attr_list) ? RESULT_OK : RESULT_FAIL;
1902                 }
1903
1904               if ( KM_SUCCESS(result) )
1905                 {
1906                   result = Writer.AddDmsGenericPartUtf8Text(global_metadata, Context, HMAC);
1907                 }
1908
1909               if ( KM_SUCCESS(result) )
1910                 {
1911                   ASDCP::MXF::GenericStreamTextBasedSet *text_object = 0;
1912                   get_current_dms_text_descriptor(Writer, text_object);
1913                   assert(text_object);
1914                   text_object->TextMIMEMediaType = "text/xml";
1915                   text_object->TextDataDescription = namespace_name;
1916
1917                   // this is not really useful when inserting multiple objects because
1918                   // it cannot be set per object without some other CLI syntax for
1919                   // associating language codes with 2057 blobs, e.g., <filename>:<lang>
1920                   text_object->RFC5646TextLanguageCode = Options.language;
1921                 }
1922             }
1923         }
1924
1925       if ( KM_SUCCESS(result) )
1926         {
1927           result = Writer.Finalize();
1928         }
1929     }
1930
1931   return result;
1932 }
1933
1934 //
1935 int
1936 main(int argc, const char** argv)
1937 {
1938   Result_t result = RESULT_OK;
1939   char     str_buf[64];
1940   g_dict = &ASDCP::DefaultSMPTEDict();
1941   assert(g_dict);
1942
1943   CommandOptions Options(argc, argv);
1944
1945   if ( Options.version_flag )
1946     banner();
1947
1948   if ( Options.help_flag )
1949     usage();
1950
1951   if ( Options.show_ul_values_flag )
1952     {
1953       g_dict->Dump(stdout);
1954     }
1955
1956   if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1957     return 0;
1958
1959   if ( Options.error_flag )
1960     {
1961       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1962       return 3;
1963     }
1964
1965   EssenceType_t EssenceType;
1966   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1967
1968   if ( ASDCP_SUCCESS(result) )
1969     {
1970       switch ( EssenceType )
1971         {
1972         case ESS_JPEG_2000:
1973           result = write_JP2K_file(Options);
1974           break;
1975          // PB
1976         case ::ESS_AS02_ACES:
1977           result = write_ACES_file(Options);
1978           break;
1979         case ESS_PCM_24b_48k:
1980         case ESS_PCM_24b_96k:
1981           result = write_PCM_file(Options);
1982           break;
1983
1984         case ESS_TIMED_TEXT:
1985           result = write_timed_text_file(Options);
1986           break;
1987
1988         case ESS_DCDATA_UNKNOWN:
1989           if ( ! Options.isxd_document_namespace.empty() )
1990             {
1991               result = write_isxd_file(Options);      
1992             }
1993           else
1994             {
1995               fprintf(stderr, "%s: Unknown synchronous data file type, not AS-02-compatible essence.\n",
1996                       Options.filenames.front().c_str());
1997               return 5;
1998             }
1999           break;
2000
2001         default:
2002           fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
2003                   Options.filenames.front().c_str());
2004           return 5;
2005         }
2006     }
2007
2008   if ( ASDCP_FAILURE(result) )
2009     {
2010       fputs("Program stopped on error.\n", stderr);
2011
2012       if ( result != RESULT_FAIL )
2013         {
2014           fputs(result, stderr);
2015           fputc('\n', stderr);
2016         }
2017
2018       return 1;
2019     }
2020
2021   return 0;
2022 }
2023
2024
2025 //
2026 // end as-02-wrap.cpp
2027 //