2 Copyright (c) 2011-2016, 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-2016, 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 <ul> - Set ChannelAssignment UL value\n\
134 -d <duration> - Number of frames to process, default all\n\
135 -D <depth> - Component depth for YCbCr images (default: 10)\n\
136 -e - Encrypt JP2K headers (default)\n\
137 -E - Do not encrypt JP2K headers\n\
138 -F (0|1) - Set field dominance for interlaced image (default: 0)\n\
139 -i - Indicates input essence is interlaced fields (forces -Y)\n\
140 -j <key-id-str> - Write key ID instead of creating a random value\n\
141 -k <key-string> - Use key for ciphertext operations\n\
142 -l <first>,<second>\n\
143 - Integer values that set the VideoLineMap when creating\n\
144 interlaced YCbCr files\n\
145 -M - Do not create HMAC values when writing\n\
146 -m <expr> - Write MCA labels using <expr>. Example:\n\
147 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
148 -o <min>,<max> - Mastering Display luminance, cd*m*m, e.g., \".05,100\"\n\
149 -O <rx>,<ry>,<gx>,<gy>,<bx>,<by>,<wx>,<wy>\n\
150 - Mastering Display Color Primaries and white point\n\
151 e.g., \".64,.33,.3,.6,.15,.06,.3457,.3585\"\n\
152 -P <string> - Set NamespaceURI property when creating timed text MXF\n\
153 -p <ul> - Set broadcast profile\n\
154 -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
155 -R - Indicates RGB image essence (default)\n\
156 -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
157 -t <min> - Set RGB component minimum code value (default: 0)\n\
158 -T <max> - Set RGB component maximum code value (default: 1023)\n\
159 -u - Print UL catalog to stderr\n\
160 -v - Verbose, prints informative messages to stderr\n\
161 -W - Read input file only, do not write source file\n\
162 -x <int> - Horizontal subsampling degree (default: 2)\n\
163 -X <int> - Vertical subsampling degree (default: 2)\n\
164 -Y - Indicates YCbCr image essence (default: RGB), uses\n\
165 default values for White Ref, Black Ref and Color Range,\n\
166 940,64,897, indicating 10 bit standard Video Range\n\
167 -y <white-ref>[,<black-ref>[,<color-range>]]\n\
168 - Same as -Y but White Ref, Black Ref and Color Range are\n\
169 set from the given argument\n\
170 -z - Fail if j2c inputs have unequal parameters (default)\n\
171 -Z - Ignore unequal parameters in j2c inputs\n\
173 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
174 o All option arguments must be separated from the option by whitespace.\n\n");
177 const float chromaticity_scale = 50000.0;
180 set_primary_from_token(const std::string& token, ui16_t& primary)
182 float raw_value = strtod(token.c_str(),0);
184 if ( raw_value == 0.0 || raw_value > 1.0 )
186 fprintf(stderr, "Invalid coordinate value \"%s\".\n", token.c_str());
190 primary = floor(0.5 + ( raw_value * chromaticity_scale ));
194 const float luminance_scale = 10000.0;
197 set_luminance_from_token(const std::string& token, ui32_t& luminance)
199 float raw_value = strtod(token.c_str(),0);
201 if ( raw_value == 0.0 || raw_value > 400000.0 )
203 fprintf(stderr, "Invalid luminance value \"%s\".\n", token.c_str());
207 luminance = floor(0.5 + ( raw_value * luminance_scale ));
211 #define SET_LUMINANCE(p,t) \
212 if ( ! set_luminance_from_token(t, p) ) { \
222 bool error_flag; // true if the given options are in error or not complete
223 bool key_flag; // true if an encryption key was given
224 bool asset_id_flag; // true if an asset ID was given
225 bool encrypt_header_flag; // true if j2c headers are to be encrypted
226 bool write_hmac; // true if HMAC values are to be generated and written
227 bool verbose_flag; // true if the verbose option was selected
228 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
229 bool no_write_flag; // true if no output files are to be written
230 bool version_flag; // true if the version display option was selected
231 bool help_flag; // true if the help display option was selected
232 ui32_t duration; // number of frames to be processed
233 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
234 bool use_cdci_descriptor; //
235 Rational edit_rate; // edit rate of JP2K sequence
236 ui32_t fb_size; // size of picture frame buffer
237 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
238 bool key_id_flag; // true if a key ID was given
239 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
240 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
241 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
242 Kumu::PathList_t filenames; // list of filenames to be processed
244 UL channel_assignment;
245 ASDCP::MXF::AS02_MCAConfigParser mca_config;
251 ui32_t horizontal_subsampling;
252 ui32_t vertical_subsampling;
253 ui32_t component_depth;
255 ASDCP::Rational aspect_ratio;
256 ui8_t field_dominance;
257 ui32_t mxf_header_size;
258 ui32_t cdci_BlackRefLevel;
259 ui32_t cdci_WhiteRefLevel;
260 ui32_t cdci_ColorRange;
262 ui32_t md_min_luminance, md_max_luminance;
263 ASDCP::MXF::ThreeColorPrimaries md_primaries;
264 ASDCP::MXF::ColorPrimary md_white_point;
266 //new attributes for AS-02 support
267 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
268 ui32_t partition_space; //Shim parameter partition_spacing
271 MXF::LineMapPair line_map;
272 std::string out_file, profile_name; //
275 bool set_video_line_map(const std::string& arg)
277 const char* sep_str = strrchr(arg.c_str(), ',');
281 fprintf(stderr, "Expecting <first>,<second>\n");
285 line_map.First = Kumu::xabs(strtol(arg.c_str(), 0, 10));
286 line_map.Second = Kumu::xabs(strtol(sep_str+1, 0, 10));
291 bool set_video_ref(const std::string& arg)
293 std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
295 switch ( ref_tokens.size() )
298 cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
299 ref_tokens.pop_back();
301 cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
302 ref_tokens.pop_back();
304 cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
308 fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
312 if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
314 fprintf(stderr, "Unexpected CDCI video referece levels.\n");
322 bool set_display_primaries(const std::string& arg)
324 std::list<std::string> coordinate_tokens = Kumu::km_token_split(arg, ",");
325 if ( coordinate_tokens.size() != 8 )
327 fprintf(stderr, "Expecting four coordinate pairs.\n");
331 std::list<std::string>::const_iterator i = coordinate_tokens.begin();
332 if ( ! set_primary_from_token(*(i++), md_primaries.First.X) ) return false;
333 if ( ! set_primary_from_token(*(i++), md_primaries.First.Y) ) return false;
334 if ( ! set_primary_from_token(*(i++), md_primaries.Second.X) ) return false;
335 if ( ! set_primary_from_token(*(i++), md_primaries.Second.Y) ) return false;
336 if ( ! set_primary_from_token(*(i++), md_primaries.Third.X) ) return false;
337 if ( ! set_primary_from_token(*(i++), md_primaries.Third.Y) ) return false;
338 if ( ! set_primary_from_token(*(i++), md_white_point.X) ) return false;
339 if ( ! set_primary_from_token(*i, md_white_point.Y) ) return false;
345 bool set_display_luminance(const std::string& arg)
347 std::list<std::string> luminance_tokens = Kumu::km_token_split(arg, ",");
348 if ( luminance_tokens.size() != 2 )
350 fprintf(stderr, "Expecting a luminance pair.\n");
354 if ( ! set_luminance_from_token(luminance_tokens.front(), md_min_luminance) ) return false;
355 if ( ! set_luminance_from_token(luminance_tokens.back(), md_max_luminance) ) return false;
360 CommandOptions(int argc, const char** argv) :
361 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
362 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
363 no_write_flag(false), version_flag(false), help_flag(false),
364 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false),
365 edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
366 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
367 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
368 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
369 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
370 mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897),
371 md_min_luminance(0), md_max_luminance(0), line_map(0,0)
373 memset(key_value, 0, KeyLen);
374 memset(key_id_value, 0, UUIDlen);
376 for ( int i = 1; i < argc; i++ )
379 if ( (strcmp( argv[i], "-help") == 0) )
385 if ( argv[i][0] == '-'
386 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
389 switch ( argv[i][1] )
392 TEST_EXTRA_ARG(i, 'A');
393 if ( ! DecodeRational(argv[i], aspect_ratio) )
395 fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
401 asset_id_flag = true;
402 TEST_EXTRA_ARG(i, 'a');
405 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
407 if ( length != UUIDlen )
409 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
416 TEST_EXTRA_ARG(i, 'b');
417 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
420 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
424 TEST_EXTRA_ARG(i, 'C');
425 if ( ! channel_assignment.DecodeHex(argv[i]) )
427 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
433 TEST_EXTRA_ARG(i, 'D');
434 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
438 TEST_EXTRA_ARG(i, 'd');
439 duration = Kumu::xabs(strtol(argv[i], 0, 10));
442 case 'E': encrypt_header_flag = false; break;
443 case 'e': encrypt_header_flag = true; break;
446 TEST_EXTRA_ARG(i, 'F');
447 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
448 if ( field_dominance > 1 )
450 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
455 case 'h': help_flag = true; break;
459 use_cdci_descriptor = true;
464 TEST_EXTRA_ARG(i, 'j');
467 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
469 if ( length != UUIDlen )
471 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
477 case 'k': key_flag = true;
478 TEST_EXTRA_ARG(i, 'k');
481 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
483 if ( length != KeyLen )
485 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
492 TEST_EXTRA_ARG(i, 'y');
493 if ( ! set_video_line_map(argv[i]) )
499 case 'M': write_hmac = false; break;
502 TEST_EXTRA_ARG(i, 'm');
503 if ( ! mca_config.DecodeString(argv[i]) )
510 TEST_EXTRA_ARG(i, ')');
511 if ( ! set_display_primaries(argv[i]) )
518 TEST_EXTRA_ARG(i, 'o');
519 if ( ! set_display_luminance(argv[i]) )
526 TEST_EXTRA_ARG(i, 'P');
527 profile_name = argv[i];
531 TEST_EXTRA_ARG(i, 'p');
532 if ( ! picture_coding.DecodeHex(argv[i]) )
534 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
540 TEST_EXTRA_ARG(i, 'r');
541 if ( ! DecodeRational(argv[i], edit_rate) )
543 fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
550 use_cdci_descriptor = false;
554 TEST_EXTRA_ARG(i, 's');
555 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
559 TEST_EXTRA_ARG(i, 't');
560 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
564 TEST_EXTRA_ARG(i, 'T');
565 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
568 case 'u': show_ul_values_flag = true; break;
570 case 'V': version_flag = true; break;
571 case 'v': verbose_flag = true; break;
572 case 'W': no_write_flag = true; break;
575 TEST_EXTRA_ARG(i, 'x');
576 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
580 TEST_EXTRA_ARG(i, 'X');
581 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
585 use_cdci_descriptor = true;
586 // default 10 bit video range YUV, ref levels already set
590 // Use values provded as argument, sharp tool, be careful
591 use_cdci_descriptor = true;
592 TEST_EXTRA_ARG(i, 'y');
593 if ( ! set_video_ref(argv[i]) )
599 case 'Z': j2c_pedantic = false; break;
600 case 'z': j2c_pedantic = true; break;
603 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
610 if ( argv[i][0] != '-' )
612 filenames.push_back(argv[i]);
616 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
622 if ( help_flag || version_flag )
625 if ( filenames.size() < 2 )
627 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
631 out_file = filenames.back();
632 filenames.pop_back();
634 if ( ! picture_coding.HasValue() )
636 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
644 //------------------------------------------------------------------------------------------
648 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
649 const ASDCP::Dictionary& dict,
650 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
651 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
653 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
656 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
657 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
660 write_JP2K_file(CommandOptions& Options)
662 AESEncContext* Context = 0;
663 HMACContext* HMAC = 0;
664 AS_02::JP2K::MXFWriter Writer;
665 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
666 JP2K::SequenceParser Parser;
667 byte_t IV_buf[CBC_BLOCK_SIZE];
668 Kumu::FortunaRNG RNG;
669 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
670 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
672 // set up essence parser
673 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
676 if ( ASDCP_SUCCESS(result) )
678 ASDCP::JP2K::PictureDescriptor PDesc;
679 Parser.FillPictureDescriptor(PDesc);
680 PDesc.EditRate = Options.edit_rate;
682 if ( Options.verbose_flag )
684 fprintf(stderr, "JPEG 2000 pictures\n");
685 fputs("PictureDescriptor:\n", stderr);
686 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
687 JP2K::PictureDescriptorDump(PDesc);
690 if ( Options.use_cdci_descriptor )
692 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
693 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
695 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
696 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
697 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
699 if ( ASDCP_SUCCESS(result) )
701 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
702 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
703 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
704 tmp_dscr->ComponentDepth = Options.component_depth;
705 tmp_dscr->FrameLayout = Options.frame_layout;
706 tmp_dscr->AspectRatio = Options.aspect_ratio;
707 tmp_dscr->FieldDominance = Options.field_dominance;
708 tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
709 tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
710 tmp_dscr->ColorRange = Options.cdci_ColorRange;
711 tmp_dscr->VideoLineMap = Options.line_map;
713 if ( Options.md_min_luminance || Options.md_max_luminance )
715 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
716 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
719 if ( Options.md_primaries.HasValue() )
721 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
722 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
725 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
730 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
731 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
733 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
734 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
735 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
737 if ( ASDCP_SUCCESS(result) )
739 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
740 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
741 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
743 if ( Options.md_min_luminance || Options.md_max_luminance )
745 tmp_dscr->MasteringDisplayMinimumLuminance = Options.md_min_luminance;
746 tmp_dscr->MasteringDisplayMaximumLuminance = Options.md_max_luminance;
749 if ( Options.md_primaries.HasValue() )
751 tmp_dscr->MasteringDisplayPrimaries = Options.md_primaries;
752 tmp_dscr->MasteringDisplayWhitePointChromaticity = Options.md_white_point;
755 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
760 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
762 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
763 Info.LabelSetType = LS_MXF_SMPTE;
765 if ( Options.asset_id_flag )
766 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
768 Kumu::GenRandomUUID(Info.AssetUUID);
770 // configure encryption
771 if( Options.key_flag )
773 Kumu::GenRandomUUID(Info.ContextID);
774 Info.EncryptedEssence = true;
776 if ( Options.key_id_flag )
778 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
782 create_random_uuid(Info.CryptographicKeyID);
785 Context = new AESEncContext;
786 result = Context->InitKey(Options.key_value);
788 if ( ASDCP_SUCCESS(result) )
789 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
791 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
793 Info.UsesHMAC = true;
794 HMAC = new HMACContext;
795 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
799 if ( ASDCP_SUCCESS(result) )
801 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
802 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
806 if ( ASDCP_SUCCESS(result) )
809 result = Parser.Reset();
811 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
813 result = Parser.ReadFrame(FrameBuffer);
815 if ( ASDCP_SUCCESS(result) )
817 if ( Options.verbose_flag )
818 FrameBuffer.Dump(stderr, Options.fb_dump_size);
820 if ( Options.encrypt_header_flag )
821 FrameBuffer.PlaintextOffset(0);
824 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
826 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
828 // The Writer class will forward the last block of ciphertext
829 // to the encryption context for use as the IV for the next
830 // frame. If you want to use non-sequitur IV values, un-comment
831 // the following line of code.
832 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
833 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
837 if ( result == RESULT_ENDOFFILE )
841 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
842 result = Writer.Finalize();
847 //------------------------------------------------------------------------------------------
851 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
852 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
855 write_PCM_file(CommandOptions& Options)
857 AESEncContext* Context = 0;
858 HMACContext* HMAC = 0;
859 PCMParserList Parser;
860 AS_02::PCM::MXFWriter Writer;
861 PCM::FrameBuffer FrameBuffer;
862 byte_t IV_buf[CBC_BLOCK_SIZE];
863 Kumu::FortunaRNG RNG;
864 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
866 // set up essence parser
867 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
870 if ( ASDCP_SUCCESS(result) )
872 ASDCP::PCM::AudioDescriptor ADesc;
873 Parser.FillAudioDescriptor(ADesc);
875 ADesc.EditRate = Options.edit_rate;
876 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
878 if ( Options.verbose_flag )
881 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
882 ADesc.AudioSamplingRate.Quotient() / 1000.0,
883 RationalToString(Options.edit_rate, buf, 64),
884 PCM::CalcSamplesPerFrame(ADesc));
885 fputs("AudioDescriptor:\n", stderr);
886 PCM::AudioDescriptorDump(ADesc);
889 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
891 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
893 if ( Options.mca_config.empty() )
895 essence_descriptor->ChannelAssignment = Options.channel_assignment;
899 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
901 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
902 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
906 // this is the d-cinema MCA label, what is the one for IMF?
907 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
911 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
913 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
914 Info.LabelSetType = LS_MXF_SMPTE;
916 if ( Options.asset_id_flag )
917 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
919 Kumu::GenRandomUUID(Info.AssetUUID);
921 // configure encryption
922 if( Options.key_flag )
924 Kumu::GenRandomUUID(Info.ContextID);
925 Info.EncryptedEssence = true;
927 if ( Options.key_id_flag )
929 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
933 create_random_uuid(Info.CryptographicKeyID);
936 Context = new AESEncContext;
937 result = Context->InitKey(Options.key_value);
939 if ( ASDCP_SUCCESS(result) )
940 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
942 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
944 Info.UsesHMAC = true;
945 HMAC = new HMACContext;
946 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
950 if ( ASDCP_SUCCESS(result) )
952 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
953 Options.mca_config, Options.edit_rate);
957 if ( ASDCP_SUCCESS(result) )
959 result = Parser.Reset();
962 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
964 result = Parser.ReadFrame(FrameBuffer);
966 if ( ASDCP_SUCCESS(result) )
968 if ( Options.verbose_flag )
969 FrameBuffer.Dump(stderr, Options.fb_dump_size);
971 if ( ! Options.no_write_flag )
973 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
975 // The Writer class will forward the last block of ciphertext
976 // to the encryption context for use as the IV for the next
977 // frame. If you want to use non-sequitur IV values, un-comment
978 // the following line of code.
979 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
980 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
985 if ( result == RESULT_ENDOFFILE )
989 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
990 result = Writer.Finalize();
998 //------------------------------------------------------------------------------------------
1002 // Write one or more plaintext timed text streams to a plaintext AS-02 file
1003 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
1006 write_timed_text_file(CommandOptions& Options)
1008 AESEncContext* Context = 0;
1009 HMACContext* HMAC = 0;
1010 AS_02::TimedText::ST2052_TextParser Parser;
1011 AS_02::TimedText::MXFWriter Writer;
1012 TimedText::FrameBuffer FrameBuffer;
1013 TimedText::TimedTextDescriptor TDesc;
1014 byte_t IV_buf[CBC_BLOCK_SIZE];
1015 Kumu::FortunaRNG RNG;
1017 // set up essence parser
1018 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(),
1019 Options.profile_name);
1021 // set up MXF writer
1022 if ( ASDCP_SUCCESS(result) )
1024 Parser.FillTimedTextDescriptor(TDesc);
1025 TDesc.EditRate = Options.edit_rate;
1026 TDesc.ContainerDuration = Options.duration;
1027 FrameBuffer.Capacity(Options.fb_size);
1029 if ( Options.verbose_flag )
1031 fputs("IMF Timed-Text Descriptor:\n", stderr);
1032 TimedText::DescriptorDump(TDesc);
1036 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1038 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1039 Info.LabelSetType = LS_MXF_SMPTE;
1041 if ( Options.asset_id_flag )
1042 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1044 Kumu::GenRandomUUID(Info.AssetUUID);
1046 // configure encryption
1047 if( Options.key_flag )
1049 Kumu::GenRandomUUID(Info.ContextID);
1050 Info.EncryptedEssence = true;
1052 if ( Options.key_id_flag )
1054 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1058 create_random_uuid(Info.CryptographicKeyID);
1061 Context = new AESEncContext;
1062 result = Context->InitKey(Options.key_value);
1064 if ( ASDCP_SUCCESS(result) )
1065 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1067 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1069 Info.UsesHMAC = true;
1070 HMAC = new HMACContext;
1071 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1075 if ( ASDCP_SUCCESS(result) )
1076 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1079 if ( ASDCP_FAILURE(result) )
1083 TimedText::ResourceList_t::const_iterator ri;
1085 result = Parser.ReadTimedTextResource(XMLDoc);
1087 if ( ASDCP_SUCCESS(result) )
1088 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1090 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1092 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1094 if ( ASDCP_SUCCESS(result) )
1096 if ( Options.verbose_flag )
1097 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1099 if ( ! Options.no_write_flag )
1101 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1103 // The Writer class will forward the last block of ciphertext
1104 // to the encryption context for use as the IV for the next
1105 // frame. If you want to use non-sequitur IV values, un-comment
1106 // the following line of code.
1107 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1108 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1112 if ( result == RESULT_ENDOFFILE )
1116 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1117 result = Writer.Finalize();
1125 main(int argc, const char** argv)
1127 Result_t result = RESULT_OK;
1129 g_dict = &ASDCP::DefaultSMPTEDict();
1132 CommandOptions Options(argc, argv);
1134 if ( Options.version_flag )
1137 if ( Options.help_flag )
1140 if ( Options.show_ul_values_flag )
1142 g_dict->Dump(stdout);
1145 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1148 if ( Options.error_flag )
1150 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1154 EssenceType_t EssenceType;
1155 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1157 if ( ASDCP_SUCCESS(result) )
1159 switch ( EssenceType )
1162 result = write_JP2K_file(Options);
1165 case ESS_PCM_24b_48k:
1166 case ESS_PCM_24b_96k:
1167 result = write_PCM_file(Options);
1170 case ESS_TIMED_TEXT:
1171 result = write_timed_text_file(Options);
1175 fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
1176 Options.filenames.front().c_str());
1181 if ( ASDCP_FAILURE(result) )
1183 fputs("Program stopped on error.\n", stderr);
1185 if ( result != RESULT_FAIL )
1187 fputs(result, stderr);
1188 fputc('\n', stderr);
1199 // end as-02-wrap.cpp