2 Copyright (c) 2011-2017, 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-2017, 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 -i - Indicates input essence is interlaced fields (forces -Y)\n\
144 -j <key-id-str> - Write key ID instead of creating a random value\n\
145 -k <key-string> - Use key for ciphertext operations\n\
146 -l <first>,<second>\n\
147 - Integer values that set the VideoLineMap when creating\n\
148 interlaced YCbCr files\n\
149 -M - Do not create HMAC values when writing\n\
150 -m <expr> - Write MCA labels using <expr>. Example:\n\
151 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
152 -n <UL> - Set the TransferCharacteristic UL\n\
153 -o <min>,<max> - Mastering Display luminance, cd*m*m, e.g., \".05,100\"\n\
154 -O <rx>,<ry>,<gx>,<gy>,<bx>,<by>,<wx>,<wy>\n\
155 - Mastering Display Color Primaries and white point\n\
156 e.g., \".64,.33,.3,.6,.15,.06,.3457,.3585\"\n\
157 -P <string> - Set NamespaceURI property when creating timed text MXF\n\
158 -p <ul> - Set broadcast profile\n\
159 -q <UL> - Set the CodingEquations UL\n\
160 -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
161 -R - Indicates RGB image essence (default except with -c)\n\
162 -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
163 -t <min> - Set RGB component minimum code value (default: 0)\n\
164 -T <max> - Set RGB component maximum code value (default: 1023)\n\
165 -u - Print UL catalog to stdout\n\
166 -v - Verbose, prints informative messages to stderr\n\
167 -W - Read input file only, do not write source file\n\
168 -x <int> - Horizontal subsampling degree (default: 2)\n\
169 -X <int> - Vertical subsampling degree (default: 2)\n\
170 -Y - Indicates YCbCr image essence (default with -c), uses\n\
171 default values for White Ref, Black Ref and Color Range,\n\
172 940,64,897, indicating 10 bit standard Video Range\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 -z - Fail if j2c inputs have unequal parameters (default)\n\
177 -Z - Ignore unequal parameters in j2c inputs\n\
179 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
180 o All option arguments must be separated from the option by whitespace.\n\n");
183 const float chromaticity_scale = 50000.0;
186 set_primary_from_token(const std::string& token, ui16_t& primary)
188 float raw_value = strtod(token.c_str(),0);
190 if ( raw_value == 0.0 || raw_value > 1.0 )
192 fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str());
196 primary = floor(0.5 + ( raw_value * chromaticity_scale ));
200 const float luminance_scale = 10000.0;
203 set_luminance_from_token(const std::string& token, ui32_t& luminance)
205 float raw_value = strtod(token.c_str(),0);
207 if ( raw_value == 0.0 || raw_value > 400000.0 )
209 fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str());
213 luminance = floor(0.5 + ( raw_value * luminance_scale ));
217 #define SET_LUMINANCE(p,t) \
218 if ( ! set_luminance_from_token(t, p) ) { \
228 bool error_flag; // true if the given options are in error or not complete
229 bool key_flag; // true if an encryption key was given
230 bool asset_id_flag; // true if an asset ID was given
231 bool encrypt_header_flag; // true if j2c headers are to be encrypted
232 bool write_hmac; // true if HMAC values are to be generated and written
233 bool verbose_flag; // true if the verbose option was selected
234 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
235 bool no_write_flag; // true if no output files are to be written
236 bool version_flag; // true if the version display option was selected
237 bool help_flag; // true if the help display option was selected
238 ui32_t duration; // number of frames to be processed
239 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
240 bool use_cdci_descriptor; //
241 Rational edit_rate; // edit rate of JP2K sequence
242 ui32_t fb_size; // size of picture frame buffer
243 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
244 bool key_id_flag; // true if a key ID was given
245 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
246 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
247 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
248 Kumu::PathList_t filenames; // list of filenames to be processed
250 UL channel_assignment, picture_coding, transfer_characteristic, color_primaries, coding_equations;
251 ASDCP::MXF::AS02_MCAConfigParser mca_config;
256 ui32_t horizontal_subsampling;
257 ui32_t vertical_subsampling;
258 ui32_t component_depth;
260 ASDCP::Rational aspect_ratio;
261 ui8_t field_dominance;
262 ui32_t mxf_header_size;
263 ui32_t cdci_BlackRefLevel;
264 ui32_t cdci_WhiteRefLevel;
265 ui32_t cdci_ColorRange;
267 ui32_t md_min_luminance, md_max_luminance;
268 ASDCP::MXF::ThreeColorPrimaries md_primaries;
269 ASDCP::MXF::ColorPrimary md_white_point;
271 //new attributes for AS-02 support
272 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
273 ui32_t partition_space; //Shim parameter partition_spacing
276 MXF::LineMapPair line_map;
277 std::string out_file, profile_name; //
280 bool set_video_line_map(const std::string& arg)
282 const char* sep_str = strrchr(arg.c_str(), ',');
286 fprintf(stderr, "Expecting <first>,<second>\n");
290 line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
291 line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
296 bool set_video_ref(const std::string& arg)
298 std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
300 switch ( ref_tokens.size() )
303 cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
304 ref_tokens.pop_back();
306 cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
307 ref_tokens.pop_back();
309 cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
313 fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
317 if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
319 fprintf(stderr, "Unexpected CDCI video referece levels.\n");
327 bool set_display_primaries(const std::string& arg)
329 std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ",");
330 if ( coordinate_tokens.size() != 8 )
332 fprintf(stderr, "Expecting four coordinate pairs.\n");
336 std::list<std::string>::const_iterator i = coordinate_tokens.begin();
337 if ( ! set_primary_from_token(*(i++), md_primaries.First.X) ) return false;
338 if ( ! set_primary_from_token(*(i++), md_primaries.First.Y) ) return false;
339 if ( ! set_primary_from_token(*(i++), md_primaries.Second.X) ) return false;
340 if ( ! set_primary_from_token(*(i++), md_primaries.Second.Y) ) return false;
341 if ( ! set_primary_from_token(*(i++), md_primaries.Third.X) ) return false;
342 if ( ! set_primary_from_token(*(i++), md_primaries.Third.Y) ) return false;
343 if ( ! set_primary_from_token(*(i++), md_white_point.X) ) return false;
344 if ( ! set_primary_from_token(*i, md_white_point.Y) ) return false;
350 bool set_display_luminance(const std::string& arg)
352 std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ",");
353 if ( luminance_tokens.size() != 2 )
355 fprintf(stderr, "Expecting a luminance pair.\n");
359 if ( ! set_luminance_from_token(luminance_tokens.front(), md_min_luminance) ) return false;
360 if ( ! set_luminance_from_token(luminance_tokens.back(), md_max_luminance) ) return false;
366 bool set_color_system_from_arg(const char* arg)
372 // Application 2 (ST 2067-20)
374 coding_equations = g_dict->ul(MDD_CodingEquations_601);
375 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
376 color_primaries = g_dict->ul(MDD_ColorPrimaries_ITU470_PAL);
377 use_cdci_descriptor = true;
381 coding_equations = g_dict->ul(MDD_CodingEquations_601);
382 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
383 color_primaries = g_dict->ul(MDD_ColorPrimaries_SMPTE170M);
384 use_cdci_descriptor = true;
388 coding_equations = g_dict->ul(MDD_CodingEquations_709);
389 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
390 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
391 use_cdci_descriptor = true;
394 // Application 2e (ST 2067-21)
396 coding_equations = g_dict->ul(MDD_CodingEquations_709);
397 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_xvYCC);
398 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
399 use_cdci_descriptor = true;
403 coding_equations = g_dict->ul(MDD_CodingEquations_709);
404 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_2020);
405 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT2020);
406 use_cdci_descriptor = true;
410 fprintf(stderr, "Unrecognized color system number, expecting one of 1-5.\n");
417 CommandOptions(int argc, const char** argv) :
418 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
419 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
420 no_write_flag(false), version_flag(false), help_flag(false),
421 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false),
422 edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
423 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
424 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
425 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
426 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
427 mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
428 md_min_luminance(0), md_max_luminance(0), line_map(0,0)
430 memset(key_value, 0, KeyLen);
431 memset(key_id_value, 0, UUIDlen);
433 coding_equations = g_dict->ul(MDD_CodingEquations_709);
434 color_primaries = g_dict->ul(MDD_ColorPrimaries_BT709);
435 transfer_characteristic = g_dict->ul(MDD_TransferCharacteristics_709);
437 for ( int i = 1; i < argc; i++ )
440 if ( (strcmp( argv[i], "-help") == 0) )
446 if ( argv[i][0] == '-'
447 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
450 switch ( argv[i][1] )
453 TEST_EXTRA_ARG(i, 'A');
454 if ( ! DecodeRational(argv[i], aspect_ratio) )
456 fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
462 asset_id_flag = true;
463 TEST_EXTRA_ARG(i, 'a');
466 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
468 if ( length != UUIDlen )
470 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
477 TEST_EXTRA_ARG(i, 'b');
478 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
481 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
486 TEST_EXTRA_ARG(i, 'c');
487 if ( ! set_color_system_from_arg(argv[i]) )
494 TEST_EXTRA_ARG(i, 'C');
495 if ( ! channel_assignment.DecodeHex(argv[i]) )
497 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
503 TEST_EXTRA_ARG(i, 'D');
504 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
508 TEST_EXTRA_ARG(i, 'd');
509 duration = Kumu::xabs(strtol(argv[i], 0, 10));
512 case 'E': encrypt_header_flag = false; break;
513 case 'e': encrypt_header_flag = true; break;
516 TEST_EXTRA_ARG(i, 'F');
517 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
518 if ( field_dominance > 1 )
520 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
525 case 'h': help_flag = true; break;
529 use_cdci_descriptor = true;
534 TEST_EXTRA_ARG(i, 'j');
537 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
539 if ( length != UUIDlen )
541 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
547 case 'k': key_flag = true;
548 TEST_EXTRA_ARG(i, 'k');
551 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
553 if ( length != KeyLen )
555 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
562 TEST_EXTRA_ARG(i, 'y');
563 if ( ! set_video_line_map(argv[i]) )
569 case 'M': write_hmac = false; break;
572 TEST_EXTRA_ARG(i, 'm');
573 if ( ! mca_config.DecodeString(argv[i]) )
580 TEST_EXTRA_ARG(i, 'n');
581 if ( ! transfer_characteristic.DecodeHex(argv[i]) )
583 fprintf(stderr, "Error decoding TransferCharacteristic UL value: %s\n", argv[i]);
589 TEST_EXTRA_ARG(i, 'O');
590 if ( ! set_display_primaries(argv[i]) )
597 TEST_EXTRA_ARG(i, 'o');
598 if ( ! set_display_luminance(argv[i]) )
605 TEST_EXTRA_ARG(i, 'P');
606 profile_name = argv[i];
610 TEST_EXTRA_ARG(i, 'p');
611 if ( ! picture_coding.DecodeHex(argv[i]) )
613 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
619 TEST_EXTRA_ARG(i, 'q');
620 if ( ! coding_equations.DecodeHex(argv[i]) )
622 fprintf(stderr, "Error decoding CodingEquations UL value: %s\n", argv[i]);
628 TEST_EXTRA_ARG(i, 'r');
629 if ( ! DecodeRational(argv[i], edit_rate) )
631 fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
638 use_cdci_descriptor = false;
642 TEST_EXTRA_ARG(i, 's');
643 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
647 TEST_EXTRA_ARG(i, 't');
648 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
652 TEST_EXTRA_ARG(i, 'T');
653 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
656 case 'u': show_ul_values_flag = true; break;
658 case 'V': version_flag = true; break;
659 case 'v': verbose_flag = true; break;
660 case 'W': no_write_flag = true; break;
663 TEST_EXTRA_ARG(i, 'x');
664 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
668 TEST_EXTRA_ARG(i, 'X');
669 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
673 use_cdci_descriptor = true;
674 // default 10 bit video range YUV, ref levels already set
678 // Use values provided as argument, sharp tool, be careful
679 use_cdci_descriptor = true;
680 TEST_EXTRA_ARG(i, 'y');
681 if ( ! set_video_ref(argv[i]) )
687 case 'Z': j2c_pedantic = false; break;
688 case 'z': j2c_pedantic = true; break;
691 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
698 if ( argv[i][0] != '-' )
700 filenames.push_back(argv[i]);
704 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
710 if ( help_flag || version_flag )
713 if ( filenames.size() < 2 )
715 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
719 out_file = filenames.back();
720 filenames.pop_back();
722 if ( ! picture_coding.HasValue() )
724 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
732 //------------------------------------------------------------------------------------------
736 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
737 const ASDCP::Dictionary& dict,
738 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
739 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
741 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
744 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
745 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
748 write_JP2K_file(CommandOptions& Options)
750 AESEncContext* Context = 0;
751 HMACContext* HMAC = 0;
752 AS_02::JP2K::MXFWriter Writer;
753 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
754 JP2K::SequenceParser Parser;
755 byte_t IV_buf[CBC_BLOCK_SIZE];
756 Kumu::FortunaRNG RNG;
757 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
758 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
760 // set up essence parser
761 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
764 if ( ASDCP_SUCCESS(result) )
766 ASDCP::JP2K::PictureDescriptor PDesc;
767 Parser.FillPictureDescriptor(PDesc);
768 PDesc.EditRate = Options.edit_rate;
770 if ( Options.verbose_flag )
772 fprintf(stderr, "JPEG 2000 pictures\n");
773 fputs("PictureDescriptor:\n", stderr);
774 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
775 JP2K::PictureDescriptorDump(PDesc);
778 if ( Options.use_cdci_descriptor )
780 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
781 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
783 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
784 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
785 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
787 if ( ASDCP_SUCCESS(result) )
789 tmp_dscr->CodingEquations = Options.coding_equations;
790 tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
791 tmp_dscr->ColorPrimaries = Options.color_primaries;
792 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
793 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
794 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
795 tmp_dscr->ComponentDepth = Options.component_depth;
796 tmp_dscr->FrameLayout = Options.frame_layout;
797 tmp_dscr->AspectRatio = Options.aspect_ratio;
798 tmp_dscr->FieldDominance = Options.field_dominance;
799 tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
800 tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
801 tmp_dscr->ColorRange = Options.cdci_ColorRange;
802 tmp_dscr->VideoLineMap = Options.line_map;
804 if ( Options.md_min_luminance || Options.md_max_luminance )
806 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
807 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
810 if ( Options.md_primaries.HasValue() )
812 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
813 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
816 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
821 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
822 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
824 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
825 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
826 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
828 if ( ASDCP_SUCCESS(result) )
830 tmp_dscr->CodingEquations = Options.coding_equations;
831 tmp_dscr->TransferCharacteristic = Options.transfer_characteristic;
832 tmp_dscr->ColorPrimaries = Options.color_primaries;
833 tmp_dscr->ScanningDirection = 0;
834 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
835 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
836 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
838 if ( Options.md_min_luminance || Options.md_max_luminance )
840 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
841 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
844 if ( Options.md_primaries.HasValue() )
846 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
847 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
850 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
855 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
857 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
858 Info.LabelSetType = LS_MXF_SMPTE;
860 if ( Options.asset_id_flag )
861 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
863 Kumu::GenRandomUUID(Info.AssetUUID);
865 // configure encryption
866 if( Options.key_flag )
868 Kumu::GenRandomUUID(Info.ContextID);
869 Info.EncryptedEssence = true;
871 if ( Options.key_id_flag )
873 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
877 create_random_uuid(Info.CryptographicKeyID);
880 Context = new AESEncContext;
881 result = Context->InitKey(Options.key_value);
883 if ( ASDCP_SUCCESS(result) )
884 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
886 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
888 Info.UsesHMAC = true;
889 HMAC = new HMACContext;
890 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
894 if ( ASDCP_SUCCESS(result) )
896 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
897 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
901 if ( ASDCP_SUCCESS(result) )
904 result = Parser.Reset();
906 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
908 result = Parser.ReadFrame(FrameBuffer);
910 if ( ASDCP_SUCCESS(result) )
912 if ( Options.verbose_flag )
913 FrameBuffer.Dump(stderr, Options.fb_dump_size);
915 if ( Options.encrypt_header_flag )
916 FrameBuffer.PlaintextOffset(0);
919 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
921 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
923 // The Writer class will forward the last block of ciphertext
924 // to the encryption context for use as the IV for the next
925 // frame. If you want to use non-sequitur IV values, un-comment
926 // the following line of code.
927 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
928 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
932 if ( result == RESULT_ENDOFFILE )
936 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
937 result = Writer.Finalize();
942 //------------------------------------------------------------------------------------------
946 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
947 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
950 write_PCM_file(CommandOptions& Options)
952 AESEncContext* Context = 0;
953 HMACContext* HMAC = 0;
954 PCMParserList Parser;
955 AS_02::PCM::MXFWriter Writer;
956 PCM::FrameBuffer FrameBuffer;
957 byte_t IV_buf[CBC_BLOCK_SIZE];
958 Kumu::FortunaRNG RNG;
959 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
961 // set up essence parser
962 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
965 if ( ASDCP_SUCCESS(result) )
967 ASDCP::PCM::AudioDescriptor ADesc;
968 Parser.FillAudioDescriptor(ADesc);
970 ADesc.EditRate = Options.edit_rate;
971 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
973 if ( Options.verbose_flag )
976 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
977 ADesc.AudioSamplingRate.Quotient() / 1000.0,
978 RationalToString(Options.edit_rate, buf, 64),
979 PCM::CalcSamplesPerFrame(ADesc));
980 fputs("AudioDescriptor:\n", stderr);
981 PCM::AudioDescriptorDump(ADesc);
984 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
986 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
988 if ( Options.mca_config.empty() )
990 essence_descriptor->ChannelAssignment = Options.channel_assignment;
994 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
996 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
997 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1001 // this is the d-cinema MCA label, what is the one for IMF?
1002 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
1006 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1008 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1009 Info.LabelSetType = LS_MXF_SMPTE;
1011 if ( Options.asset_id_flag )
1012 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1014 Kumu::GenRandomUUID(Info.AssetUUID);
1016 // configure encryption
1017 if( Options.key_flag )
1019 Kumu::GenRandomUUID(Info.ContextID);
1020 Info.EncryptedEssence = true;
1022 if ( Options.key_id_flag )
1024 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1028 create_random_uuid(Info.CryptographicKeyID);
1031 Context = new AESEncContext;
1032 result = Context->InitKey(Options.key_value);
1034 if ( ASDCP_SUCCESS(result) )
1035 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1037 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1039 Info.UsesHMAC = true;
1040 HMAC = new HMACContext;
1041 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1045 if ( ASDCP_SUCCESS(result) )
1047 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
1048 Options.mca_config, Options.edit_rate);
1052 if ( ASDCP_SUCCESS(result) )
1054 result = Parser.Reset();
1055 ui32_t duration = 0;
1057 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1059 result = Parser.ReadFrame(FrameBuffer);
1061 if ( ASDCP_SUCCESS(result) )
1063 if ( Options.verbose_flag )
1064 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1066 if ( ! Options.no_write_flag )
1068 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1070 // The Writer class will forward the last block of ciphertext
1071 // to the encryption context for use as the IV for the next
1072 // frame. If you want to use non-sequitur IV values, un-comment
1073 // the following line of code.
1074 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1075 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1080 if ( result == RESULT_ENDOFFILE )
1084 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1085 result = Writer.Finalize();
1093 //------------------------------------------------------------------------------------------
1094 // TimedText essence
1097 // Write one or more plaintext timed text streams to a plaintext AS-02 file
1098 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
1101 write_timed_text_file(CommandOptions& Options)
1103 AESEncContext* Context = 0;
1104 HMACContext* HMAC = 0;
1105 AS_02::TimedText::ST2052_TextParser Parser;
1106 AS_02::TimedText::MXFWriter Writer;
1107 TimedText::FrameBuffer FrameBuffer;
1108 TimedText::TimedTextDescriptor TDesc;
1109 byte_t IV_buf[CBC_BLOCK_SIZE];
1110 Kumu::FortunaRNG RNG;
1112 // set up essence parser
1113 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(),
1114 Options.profile_name);
1116 // set up MXF writer
1117 if ( ASDCP_SUCCESS(result) )
1119 Parser.FillTimedTextDescriptor(TDesc);
1120 TDesc.EditRate = Options.edit_rate;
1121 TDesc.ContainerDuration = Options.duration;
1122 FrameBuffer.Capacity(Options.fb_size);
1124 if ( Options.verbose_flag )
1126 fputs("IMF Timed-Text Descriptor:\n", stderr);
1127 TimedText::DescriptorDump(TDesc);
1131 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1133 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1134 Info.LabelSetType = LS_MXF_SMPTE;
1136 if ( Options.asset_id_flag )
1137 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1139 Kumu::GenRandomUUID(Info.AssetUUID);
1141 // configure encryption
1142 if( Options.key_flag )
1144 Kumu::GenRandomUUID(Info.ContextID);
1145 Info.EncryptedEssence = true;
1147 if ( Options.key_id_flag )
1149 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1153 create_random_uuid(Info.CryptographicKeyID);
1156 Context = new AESEncContext;
1157 result = Context->InitKey(Options.key_value);
1159 if ( ASDCP_SUCCESS(result) )
1160 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1162 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1164 Info.UsesHMAC = true;
1165 HMAC = new HMACContext;
1166 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1170 if ( ASDCP_SUCCESS(result) )
1171 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1174 if ( ASDCP_FAILURE(result) )
1178 TimedText::ResourceList_t::const_iterator ri;
1180 result = Parser.ReadTimedTextResource(XMLDoc);
1182 if ( ASDCP_SUCCESS(result) )
1183 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1185 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1187 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1189 if ( ASDCP_SUCCESS(result) )
1191 if ( Options.verbose_flag )
1192 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1194 if ( ! Options.no_write_flag )
1196 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1198 // The Writer class will forward the last block of ciphertext
1199 // to the encryption context for use as the IV for the next
1200 // frame. If you want to use non-sequitur IV values, un-comment
1201 // the following line of code.
1202 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1203 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1207 if ( result == RESULT_ENDOFFILE )
1211 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1212 result = Writer.Finalize();
1220 main(int argc, const char** argv)
1222 Result_t result = RESULT_OK;
1224 g_dict = &ASDCP::DefaultSMPTEDict();
1227 CommandOptions Options(argc, argv);
1229 if ( Options.version_flag )
1232 if ( Options.help_flag )
1235 if ( Options.show_ul_values_flag )
1237 g_dict->Dump(stdout);
1240 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1243 if ( Options.error_flag )
1245 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1249 EssenceType_t EssenceType;
1250 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1252 if ( ASDCP_SUCCESS(result) )
1254 switch ( EssenceType )
1257 result = write_JP2K_file(Options);
1260 case ESS_PCM_24b_48k:
1261 case ESS_PCM_24b_96k:
1262 result = write_PCM_file(Options);
1265 case ESS_TIMED_TEXT:
1266 result = write_timed_text_file(Options);
1270 fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
1271 Options.filenames.front().c_str());
1276 if ( ASDCP_FAILURE(result) )
1278 fputs("Program stopped on error.\n", stderr);
1280 if ( result != RESULT_FAIL )
1282 fputs(result, stderr);
1283 fputc('\n', stderr);
1294 // end as-02-wrap.cpp