2 Copyright (c) 2011-2013, 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;
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)); \
96 banner(FILE* stream = stdout)
100 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
101 asdcplib may be copied only under the terms of the license found at\n\
102 the top of every file in the asdcplib distribution kit.\n\n\
103 Specify the -h (help) option for further information about %s\n\n",
104 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
109 usage(FILE* stream = stdout)
112 USAGE: %s [-h|-help] [-V]\n\
114 %s [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
115 [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
116 [-M] [-m <expr>] [-r <n>/<d>] [-s <seconds>] [-v] [-W]\n\
117 [-z|-Z] <input-file>+ <output-file>\n\n",
118 PROGRAM_NAME, PROGRAM_NAME);
122 -C <UL> - Set ChannelAssignment UL value\n\
123 -h | -help - Show help\n\
124 -V - Show version information\n\
125 -e - Encrypt JP2K headers (default)\n\
126 -E - Do not encrypt JP2K headers\n\
127 -j <key-id-str> - Write key ID instead of creating a random value\n\
128 -k <key-string> - Use key for ciphertext operations\n\
129 -M - Do not create HMAC values when writing\n\
130 -m <expr> - Write MCA labels using <expr>. Example:\n\
131 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
132 -a <UUID> - Specify the Asset ID of the file\n\
133 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
134 Defaults to 4,194,304 (4MB)\n\
135 -d <duration> - Number of frames to process, default all\n\
136 -f <start-frame> - Starting frame number, default 0\n\
137 -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
138 -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
139 -v - Verbose, prints informative messages to stderr\n\
140 -W - Read input file only, do not write source file\n\
141 -z - Fail if j2c inputs have unequal parameters (default)\n\
142 -Z - Ignore unequal parameters in j2c inputs\n\
144 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
145 o All option arguments must be separated from the option by whitespace.\n\n");
149 static ASDCP::Rational
150 decode_rational(const char* str_rat)
153 ui32_t Num = atoi(str_rat);
156 const char* den_str = strrchr(str_rat, ' ');
158 Den = atoi(den_str+1);
160 return ASDCP::Rational(Num, Den);
169 bool error_flag; // true if the given options are in error or not complete
170 bool key_flag; // true if an encryption key was given
171 bool asset_id_flag; // true if an asset ID was given
172 bool encrypt_header_flag; // true if j2c headers are to be encrypted
173 bool write_hmac; // true if HMAC values are to be generated and written
174 bool verbose_flag; // true if the verbose option was selected
175 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
176 bool no_write_flag; // true if no output files are to be written
177 bool version_flag; // true if the version display option was selected
178 bool help_flag; // true if the help display option was selected
179 ui32_t start_frame; // frame number to begin processing
180 ui32_t duration; // number of frames to be processed
181 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
182 bool use_cdci_descriptor; //
183 Rational edit_rate; // edit rate of JP2K sequence
184 ui32_t fb_size; // size of picture frame buffer
185 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
186 bool key_id_flag; // true if a key ID was given
187 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
188 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
189 std::string out_file; //
190 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
191 Kumu::PathList_t filenames; // list of filenames to be processed
192 UL channel_assignment;
193 ASDCP::MXF::AS02_MCAConfigParser mca_config;
197 ui32_t mxf_header_size;
199 //new attributes for AS-02 support
200 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
201 ui32_t partition_space; //Shim parameter partition_spacing
204 CommandOptions(int argc, const char** argv) :
205 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
206 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
207 no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
208 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
209 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
210 mca_config(g_dict), cdci_depth(0), rgba_MaxRef(1024), rgba_MinRef(0), mxf_header_size(16384)
213 memset(key_value, 0, KeyLen);
214 memset(key_id_value, 0, UUIDlen);
216 for ( int i = 1; i < argc; i++ )
219 if ( (strcmp( argv[i], "-help") == 0) )
225 if ( argv[i][0] == '-'
226 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
229 switch ( argv[i][1] )
232 asset_id_flag = true;
233 TEST_EXTRA_ARG(i, 'a');
236 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
238 if ( length != UUIDlen )
240 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
247 TEST_EXTRA_ARG(i, 'b');
248 fb_size = abs(atoi(argv[i]));
251 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
256 TEST_EXTRA_ARG(i, 'U');
257 if ( ! channel_assignment.DecodeHex(argv[i]) )
259 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
265 TEST_EXTRA_ARG(i, 'd');
266 duration = abs(atoi(argv[i]));
269 case 'E': encrypt_header_flag = false; break;
270 case 'e': encrypt_header_flag = true; break;
273 TEST_EXTRA_ARG(i, 'f');
274 start_frame = abs(atoi(argv[i]));
277 case 'h': help_flag = true; break;
279 case 'j': key_id_flag = true;
280 TEST_EXTRA_ARG(i, 'j');
283 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
285 if ( length != UUIDlen )
287 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
293 case 'k': key_flag = true;
294 TEST_EXTRA_ARG(i, 'k');
297 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
299 if ( length != KeyLen )
301 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
307 case 'M': write_hmac = false; break;
310 TEST_EXTRA_ARG(i, 'm');
311 if ( ! mca_config.DecodeString(argv[i]) )
318 TEST_EXTRA_ARG(i, 'r');
319 edit_rate = decode_rational(argv[i]);
323 TEST_EXTRA_ARG(i, 's');
324 partition_space = abs(atoi(argv[i]));
327 case 'u': show_ul_values_flag = true; break;
328 case 'V': version_flag = true; break;
329 case 'v': verbose_flag = true; break;
330 case 'W': no_write_flag = true; break;
331 case 'Z': j2c_pedantic = false; break;
332 case 'z': j2c_pedantic = true; break;
335 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
342 if ( argv[i][0] != '-' )
344 filenames.push_back(argv[i]);
348 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
354 if ( help_flag || version_flag )
357 if ( filenames.size() < 2 )
359 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
363 out_file = filenames.back();
364 filenames.pop_back();
370 //------------------------------------------------------------------------------------------
374 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
375 const ASDCP::Dictionary& dict,
376 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
377 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
379 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
382 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
383 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
386 write_JP2K_file(CommandOptions& Options)
388 AESEncContext* Context = 0;
389 HMACContext* HMAC = 0;
390 AS_02::JP2K::MXFWriter Writer;
391 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
392 JP2K::SequenceParser Parser;
393 byte_t IV_buf[CBC_BLOCK_SIZE];
394 Kumu::FortunaRNG RNG;
395 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
396 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
398 // set up essence parser
399 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
402 if ( ASDCP_SUCCESS(result) )
404 ASDCP::JP2K::PictureDescriptor PDesc;
405 Parser.FillPictureDescriptor(PDesc);
406 PDesc.EditRate = Options.edit_rate;
408 if ( Options.verbose_flag )
410 fprintf(stderr, "JPEG 2000 pictures\n");
411 fputs("PictureDescriptor:\n", stderr);
412 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
413 JP2K::PictureDescriptorDump(PDesc);
416 if ( Options.use_cdci_descriptor )
418 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
419 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
421 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
422 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
423 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
425 if ( ASDCP_SUCCESS(result) )
427 // TODO, select profile
428 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
429 tmp_dscr->ComponentDepth = Options.cdci_depth;
433 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
438 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
439 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
441 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
442 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
443 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
445 if ( ASDCP_SUCCESS(result) )
447 // TODO, select profile
448 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
449 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
450 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
454 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
459 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
461 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
462 Info.LabelSetType = LS_MXF_SMPTE;
464 if ( Options.asset_id_flag )
465 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
467 Kumu::GenRandomUUID(Info.AssetUUID);
469 // configure encryption
470 if( Options.key_flag )
472 Kumu::GenRandomUUID(Info.ContextID);
473 Info.EncryptedEssence = true;
475 if ( Options.key_id_flag )
476 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
478 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
480 Context = new AESEncContext;
481 result = Context->InitKey(Options.key_value);
483 if ( ASDCP_SUCCESS(result) )
484 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
486 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
488 Info.UsesHMAC = true;
489 HMAC = new HMACContext;
490 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
494 if ( ASDCP_SUCCESS(result) )
496 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
497 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
501 if ( ASDCP_SUCCESS(result) )
504 result = Parser.Reset();
506 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
508 result = Parser.ReadFrame(FrameBuffer);
510 if ( ASDCP_SUCCESS(result) )
512 if ( Options.verbose_flag )
513 FrameBuffer.Dump(stderr, Options.fb_dump_size);
515 if ( Options.encrypt_header_flag )
516 FrameBuffer.PlaintextOffset(0);
519 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
521 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
523 // The Writer class will forward the last block of ciphertext
524 // to the encryption context for use as the IV for the next
525 // frame. If you want to use non-sequitur IV values, un-comment
526 // the following line of code.
527 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
528 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
532 if ( result == RESULT_ENDOFFILE )
536 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
537 result = Writer.Finalize();
542 //------------------------------------------------------------------------------------------
546 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
547 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
550 write_PCM_file(CommandOptions& Options)
552 AESEncContext* Context = 0;
553 HMACContext* HMAC = 0;
554 PCMParserList Parser;
555 AS_02::PCM::MXFWriter Writer;
556 PCM::FrameBuffer FrameBuffer;
557 byte_t IV_buf[CBC_BLOCK_SIZE];
558 Kumu::FortunaRNG RNG;
559 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
561 // set up essence parser
562 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
565 if ( ASDCP_SUCCESS(result) )
567 ASDCP::PCM::AudioDescriptor ADesc;
568 Parser.FillAudioDescriptor(ADesc);
570 ADesc.EditRate = Options.edit_rate;
571 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
573 if ( Options.verbose_flag )
576 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
577 ADesc.AudioSamplingRate.Quotient() / 1000.0,
578 RationalToString(Options.edit_rate, buf, 64),
579 PCM::CalcSamplesPerFrame(ADesc));
580 fputs("AudioDescriptor:\n", stderr);
581 PCM::AudioDescriptorDump(ADesc);
584 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
586 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
588 if ( Options.mca_config.empty() )
590 essence_descriptor->ChannelAssignment = Options.channel_assignment;
594 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
596 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
597 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
601 // this is the d-cinema MCA label, what is the one for IMF?
602 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
606 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
608 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
609 Info.LabelSetType = LS_MXF_SMPTE;
611 if ( Options.asset_id_flag )
612 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
614 Kumu::GenRandomUUID(Info.AssetUUID);
616 // configure encryption
617 if( Options.key_flag )
619 Kumu::GenRandomUUID(Info.ContextID);
620 Info.EncryptedEssence = true;
622 if ( Options.key_id_flag )
623 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
625 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
627 Context = new AESEncContext;
628 result = Context->InitKey(Options.key_value);
630 if ( ASDCP_SUCCESS(result) )
631 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
633 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
635 Info.UsesHMAC = true;
636 HMAC = new HMACContext;
637 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
641 if ( ASDCP_SUCCESS(result) )
643 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
644 Options.mca_config, Options.edit_rate);
648 if ( ASDCP_SUCCESS(result) )
650 result = Parser.Reset();
653 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
655 result = Parser.ReadFrame(FrameBuffer);
657 if ( ASDCP_SUCCESS(result) )
659 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
661 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
662 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
663 result = RESULT_ENDOFFILE;
667 if ( Options.verbose_flag )
668 FrameBuffer.Dump(stderr, Options.fb_dump_size);
670 if ( ! Options.no_write_flag )
672 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
674 // The Writer class will forward the last block of ciphertext
675 // to the encryption context for use as the IV for the next
676 // frame. If you want to use non-sequitur IV values, un-comment
677 // the following line of code.
678 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
679 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
684 if ( result == RESULT_ENDOFFILE )
688 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
689 result = Writer.Finalize();
695 //------------------------------------------------------------------------------------------
699 // Write one or more plaintext timed text streams to a plaintext AS-02 file
700 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
703 write_timed_text_file(CommandOptions& Options)
705 AESEncContext* Context = 0;
706 HMACContext* HMAC = 0;
707 AS_02::TimedText::ST2052_TextParser Parser;
708 AS_02::TimedText::MXFWriter Writer;
709 TimedText::FrameBuffer FrameBuffer;
710 TimedText::TimedTextDescriptor TDesc;
711 byte_t IV_buf[CBC_BLOCK_SIZE];
712 Kumu::FortunaRNG RNG;
714 // set up essence parser
715 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
718 if ( ASDCP_SUCCESS(result) )
720 Parser.FillTimedTextDescriptor(TDesc);
721 FrameBuffer.Capacity(Options.fb_size);
723 if ( Options.verbose_flag )
725 fputs("IMF Timed-Text Descriptor:\n", stderr);
726 TimedText::DescriptorDump(TDesc);
730 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
732 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
733 if ( Options.asset_id_flag )
734 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
736 Kumu::GenRandomUUID(Info.AssetUUID);
738 // configure encryption
739 if( Options.key_flag )
741 Kumu::GenRandomUUID(Info.ContextID);
742 Info.EncryptedEssence = true;
744 if ( Options.key_id_flag )
745 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
747 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
749 Context = new AESEncContext;
750 result = Context->InitKey(Options.key_value);
752 if ( ASDCP_SUCCESS(result) )
753 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
755 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
757 Info.UsesHMAC = true;
758 HMAC = new HMACContext;
759 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
763 if ( ASDCP_SUCCESS(result) )
764 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
767 if ( ASDCP_FAILURE(result) )
771 TimedText::ResourceList_t::const_iterator ri;
773 result = Parser.ReadTimedTextResource(XMLDoc);
775 if ( ASDCP_SUCCESS(result) )
776 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
778 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
780 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
782 if ( ASDCP_SUCCESS(result) )
784 if ( Options.verbose_flag )
785 FrameBuffer.Dump(stderr, Options.fb_dump_size);
787 if ( ! Options.no_write_flag )
789 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
791 // The Writer class will forward the last block of ciphertext
792 // to the encryption context for use as the IV for the next
793 // frame. If you want to use non-sequitur IV values, un-comment
794 // the following line of code.
795 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
796 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
800 if ( result == RESULT_ENDOFFILE )
804 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
805 result = Writer.Finalize();
812 main(int argc, const char** argv)
814 Result_t result = RESULT_OK;
816 g_dict = &ASDCP::DefaultSMPTEDict();
819 CommandOptions Options(argc, argv);
821 if ( Options.version_flag )
824 if ( Options.help_flag )
827 if ( Options.show_ul_values_flag )
829 g_dict->Dump(stdout);
832 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
835 if ( Options.error_flag )
837 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
841 EssenceType_t EssenceType;
842 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
844 if ( ASDCP_SUCCESS(result) )
846 switch ( EssenceType )
849 result = write_JP2K_file(Options);
852 case ESS_PCM_24b_48k:
853 case ESS_PCM_24b_96k:
854 result = write_PCM_file(Options);
858 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
859 Options.filenames.front().c_str());
864 if ( ASDCP_FAILURE(result) )
866 fputs("Program stopped on error.\n", stderr);
868 if ( result != RESULT_FAIL )
870 fputs(result, stderr);
882 // end as-02-wrap.cpp