2 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst, Wolfgang Ruppel
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
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.
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.
29 /*! \file as-02-wrap.cpp
31 \brief AS-02 file manipulation utility
33 This program wraps IMF essence (picture or sound) in to an AS-02 MXF file.
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
39 #include <KM_fileio.h>
43 #include "AS_02_ACES.h"
44 #include <PCMParserList.h>
47 using namespace ASDCP;
49 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
50 const ASDCP::Dictionary *g_dict = 0;
53 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
55 snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
60 //------------------------------------------------------------------------------------------
62 // command line option parser class
64 static const char* PROGRAM_NAME = "as-02-wrap"; // program name for messages
66 // local program identification info written to file headers
67 class MyInfo : public WriterInfo
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 };
76 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
77 CompanyName = "WidgetCo";
78 ProductName = "as-02-wrap";
79 ProductVersion = ASDCP::Version();
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)); \
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)); \
103 create_random_uuid(byte_t* uuidbuf)
106 GenRandomValue(tmp_id);
107 memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
112 banner(FILE* stream = stdout)
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);
125 usage(FILE* stream = stdout)
128 USAGE: %s [-h|-help] [-V]\n\
130 %s [options] <input-file>+ <output-file>\n\n",
131 PROGRAM_NAME, PROGRAM_NAME);
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\
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\
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\
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");
220 const float chromaticity_scale = 50000.0;
223 set_primary_from_token(const std::string& token, ui16_t& primary)
225 float raw_value = strtod(token.c_str(),0);
227 if ( raw_value == 0.0 || raw_value > 1.0 )
229 fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str());
233 primary = floor(0.5 + ( raw_value * chromaticity_scale ));
237 const float luminance_scale = 10000.0;
240 set_luminance_from_token(const std::string& token, ui32_t& luminance)
242 float raw_value = strtod(token.c_str(),0);
244 if ( raw_value == 0.0 || raw_value > 400000.0 )
246 fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str());
250 luminance = floor(0.5 + ( raw_value * luminance_scale ));
254 #define SET_LUMINANCE(p,t) \
255 if ( ! set_luminance_from_token(t, p) ) { \
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
287 UL channel_assignment, picture_coding, transfer_characteristic, color_primaries, coding_equations;
288 ASDCP::MXF::AS02_MCAConfigParser mca_config;
289 std::string language;
294 ui32_t horizontal_subsampling;
295 ui32_t vertical_subsampling;
296 ui32_t component_depth;
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;
306 ui32_t md_min_luminance, md_max_luminance;
307 ASDCP::MXF::ThreeColorPrimaries md_primaries;
308 ASDCP::MXF::ColorPrimary md_white_point;
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
315 std::string isxd_document_namespace;
316 std::list<std::string> global_isxd_metadata;
319 MXF::LineMapPair line_map;
321 std::string out_file, profile_name; //
322 std::string mca_audio_element_kind, mca_audio_content_kind;
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;
334 bool set_video_line_map(const std::string& arg)
336 const char* sep_str = strrchr(arg.c_str(), ',');
340 fprintf(stderr, "Expecting <first>,<second>\n");
344 line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
345 line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
350 bool set_video_ref(const std::string& arg)
352 std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
354 switch ( ref_tokens.size() )
357 cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
358 ref_tokens.pop_back();
360 cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
361 ref_tokens.pop_back();
363 cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
367 fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
371 if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
373 fprintf(stderr, "Unexpected CDCI video referece levels.\n");
381 bool set_display_primaries(const std::string& arg)
383 std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ",");
384 if ( coordinate_tokens.size() != 8 )
386 fprintf(stderr, "Expecting four coordinate pairs.\n");
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;
404 bool set_display_luminance(const std::string& arg)
406 std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ",");
407 if ( luminance_tokens.size() != 2 )
409 fprintf(stderr, "Expecting a luminance pair.\n");
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;
420 bool set_color_system_from_arg(const char* arg)
426 // Application 2 (ST 2067-20)
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;
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;
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;
448 // Application 2e (ST 2067-21)
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;
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;
464 fprintf(stderr, "Unrecognized color system number, expecting one of 1-5.\n");
471 bool set_target_frame_min_max_code_value(const std::string& arg)
473 std::list<std::string> range_tokens = Kumu::km_token_split(arg, ",");
474 if ( range_tokens.size() != 2 )
476 fprintf(stderr, "Expecting a code value pair.\n");
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);
486 bool set_target_frame_index_list(const std::string& arg, std::list<ui64_t>& r_target_frame_index_list)
488 std::list<std::string> index_tokens = Kumu::km_token_split(arg, ",");
489 if ( index_tokens.size() == 0 )
491 fprintf(stderr, "Expecting at least one Target Frame Index.\n");
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));
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)
521 memset(key_value, 0, KeyLen);
522 memset(key_id_value, 0, UUIDlen);
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;
529 for ( int i = 1; i < argc; i++ )
532 if ( (strcmp( argv[i], "-help") == 0) )
538 if ( (strcmp( argv[i], "-suba") == 0) )
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;
548 if ( (strcmp( argv[i], "-subt") == 0) )
550 target_frame_subdescriptor_flag = true;
551 TEST_EXTRA_ARG_STRING(i, "subt");
552 target_frame_directory = argv[i];
556 if ( (strcmp( argv[i], "-tfi") == 0) )
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;
565 if ( (strcmp( argv[i], "-tft") == 0) )
567 TEST_EXTRA_ARG_STRING(i, "tft");
569 const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
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);
578 if ( (strcmp( argv[i], "-tfc") == 0) )
580 TEST_EXTRA_ARG_STRING(i, "tfc");
582 const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
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);
591 if ( (strcmp( argv[i], "-tfr") == 0) )
593 TEST_EXTRA_ARG(i, 'o');
594 if ( ! set_target_frame_min_max_code_value(argv[i]) )
598 target_frame_min_max_ref_flag = true;
602 if ( (strcmp( argv[i], "-tfv") == 0) )
604 TEST_EXTRA_ARG_STRING(i, "tfv");
606 const ASDCP::MDDEntry* entry = g_dict->FindSymbol(std::string(argv[i]));
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);
616 if ( argv[i][0] == '-'
617 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
620 switch ( argv[i][1] )
623 TEST_EXTRA_ARG(i, 'A');
624 if ( ! DecodeRational(argv[i], aspect_ratio) )
626 fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
631 aspect_ratio_flag = true;
636 asset_id_flag = true;
637 TEST_EXTRA_ARG(i, 'a');
640 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
642 if ( length != UUIDlen )
644 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
651 TEST_EXTRA_ARG(i, 'b');
652 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
655 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
660 TEST_EXTRA_ARG(i, 'c');
661 if ( ! set_color_system_from_arg(argv[i]) )
668 TEST_EXTRA_ARG(i, 'C');
669 if ( ! channel_assignment.DecodeHex(argv[i]) )
671 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
677 TEST_EXTRA_ARG(i, 'D');
678 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
682 TEST_EXTRA_ARG(i, 'd');
683 duration = Kumu::xabs(strtol(argv[i], 0, 10));
686 case 'E': encrypt_header_flag = false; break;
687 case 'e': encrypt_header_flag = true; break;
690 TEST_EXTRA_ARG(i, 'F');
691 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
692 if ( field_dominance > 1 )
694 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
700 TEST_EXTRA_ARG(i, 'g');
705 TEST_EXTRA_ARG(i, 'G');
706 global_isxd_metadata.push_back(argv[i]);
709 case 'h': help_flag = true; break;
713 use_cdci_descriptor = true;
718 TEST_EXTRA_ARG(i, 'j');
721 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
723 if ( length != UUIDlen )
725 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
731 case 'k': key_flag = true;
732 TEST_EXTRA_ARG(i, 'k');
735 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
737 if ( length != KeyLen )
739 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
746 TEST_EXTRA_ARG(i, 'y');
747 if ( ! set_video_line_map(argv[i]) )
751 line_map_flag = true;
755 case 'M': write_hmac = false; break;
758 TEST_EXTRA_ARG(i, 'm');
759 mca_config_str = argv[i];
763 TEST_EXTRA_ARG(i, 'n');
764 if ( ! transfer_characteristic.DecodeHex(argv[i]) )
766 fprintf(stderr, "Error decoding TransferCharacteristic UL value: %s\n", argv[i]);
772 TEST_EXTRA_ARG(i, 'O');
773 if ( ! set_display_primaries(argv[i]) )
780 TEST_EXTRA_ARG(i, 'o');
781 if ( ! set_display_luminance(argv[i]) )
788 TEST_EXTRA_ARG(i, 'P');
789 profile_name = argv[i];
793 TEST_EXTRA_ARG(i, 'p');
794 if ( ! picture_coding.DecodeHex(argv[i]) )
796 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
802 TEST_EXTRA_ARG(i, 'q');
803 if ( ! coding_equations.DecodeHex(argv[i]) )
805 fprintf(stderr, "Error decoding CodingEquations UL value: %s\n", argv[i]);
811 TEST_EXTRA_ARG(i, 'r');
812 if ( ! DecodeRational(argv[i], edit_rate) )
814 fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
821 use_cdci_descriptor = false;
825 TEST_EXTRA_ARG(i, 's');
826 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
830 TEST_EXTRA_ARG(i, 't');
831 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
835 TEST_EXTRA_ARG(i, 'T');
836 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
839 case 'u': show_ul_values_flag = true; break;
842 TEST_EXTRA_ARG(i, 'U');
843 isxd_document_namespace = argv[i];
846 case 'V': version_flag = true; break;
847 case 'v': verbose_flag = true; break;
848 case 'W': no_write_flag = true; break;
851 TEST_EXTRA_ARG(i, 'x');
852 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
856 TEST_EXTRA_ARG(i, 'X');
857 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
861 use_cdci_descriptor = true;
862 // default 10 bit video range YUV, ref levels already set
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]) )
875 case 'Z': j2c_pedantic = false; break;
876 case 'z': j2c_pedantic = true; break;
879 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
883 else if ( argv[i][0] == '-' && argv[i][1] == '-' && isalpha(argv[i][2]) )
885 if ( strcmp(argv[i]+2, "mca-audio-content-kind") == 0 )
887 if ( ++i >= argc || argv[(i)][0] == '-' )
889 fprintf(stderr, "Argument not found for option -mca-audio-content-kind.\n");
893 mca_audio_content_kind = argv[i];
895 else if ( strcmp(argv[i]+2, "mca-audio-element-kind") == 0 )
897 if ( ++i >= argc || argv[(i)][0] == '-' )
899 fprintf(stderr, "Argument not found for option -mca-audio-element-kind.\n");
903 mca_audio_element_kind = argv[i];
907 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
913 if ( argv[i][0] != '-' )
915 filenames.push_back(argv[i]);
919 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
925 if ( ! mca_config_str.empty() )
927 if ( language.empty() )
929 if ( ! mca_config.DecodeString(mca_config_str) )
936 if ( ! mca_config.DecodeString(mca_config_str, language) )
943 if ( help_flag || version_flag || show_ul_values_flag )
948 if ( filenames.size() < 2 )
950 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
954 out_file = filenames.back();
955 filenames.pop_back();
957 if ( ! picture_coding.HasValue() )
959 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
967 //------------------------------------------------------------------------------------------
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);
976 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
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
983 write_JP2K_file(CommandOptions& Options)
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;
995 // set up essence parser
996 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
999 if ( ASDCP_SUCCESS(result) )
1001 ASDCP::JP2K::PictureDescriptor PDesc;
1002 Parser.FillPictureDescriptor(PDesc);
1003 PDesc.EditRate = Options.edit_rate;
1005 if ( Options.verbose_flag )
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);
1013 if ( Options.use_cdci_descriptor )
1015 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
1016 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
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()));
1022 if ( ASDCP_SUCCESS(result) )
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;
1039 if ( Options.md_min_luminance || Options.md_max_luminance )
1041 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
1042 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
1045 if ( Options.md_primaries.HasValue() )
1047 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
1048 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
1051 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1056 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
1057 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
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()));
1063 if ( ASDCP_SUCCESS(result) )
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;
1074 if ( Options.md_min_luminance || Options.md_max_luminance )
1076 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
1077 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
1080 if ( Options.md_primaries.HasValue() )
1082 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
1083 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
1086 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1091 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1093 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1094 Info.LabelSetType = LS_MXF_SMPTE;
1096 if ( Options.asset_id_flag )
1097 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1099 Kumu::GenRandomUUID(Info.AssetUUID);
1101 // configure encryption
1102 if( Options.key_flag )
1104 Kumu::GenRandomUUID(Info.ContextID);
1105 Info.EncryptedEssence = true;
1107 if ( Options.key_id_flag )
1109 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1113 create_random_uuid(Info.CryptographicKeyID);
1116 Context = new AESEncContext;
1117 result = Context->InitKey(Options.key_value);
1119 if ( ASDCP_SUCCESS(result) )
1120 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1122 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1124 Info.UsesHMAC = true;
1125 HMAC = new HMACContext;
1126 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1130 if ( ASDCP_SUCCESS(result) )
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);
1137 if ( ASDCP_SUCCESS(result) )
1139 ui32_t duration = 0;
1140 result = Parser.Reset();
1142 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1144 result = Parser.ReadFrame(FrameBuffer);
1146 if ( ASDCP_SUCCESS(result) )
1148 if ( Options.verbose_flag )
1149 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1151 if ( Options.encrypt_header_flag )
1152 FrameBuffer.PlaintextOffset(0);
1155 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1157 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
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));
1168 if ( result == RESULT_ENDOFFILE )
1172 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1173 result = Writer.Finalize();
1179 //------------------------------------------------------------------------------------------
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
1187 write_ACES_file(CommandOptions& Options)
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;
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)
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) )
1213 while ( KM_SUCCESS(dir_reader.GetNext(next_item, ft)) )
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);
1221 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic, target_frame_file_list);
1223 // set up MXF writer
1224 if (ASDCP_SUCCESS(result))
1226 Parser.FillPictureDescriptor(PDesc);
1227 Parser.FillResourceList(resource_list_t);
1228 PDesc.EditRate = Options.edit_rate;
1230 if (Options.verbose_flag)
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);
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);
1247 if (ASDCP_SUCCESS(result))
1249 if (Options.aspect_ratio_flag) tmp_dscr->AspectRatio = Options.aspect_ratio;
1251 if (Options.aces_picture_subdescriptor_flag)
1253 if (Options.aces_authoring_information_flag) aces_picture_subdescriptor->ACESAuthoringInformation = Options.aces_authoring_information;
1254 if (Options.md_primaries.HasValue())
1256 aces_picture_subdescriptor->ACESMasteringDisplayPrimaries = Options.md_primaries;
1257 aces_picture_subdescriptor->ACESMasteringDisplayWhitePointChromaticity = Options.md_white_point;
1259 if (Options.md_min_luminance && Options.md_max_luminance)
1261 aces_picture_subdescriptor->ACESMasteringDisplayMinimumLuminance = Options.md_min_luminance;
1262 aces_picture_subdescriptor->ACESMasteringDisplayMaximumLuminance = Options.md_max_luminance;
1264 essence_sub_descriptors.push_back(aces_picture_subdescriptor);
1267 if (Options.target_frame_subdescriptor_flag)
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++ )
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)
1280 if (Options.target_frame_index_list.size() > 0)
1282 target_frame_subdescriptor->TargetFrameIndex = Options.target_frame_index_list.front();
1283 Options.target_frame_index_list.pop_front();
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());
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)
1294 target_frame_subdescriptor->TargetFrameComponentMinRef = Options.target_frame_min_ref;
1295 target_frame_subdescriptor->TargetFrameComponentMaxRef = Options.target_frame_max_ref;
1297 if (Options.aces_picture_subdescriptor_flag) target_frame_subdescriptor->ACESPictureSubDescriptorInstanceID = aces_picture_subdescriptor->InstanceUID;
1298 essence_sub_descriptors.push_back(target_frame_subdescriptor);
1302 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1303 if (Options.line_map_flag) tmp_dscr->VideoLineMap = Options.line_map;
1307 if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1309 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1310 Info.LabelSetType = LS_MXF_SMPTE;
1312 if (Options.asset_id_flag)
1313 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1315 Kumu::GenRandomUUID(Info.AssetUUID);
1317 // configure encryption
1318 if (Options.key_flag)
1320 Kumu::GenRandomUUID(Info.ContextID);
1321 Info.EncryptedEssence = true;
1323 if (Options.key_id_flag)
1325 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1329 create_random_uuid(Info.CryptographicKeyID);
1332 Context = new AESEncContext;
1333 result = Context->InitKey(Options.key_value);
1335 if (ASDCP_SUCCESS(result))
1336 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1338 if (ASDCP_SUCCESS(result) && Options.write_hmac)
1340 Info.UsesHMAC = true;
1341 HMAC = new HMACContext;
1342 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1346 if (ASDCP_SUCCESS(result))
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);
1353 if (ASDCP_SUCCESS(result))
1355 ui32_t duration = 0;
1356 result = Parser.Reset();
1358 while (ASDCP_SUCCESS(result) && duration++ < Options.duration)
1360 result = Parser.ReadFrame(FrameBuffer);
1362 if (ASDCP_SUCCESS(result))
1364 if (Options.verbose_flag)
1365 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1367 if (Options.key_flag && Options.encrypt_header_flag)
1368 FrameBuffer.PlaintextOffset(0);
1371 if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1373 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
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));
1384 if (result == RESULT_ENDOFFILE)
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++ )
1390 result = Parser.ReadAncillaryResource((*ri).filePath, FrameBuffer);
1392 if ( ASDCP_SUCCESS(result) )
1394 if ( Options.verbose_flag )
1395 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1397 if ( ! Options.no_write_flag )
1399 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
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));
1410 if ( result == RESULT_ENDOFFILE )
1416 if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1417 result = Writer.Finalize();
1425 //------------------------------------------------------------------------------------------
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
1431 write_PCM_file(CommandOptions& Options)
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;
1442 // set up essence parser
1443 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
1445 // set up MXF writer
1446 if ( ASDCP_SUCCESS(result) )
1448 ASDCP::PCM::AudioDescriptor ADesc;
1449 Parser.FillAudioDescriptor(ADesc);
1451 ADesc.EditRate = Options.edit_rate;
1452 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1454 if ( Options.verbose_flag )
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);
1465 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
1467 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
1469 if ( Options.mca_config.empty() )
1471 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1475 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1477 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1478 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
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 )
1486 MXF::SoundfieldGroupLabelSubDescriptor * desc = dynamic_cast<MXF::SoundfieldGroupLabelSubDescriptor*>(*i);
1489 if ( ! Options.mca_audio_content_kind.empty() )
1491 desc->MCAAudioContentKind = Options.mca_audio_content_kind;
1493 if ( ! Options.mca_audio_element_kind.empty() )
1495 desc->MCAAudioElementKind = Options.mca_audio_element_kind;
1500 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
1504 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1506 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1507 Info.LabelSetType = LS_MXF_SMPTE;
1509 if ( Options.asset_id_flag )
1510 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1512 Kumu::GenRandomUUID(Info.AssetUUID);
1514 // configure encryption
1515 if( Options.key_flag )
1517 Kumu::GenRandomUUID(Info.ContextID);
1518 Info.EncryptedEssence = true;
1520 if ( Options.key_id_flag )
1522 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1526 create_random_uuid(Info.CryptographicKeyID);
1529 Context = new AESEncContext;
1530 result = Context->InitKey(Options.key_value);
1532 if ( ASDCP_SUCCESS(result) )
1533 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1535 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1537 Info.UsesHMAC = true;
1538 HMAC = new HMACContext;
1539 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1543 if ( ASDCP_SUCCESS(result) )
1545 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
1546 Options.mca_config, Options.edit_rate);
1550 if ( ASDCP_SUCCESS(result) )
1552 result = Parser.Reset();
1553 ui32_t duration = 0;
1555 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1557 result = Parser.ReadFrame(FrameBuffer);
1559 if ( ASDCP_SUCCESS(result) )
1561 if ( Options.verbose_flag )
1562 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1564 if ( ! Options.no_write_flag )
1566 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
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));
1578 if ( result == RESULT_ENDOFFILE )
1582 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1583 result = Writer.Finalize();
1591 //------------------------------------------------------------------------------------------
1592 // TimedText essence
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
1599 write_timed_text_file(CommandOptions& Options)
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;
1610 // set up essence parser
1611 Result_t result = Parser.OpenRead(Options.filenames.front());
1613 // set up MXF writer
1614 if ( ASDCP_SUCCESS(result) )
1616 Parser.FillTimedTextDescriptor(TDesc);
1617 TDesc.EditRate = Options.edit_rate;
1618 TDesc.ContainerDuration = Options.duration;
1619 FrameBuffer.Capacity(Options.fb_size);
1621 if ( ! Options.profile_name.empty() )
1623 TDesc.NamespaceName = Options.profile_name;
1626 if ( Options.verbose_flag )
1628 fputs("IMF Timed-Text Descriptor:\n", stderr);
1629 TimedText::DescriptorDump(TDesc);
1633 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1635 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1636 Info.LabelSetType = LS_MXF_SMPTE;
1638 if ( Options.asset_id_flag )
1639 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1641 Kumu::GenRandomUUID(Info.AssetUUID);
1643 // configure encryption
1644 if( Options.key_flag )
1646 Kumu::GenRandomUUID(Info.ContextID);
1647 Info.EncryptedEssence = true;
1649 if ( Options.key_id_flag )
1651 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1655 create_random_uuid(Info.CryptographicKeyID);
1658 Context = new AESEncContext;
1659 result = Context->InitKey(Options.key_value);
1661 if ( ASDCP_SUCCESS(result) )
1662 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1664 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1666 Info.UsesHMAC = true;
1667 HMAC = new HMACContext;
1668 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1672 if ( ASDCP_SUCCESS(result) )
1673 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1676 if ( ASDCP_FAILURE(result) )
1680 TimedText::ResourceList_t::const_iterator ri;
1682 result = Parser.ReadTimedTextResource(XMLDoc);
1684 if ( ASDCP_SUCCESS(result) )
1685 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1687 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1689 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1691 if ( ASDCP_SUCCESS(result) )
1693 if ( Options.verbose_flag )
1694 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1696 if ( ! Options.no_write_flag )
1698 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
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));
1709 if ( result == RESULT_ENDOFFILE )
1713 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1714 result = Writer.Finalize();
1721 get_current_dms_text_descriptor(AS_02::ISXD::MXFWriter& writer, ASDCP::MXF::GenericStreamTextBasedSet *&text_object)
1723 std::list<MXF::InterchangeObject*> object_list;
1724 writer.OP1aHeader().GetMDObjectsByType(DefaultSMPTEDict().ul(MDD_GenericStreamTextBasedSet), object_list);
1726 if ( object_list.empty() )
1731 text_object = dynamic_cast<MXF::GenericStreamTextBasedSet*>(object_list.back());
1732 assert(text_object != 0);
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
1741 write_isxd_file(CommandOptions& Options)
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;
1751 // set up essence parser
1752 Result_t result = Parser.OpenRead(Options.filenames.front());
1754 // set up MXF writer
1755 if ( ASDCP_SUCCESS(result) )
1758 if ( Options.verbose_flag )
1760 fprintf(stderr, "ISXD Data\n");
1761 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1765 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
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);
1771 Kumu::GenRandomUUID(Info.AssetUUID);
1773 Info.LabelSetType = LS_MXF_SMPTE;
1775 // configure encryption
1776 if( Options.key_flag )
1778 Kumu::GenRandomUUID(Info.ContextID);
1779 Info.EncryptedEssence = true;
1781 if ( Options.key_id_flag )
1783 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1787 create_random_uuid(Info.CryptographicKeyID);
1790 Context = new AESEncContext;
1791 result = Context->InitKey(Options.key_value);
1793 if ( ASDCP_SUCCESS(result) )
1794 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1796 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1798 Info.UsesHMAC = true;
1799 HMAC = new HMACContext;
1800 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1804 if ( ASDCP_SUCCESS(result) )
1806 if ( Options.isxd_document_namespace == "auto" )
1808 // get ns of first item
1809 std::string ns_prefix, type_name, namespace_name;
1810 result = Parser.ReadFrame(FrameBuffer);
1812 if ( ASDCP_SUCCESS(result) )
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;
1819 if ( ASDCP_SUCCESS(result) && ! namespace_name.empty() )
1821 Options.isxd_document_namespace = namespace_name;
1825 fprintf(stderr, "Unable to parse an XML namespace name from the input document.\n");
1830 result = Writer.OpenWrite(Options.out_file, Info, Options.isxd_document_namespace, Options.edit_rate);
1834 if ( ASDCP_SUCCESS(result) )
1836 ui32_t duration = 0;
1837 result = Parser.Reset();
1839 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1841 result = Parser.ReadFrame(FrameBuffer);
1843 if ( ASDCP_SUCCESS(result) )
1845 if ( Options.verbose_flag )
1846 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1848 if ( Options.encrypt_header_flag )
1849 FrameBuffer.PlaintextOffset(0);
1852 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1854 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
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));
1865 if ( result == RESULT_ENDOFFILE )
1871 if ( KM_SUCCESS(result) && ! Options.no_write_flag )
1873 ASDCP::FrameBuffer global_metadata;
1874 std::list<std::string>::iterator i;
1876 for ( i = Options.global_isxd_metadata.begin(); i != Options.global_isxd_metadata.end(); ++i )
1878 ui32_t file_size = Kumu::FileSize(*i);
1879 result = global_metadata.Capacity(file_size);
1881 if ( KM_SUCCESS(result) )
1883 ui32_t read_count = 0;
1884 Kumu::FileReader Reader;
1885 std::string namespace_name;
1887 result = Reader.OpenRead(*i);
1889 if ( KM_SUCCESS(result) )
1891 result = Reader.Read(global_metadata.Data(), file_size, &read_count);
1894 if ( KM_SUCCESS(result) )
1896 if ( file_size != read_count)
1897 return RESULT_READFAIL;
1899 global_metadata.Size(read_count);
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;
1907 if ( KM_SUCCESS(result) )
1909 result = Writer.AddDmsGenericPartUtf8Text(global_metadata, Context, HMAC);
1912 if ( KM_SUCCESS(result) )
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;
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;
1928 if ( KM_SUCCESS(result) )
1930 result = Writer.Finalize();
1939 main(int argc, const char** argv)
1941 Result_t result = RESULT_OK;
1943 g_dict = &ASDCP::DefaultSMPTEDict();
1946 CommandOptions Options(argc, argv);
1948 if ( Options.version_flag )
1951 if ( Options.help_flag )
1954 if ( Options.show_ul_values_flag )
1956 g_dict->Dump(stdout);
1959 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1962 if ( Options.error_flag )
1964 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1968 EssenceType_t EssenceType;
1969 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1971 if ( ASDCP_SUCCESS(result) )
1973 switch ( EssenceType )
1976 result = write_JP2K_file(Options);
1979 case ::ESS_AS02_ACES:
1980 result = write_ACES_file(Options);
1982 case ESS_PCM_24b_48k:
1983 case ESS_PCM_24b_96k:
1984 result = write_PCM_file(Options);
1987 case ESS_TIMED_TEXT:
1988 result = write_timed_text_file(Options);
1991 case ESS_DCDATA_UNKNOWN:
1992 if ( ! Options.isxd_document_namespace.empty() )
1994 result = write_isxd_file(Options);
1998 fprintf(stderr, "%s: Unknown synchronous data file type, not AS-02-compatible essence.\n",
1999 Options.filenames.front().c_str());
2005 fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
2006 Options.filenames.front().c_str());
2011 if ( ASDCP_FAILURE(result) )
2013 fputs("Program stopped on error.\n", stderr);
2015 if ( result != RESULT_FAIL )
2017 fputs(result, stderr);
2018 fputc('\n', stderr);
2029 // end as-02-wrap.cpp