2 Copyright (c) 2011-2015, 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
30 \version $Id: as-02-wrap.cpp,v 1.22 2015/10/16 16:55:33 jhurst Exp $
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;
52 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
54 snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
60 //------------------------------------------------------------------------------------------
62 // command line option parser class
64 static const char* PROGRAM_NAME = "as-02-wrap"; // program name for messages
66 // local program identification info written to file headers
67 class MyInfo : public WriterInfo
72 static byte_t default_ProductUUID_Data[UUIDlen] =
73 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
74 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
76 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
77 CompanyName = "WidgetCo";
78 ProductName = "as-02-wrap";
79 ProductVersion = ASDCP::Version();
85 // Increment the iterator, test for an additional non-option command line argument.
86 // Causes the caller to return if there are no remaining arguments or if the next
87 // argument begins with '-'.
88 #define TEST_EXTRA_ARG(i,c) \
89 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
90 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
97 create_random_uuid(byte_t* uuidbuf)
100 GenRandomValue(tmp_id);
101 memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
106 banner(FILE* stream = stdout)
109 %s (asdcplib %s)\n\n\
110 Copyright (c) 2011-2015, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
111 asdcplib may be copied only under the terms of the license found at\n\
112 the top of every file in the asdcplib distribution kit.\n\n\
113 Specify the -h (help) option for further information about %s\n\n",
114 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
119 usage(FILE* stream = stdout)
122 USAGE: %s [-h|-help] [-V]\n\
124 %s [-a <uuid>] [-A <w>/<h>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
125 [-D <depth>] [-e|-E] [-i] [-j <key-id-string>] [-k <key-string>]\n\
126 [-M] [-m <expr>] [-p <ul>] [-r <n>/<d>] [-R] [-s <seconds>]\n\
127 [-t <min>] [-T <max>] [-u] [-v] [-W] [-x <int>] [-X <int>] [-Y]\n\
128 [-z|-Z] <input-file>+ <output-file>\n\n",
129 PROGRAM_NAME, PROGRAM_NAME);
133 -h | -help - Show help\n\
134 -V - Show version information\n\
135 -a <uuid> - Specify the Asset ID of the file\n\
136 -A <w>/<h> - Set aspect ratio for image (default 4/3)\n\
137 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
138 Defaults to 4,194,304 (4MB)\n\
139 -C <ul> - Set ChannelAssignment UL value\n\
140 -d <duration> - Number of frames to process, default all\n\
141 -D <depth> - Component depth for YCbCr images (default: 10)\n\
142 -e - Encrypt JP2K headers (default)\n\
143 -E - Do not encrypt JP2K headers\n\
144 -F (0|1) - Set field dominance for interlaced image (default: 0)\n\
145 -i - Indicates input essence is interlaced fields (forces -Y)\n\
146 -j <key-id-str> - Write key ID instead of creating a random value\n\
147 -k <key-string> - Use key for ciphertext operations\n\
148 -M - Do not create HMAC values when writing\n\
149 -m <expr> - Write MCA labels using <expr>. Example:\n\
150 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
151 -p <ul> - Set broadcast profile\n\
152 -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
153 -R - Indicates RGB image essence (default)\n\
154 -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
155 -t <min> - Set RGB component minimum code value (default: 0)\n\
156 -T <max> - Set RGB component maximum code value (default: 1023)\n\
157 -u - Print UL catalog to stderr\n\
158 -v - Verbose, prints informative messages to stderr\n\
159 -W - Read input file only, do not write source file\n\
160 -x <int> - Horizontal subsampling degree (default: 2)\n\
161 -X <int> - Vertical subsampling degree (default: 2)\n\
162 -Y - Indicates YCbCr image essence (default: RGB)\n\
163 -z - Fail if j2c inputs have unequal parameters (default)\n\
164 -Z - Ignore unequal parameters in j2c inputs\n\
166 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
167 o All option arguments must be separated from the option by whitespace.\n\n");
178 bool error_flag; // true if the given options are in error or not complete
179 bool key_flag; // true if an encryption key was given
180 bool asset_id_flag; // true if an asset ID was given
181 bool encrypt_header_flag; // true if j2c headers are to be encrypted
182 bool write_hmac; // true if HMAC values are to be generated and written
183 bool verbose_flag; // true if the verbose option was selected
184 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
185 bool no_write_flag; // true if no output files are to be written
186 bool version_flag; // true if the version display option was selected
187 bool help_flag; // true if the help display option was selected
188 ui32_t duration; // number of frames to be processed
189 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
190 bool use_cdci_descriptor; //
191 Rational edit_rate; // edit rate of JP2K sequence
192 ui32_t fb_size; // size of picture frame buffer
193 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
194 bool key_id_flag; // true if a key ID was given
195 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
196 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
197 std::string out_file; //
198 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
199 Kumu::PathList_t filenames; // list of filenames to be processed
201 UL channel_assignment;
202 ASDCP::MXF::AS02_MCAConfigParser mca_config;
208 ui32_t horizontal_subsampling;
209 ui32_t vertical_subsampling;
210 ui32_t component_depth;
212 ASDCP::Rational aspect_ratio;
213 ui8_t field_dominance;
214 ui32_t mxf_header_size;
216 //new attributes for AS-02 support
217 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
218 ui32_t partition_space; //Shim parameter partition_spacing
221 CommandOptions(int argc, const char** argv) :
222 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
223 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
224 no_write_flag(false), version_flag(false), help_flag(false),
225 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
226 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
227 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
228 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
229 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
230 mxf_header_size(16384)
232 memset(key_value, 0, KeyLen);
233 memset(key_id_value, 0, UUIDlen);
235 for ( int i = 1; i < argc; i++ )
238 if ( (strcmp( argv[i], "-help") == 0) )
244 if ( argv[i][0] == '-'
245 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
248 switch ( argv[i][1] )
251 TEST_EXTRA_ARG(i, 'A');
252 if ( ! DecodeRational(argv[i], aspect_ratio) )
254 fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
260 asset_id_flag = true;
261 TEST_EXTRA_ARG(i, 'a');
264 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
266 if ( length != UUIDlen )
268 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
275 TEST_EXTRA_ARG(i, 'b');
276 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
279 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
284 TEST_EXTRA_ARG(i, 'C');
285 if ( ! channel_assignment.DecodeHex(argv[i]) )
287 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
293 TEST_EXTRA_ARG(i, 'D');
294 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
298 TEST_EXTRA_ARG(i, 'd');
299 duration = Kumu::xabs(strtol(argv[i], 0, 10));
302 case 'E': encrypt_header_flag = false; break;
303 case 'e': encrypt_header_flag = true; break;
306 TEST_EXTRA_ARG(i, 'F');
307 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
308 if ( field_dominance > 1 )
310 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
315 case 'h': help_flag = true; break;
319 use_cdci_descriptor = true;
324 TEST_EXTRA_ARG(i, 'j');
327 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
329 if ( length != UUIDlen )
331 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
337 case 'k': key_flag = true;
338 TEST_EXTRA_ARG(i, 'k');
341 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
343 if ( length != KeyLen )
345 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
351 case 'M': write_hmac = false; break;
354 TEST_EXTRA_ARG(i, 'm');
355 if ( ! mca_config.DecodeString(argv[i]) )
362 TEST_EXTRA_ARG(i, 'p');
363 if ( ! picture_coding.DecodeHex(argv[i]) )
365 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
371 TEST_EXTRA_ARG(i, 'r');
372 if ( ! DecodeRational(argv[i], edit_rate) )
374 fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
381 use_cdci_descriptor = false;
385 TEST_EXTRA_ARG(i, 's');
386 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
390 TEST_EXTRA_ARG(i, 't');
391 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
395 TEST_EXTRA_ARG(i, 'T');
396 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
399 case 'u': show_ul_values_flag = true; break;
400 case 'V': version_flag = true; break;
401 case 'v': verbose_flag = true; break;
402 case 'W': no_write_flag = true; break;
405 TEST_EXTRA_ARG(i, 'x');
406 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
410 TEST_EXTRA_ARG(i, 'X');
411 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
415 use_cdci_descriptor = true;
418 case 'Z': j2c_pedantic = false; break;
419 case 'z': j2c_pedantic = true; break;
422 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
429 if ( argv[i][0] != '-' )
431 filenames.push_back(argv[i]);
435 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
441 if ( help_flag || version_flag )
444 if ( filenames.size() < 2 )
446 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
450 out_file = filenames.back();
451 filenames.pop_back();
453 if ( ! picture_coding.HasValue() )
455 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
463 //------------------------------------------------------------------------------------------
467 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
468 const ASDCP::Dictionary& dict,
469 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
470 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
472 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
475 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
476 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
479 write_JP2K_file(CommandOptions& Options)
481 AESEncContext* Context = 0;
482 HMACContext* HMAC = 0;
483 AS_02::JP2K::MXFWriter Writer;
484 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
485 JP2K::SequenceParser Parser;
486 byte_t IV_buf[CBC_BLOCK_SIZE];
487 Kumu::FortunaRNG RNG;
488 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
489 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
491 // set up essence parser
492 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
495 if ( ASDCP_SUCCESS(result) )
497 ASDCP::JP2K::PictureDescriptor PDesc;
498 Parser.FillPictureDescriptor(PDesc);
499 PDesc.EditRate = Options.edit_rate;
501 if ( Options.verbose_flag )
503 fprintf(stderr, "JPEG 2000 pictures\n");
504 fputs("PictureDescriptor:\n", stderr);
505 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
506 JP2K::PictureDescriptorDump(PDesc);
509 if ( Options.use_cdci_descriptor )
511 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
512 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
514 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
515 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
516 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
518 if ( ASDCP_SUCCESS(result) )
520 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
521 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
522 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
523 tmp_dscr->ComponentDepth = Options.component_depth;
524 tmp_dscr->FrameLayout = Options.frame_layout;
525 tmp_dscr->AspectRatio = Options.aspect_ratio;
526 tmp_dscr->FieldDominance = Options.field_dominance;
527 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
532 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
533 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
535 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
536 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
537 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
539 if ( ASDCP_SUCCESS(result) )
541 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
542 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
543 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
544 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
549 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
551 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
552 Info.LabelSetType = LS_MXF_SMPTE;
554 if ( Options.asset_id_flag )
555 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
557 Kumu::GenRandomUUID(Info.AssetUUID);
559 // configure encryption
560 if( Options.key_flag )
562 Kumu::GenRandomUUID(Info.ContextID);
563 Info.EncryptedEssence = true;
565 if ( Options.key_id_flag )
567 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
571 create_random_uuid(Info.CryptographicKeyID);
574 Context = new AESEncContext;
575 result = Context->InitKey(Options.key_value);
577 if ( ASDCP_SUCCESS(result) )
578 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
580 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
582 Info.UsesHMAC = true;
583 HMAC = new HMACContext;
584 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
588 if ( ASDCP_SUCCESS(result) )
590 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
591 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
595 if ( ASDCP_SUCCESS(result) )
598 result = Parser.Reset();
600 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
602 result = Parser.ReadFrame(FrameBuffer);
604 if ( ASDCP_SUCCESS(result) )
606 if ( Options.verbose_flag )
607 FrameBuffer.Dump(stderr, Options.fb_dump_size);
609 if ( Options.encrypt_header_flag )
610 FrameBuffer.PlaintextOffset(0);
613 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
615 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
617 // The Writer class will forward the last block of ciphertext
618 // to the encryption context for use as the IV for the next
619 // frame. If you want to use non-sequitur IV values, un-comment
620 // the following line of code.
621 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
622 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
626 if ( result == RESULT_ENDOFFILE )
630 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
631 result = Writer.Finalize();
636 //------------------------------------------------------------------------------------------
640 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
641 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
644 write_PCM_file(CommandOptions& Options)
646 AESEncContext* Context = 0;
647 HMACContext* HMAC = 0;
648 PCMParserList Parser;
649 AS_02::PCM::MXFWriter Writer;
650 PCM::FrameBuffer FrameBuffer;
651 byte_t IV_buf[CBC_BLOCK_SIZE];
652 Kumu::FortunaRNG RNG;
653 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
655 // set up essence parser
656 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
659 if ( ASDCP_SUCCESS(result) )
661 ASDCP::PCM::AudioDescriptor ADesc;
662 Parser.FillAudioDescriptor(ADesc);
664 ADesc.EditRate = Options.edit_rate;
665 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
667 if ( Options.verbose_flag )
670 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
671 ADesc.AudioSamplingRate.Quotient() / 1000.0,
672 RationalToString(Options.edit_rate, buf, 64),
673 PCM::CalcSamplesPerFrame(ADesc));
674 fputs("AudioDescriptor:\n", stderr);
675 PCM::AudioDescriptorDump(ADesc);
678 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
680 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
682 if ( Options.mca_config.empty() )
684 essence_descriptor->ChannelAssignment = Options.channel_assignment;
688 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
690 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
691 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
695 // this is the d-cinema MCA label, what is the one for IMF?
696 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
700 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
702 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
703 Info.LabelSetType = LS_MXF_SMPTE;
705 if ( Options.asset_id_flag )
706 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
708 Kumu::GenRandomUUID(Info.AssetUUID);
710 // configure encryption
711 if( Options.key_flag )
713 Kumu::GenRandomUUID(Info.ContextID);
714 Info.EncryptedEssence = true;
716 if ( Options.key_id_flag )
718 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
722 create_random_uuid(Info.CryptographicKeyID);
725 Context = new AESEncContext;
726 result = Context->InitKey(Options.key_value);
728 if ( ASDCP_SUCCESS(result) )
729 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
731 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
733 Info.UsesHMAC = true;
734 HMAC = new HMACContext;
735 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
739 if ( ASDCP_SUCCESS(result) )
741 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
742 Options.mca_config, Options.edit_rate);
746 if ( ASDCP_SUCCESS(result) )
748 result = Parser.Reset();
751 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
753 result = Parser.ReadFrame(FrameBuffer);
755 if ( ASDCP_SUCCESS(result) )
757 if ( Options.verbose_flag )
758 FrameBuffer.Dump(stderr, Options.fb_dump_size);
760 if ( ! Options.no_write_flag )
762 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
764 // The Writer class will forward the last block of ciphertext
765 // to the encryption context for use as the IV for the next
766 // frame. If you want to use non-sequitur IV values, un-comment
767 // the following line of code.
768 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
769 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
774 if ( result == RESULT_ENDOFFILE )
778 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
779 result = Writer.Finalize();
787 //------------------------------------------------------------------------------------------
791 // Write one or more plaintext timed text streams to a plaintext AS-02 file
792 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
795 write_timed_text_file(CommandOptions& Options)
797 AESEncContext* Context = 0;
798 HMACContext* HMAC = 0;
799 AS_02::TimedText::ST2052_TextParser Parser;
800 AS_02::TimedText::MXFWriter Writer;
801 TimedText::FrameBuffer FrameBuffer;
802 TimedText::TimedTextDescriptor TDesc;
803 byte_t IV_buf[CBC_BLOCK_SIZE];
804 Kumu::FortunaRNG RNG;
806 // set up essence parser
807 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
810 if ( ASDCP_SUCCESS(result) )
812 Parser.FillTimedTextDescriptor(TDesc);
813 TDesc.EditRate = Options.edit_rate;
814 TDesc.ContainerDuration = Options.duration;
815 FrameBuffer.Capacity(Options.fb_size);
817 if ( Options.verbose_flag )
819 fputs("IMF Timed-Text Descriptor:\n", stderr);
820 TimedText::DescriptorDump(TDesc);
824 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
826 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
827 Info.LabelSetType = LS_MXF_SMPTE;
829 if ( Options.asset_id_flag )
830 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
832 Kumu::GenRandomUUID(Info.AssetUUID);
834 // configure encryption
835 if( Options.key_flag )
837 Kumu::GenRandomUUID(Info.ContextID);
838 Info.EncryptedEssence = true;
840 if ( Options.key_id_flag )
842 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
846 create_random_uuid(Info.CryptographicKeyID);
849 Context = new AESEncContext;
850 result = Context->InitKey(Options.key_value);
852 if ( ASDCP_SUCCESS(result) )
853 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
855 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
857 Info.UsesHMAC = true;
858 HMAC = new HMACContext;
859 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
863 if ( ASDCP_SUCCESS(result) )
864 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
867 if ( ASDCP_FAILURE(result) )
871 TimedText::ResourceList_t::const_iterator ri;
873 result = Parser.ReadTimedTextResource(XMLDoc);
875 if ( ASDCP_SUCCESS(result) )
876 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
878 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
880 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
882 if ( ASDCP_SUCCESS(result) )
884 if ( Options.verbose_flag )
885 FrameBuffer.Dump(stderr, Options.fb_dump_size);
887 if ( ! Options.no_write_flag )
889 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
891 // The Writer class will forward the last block of ciphertext
892 // to the encryption context for use as the IV for the next
893 // frame. If you want to use non-sequitur IV values, un-comment
894 // the following line of code.
895 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
896 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
900 if ( result == RESULT_ENDOFFILE )
904 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
905 result = Writer.Finalize();
912 main(int argc, const char** argv)
914 Result_t result = RESULT_OK;
916 g_dict = &ASDCP::DefaultSMPTEDict();
919 CommandOptions Options(argc, argv);
921 if ( Options.version_flag )
924 if ( Options.help_flag )
927 if ( Options.show_ul_values_flag )
929 g_dict->Dump(stdout);
932 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
935 if ( Options.error_flag )
937 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
941 EssenceType_t EssenceType;
942 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
944 if ( ASDCP_SUCCESS(result) )
946 switch ( EssenceType )
949 result = write_JP2K_file(Options);
952 case ESS_PCM_24b_48k:
953 case ESS_PCM_24b_96k:
954 result = write_PCM_file(Options);
958 result = write_timed_text_file(Options);
962 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
963 Options.filenames.front().c_str());
968 if ( ASDCP_FAILURE(result) )
970 fputs("Program stopped on error.\n", stderr);
972 if ( result != RESULT_FAIL )
974 fputs(result, stderr);
986 // end as-02-wrap.cpp