2 Copyright (c) 2011-2014, 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-2014, 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>] [-A <w>/<h>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
115 [-D <depth>] [-e|-E] [-i] [-j <key-id-string>] [-k <key-string>]\n\
116 [-M] [-m <expr>] [-p <ul>] [-r <n>/<d>] [-R] [-s <seconds>]\n\
117 [-t <min>] [-T <max>] [-u] [-v] [-W] [-x <int>] [-X <int>] [-Y]\n\
118 [-z|-Z] <input-file>+ <output-file>\n\n",
119 PROGRAM_NAME, PROGRAM_NAME);
123 -h | -help - Show help\n\
124 -V - Show version information\n\
125 -a <uuid> - Specify the Asset ID of the file\n\
126 -A <w>/<h> - Set aspect ratio for image (default 4/3)\n\
127 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
128 Defaults to 4,194,304 (4MB)\n\
129 -C <ul> - Set ChannelAssignment UL value\n\
130 -d <duration> - Number of frames to process, default all\n\
131 -D <depth> - Component depth for YCbCr images (default: 10)\n\
132 -e - Encrypt JP2K headers (default)\n\
133 -E - Do not encrypt JP2K headers\n\
134 -F (0|1) - Set field dominance for interlaced image (default: 0)\n\
135 -i - Indicates input essence is interlaced fields (forces -Y)\n\
136 -j <key-id-str> - Write key ID instead of creating a random value\n\
137 -k <key-string> - Use key for ciphertext operations\n\
138 -M - Do not create HMAC values when writing\n\
139 -m <expr> - Write MCA labels using <expr>. Example:\n\
140 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
141 -p <ul> - Set broadcast profile\n\
142 -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
143 -R - Indicates RGB image essence (default)\n\
144 -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
145 -t <min> - Set RGB component minimum code value (default: 0)\n\
146 -T <max> - Set RGB component maximum code value (default: 1023)\n\
147 -u - Print UL catalog to stderr\n\
148 -v - Verbose, prints informative messages to stderr\n\
149 -W - Read input file only, do not write source file\n\
150 -x <int> - Horizontal subsampling degree (default: 2)\n\
151 -X <int> - Vertical subsampling degree (default: 2)\n\
152 -Y - Indicates YCbCr image essence (default: RGB)\n\
153 -z - Fail if j2c inputs have unequal parameters (default)\n\
154 -Z - Ignore unequal parameters in j2c inputs\n\
156 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
157 o All option arguments must be separated from the option by whitespace.\n\n");
161 static ASDCP::Rational
162 decode_rational(const char* str_rat)
165 ui32_t Num = atoi(str_rat);
168 const char* den_str = strrchr(str_rat, '/');
170 Den = atoi(den_str+1);
172 return ASDCP::Rational(Num, Den);
182 bool error_flag; // true if the given options are in error or not complete
183 bool key_flag; // true if an encryption key was given
184 bool asset_id_flag; // true if an asset ID was given
185 bool encrypt_header_flag; // true if j2c headers are to be encrypted
186 bool write_hmac; // true if HMAC values are to be generated and written
187 bool verbose_flag; // true if the verbose option was selected
188 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
189 bool no_write_flag; // true if no output files are to be written
190 bool version_flag; // true if the version display option was selected
191 bool help_flag; // true if the help display option was selected
192 ui32_t duration; // number of frames to be processed
193 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
194 bool use_cdci_descriptor; //
195 Rational edit_rate; // edit rate of JP2K sequence
196 ui32_t fb_size; // size of picture frame buffer
197 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
198 bool key_id_flag; // true if a key ID was given
199 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
200 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
201 std::string out_file; //
202 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
203 Kumu::PathList_t filenames; // list of filenames to be processed
205 UL channel_assignment;
206 ASDCP::MXF::AS02_MCAConfigParser mca_config;
212 ui32_t horizontal_subsampling;
213 ui32_t vertical_subsampling;
214 ui32_t component_depth;
216 ASDCP::Rational aspect_ratio;
217 ui8_t field_dominance;
218 ui32_t mxf_header_size;
220 //new attributes for AS-02 support
221 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
222 ui32_t partition_space; //Shim parameter partition_spacing
225 CommandOptions(int argc, const char** argv) :
226 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
227 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
228 no_write_flag(false), version_flag(false), help_flag(false),
229 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
230 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
231 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
232 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
233 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
234 mxf_header_size(16384)
236 memset(key_value, 0, KeyLen);
237 memset(key_id_value, 0, UUIDlen);
239 for ( int i = 1; i < argc; i++ )
242 if ( (strcmp( argv[i], "-help") == 0) )
248 if ( argv[i][0] == '-'
249 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
252 switch ( argv[i][1] )
255 TEST_EXTRA_ARG(i, 'A');
256 edit_rate = decode_rational(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 = abs(atoi(argv[i]));
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 = abs(atoi(argv[i]));
298 TEST_EXTRA_ARG(i, 'd');
299 duration = abs(atoi(argv[i]));
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 = abs(atoi(argv[i]));
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 edit_rate = decode_rational(argv[i]);
376 use_cdci_descriptor = false;
380 TEST_EXTRA_ARG(i, 's');
381 partition_space = abs(atoi(argv[i]));
385 TEST_EXTRA_ARG(i, 't');
386 rgba_MinRef = abs(atoi(argv[i]));
390 TEST_EXTRA_ARG(i, 'T');
391 rgba_MaxRef = abs(atoi(argv[i]));
394 case 'u': show_ul_values_flag = true; break;
395 case 'V': version_flag = true; break;
396 case 'v': verbose_flag = true; break;
397 case 'W': no_write_flag = true; break;
400 TEST_EXTRA_ARG(i, 'x');
401 horizontal_subsampling = abs(atoi(argv[i]));
405 TEST_EXTRA_ARG(i, 'X');
406 vertical_subsampling = abs(atoi(argv[i]));
410 use_cdci_descriptor = true;
413 case 'Z': j2c_pedantic = false; break;
414 case 'z': j2c_pedantic = true; break;
417 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
424 if ( argv[i][0] != '-' )
426 filenames.push_back(argv[i]);
430 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
436 if ( help_flag || version_flag )
439 if ( filenames.size() < 2 )
441 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
445 out_file = filenames.back();
446 filenames.pop_back();
448 if ( ! picture_coding.HasValue() )
450 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
458 //------------------------------------------------------------------------------------------
462 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
463 const ASDCP::Dictionary& dict,
464 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
465 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
467 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
470 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
471 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
474 write_JP2K_file(CommandOptions& Options)
476 AESEncContext* Context = 0;
477 HMACContext* HMAC = 0;
478 AS_02::JP2K::MXFWriter Writer;
479 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
480 JP2K::SequenceParser Parser;
481 byte_t IV_buf[CBC_BLOCK_SIZE];
482 Kumu::FortunaRNG RNG;
483 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
484 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
486 // set up essence parser
487 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
490 if ( ASDCP_SUCCESS(result) )
492 ASDCP::JP2K::PictureDescriptor PDesc;
493 Parser.FillPictureDescriptor(PDesc);
494 PDesc.EditRate = Options.edit_rate;
496 if ( Options.verbose_flag )
498 fprintf(stderr, "JPEG 2000 pictures\n");
499 fputs("PictureDescriptor:\n", stderr);
500 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
501 JP2K::PictureDescriptorDump(PDesc);
504 if ( Options.use_cdci_descriptor )
506 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
507 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
509 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
510 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
511 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
513 if ( ASDCP_SUCCESS(result) )
515 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
516 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
517 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
518 tmp_dscr->ComponentDepth = Options.component_depth;
519 tmp_dscr->FrameLayout = Options.frame_layout;
520 tmp_dscr->AspectRatio = Options.aspect_ratio;
521 tmp_dscr->FieldDominance = Options.field_dominance;
522 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
527 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
528 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
530 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
531 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
532 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
534 if ( ASDCP_SUCCESS(result) )
536 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
537 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
538 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
539 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
544 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
546 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
547 Info.LabelSetType = LS_MXF_SMPTE;
549 if ( Options.asset_id_flag )
550 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
552 Kumu::GenRandomUUID(Info.AssetUUID);
554 // configure encryption
555 if( Options.key_flag )
557 Kumu::GenRandomUUID(Info.ContextID);
558 Info.EncryptedEssence = true;
560 if ( Options.key_id_flag )
561 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
563 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
565 Context = new AESEncContext;
566 result = Context->InitKey(Options.key_value);
568 if ( ASDCP_SUCCESS(result) )
569 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
571 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
573 Info.UsesHMAC = true;
574 HMAC = new HMACContext;
575 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
579 if ( ASDCP_SUCCESS(result) )
581 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
582 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
586 if ( ASDCP_SUCCESS(result) )
589 result = Parser.Reset();
591 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
593 result = Parser.ReadFrame(FrameBuffer);
595 if ( ASDCP_SUCCESS(result) )
597 if ( Options.verbose_flag )
598 FrameBuffer.Dump(stderr, Options.fb_dump_size);
600 if ( Options.encrypt_header_flag )
601 FrameBuffer.PlaintextOffset(0);
604 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
606 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
608 // The Writer class will forward the last block of ciphertext
609 // to the encryption context for use as the IV for the next
610 // frame. If you want to use non-sequitur IV values, un-comment
611 // the following line of code.
612 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
613 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
617 if ( result == RESULT_ENDOFFILE )
621 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
622 result = Writer.Finalize();
627 //------------------------------------------------------------------------------------------
631 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
632 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
635 write_PCM_file(CommandOptions& Options)
637 AESEncContext* Context = 0;
638 HMACContext* HMAC = 0;
639 PCMParserList Parser;
640 AS_02::PCM::MXFWriter Writer;
641 PCM::FrameBuffer FrameBuffer;
642 byte_t IV_buf[CBC_BLOCK_SIZE];
643 Kumu::FortunaRNG RNG;
644 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
646 // set up essence parser
647 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
650 if ( ASDCP_SUCCESS(result) )
652 ASDCP::PCM::AudioDescriptor ADesc;
653 Parser.FillAudioDescriptor(ADesc);
655 ADesc.EditRate = Options.edit_rate;
656 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
658 if ( Options.verbose_flag )
661 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
662 ADesc.AudioSamplingRate.Quotient() / 1000.0,
663 RationalToString(Options.edit_rate, buf, 64),
664 PCM::CalcSamplesPerFrame(ADesc));
665 fputs("AudioDescriptor:\n", stderr);
666 PCM::AudioDescriptorDump(ADesc);
669 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
671 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
673 if ( Options.mca_config.empty() )
675 essence_descriptor->ChannelAssignment = Options.channel_assignment;
679 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
681 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
682 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
686 // this is the d-cinema MCA label, what is the one for IMF?
687 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
691 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
693 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
694 Info.LabelSetType = LS_MXF_SMPTE;
696 if ( Options.asset_id_flag )
697 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
699 Kumu::GenRandomUUID(Info.AssetUUID);
701 // configure encryption
702 if( Options.key_flag )
704 Kumu::GenRandomUUID(Info.ContextID);
705 Info.EncryptedEssence = true;
707 if ( Options.key_id_flag )
708 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
710 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
712 Context = new AESEncContext;
713 result = Context->InitKey(Options.key_value);
715 if ( ASDCP_SUCCESS(result) )
716 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
718 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
720 Info.UsesHMAC = true;
721 HMAC = new HMACContext;
722 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
726 if ( ASDCP_SUCCESS(result) )
728 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
729 Options.mca_config, Options.edit_rate);
733 if ( ASDCP_SUCCESS(result) )
735 result = Parser.Reset();
738 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
740 result = Parser.ReadFrame(FrameBuffer);
742 if ( ASDCP_SUCCESS(result) )
744 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
746 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
747 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
748 result = RESULT_ENDOFFILE;
752 if ( Options.verbose_flag )
753 FrameBuffer.Dump(stderr, Options.fb_dump_size);
755 if ( ! Options.no_write_flag )
757 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
759 // The Writer class will forward the last block of ciphertext
760 // to the encryption context for use as the IV for the next
761 // frame. If you want to use non-sequitur IV values, un-comment
762 // the following line of code.
763 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
764 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
769 if ( result == RESULT_ENDOFFILE )
773 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
774 result = Writer.Finalize();
782 // NOT YET, unfinished business with ST 2052-1
785 //------------------------------------------------------------------------------------------
789 // Write one or more plaintext timed text streams to a plaintext AS-02 file
790 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
793 write_timed_text_file(CommandOptions& Options)
795 AESEncContext* Context = 0;
796 HMACContext* HMAC = 0;
797 AS_02::TimedText::ST2052_TextParser Parser;
798 AS_02::TimedText::MXFWriter Writer;
799 TimedText::FrameBuffer FrameBuffer;
800 TimedText::TimedTextDescriptor TDesc;
801 byte_t IV_buf[CBC_BLOCK_SIZE];
802 Kumu::FortunaRNG RNG;
804 // set up essence parser
805 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
808 if ( ASDCP_SUCCESS(result) )
810 Parser.FillTimedTextDescriptor(TDesc);
811 FrameBuffer.Capacity(Options.fb_size);
813 if ( Options.verbose_flag )
815 fputs("IMF Timed-Text Descriptor:\n", stderr);
816 TimedText::DescriptorDump(TDesc);
820 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
822 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
823 if ( Options.asset_id_flag )
824 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
826 Kumu::GenRandomUUID(Info.AssetUUID);
828 // configure encryption
829 if( Options.key_flag )
831 Kumu::GenRandomUUID(Info.ContextID);
832 Info.EncryptedEssence = true;
834 if ( Options.key_id_flag )
835 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
837 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
839 Context = new AESEncContext;
840 result = Context->InitKey(Options.key_value);
842 if ( ASDCP_SUCCESS(result) )
843 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
845 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
847 Info.UsesHMAC = true;
848 HMAC = new HMACContext;
849 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
853 if ( ASDCP_SUCCESS(result) )
854 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
857 if ( ASDCP_FAILURE(result) )
861 TimedText::ResourceList_t::const_iterator ri;
863 result = Parser.ReadTimedTextResource(XMLDoc);
865 if ( ASDCP_SUCCESS(result) )
866 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
868 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
870 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
872 if ( ASDCP_SUCCESS(result) )
874 if ( Options.verbose_flag )
875 FrameBuffer.Dump(stderr, Options.fb_dump_size);
877 if ( ! Options.no_write_flag )
879 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
881 // The Writer class will forward the last block of ciphertext
882 // to the encryption context for use as the IV for the next
883 // frame. If you want to use non-sequitur IV values, un-comment
884 // the following line of code.
885 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
886 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
890 if ( result == RESULT_ENDOFFILE )
894 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
895 result = Writer.Finalize();
904 main(int argc, const char** argv)
906 Result_t result = RESULT_OK;
908 g_dict = &ASDCP::DefaultSMPTEDict();
911 CommandOptions Options(argc, argv);
913 if ( Options.version_flag )
916 if ( Options.help_flag )
919 if ( Options.show_ul_values_flag )
921 g_dict->Dump(stdout);
924 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
927 if ( Options.error_flag )
929 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
933 EssenceType_t EssenceType;
934 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
936 if ( ASDCP_SUCCESS(result) )
938 switch ( EssenceType )
941 result = write_JP2K_file(Options);
944 case ESS_PCM_24b_48k:
945 case ESS_PCM_24b_96k:
946 result = write_PCM_file(Options);
950 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
951 Options.filenames.front().c_str());
956 if ( ASDCP_FAILURE(result) )
958 fputs("Program stopped on error.\n", stderr);
960 if ( result != RESULT_FAIL )
962 fputs(result, stderr);
974 // end as-02-wrap.cpp