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
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");
171 static ASDCP::Rational
172 decode_rational(const char* str_rat)
175 ui32_t Num = atoi(str_rat);
178 const char* den_str = strrchr(str_rat, '/');
180 Den = atoi(den_str+1);
182 return ASDCP::Rational(Num, Den);
192 bool error_flag; // true if the given options are in error or not complete
193 bool key_flag; // true if an encryption key was given
194 bool asset_id_flag; // true if an asset ID was given
195 bool encrypt_header_flag; // true if j2c headers are to be encrypted
196 bool write_hmac; // true if HMAC values are to be generated and written
197 bool verbose_flag; // true if the verbose option was selected
198 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
199 bool no_write_flag; // true if no output files are to be written
200 bool version_flag; // true if the version display option was selected
201 bool help_flag; // true if the help display option was selected
202 ui32_t duration; // number of frames to be processed
203 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
204 bool use_cdci_descriptor; //
205 Rational edit_rate; // edit rate of JP2K sequence
206 ui32_t fb_size; // size of picture frame buffer
207 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
208 bool key_id_flag; // true if a key ID was given
209 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
210 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
211 std::string out_file; //
212 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
213 Kumu::PathList_t filenames; // list of filenames to be processed
215 UL channel_assignment;
216 ASDCP::MXF::AS02_MCAConfigParser mca_config;
222 ui32_t horizontal_subsampling;
223 ui32_t vertical_subsampling;
224 ui32_t component_depth;
226 ASDCP::Rational aspect_ratio;
227 ui8_t field_dominance;
228 ui32_t mxf_header_size;
230 //new attributes for AS-02 support
231 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
232 ui32_t partition_space; //Shim parameter partition_spacing
235 CommandOptions(int argc, const char** argv) :
236 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
237 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
238 no_write_flag(false), version_flag(false), help_flag(false),
239 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
240 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
241 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
242 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
243 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
244 mxf_header_size(16384)
246 memset(key_value, 0, KeyLen);
247 memset(key_id_value, 0, UUIDlen);
249 for ( int i = 1; i < argc; i++ )
252 if ( (strcmp( argv[i], "-help") == 0) )
258 if ( argv[i][0] == '-'
259 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
262 switch ( argv[i][1] )
265 TEST_EXTRA_ARG(i, 'A');
266 edit_rate = decode_rational(argv[i]);
270 asset_id_flag = true;
271 TEST_EXTRA_ARG(i, 'a');
274 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
276 if ( length != UUIDlen )
278 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
285 TEST_EXTRA_ARG(i, 'b');
286 fb_size = abs(atoi(argv[i]));
289 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
294 TEST_EXTRA_ARG(i, 'C');
295 if ( ! channel_assignment.DecodeHex(argv[i]) )
297 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
303 TEST_EXTRA_ARG(i, 'D');
304 component_depth = abs(atoi(argv[i]));
308 TEST_EXTRA_ARG(i, 'd');
309 duration = abs(atoi(argv[i]));
312 case 'E': encrypt_header_flag = false; break;
313 case 'e': encrypt_header_flag = true; break;
316 TEST_EXTRA_ARG(i, 'F');
317 field_dominance = abs(atoi(argv[i]));
318 if ( field_dominance > 1 )
320 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
325 case 'h': help_flag = true; break;
329 use_cdci_descriptor = true;
334 TEST_EXTRA_ARG(i, 'j');
337 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
339 if ( length != UUIDlen )
341 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
347 case 'k': key_flag = true;
348 TEST_EXTRA_ARG(i, 'k');
351 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
353 if ( length != KeyLen )
355 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
361 case 'M': write_hmac = false; break;
364 TEST_EXTRA_ARG(i, 'm');
365 if ( ! mca_config.DecodeString(argv[i]) )
372 TEST_EXTRA_ARG(i, 'p');
373 if ( ! picture_coding.DecodeHex(argv[i]) )
375 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
381 TEST_EXTRA_ARG(i, 'r');
382 edit_rate = decode_rational(argv[i]);
386 use_cdci_descriptor = false;
390 TEST_EXTRA_ARG(i, 's');
391 partition_space = abs(atoi(argv[i]));
395 TEST_EXTRA_ARG(i, 't');
396 rgba_MinRef = abs(atoi(argv[i]));
400 TEST_EXTRA_ARG(i, 'T');
401 rgba_MaxRef = abs(atoi(argv[i]));
404 case 'u': show_ul_values_flag = true; break;
405 case 'V': version_flag = true; break;
406 case 'v': verbose_flag = true; break;
407 case 'W': no_write_flag = true; break;
410 TEST_EXTRA_ARG(i, 'x');
411 horizontal_subsampling = abs(atoi(argv[i]));
415 TEST_EXTRA_ARG(i, 'X');
416 vertical_subsampling = abs(atoi(argv[i]));
420 use_cdci_descriptor = true;
423 case 'Z': j2c_pedantic = false; break;
424 case 'z': j2c_pedantic = true; break;
427 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
434 if ( argv[i][0] != '-' )
436 filenames.push_back(argv[i]);
440 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
446 if ( help_flag || version_flag )
449 if ( filenames.size() < 2 )
451 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
455 out_file = filenames.back();
456 filenames.pop_back();
458 if ( ! picture_coding.HasValue() )
460 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
468 //------------------------------------------------------------------------------------------
472 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
473 const ASDCP::Dictionary& dict,
474 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
475 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
477 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
480 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
481 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
484 write_JP2K_file(CommandOptions& Options)
486 AESEncContext* Context = 0;
487 HMACContext* HMAC = 0;
488 AS_02::JP2K::MXFWriter Writer;
489 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
490 JP2K::SequenceParser Parser;
491 byte_t IV_buf[CBC_BLOCK_SIZE];
492 Kumu::FortunaRNG RNG;
493 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
494 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
496 // set up essence parser
497 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
500 if ( ASDCP_SUCCESS(result) )
502 ASDCP::JP2K::PictureDescriptor PDesc;
503 Parser.FillPictureDescriptor(PDesc);
504 PDesc.EditRate = Options.edit_rate;
506 if ( Options.verbose_flag )
508 fprintf(stderr, "JPEG 2000 pictures\n");
509 fputs("PictureDescriptor:\n", stderr);
510 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
511 JP2K::PictureDescriptorDump(PDesc);
514 if ( Options.use_cdci_descriptor )
516 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
517 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
519 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
520 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
521 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
523 if ( ASDCP_SUCCESS(result) )
525 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
526 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
527 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
528 tmp_dscr->ComponentDepth = Options.component_depth;
529 tmp_dscr->FrameLayout = Options.frame_layout;
530 tmp_dscr->AspectRatio = Options.aspect_ratio;
531 tmp_dscr->FieldDominance = Options.field_dominance;
532 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
537 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
538 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
540 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
541 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
542 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
544 if ( ASDCP_SUCCESS(result) )
546 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
547 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
548 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
549 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
554 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
556 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
557 Info.LabelSetType = LS_MXF_SMPTE;
559 if ( Options.asset_id_flag )
560 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
562 Kumu::GenRandomUUID(Info.AssetUUID);
564 // configure encryption
565 if( Options.key_flag )
567 Kumu::GenRandomUUID(Info.ContextID);
568 Info.EncryptedEssence = true;
570 if ( Options.key_id_flag )
572 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
576 create_random_uuid(Info.CryptographicKeyID);
579 Context = new AESEncContext;
580 result = Context->InitKey(Options.key_value);
582 if ( ASDCP_SUCCESS(result) )
583 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
585 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
587 Info.UsesHMAC = true;
588 HMAC = new HMACContext;
589 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
593 if ( ASDCP_SUCCESS(result) )
595 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
596 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
600 if ( ASDCP_SUCCESS(result) )
603 result = Parser.Reset();
605 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
607 result = Parser.ReadFrame(FrameBuffer);
609 if ( ASDCP_SUCCESS(result) )
611 if ( Options.verbose_flag )
612 FrameBuffer.Dump(stderr, Options.fb_dump_size);
614 if ( Options.encrypt_header_flag )
615 FrameBuffer.PlaintextOffset(0);
618 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
620 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
622 // The Writer class will forward the last block of ciphertext
623 // to the encryption context for use as the IV for the next
624 // frame. If you want to use non-sequitur IV values, un-comment
625 // the following line of code.
626 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
627 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
631 if ( result == RESULT_ENDOFFILE )
635 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
636 result = Writer.Finalize();
641 //------------------------------------------------------------------------------------------
645 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
646 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
649 write_PCM_file(CommandOptions& Options)
651 AESEncContext* Context = 0;
652 HMACContext* HMAC = 0;
653 PCMParserList Parser;
654 AS_02::PCM::MXFWriter Writer;
655 PCM::FrameBuffer FrameBuffer;
656 byte_t IV_buf[CBC_BLOCK_SIZE];
657 Kumu::FortunaRNG RNG;
658 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
660 // set up essence parser
661 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
664 if ( ASDCP_SUCCESS(result) )
666 ASDCP::PCM::AudioDescriptor ADesc;
667 Parser.FillAudioDescriptor(ADesc);
669 ADesc.EditRate = Options.edit_rate;
670 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
672 if ( Options.verbose_flag )
675 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
676 ADesc.AudioSamplingRate.Quotient() / 1000.0,
677 RationalToString(Options.edit_rate, buf, 64),
678 PCM::CalcSamplesPerFrame(ADesc));
679 fputs("AudioDescriptor:\n", stderr);
680 PCM::AudioDescriptorDump(ADesc);
683 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
685 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
687 if ( Options.mca_config.empty() )
689 essence_descriptor->ChannelAssignment = Options.channel_assignment;
693 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
695 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
696 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
700 // this is the d-cinema MCA label, what is the one for IMF?
701 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
705 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
707 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
708 Info.LabelSetType = LS_MXF_SMPTE;
710 if ( Options.asset_id_flag )
711 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
713 Kumu::GenRandomUUID(Info.AssetUUID);
715 // configure encryption
716 if( Options.key_flag )
718 Kumu::GenRandomUUID(Info.ContextID);
719 Info.EncryptedEssence = true;
721 if ( Options.key_id_flag )
723 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
727 create_random_uuid(Info.CryptographicKeyID);
730 Context = new AESEncContext;
731 result = Context->InitKey(Options.key_value);
733 if ( ASDCP_SUCCESS(result) )
734 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
736 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
738 Info.UsesHMAC = true;
739 HMAC = new HMACContext;
740 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
744 if ( ASDCP_SUCCESS(result) )
746 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
747 Options.mca_config, Options.edit_rate);
751 if ( ASDCP_SUCCESS(result) )
753 result = Parser.Reset();
756 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
758 result = Parser.ReadFrame(FrameBuffer);
760 if ( ASDCP_SUCCESS(result) )
762 if ( Options.verbose_flag )
763 FrameBuffer.Dump(stderr, Options.fb_dump_size);
765 if ( ! Options.no_write_flag )
767 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
769 // The Writer class will forward the last block of ciphertext
770 // to the encryption context for use as the IV for the next
771 // frame. If you want to use non-sequitur IV values, un-comment
772 // the following line of code.
773 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
774 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
779 if ( result == RESULT_ENDOFFILE )
783 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
784 result = Writer.Finalize();
792 //------------------------------------------------------------------------------------------
796 // Write one or more plaintext timed text streams to a plaintext AS-02 file
797 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
800 write_timed_text_file(CommandOptions& Options)
802 AESEncContext* Context = 0;
803 HMACContext* HMAC = 0;
804 AS_02::TimedText::ST2052_TextParser Parser;
805 AS_02::TimedText::MXFWriter Writer;
806 TimedText::FrameBuffer FrameBuffer;
807 TimedText::TimedTextDescriptor TDesc;
808 byte_t IV_buf[CBC_BLOCK_SIZE];
809 Kumu::FortunaRNG RNG;
811 // set up essence parser
812 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
815 if ( ASDCP_SUCCESS(result) )
817 Parser.FillTimedTextDescriptor(TDesc);
818 FrameBuffer.Capacity(Options.fb_size);
820 if ( Options.verbose_flag )
822 fputs("IMF Timed-Text Descriptor:\n", stderr);
823 TimedText::DescriptorDump(TDesc);
827 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
829 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
830 if ( Options.asset_id_flag )
831 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
833 Kumu::GenRandomUUID(Info.AssetUUID);
835 // configure encryption
836 if( Options.key_flag )
838 Kumu::GenRandomUUID(Info.ContextID);
839 Info.EncryptedEssence = true;
841 if ( Options.key_id_flag )
843 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
847 create_random_uuid(Info.CryptographicKeyID);
850 Context = new AESEncContext;
851 result = Context->InitKey(Options.key_value);
853 if ( ASDCP_SUCCESS(result) )
854 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
856 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
858 Info.UsesHMAC = true;
859 HMAC = new HMACContext;
860 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
864 if ( ASDCP_SUCCESS(result) )
865 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
868 if ( ASDCP_FAILURE(result) )
872 TimedText::ResourceList_t::const_iterator ri;
874 result = Parser.ReadTimedTextResource(XMLDoc);
876 if ( ASDCP_SUCCESS(result) )
877 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
879 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
881 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
883 if ( ASDCP_SUCCESS(result) )
885 if ( Options.verbose_flag )
886 FrameBuffer.Dump(stderr, Options.fb_dump_size);
888 if ( ! Options.no_write_flag )
890 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
892 // The Writer class will forward the last block of ciphertext
893 // to the encryption context for use as the IV for the next
894 // frame. If you want to use non-sequitur IV values, un-comment
895 // the following line of code.
896 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
897 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
901 if ( result == RESULT_ENDOFFILE )
905 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
906 result = Writer.Finalize();
913 main(int argc, const char** argv)
915 Result_t result = RESULT_OK;
917 g_dict = &ASDCP::DefaultSMPTEDict();
920 CommandOptions Options(argc, argv);
922 if ( Options.version_flag )
925 if ( Options.help_flag )
928 if ( Options.show_ul_values_flag )
930 g_dict->Dump(stdout);
933 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
936 if ( Options.error_flag )
938 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
942 EssenceType_t EssenceType;
943 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
945 if ( ASDCP_SUCCESS(result) )
947 switch ( EssenceType )
950 result = write_JP2K_file(Options);
953 case ESS_PCM_24b_48k:
954 case ESS_PCM_24b_96k:
955 result = write_PCM_file(Options);
959 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
960 Options.filenames.front().c_str());
965 if ( ASDCP_FAILURE(result) )
967 fputs("Program stopped on error.\n", stderr);
969 if ( result != RESULT_FAIL )
971 fputs(result, stderr);
983 // end as-02-wrap.cpp