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 luminance pair.\n");
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;
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);
1244 if (ASDCP_SUCCESS(result))
1246 if (Options.aspect_ratio_flag) tmp_dscr->AspectRatio = Options.aspect_ratio;
1248 if (Options.aces_picture_subdescriptor_flag)
1250 if (Options.aces_authoring_information_flag) aces_picture_subdescriptor->ACESAuthoringInformation = Options.aces_authoring_information;
1251 if (Options.md_primaries.HasValue())
1253 aces_picture_subdescriptor->ACESMasteringDisplayPrimaries = Options.md_primaries;
1254 aces_picture_subdescriptor->ACESMasteringDisplayWhitePointChromaticity = Options.md_white_point;
1256 if (Options.md_min_luminance && Options.md_max_luminance)
1258 aces_picture_subdescriptor->ACESMasteringDisplayMinimumLuminance = Options.md_min_luminance;
1259 aces_picture_subdescriptor->ACESMasteringDisplayMaximumLuminance = Options.md_max_luminance;
1261 essence_sub_descriptors.push_back(aces_picture_subdescriptor);
1264 if (Options.target_frame_subdescriptor_flag)
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++ )
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)
1277 if (Options.target_frame_index_list.size() > 0)
1279 target_frame_subdescriptor->TargetFrameIndex = Options.target_frame_index_list.front();
1280 Options.target_frame_index_list.pop_front();
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());
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)
1291 target_frame_subdescriptor->TargetFrameComponentMinRef = Options.target_frame_min_ref;
1292 target_frame_subdescriptor->TargetFrameComponentMaxRef = Options.target_frame_max_ref;
1294 if (Options.aces_picture_subdescriptor_flag) target_frame_subdescriptor->ACESPictureSubDescriptorInstanceID = aces_picture_subdescriptor->InstanceUID;
1295 essence_sub_descriptors.push_back(target_frame_subdescriptor);
1299 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
1300 if (Options.line_map_flag) tmp_dscr->VideoLineMap = Options.line_map;
1304 if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1306 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1307 Info.LabelSetType = LS_MXF_SMPTE;
1309 if (Options.asset_id_flag)
1310 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1312 Kumu::GenRandomUUID(Info.AssetUUID);
1314 // configure encryption
1315 if (Options.key_flag)
1317 Kumu::GenRandomUUID(Info.ContextID);
1318 Info.EncryptedEssence = true;
1320 if (Options.key_id_flag)
1322 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1326 create_random_uuid(Info.CryptographicKeyID);
1329 Context = new AESEncContext;
1330 result = Context->InitKey(Options.key_value);
1332 if (ASDCP_SUCCESS(result))
1333 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1335 if (ASDCP_SUCCESS(result) && Options.write_hmac)
1337 Info.UsesHMAC = true;
1338 HMAC = new HMACContext;
1339 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1343 if (ASDCP_SUCCESS(result))
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);
1350 if (ASDCP_SUCCESS(result))
1352 ui32_t duration = 0;
1353 result = Parser.Reset();
1355 while (ASDCP_SUCCESS(result) && duration++ < Options.duration)
1357 result = Parser.ReadFrame(FrameBuffer);
1359 if (ASDCP_SUCCESS(result))
1361 if (Options.verbose_flag)
1362 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1364 if (Options.key_flag && Options.encrypt_header_flag)
1365 FrameBuffer.PlaintextOffset(0);
1368 if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1370 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
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));
1381 if (result == RESULT_ENDOFFILE)
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++ )
1387 result = Parser.ReadAncillaryResource((*ri).filePath, FrameBuffer);
1389 if ( ASDCP_SUCCESS(result) )
1391 if ( Options.verbose_flag )
1392 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1394 if ( ! Options.no_write_flag )
1396 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
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));
1407 if ( result == RESULT_ENDOFFILE )
1413 if (ASDCP_SUCCESS(result) && !Options.no_write_flag)
1414 result = Writer.Finalize();
1422 //------------------------------------------------------------------------------------------
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
1428 write_PCM_file(CommandOptions& Options)
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;
1439 // set up essence parser
1440 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
1442 // set up MXF writer
1443 if ( ASDCP_SUCCESS(result) )
1445 ASDCP::PCM::AudioDescriptor ADesc;
1446 Parser.FillAudioDescriptor(ADesc);
1448 ADesc.EditRate = Options.edit_rate;
1449 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1451 if ( Options.verbose_flag )
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);
1462 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
1464 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
1466 if ( Options.mca_config.empty() )
1468 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1472 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1474 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1475 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
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 )
1483 MXF::SoundfieldGroupLabelSubDescriptor * desc = dynamic_cast<MXF::SoundfieldGroupLabelSubDescriptor*>(*i);
1486 if ( ! Options.mca_audio_content_kind.empty() )
1488 desc->MCAAudioContentKind = Options.mca_audio_content_kind;
1490 if ( ! Options.mca_audio_element_kind.empty() )
1492 desc->MCAAudioElementKind = Options.mca_audio_element_kind;
1497 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
1501 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1503 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1504 Info.LabelSetType = LS_MXF_SMPTE;
1506 if ( Options.asset_id_flag )
1507 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1509 Kumu::GenRandomUUID(Info.AssetUUID);
1511 // configure encryption
1512 if( Options.key_flag )
1514 Kumu::GenRandomUUID(Info.ContextID);
1515 Info.EncryptedEssence = true;
1517 if ( Options.key_id_flag )
1519 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1523 create_random_uuid(Info.CryptographicKeyID);
1526 Context = new AESEncContext;
1527 result = Context->InitKey(Options.key_value);
1529 if ( ASDCP_SUCCESS(result) )
1530 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1532 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1534 Info.UsesHMAC = true;
1535 HMAC = new HMACContext;
1536 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1540 if ( ASDCP_SUCCESS(result) )
1542 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
1543 Options.mca_config, Options.edit_rate);
1547 if ( ASDCP_SUCCESS(result) )
1549 result = Parser.Reset();
1550 ui32_t duration = 0;
1552 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1554 result = Parser.ReadFrame(FrameBuffer);
1556 if ( ASDCP_SUCCESS(result) )
1558 if ( Options.verbose_flag )
1559 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1561 if ( ! Options.no_write_flag )
1563 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
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));
1575 if ( result == RESULT_ENDOFFILE )
1579 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1580 result = Writer.Finalize();
1588 //------------------------------------------------------------------------------------------
1589 // TimedText essence
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
1596 write_timed_text_file(CommandOptions& Options)
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;
1607 // set up essence parser
1608 Result_t result = Parser.OpenRead(Options.filenames.front());
1610 // set up MXF writer
1611 if ( ASDCP_SUCCESS(result) )
1613 Parser.FillTimedTextDescriptor(TDesc);
1614 TDesc.EditRate = Options.edit_rate;
1615 TDesc.ContainerDuration = Options.duration;
1616 FrameBuffer.Capacity(Options.fb_size);
1618 if ( ! Options.profile_name.empty() )
1620 TDesc.NamespaceName = Options.profile_name;
1623 if ( Options.verbose_flag )
1625 fputs("IMF Timed-Text Descriptor:\n", stderr);
1626 TimedText::DescriptorDump(TDesc);
1630 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1632 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1633 Info.LabelSetType = LS_MXF_SMPTE;
1635 if ( Options.asset_id_flag )
1636 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1638 Kumu::GenRandomUUID(Info.AssetUUID);
1640 // configure encryption
1641 if( Options.key_flag )
1643 Kumu::GenRandomUUID(Info.ContextID);
1644 Info.EncryptedEssence = true;
1646 if ( Options.key_id_flag )
1648 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1652 create_random_uuid(Info.CryptographicKeyID);
1655 Context = new AESEncContext;
1656 result = Context->InitKey(Options.key_value);
1658 if ( ASDCP_SUCCESS(result) )
1659 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1661 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1663 Info.UsesHMAC = true;
1664 HMAC = new HMACContext;
1665 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1669 if ( ASDCP_SUCCESS(result) )
1670 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1673 if ( ASDCP_FAILURE(result) )
1677 TimedText::ResourceList_t::const_iterator ri;
1679 result = Parser.ReadTimedTextResource(XMLDoc);
1681 if ( ASDCP_SUCCESS(result) )
1682 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1684 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1686 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1688 if ( ASDCP_SUCCESS(result) )
1690 if ( Options.verbose_flag )
1691 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1693 if ( ! Options.no_write_flag )
1695 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
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));
1706 if ( result == RESULT_ENDOFFILE )
1710 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1711 result = Writer.Finalize();
1718 get_current_dms_text_descriptor(AS_02::ISXD::MXFWriter& writer, ASDCP::MXF::GenericStreamTextBasedSet *&text_object)
1720 std::list<MXF::InterchangeObject*> object_list;
1721 writer.OP1aHeader().GetMDObjectsByType(DefaultSMPTEDict().ul(MDD_GenericStreamTextBasedSet), object_list);
1723 if ( object_list.empty() )
1728 text_object = dynamic_cast<MXF::GenericStreamTextBasedSet*>(object_list.back());
1729 assert(text_object != 0);
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
1738 write_isxd_file(CommandOptions& Options)
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;
1748 // set up essence parser
1749 Result_t result = Parser.OpenRead(Options.filenames.front());
1751 // set up MXF writer
1752 if ( ASDCP_SUCCESS(result) )
1755 if ( Options.verbose_flag )
1757 fprintf(stderr, "ISXD Data\n");
1758 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1762 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
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);
1768 Kumu::GenRandomUUID(Info.AssetUUID);
1770 Info.LabelSetType = LS_MXF_SMPTE;
1772 // configure encryption
1773 if( Options.key_flag )
1775 Kumu::GenRandomUUID(Info.ContextID);
1776 Info.EncryptedEssence = true;
1778 if ( Options.key_id_flag )
1780 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1784 create_random_uuid(Info.CryptographicKeyID);
1787 Context = new AESEncContext;
1788 result = Context->InitKey(Options.key_value);
1790 if ( ASDCP_SUCCESS(result) )
1791 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1793 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1795 Info.UsesHMAC = true;
1796 HMAC = new HMACContext;
1797 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1801 if ( ASDCP_SUCCESS(result) )
1803 if ( Options.isxd_document_namespace == "auto" )
1805 // get ns of first item
1806 std::string ns_prefix, type_name, namespace_name;
1807 result = Parser.ReadFrame(FrameBuffer);
1809 if ( ASDCP_SUCCESS(result) )
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;
1816 if ( ASDCP_SUCCESS(result) && ! namespace_name.empty() )
1818 Options.isxd_document_namespace = namespace_name;
1822 fprintf(stderr, "Unable to parse an XML namespace name from the input document.\n");
1827 result = Writer.OpenWrite(Options.out_file, Info, Options.isxd_document_namespace, Options.edit_rate);
1831 if ( ASDCP_SUCCESS(result) )
1833 ui32_t duration = 0;
1834 result = Parser.Reset();
1836 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1838 result = Parser.ReadFrame(FrameBuffer);
1840 if ( ASDCP_SUCCESS(result) )
1842 if ( Options.verbose_flag )
1843 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1845 if ( Options.encrypt_header_flag )
1846 FrameBuffer.PlaintextOffset(0);
1849 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1851 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
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));
1862 if ( result == RESULT_ENDOFFILE )
1868 if ( KM_SUCCESS(result) && ! Options.no_write_flag )
1870 ASDCP::FrameBuffer global_metadata;
1871 std::list<std::string>::iterator i;
1873 for ( i = Options.global_isxd_metadata.begin(); i != Options.global_isxd_metadata.end(); ++i )
1875 ui32_t file_size = Kumu::FileSize(*i);
1876 result = global_metadata.Capacity(file_size);
1878 if ( KM_SUCCESS(result) )
1880 ui32_t read_count = 0;
1881 Kumu::FileReader Reader;
1882 std::string namespace_name;
1884 result = Reader.OpenRead(*i);
1886 if ( KM_SUCCESS(result) )
1888 result = Reader.Read(global_metadata.Data(), file_size, &read_count);
1891 if ( KM_SUCCESS(result) )
1893 if ( file_size != read_count)
1894 return RESULT_READFAIL;
1896 global_metadata.Size(read_count);
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;
1904 if ( KM_SUCCESS(result) )
1906 result = Writer.AddDmsGenericPartUtf8Text(global_metadata, Context, HMAC);
1909 if ( KM_SUCCESS(result) )
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;
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;
1925 if ( KM_SUCCESS(result) )
1927 result = Writer.Finalize();
1936 main(int argc, const char** argv)
1938 Result_t result = RESULT_OK;
1940 g_dict = &ASDCP::DefaultSMPTEDict();
1943 CommandOptions Options(argc, argv);
1945 if ( Options.version_flag )
1948 if ( Options.help_flag )
1951 if ( Options.show_ul_values_flag )
1953 g_dict->Dump(stdout);
1956 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1959 if ( Options.error_flag )
1961 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1965 EssenceType_t EssenceType;
1966 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1968 if ( ASDCP_SUCCESS(result) )
1970 switch ( EssenceType )
1973 result = write_JP2K_file(Options);
1976 case ::ESS_AS02_ACES:
1977 result = write_ACES_file(Options);
1979 case ESS_PCM_24b_48k:
1980 case ESS_PCM_24b_96k:
1981 result = write_PCM_file(Options);
1984 case ESS_TIMED_TEXT:
1985 result = write_timed_text_file(Options);
1988 case ESS_DCDATA_UNKNOWN:
1989 if ( ! Options.isxd_document_namespace.empty() )
1991 result = write_isxd_file(Options);
1995 fprintf(stderr, "%s: Unknown synchronous data file type, not AS-02-compatible essence.\n",
1996 Options.filenames.front().c_str());
2002 fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
2003 Options.filenames.front().c_str());
2008 if ( ASDCP_FAILURE(result) )
2010 fputs("Program stopped on error.\n", stderr);
2012 if ( result != RESULT_FAIL )
2014 fputs(result, stderr);
2015 fputc('\n', stderr);
2026 // end as-02-wrap.cpp