2 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
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>
42 #include <PCMParserList.h>
45 using namespace ASDCP;
47 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
48 const ASDCP::Dictionary *g_dict = 0;
51 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
53 snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
58 //------------------------------------------------------------------------------------------
60 // command line option parser class
62 static const char* PROGRAM_NAME = "as-02-wrap"; // program name for messages
64 // local program identification info written to file headers
65 class MyInfo : public WriterInfo
70 static byte_t default_ProductUUID_Data[UUIDlen] =
71 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
72 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
74 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
75 CompanyName = "WidgetCo";
76 ProductName = "as-02-wrap";
77 ProductVersion = ASDCP::Version();
83 // Increment the iterator, test for an additional non-option command line argument.
84 // Causes the caller to return if there are no remaining arguments or if the next
85 // argument begins with '-'.
86 #define TEST_EXTRA_ARG(i,c) \
87 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
88 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
95 create_random_uuid(byte_t* uuidbuf)
98 GenRandomValue(tmp_id);
99 memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
104 banner(FILE* stream = stdout)
107 %s (asdcplib %s)\n\n\
108 Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
109 asdcplib may be copied only under the terms of the license found at\n\
110 the top of every file in the asdcplib distribution kit.\n\n\
111 Specify the -h (help) option for further information about %s\n\n",
112 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
117 usage(FILE* stream = stdout)
120 USAGE: %s [-h|-help] [-V]\n\
122 %s [options] <input-file>+ <output-file>\n\n",
123 PROGRAM_NAME, PROGRAM_NAME);
127 -h | -help - Show help\n\
128 -V - Show version information\n\
129 -a <uuid> - Specify the Asset ID of the file\n\
130 -A <w>/<h> - Set aspect ratio for image (default 4/3)\n\
131 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
132 Defaults to 4,194,304 (4MB)\n\
133 -c <num> - Select the IMF color system to be signaled:\n\
134 Application 2 (2067-20): 1, 2, or 3\n\
135 Application 2e (2067-21): 4 or 5\n\
136 All color system values assume YCbCr; also use -R for RGB\n\
137 -C <ul> - Set ChannelAssignment UL value\n\
138 -d <duration> - Number of frames to process, default all\n\
139 -D <depth> - Component depth for YCbCr images (default: 10)\n\
140 -e - Encrypt JP2K headers (default)\n\
141 -E - Do not encrypt JP2K headers\n\
142 -F (0|1) - Set field dominance for interlaced image (default: 0)\n\
143 -g <rfc-5646-code>\n\
144 - Create MCA labels having the given RFC 5646 language code\n\
145 (requires option \"-m\")\n\
146 -i - Indicates input essence is interlaced fields (forces -Y)\n\
147 -j <key-id-str> - Write key ID instead of creating a random value\n\
148 -k <key-string> - Use key for ciphertext operations\n\
149 -l <first>,<second>\n\
150 - Integer values that set the VideoLineMap when creating\n\
151 interlaced YCbCr files\n\
152 -m <expr> - Write MCA labels using <expr>. Example:\n\
153 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
154 -M - Do not create HMAC values when writing\n\
155 -n <UL> - Set the TransferCharacteristic UL\n\
156 -o <min>,<max> - Mastering Display luminance, cd*m*m, e.g., \".05,100\"\n\
157 -O <rx>,<ry>,<gx>,<gy>,<bx>,<by>,<wx>,<wy>\n\
158 - Mastering Display Color Primaries and white point\n\
159 e.g., \".64,.33,.3,.6,.15,.06,.3457,.3585\"\n\
160 -p <ul> - Set broadcast profile\n\
161 -P <string> - Set NamespaceURI property when creating timed text MXF\n\
162 -q <UL> - Set the CodingEquations UL\n\
163 -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
164 -R - Indicates RGB image essence (default except with -c)\n\
165 -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
166 -t <min> - Set RGB component minimum code value (default: 0)\n\
167 -T <max> - Set RGB component maximum code value (default: 1023)\n\
168 -u - Print UL catalog to stdout\n\
169 -v - Verbose, prints informative messages to stderr\n\
170 -W - Read input file only, do not write source file\n\
171 -x <int> - Horizontal subsampling degree (default: 2)\n\
172 -X <int> - Vertical subsampling degree (default: 2)\n\
173 -y <white-ref>[,<black-ref>[,<color-range>]]\n\
174 - Same as -Y but White Ref, Black Ref and Color Range are\n\
175 set from the given argument\n\
176 -Y - Indicates YCbCr image essence (default with -c), uses\n\
177 default values for White Ref, Black Ref and Color Range,\n\
178 940,64,897, indicating 10 bit standard Video Range\n\
179 -z - Fail if j2c inputs have unequal parameters (default)\n\
180 -Z - Ignore unequal parameters in j2c inputs\n\
182 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
183 o All option arguments must be separated from the option by whitespace.\n\n");
186 const float chromaticity_scale = 50000.0;
189 set_primary_from_token(const std::string& token, ui16_t& primary)
191 float raw_value = strtod(token.c_str(),0);
193 if ( raw_value == 0.0 || raw_value > 1.0 )
195 fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str());
199 primary = floor(0.5 + ( raw_value * chromaticity_scale ));
203 const float luminance_scale = 10000.0;
206 set_luminance_from_token(const std::string& token, ui32_t& luminance)
208 float raw_value = strtod(token.c_str(),0);
210 if ( raw_value == 0.0 || raw_value > 400000.0 )
212 fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str());
216 luminance = floor(0.5 + ( raw_value * luminance_scale ));
220 #define SET_LUMINANCE(p,t) \
221 if ( ! set_luminance_from_token(t, p) ) { \
231 bool error_flag; // true if the given options are in error or not complete
232 bool key_flag; // true if an encryption key was given
233 bool asset_id_flag; // true if an asset ID was given
234 bool encrypt_header_flag; // true if j2c headers are to be encrypted
235 bool write_hmac; // true if HMAC values are to be generated and written
236 bool verbose_flag; // true if the verbose option was selected
237 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
238 bool no_write_flag; // true if no output files are to be written
239 bool version_flag; // true if the version display option was selected
240 bool help_flag; // true if the help display option was selected
241 ui32_t duration; // number of frames to be processed
242 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
243 bool use_cdci_descriptor; //
244 Rational edit_rate; // edit rate of JP2K sequence
245 ui32_t fb_size; // size of picture frame buffer
246 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
247 bool key_id_flag; // true if a key ID was given
248 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
249 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
250 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
251 Kumu::PathList_t filenames; // list of filenames to be processed
253 UL channel_assignment, picture_coding, transfer_characteristic, color_primaries, coding_equations;
254 ASDCP::MXF::AS02_MCAConfigParser mca_config;
255 std::string mca_language;
260 ui32_t horizontal_subsampling;
261 ui32_t vertical_subsampling;
262 ui32_t component_depth;
264 ASDCP::Rational aspect_ratio;
265 ui8_t field_dominance;
266 ui32_t mxf_header_size;
267 ui32_t cdci_BlackRefLevel;
268 ui32_t cdci_WhiteRefLevel;
269 ui32_t cdci_ColorRange;
271 ui32_t md_min_luminance, md_max_luminance;
272 ASDCP::MXF::ThreeColorPrimaries md_primaries;
273 ASDCP::MXF::ColorPrimary md_white_point;
275 //new attributes for AS-02 support
276 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
277 ui32_t partition_space; //Shim parameter partition_spacing
280 MXF::LineMapPair line_map;
281 std::string out_file, profile_name; //
284 bool set_video_line_map(const std::string& arg)
286 const char* sep_str = strrchr(arg.c_str(), ',');
290 fprintf(stderr, "Expecting <first>,<second>\n");
294 line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
295 line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
300 bool set_video_ref(const std::string& arg)
302 std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
304 switch ( ref_tokens.size() )
307 cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
308 ref_tokens.pop_back();
310 cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
311 ref_tokens.pop_back();
313 cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
317 fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
321 if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
323 fprintf(stderr, "Unexpected CDCI video referece levels.\n");
331 bool set_display_primaries(const std::string& arg)
333 std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ",");
334 if ( coordinate_tokens.size() != 8 )
336 fprintf(stderr, "Expecting four coordinate pairs.\n");
340 std::list<std::string>::const_iterator i = coordinate_tokens.begin();
341 if ( ! set_primary_from_token(*(i++), md_primaries.First.X) ) return false;
342 if ( ! set_primary_from_token(*(i++), md_primaries.First.Y) ) return false;
343 if ( ! set_primary_from_token(*(i++), md_primaries.Second.X) ) return false;
344 if ( ! set_primary_from_token(*(i++), md_primaries.Second.Y) ) return false;
345 if ( ! set_primary_from_token(*(i++), md_primaries.Third.X) ) return false;
346 if ( ! set_primary_from_token(*(i++), md_primaries.Third.Y) ) return false;
347 if ( ! set_primary_from_token(*(i++), md_white_point.X) ) return false;
348 if ( ! set_primary_from_token(*i, md_white_point.Y) ) return false;
354 bool set_display_luminance(const std::string& arg)
356 std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ",");
357 if ( luminance_tokens.size() != 2 )
359 fprintf(stderr, "Expecting a luminance pair.\n");
363 if ( ! set_luminance_from_token(luminance_tokens.front(), md_min_luminance) ) return false;
364 if ( ! set_luminance_from_token(luminance_tokens.back(), md_max_luminance) ) return false;
370 bool set_color_system_from_arg(const char* arg)
376 // Application 2 (ST 2067-20)
378 coding_equations = g_dict->ul(MDD_CodingEquations_601);
379 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
380 color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU470_PAL);
381 use_cdci_descriptor = true;
385 coding_equations = g_dict->ul(MDD_CodingEquations_601);
386 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
387 color_primaries = g_dict->ul(MDD_ColorPrimaries_SMPTE170M);
388 use_cdci_descriptor = true;
392 coding_equations = g_dict->ul(MDD_CodingEquations_709);
393 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
394 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
395 use_cdci_descriptor = true;
398 // Application 2e (ST 2067-21)
400 coding_equations = g_dict->ul(MDD_CodingEquations_709);
401 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_xvYCC);
402 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
403 use_cdci_descriptor = true;
407 coding_equations = g_dict->ul(MDD_CodingEquations_709);
408 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_2020);
409 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT2020);
410 use_cdci_descriptor = true;
414 fprintf(stderr, "Unrecognized color system number, expecting one of 1-5.\n");
421 CommandOptions(int argc, const char** argv) :
422 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
423 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
424 no_write_flag(false), version_flag(false), help_flag(false),
425 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false),
426 edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
427 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
428 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
429 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
430 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
431 mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
432 md_min_luminance(0), md_max_luminance(0), line_map(0,0)
434 memset(key_value, 0, KeyLen);
435 memset(key_id_value, 0, UUIDlen);
437 coding_equations = g_dict->ul(MDD_CodingEquations_709);
438 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
439 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
440 std::string mca_config_str;
442 for ( int i = 1; i < argc; i++ )
445 if ( (strcmp( argv[i], "-help") == 0) )
451 if ( argv[i][0] == '-'
452 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
455 switch ( argv[i][1] )
458 TEST_EXTRA_ARG(i, 'A');
459 if ( ! DecodeRational(argv[i], aspect_ratio) )
461 fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
467 asset_id_flag = true;
468 TEST_EXTRA_ARG(i, 'a');
471 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
473 if ( length != UUIDlen )
475 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
482 TEST_EXTRA_ARG(i, 'b');
483 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
486 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
491 TEST_EXTRA_ARG(i, 'c');
492 if ( ! set_color_system_from_arg(argv[i]) )
499 TEST_EXTRA_ARG(i, 'C');
500 if ( ! channel_assignment.DecodeHex(argv[i]) )
502 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
508 TEST_EXTRA_ARG(i, 'D');
509 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
513 TEST_EXTRA_ARG(i, 'd');
514 duration = Kumu::xabs(strtol(argv[i], 0, 10));
517 case 'E': encrypt_header_flag = false; break;
518 case 'e': encrypt_header_flag = true; break;
521 TEST_EXTRA_ARG(i, 'F');
522 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
523 if ( field_dominance > 1 )
525 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
531 TEST_EXTRA_ARG(i, 'g');
532 mca_language = argv[i];
535 case 'h': help_flag = true; break;
539 use_cdci_descriptor = true;
544 TEST_EXTRA_ARG(i, 'j');
547 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
549 if ( length != UUIDlen )
551 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
557 case 'k': key_flag = true;
558 TEST_EXTRA_ARG(i, 'k');
561 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
563 if ( length != KeyLen )
565 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
572 TEST_EXTRA_ARG(i, 'y');
573 if ( ! set_video_line_map(argv[i]) )
579 case 'M': write_hmac = false; break;
582 TEST_EXTRA_ARG(i, 'm');
583 mca_config_str = argv[i];
587 TEST_EXTRA_ARG(i, 'n');
588 if ( ! transfer_characteristic.DecodeHex(argv[i]) )
590 fprintf(stderr, "Error decoding TransferCharacteristic UL value: %s\n", argv[i]);
596 TEST_EXTRA_ARG(i, 'O');
597 if ( ! set_display_primaries(argv[i]) )
604 TEST_EXTRA_ARG(i, 'o');
605 if ( ! set_display_luminance(argv[i]) )
612 TEST_EXTRA_ARG(i, 'P');
613 profile_name = argv[i];
617 TEST_EXTRA_ARG(i, 'p');
618 if ( ! picture_coding.DecodeHex(argv[i]) )
620 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
626 TEST_EXTRA_ARG(i, 'q');
627 if ( ! coding_equations.DecodeHex(argv[i]) )
629 fprintf(stderr, "Error decoding CodingEquations UL value: %s\n", argv[i]);
635 TEST_EXTRA_ARG(i, 'r');
636 if ( ! DecodeRational(argv[i], edit_rate) )
638 fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
645 use_cdci_descriptor = false;
649 TEST_EXTRA_ARG(i, 's');
650 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
654 TEST_EXTRA_ARG(i, 't');
655 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
659 TEST_EXTRA_ARG(i, 'T');
660 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
663 case 'u': show_ul_values_flag = true; break;
665 case 'V': version_flag = true; break;
666 case 'v': verbose_flag = true; break;
667 case 'W': no_write_flag = true; break;
670 TEST_EXTRA_ARG(i, 'x');
671 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
675 TEST_EXTRA_ARG(i, 'X');
676 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
680 use_cdci_descriptor = true;
681 // default 10 bit video range YUV, ref levels already set
685 // Use values provided as argument, sharp tool, be careful
686 use_cdci_descriptor = true;
687 TEST_EXTRA_ARG(i, 'y');
688 if ( ! set_video_ref(argv[i]) )
694 case 'Z': j2c_pedantic = false; break;
695 case 'z': j2c_pedantic = true; break;
698 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
705 if ( argv[i][0] != '-' )
707 filenames.push_back(argv[i]);
711 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
717 if ( ! mca_config_str.empty() )
719 if ( mca_language.empty() )
721 if ( ! mca_config.DecodeString(mca_config_str) )
728 if ( ! mca_config.DecodeString(mca_config_str, mca_language) )
735 if ( help_flag || version_flag || show_ul_values_flag )
740 if ( filenames.size() < 2 )
742 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
746 out_file = filenames.back();
747 filenames.pop_back();
749 if ( ! picture_coding.HasValue() )
751 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
759 //------------------------------------------------------------------------------------------
763 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
764 const ASDCP::Dictionary& dict,
765 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
766 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
768 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
771 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
772 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
775 write_JP2K_file(CommandOptions& Options)
777 AESEncContext* Context = 0;
778 HMACContext* HMAC = 0;
779 AS_02::JP2K::MXFWriter Writer;
780 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
781 JP2K::SequenceParser Parser;
782 byte_t IV_buf[CBC_BLOCK_SIZE];
783 Kumu::FortunaRNG RNG;
784 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
785 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
787 // set up essence parser
788 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
791 if ( ASDCP_SUCCESS(result) )
793 ASDCP::JP2K::PictureDescriptor PDesc;
794 Parser.FillPictureDescriptor(PDesc);
795 PDesc.EditRate = Options.edit_rate;
797 if ( Options.verbose_flag )
799 fprintf(stderr, "JPEG 2000 pictures\n");
800 fputs("PictureDescriptor:\n", stderr);
801 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
802 JP2K::PictureDescriptorDump(PDesc);
805 if ( Options.use_cdci_descriptor )
807 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
808 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
810 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
811 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
812 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
814 if ( ASDCP_SUCCESS(result) )
816 tmp_dscr->CodingEquations = Options.coding_equations;
817 tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
818 tmp_dscr->ColorPrimaries = Options.color_primaries;
819 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
820 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
821 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
822 tmp_dscr->ComponentDepth = Options.component_depth;
823 tmp_dscr->FrameLayout = Options.frame_layout;
824 tmp_dscr->AspectRatio = Options.aspect_ratio;
825 tmp_dscr->FieldDominance = Options.field_dominance;
826 tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
827 tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
828 tmp_dscr->ColorRange = Options.cdci_ColorRange;
829 tmp_dscr->VideoLineMap = Options.line_map;
831 if ( Options.md_min_luminance || Options.md_max_luminance )
833 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
834 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
837 if ( Options.md_primaries.HasValue() )
839 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
840 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
843 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
848 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
849 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
851 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
852 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
853 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
855 if ( ASDCP_SUCCESS(result) )
857 tmp_dscr->CodingEquations = Options.coding_equations;
858 tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
859 tmp_dscr->ColorPrimaries = Options.color_primaries;
860 tmp_dscr->ScanningDirection = 0;
861 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
862 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
863 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
865 if ( Options.md_min_luminance || Options.md_max_luminance )
867 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
868 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
871 if ( Options.md_primaries.HasValue() )
873 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
874 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
877 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
882 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
884 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
885 Info.LabelSetType = LS_MXF_SMPTE;
887 if ( Options.asset_id_flag )
888 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
890 Kumu::GenRandomUUID(Info.AssetUUID);
892 // configure encryption
893 if( Options.key_flag )
895 Kumu::GenRandomUUID(Info.ContextID);
896 Info.EncryptedEssence = true;
898 if ( Options.key_id_flag )
900 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
904 create_random_uuid(Info.CryptographicKeyID);
907 Context = new AESEncContext;
908 result = Context->InitKey(Options.key_value);
910 if ( ASDCP_SUCCESS(result) )
911 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
913 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
915 Info.UsesHMAC = true;
916 HMAC = new HMACContext;
917 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
921 if ( ASDCP_SUCCESS(result) )
923 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
924 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
928 if ( ASDCP_SUCCESS(result) )
931 result = Parser.Reset();
933 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
935 result = Parser.ReadFrame(FrameBuffer);
937 if ( ASDCP_SUCCESS(result) )
939 if ( Options.verbose_flag )
940 FrameBuffer.Dump(stderr, Options.fb_dump_size);
942 if ( Options.encrypt_header_flag )
943 FrameBuffer.PlaintextOffset(0);
946 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
948 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
950 // The Writer class will forward the last block of ciphertext
951 // to the encryption context for use as the IV for the next
952 // frame. If you want to use non-sequitur IV values, un-comment
953 // the following line of code.
954 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
955 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
959 if ( result == RESULT_ENDOFFILE )
963 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
964 result = Writer.Finalize();
969 //------------------------------------------------------------------------------------------
973 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
974 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
977 write_PCM_file(CommandOptions& Options)
979 AESEncContext* Context = 0;
980 HMACContext* HMAC = 0;
981 PCMParserList Parser;
982 AS_02::PCM::MXFWriter Writer;
983 PCM::FrameBuffer FrameBuffer;
984 byte_t IV_buf[CBC_BLOCK_SIZE];
985 Kumu::FortunaRNG RNG;
986 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
988 // set up essence parser
989 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
992 if ( ASDCP_SUCCESS(result) )
994 ASDCP::PCM::AudioDescriptor ADesc;
995 Parser.FillAudioDescriptor(ADesc);
997 ADesc.EditRate = Options.edit_rate;
998 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1000 if ( Options.verbose_flag )
1003 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1004 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1005 RationalToString(Options.edit_rate, buf, 64),
1006 PCM::CalcSamplesPerFrame(ADesc));
1007 fputs("AudioDescriptor:\n", stderr);
1008 PCM::AudioDescriptorDump(ADesc);
1011 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
1013 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
1015 if ( Options.mca_config.empty() )
1017 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1021 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1023 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1024 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1028 // this is the d-cinema MCA label, what is the one for IMF?
1029 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
1033 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1035 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1036 Info.LabelSetType = LS_MXF_SMPTE;
1038 if ( Options.asset_id_flag )
1039 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1041 Kumu::GenRandomUUID(Info.AssetUUID);
1043 // configure encryption
1044 if( Options.key_flag )
1046 Kumu::GenRandomUUID(Info.ContextID);
1047 Info.EncryptedEssence = true;
1049 if ( Options.key_id_flag )
1051 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1055 create_random_uuid(Info.CryptographicKeyID);
1058 Context = new AESEncContext;
1059 result = Context->InitKey(Options.key_value);
1061 if ( ASDCP_SUCCESS(result) )
1062 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1064 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1066 Info.UsesHMAC = true;
1067 HMAC = new HMACContext;
1068 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1072 if ( ASDCP_SUCCESS(result) )
1074 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
1075 Options.mca_config, Options.edit_rate);
1079 if ( ASDCP_SUCCESS(result) )
1081 result = Parser.Reset();
1082 ui32_t duration = 0;
1084 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1086 result = Parser.ReadFrame(FrameBuffer);
1088 if ( ASDCP_SUCCESS(result) )
1090 if ( Options.verbose_flag )
1091 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1093 if ( ! Options.no_write_flag )
1095 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1097 // The Writer class will forward the last block of ciphertext
1098 // to the encryption context for use as the IV for the next
1099 // frame. If you want to use non-sequitur IV values, un-comment
1100 // the following line of code.
1101 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1102 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1107 if ( result == RESULT_ENDOFFILE )
1111 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1112 result = Writer.Finalize();
1120 //------------------------------------------------------------------------------------------
1121 // TimedText essence
1124 // Write one or more plaintext timed text streams to a plaintext AS-02 file
1125 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
1128 write_timed_text_file(CommandOptions& Options)
1130 AESEncContext* Context = 0;
1131 HMACContext* HMAC = 0;
1132 AS_02::TimedText::ST2052_TextParser Parser;
1133 AS_02::TimedText::MXFWriter Writer;
1134 TimedText::FrameBuffer FrameBuffer;
1135 TimedText::TimedTextDescriptor TDesc;
1136 byte_t IV_buf[CBC_BLOCK_SIZE];
1137 Kumu::FortunaRNG RNG;
1139 // set up essence parser
1140 Result_t result = Parser.OpenRead(Options.filenames.front());
1142 // set up MXF writer
1143 if ( ASDCP_SUCCESS(result) )
1145 Parser.FillTimedTextDescriptor(TDesc);
1146 TDesc.EditRate = Options.edit_rate;
1147 TDesc.ContainerDuration = Options.duration;
1148 FrameBuffer.Capacity(Options.fb_size);
1150 if ( ! Options.profile_name.empty() )
1152 TDesc.NamespaceName = Options.profile_name;
1155 if ( Options.verbose_flag )
1157 fputs("IMF Timed-Text Descriptor:\n", stderr);
1158 TimedText::DescriptorDump(TDesc);
1162 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1164 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1165 Info.LabelSetType = LS_MXF_SMPTE;
1167 if ( Options.asset_id_flag )
1168 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1170 Kumu::GenRandomUUID(Info.AssetUUID);
1172 // configure encryption
1173 if( Options.key_flag )
1175 Kumu::GenRandomUUID(Info.ContextID);
1176 Info.EncryptedEssence = true;
1178 if ( Options.key_id_flag )
1180 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1184 create_random_uuid(Info.CryptographicKeyID);
1187 Context = new AESEncContext;
1188 result = Context->InitKey(Options.key_value);
1190 if ( ASDCP_SUCCESS(result) )
1191 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1193 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1195 Info.UsesHMAC = true;
1196 HMAC = new HMACContext;
1197 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1201 if ( ASDCP_SUCCESS(result) )
1202 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1205 if ( ASDCP_FAILURE(result) )
1209 TimedText::ResourceList_t::const_iterator ri;
1211 result = Parser.ReadTimedTextResource(XMLDoc);
1213 if ( ASDCP_SUCCESS(result) )
1214 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1216 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1218 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1220 if ( ASDCP_SUCCESS(result) )
1222 if ( Options.verbose_flag )
1223 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1225 if ( ! Options.no_write_flag )
1227 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1229 // The Writer class will forward the last block of ciphertext
1230 // to the encryption context for use as the IV for the next
1231 // frame. If you want to use non-sequitur IV values, un-comment
1232 // the following line of code.
1233 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1234 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1238 if ( result == RESULT_ENDOFFILE )
1242 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1243 result = Writer.Finalize();
1251 main(int argc, const char** argv)
1253 Result_t result = RESULT_OK;
1255 g_dict = &ASDCP::DefaultSMPTEDict();
1258 CommandOptions Options(argc, argv);
1260 if ( Options.version_flag )
1263 if ( Options.help_flag )
1266 if ( Options.show_ul_values_flag )
1268 g_dict->Dump(stdout);
1271 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1274 if ( Options.error_flag )
1276 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1280 EssenceType_t EssenceType;
1281 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1283 if ( ASDCP_SUCCESS(result) )
1285 switch ( EssenceType )
1288 result = write_JP2K_file(Options);
1291 case ESS_PCM_24b_48k:
1292 case ESS_PCM_24b_96k:
1293 result = write_PCM_file(Options);
1296 case ESS_TIMED_TEXT:
1297 result = write_timed_text_file(Options);
1301 fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
1302 Options.filenames.front().c_str());
1307 if ( ASDCP_FAILURE(result) )
1309 fputs("Program stopped on error.\n", stderr);
1311 if ( result != RESULT_FAIL )
1313 fputs(result, stderr);
1314 fputc('\n', stderr);
1325 // end as-02-wrap.cpp