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>] [-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 -h | -help - Show help\n\
123 -V - Show version information\n\
124 -a <UUID> - Specify the Asset ID of the file\n\
125 -A <w>/<h> - Set aspect ratio for image (default 4/3)\n\
126 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
127 Defaults to 4,194,304 (4MB)\n\
128 -C <UL> - Set ChannelAssignment UL value\n\
129 -d <duration> - Number of frames to process, default all\n\
130 -D <depth> - Component depth for YCbCr images (default: 10)\n\
131 -e - Encrypt JP2K headers (default)\n\
132 -E - Do not encrypt JP2K headers\n\
133 -f <start-frame> - Starting frame number, default 0\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: 1024)\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 start_frame; // frame number to begin processing
193 ui32_t duration; // number of frames to be processed
194 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
195 bool use_cdci_descriptor; //
196 Rational edit_rate; // edit rate of JP2K sequence
197 ui32_t fb_size; // size of picture frame buffer
198 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
199 bool key_id_flag; // true if a key ID was given
200 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
201 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
202 std::string out_file; //
203 bool show_ul_values_flag; /// if true, dump the UL table before going tp work.
204 Kumu::PathList_t filenames; // list of filenames to be processed
206 UL channel_assignment;
207 ASDCP::MXF::AS02_MCAConfigParser mca_config;
213 ui32_t horizontal_subsampling;
214 ui32_t vertical_subsampling;
215 ui32_t component_depth;
217 ASDCP::Rational aspect_ratio;
218 ui8_t field_dominance;
219 ui32_t mxf_header_size;
221 //new attributes for AS-02 support
222 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
223 ui32_t partition_space; //Shim parameter partition_spacing
226 CommandOptions(int argc, const char** argv) :
227 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
228 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
229 no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
230 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
231 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
232 mca_config(g_dict), rgba_MaxRef(1024), rgba_MinRef(0),
233 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
234 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
235 mxf_header_size(16384)
237 memset(key_value, 0, KeyLen);
238 memset(key_id_value, 0, UUIDlen);
240 for ( int i = 1; i < argc; i++ )
243 if ( (strcmp( argv[i], "-help") == 0) )
249 if ( argv[i][0] == '-'
250 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
253 switch ( argv[i][1] )
256 TEST_EXTRA_ARG(i, 'A');
257 edit_rate = decode_rational(argv[i]);
261 asset_id_flag = true;
262 TEST_EXTRA_ARG(i, 'a');
265 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
267 if ( length != UUIDlen )
269 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
276 TEST_EXTRA_ARG(i, 'b');
277 fb_size = abs(atoi(argv[i]));
280 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
285 TEST_EXTRA_ARG(i, 'C');
286 if ( ! channel_assignment.DecodeHex(argv[i]) )
288 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
294 TEST_EXTRA_ARG(i, 'D');
295 component_depth = abs(atoi(argv[i]));
299 TEST_EXTRA_ARG(i, 'd');
300 duration = abs(atoi(argv[i]));
303 case 'E': encrypt_header_flag = false; break;
304 case 'e': encrypt_header_flag = true; break;
307 TEST_EXTRA_ARG(i, 'F');
308 field_dominance = abs(atoi(argv[i]));
309 if ( field_dominance > 1 )
311 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
317 TEST_EXTRA_ARG(i, 'f');
318 start_frame = abs(atoi(argv[i]));
321 case 'h': help_flag = true; break;
325 use_cdci_descriptor = true;
330 TEST_EXTRA_ARG(i, 'j');
333 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
335 if ( length != UUIDlen )
337 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
343 case 'k': key_flag = true;
344 TEST_EXTRA_ARG(i, 'k');
347 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
349 if ( length != KeyLen )
351 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
357 case 'M': write_hmac = false; break;
360 TEST_EXTRA_ARG(i, 'm');
361 if ( ! mca_config.DecodeString(argv[i]) )
368 TEST_EXTRA_ARG(i, 'p');
369 if ( ! picture_coding.DecodeHex(argv[i]) )
371 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
377 TEST_EXTRA_ARG(i, 'r');
378 edit_rate = decode_rational(argv[i]);
382 use_cdci_descriptor = false;
386 TEST_EXTRA_ARG(i, 's');
387 partition_space = abs(atoi(argv[i]));
391 TEST_EXTRA_ARG(i, 't');
392 rgba_MinRef = abs(atoi(argv[i]));
396 TEST_EXTRA_ARG(i, 'T');
397 rgba_MaxRef = abs(atoi(argv[i]));
400 case 'u': show_ul_values_flag = true; break;
401 case 'V': version_flag = true; break;
402 case 'v': verbose_flag = true; break;
403 case 'W': no_write_flag = true; break;
406 TEST_EXTRA_ARG(i, 'x');
407 horizontal_subsampling = abs(atoi(argv[i]));
411 TEST_EXTRA_ARG(i, 'X');
412 vertical_subsampling = abs(atoi(argv[i]));
416 use_cdci_descriptor = true;
419 case 'Z': j2c_pedantic = false; break;
420 case 'z': j2c_pedantic = true; break;
423 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
430 if ( argv[i][0] != '-' )
432 filenames.push_back(argv[i]);
436 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
442 if ( help_flag || version_flag )
445 if ( filenames.size() < 2 )
447 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
451 out_file = filenames.back();
452 filenames.pop_back();
454 if ( ! picture_coding.HasValue() )
456 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
464 //------------------------------------------------------------------------------------------
468 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
469 const ASDCP::Dictionary& dict,
470 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
471 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
473 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
476 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
477 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
480 write_JP2K_file(CommandOptions& Options)
482 AESEncContext* Context = 0;
483 HMACContext* HMAC = 0;
484 AS_02::JP2K::MXFWriter Writer;
485 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
486 JP2K::SequenceParser Parser;
487 byte_t IV_buf[CBC_BLOCK_SIZE];
488 Kumu::FortunaRNG RNG;
489 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
490 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
492 // set up essence parser
493 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
496 if ( ASDCP_SUCCESS(result) )
498 ASDCP::JP2K::PictureDescriptor PDesc;
499 Parser.FillPictureDescriptor(PDesc);
500 PDesc.EditRate = Options.edit_rate;
502 if ( Options.verbose_flag )
504 fprintf(stderr, "JPEG 2000 pictures\n");
505 fputs("PictureDescriptor:\n", stderr);
506 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
507 JP2K::PictureDescriptorDump(PDesc);
510 if ( Options.use_cdci_descriptor )
512 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
513 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
515 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
516 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
517 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
519 if ( ASDCP_SUCCESS(result) )
521 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
522 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
523 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
524 tmp_dscr->ComponentDepth = Options.component_depth;
525 tmp_dscr->FrameLayout = Options.frame_layout;
526 tmp_dscr->AspectRatio = Options.aspect_ratio;
527 tmp_dscr->FieldDominance = Options.field_dominance;
528 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
533 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
534 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
536 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
537 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
538 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
540 if ( ASDCP_SUCCESS(result) )
542 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
543 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
544 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
545 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
550 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
552 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
553 Info.LabelSetType = LS_MXF_SMPTE;
555 if ( Options.asset_id_flag )
556 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
558 Kumu::GenRandomUUID(Info.AssetUUID);
560 // configure encryption
561 if( Options.key_flag )
563 Kumu::GenRandomUUID(Info.ContextID);
564 Info.EncryptedEssence = true;
566 if ( Options.key_id_flag )
567 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
569 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
571 Context = new AESEncContext;
572 result = Context->InitKey(Options.key_value);
574 if ( ASDCP_SUCCESS(result) )
575 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
577 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
579 Info.UsesHMAC = true;
580 HMAC = new HMACContext;
581 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
585 if ( ASDCP_SUCCESS(result) )
587 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
588 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
592 if ( ASDCP_SUCCESS(result) )
595 result = Parser.Reset();
597 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
599 result = Parser.ReadFrame(FrameBuffer);
601 if ( ASDCP_SUCCESS(result) )
603 if ( Options.verbose_flag )
604 FrameBuffer.Dump(stderr, Options.fb_dump_size);
606 if ( Options.encrypt_header_flag )
607 FrameBuffer.PlaintextOffset(0);
610 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
612 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
614 // The Writer class will forward the last block of ciphertext
615 // to the encryption context for use as the IV for the next
616 // frame. If you want to use non-sequitur IV values, un-comment
617 // the following line of code.
618 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
619 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
623 if ( result == RESULT_ENDOFFILE )
627 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
628 result = Writer.Finalize();
633 //------------------------------------------------------------------------------------------
637 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
638 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
641 write_PCM_file(CommandOptions& Options)
643 AESEncContext* Context = 0;
644 HMACContext* HMAC = 0;
645 PCMParserList Parser;
646 AS_02::PCM::MXFWriter Writer;
647 PCM::FrameBuffer FrameBuffer;
648 byte_t IV_buf[CBC_BLOCK_SIZE];
649 Kumu::FortunaRNG RNG;
650 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
652 // set up essence parser
653 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
656 if ( ASDCP_SUCCESS(result) )
658 ASDCP::PCM::AudioDescriptor ADesc;
659 Parser.FillAudioDescriptor(ADesc);
661 ADesc.EditRate = Options.edit_rate;
662 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
664 if ( Options.verbose_flag )
667 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
668 ADesc.AudioSamplingRate.Quotient() / 1000.0,
669 RationalToString(Options.edit_rate, buf, 64),
670 PCM::CalcSamplesPerFrame(ADesc));
671 fputs("AudioDescriptor:\n", stderr);
672 PCM::AudioDescriptorDump(ADesc);
675 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
677 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
679 if ( Options.mca_config.empty() )
681 essence_descriptor->ChannelAssignment = Options.channel_assignment;
685 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
687 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
688 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
692 // this is the d-cinema MCA label, what is the one for IMF?
693 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
697 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
699 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
700 Info.LabelSetType = LS_MXF_SMPTE;
702 if ( Options.asset_id_flag )
703 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
705 Kumu::GenRandomUUID(Info.AssetUUID);
707 // configure encryption
708 if( Options.key_flag )
710 Kumu::GenRandomUUID(Info.ContextID);
711 Info.EncryptedEssence = true;
713 if ( Options.key_id_flag )
714 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
716 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
718 Context = new AESEncContext;
719 result = Context->InitKey(Options.key_value);
721 if ( ASDCP_SUCCESS(result) )
722 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
724 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
726 Info.UsesHMAC = true;
727 HMAC = new HMACContext;
728 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
732 if ( ASDCP_SUCCESS(result) )
734 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
735 Options.mca_config, Options.edit_rate);
739 if ( ASDCP_SUCCESS(result) )
741 result = Parser.Reset();
744 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
746 result = Parser.ReadFrame(FrameBuffer);
748 if ( ASDCP_SUCCESS(result) )
750 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
752 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
753 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
754 result = RESULT_ENDOFFILE;
758 if ( Options.verbose_flag )
759 FrameBuffer.Dump(stderr, Options.fb_dump_size);
761 if ( ! Options.no_write_flag )
763 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
765 // The Writer class will forward the last block of ciphertext
766 // to the encryption context for use as the IV for the next
767 // frame. If you want to use non-sequitur IV values, un-comment
768 // the following line of code.
769 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
770 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
775 if ( result == RESULT_ENDOFFILE )
779 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
780 result = Writer.Finalize();
788 // NOT YET, unfinished business with ST 2052-1
791 //------------------------------------------------------------------------------------------
795 // Write one or more plaintext timed text streams to a plaintext AS-02 file
796 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
799 write_timed_text_file(CommandOptions& Options)
801 AESEncContext* Context = 0;
802 HMACContext* HMAC = 0;
803 AS_02::TimedText::ST2052_TextParser Parser;
804 AS_02::TimedText::MXFWriter Writer;
805 TimedText::FrameBuffer FrameBuffer;
806 TimedText::TimedTextDescriptor TDesc;
807 byte_t IV_buf[CBC_BLOCK_SIZE];
808 Kumu::FortunaRNG RNG;
810 // set up essence parser
811 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
814 if ( ASDCP_SUCCESS(result) )
816 Parser.FillTimedTextDescriptor(TDesc);
817 FrameBuffer.Capacity(Options.fb_size);
819 if ( Options.verbose_flag )
821 fputs("IMF Timed-Text Descriptor:\n", stderr);
822 TimedText::DescriptorDump(TDesc);
826 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
828 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
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 )
841 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
843 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
845 Context = new AESEncContext;
846 result = Context->InitKey(Options.key_value);
848 if ( ASDCP_SUCCESS(result) )
849 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
851 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
853 Info.UsesHMAC = true;
854 HMAC = new HMACContext;
855 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
859 if ( ASDCP_SUCCESS(result) )
860 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
863 if ( ASDCP_FAILURE(result) )
867 TimedText::ResourceList_t::const_iterator ri;
869 result = Parser.ReadTimedTextResource(XMLDoc);
871 if ( ASDCP_SUCCESS(result) )
872 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
874 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
876 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
878 if ( ASDCP_SUCCESS(result) )
880 if ( Options.verbose_flag )
881 FrameBuffer.Dump(stderr, Options.fb_dump_size);
883 if ( ! Options.no_write_flag )
885 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
887 // The Writer class will forward the last block of ciphertext
888 // to the encryption context for use as the IV for the next
889 // frame. If you want to use non-sequitur IV values, un-comment
890 // the following line of code.
891 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
892 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
896 if ( result == RESULT_ENDOFFILE )
900 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
901 result = Writer.Finalize();
910 main(int argc, const char** argv)
912 Result_t result = RESULT_OK;
914 g_dict = &ASDCP::DefaultSMPTEDict();
917 CommandOptions Options(argc, argv);
919 if ( Options.version_flag )
922 if ( Options.help_flag )
925 if ( Options.show_ul_values_flag )
927 g_dict->Dump(stdout);
930 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
933 if ( Options.error_flag )
935 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
939 EssenceType_t EssenceType;
940 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
942 if ( ASDCP_SUCCESS(result) )
944 switch ( EssenceType )
947 result = write_JP2K_file(Options);
950 case ESS_PCM_24b_48k:
951 case ESS_PCM_24b_96k:
952 result = write_PCM_file(Options);
956 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
957 Options.filenames.front().c_str());
962 if ( ASDCP_FAILURE(result) )
964 fputs("Program stopped on error.\n", stderr);
966 if ( result != RESULT_FAIL )
968 fputs(result, stderr);
980 // end as-02-wrap.cpp