2 Copyright (c) 2011-2016, 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;
51 RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
53 snprintf(buf, len, "%d/%d", r.Numerator, r.Denominator);
58 //------------------------------------------------------------------------------------------
60 // command line option parser class
62 static const char* PROGRAM_NAME = "as-02-wrap"; // program name for messages
64 // local program identification info written to file headers
65 class MyInfo : public WriterInfo
70 static byte_t default_ProductUUID_Data[UUIDlen] =
71 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
72 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
74 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
75 CompanyName = "WidgetCo";
76 ProductName = "as-02-wrap";
77 ProductVersion = ASDCP::Version();
83 // Increment the iterator, test for an additional non-option command line argument.
84 // Causes the caller to return if there are no remaining arguments or if the next
85 // argument begins with '-'.
86 #define TEST_EXTRA_ARG(i,c) \
87 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
88 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
95 create_random_uuid(byte_t* uuidbuf)
98 GenRandomValue(tmp_id);
99 memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
104 banner(FILE* stream = stdout)
107 %s (asdcplib %s)\n\n\
108 Copyright (c) 2011-2016, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
109 asdcplib may be copied only under the terms of the license found at\n\
110 the top of every file in the asdcplib distribution kit.\n\n\
111 Specify the -h (help) option for further information about %s\n\n",
112 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
117 usage(FILE* stream = stdout)
120 USAGE: %s [-h|-help] [-V]\n\
122 %s [-a <uuid>] [-A <w>/<h>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
123 [-D <depth>] [-e|-E] [-i] [-j <key-id-string>] [-k <key-string>]\n\
124 [-M] [-m <expr>] [-p <ul>] [-r <n>/<d>] [-R] [-s <seconds>]\n\
125 [-t <min>] [-T <max>] [-u] [-v] [-W] [-x <int>] [-X <int>] [-Y]\n\
126 [-y <white-ref>[,<black-ref>[,<color-range>]]]\n\
127 [-z|-Z] <input-file>+ <output-file>\n\n",
128 PROGRAM_NAME, PROGRAM_NAME);
132 -h | -help - Show help\n\
133 -V - Show version information\n\
134 -a <uuid> - Specify the Asset ID of the file\n\
135 -A <w>/<h> - Set aspect ratio for image (default 4/3)\n\
136 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
137 Defaults to 4,194,304 (4MB)\n\
138 -C <ul> - Set ChannelAssignment UL value\n\
139 -d <duration> - Number of frames to process, default all\n\
140 -D <depth> - Component depth for YCbCr images (default: 10)\n\
141 -e - Encrypt JP2K headers (default)\n\
142 -E - Do not encrypt JP2K headers\n\
143 -F (0|1) - Set field dominance for interlaced image (default: 0)\n\
144 -i - Indicates input essence is interlaced fields (forces -Y)\n\
145 -j <key-id-str> - Write key ID instead of creating a random value\n\
146 -k <key-string> - Use key for ciphertext operations\n\
147 -M - Do not create HMAC values when writing\n\
148 -m <expr> - Write MCA labels using <expr>. Example:\n\
149 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
150 -p <ul> - Set broadcast profile\n\
151 -r <n>/<d> - Edit Rate of the output file. 24/1 is the default\n\
152 -R - Indicates RGB image essence (default)\n\
153 -s <seconds> - Duration of a frame-wrapped partition (default 60)\n\
154 -t <min> - Set RGB component minimum code value (default: 0)\n\
155 -T <max> - Set RGB component maximum code value (default: 1023)\n\
156 -u - Print UL catalog to stderr\n\
157 -v - Verbose, prints informative messages to stderr\n\
158 -W - Read input file only, do not write source file\n\
159 -x <int> - Horizontal subsampling degree (default: 2)\n\
160 -X <int> - Vertical subsampling degree (default: 2)\n\
161 -Y - Indicates YCbCr image essence (default: RGB), uses\n\
162 default values for White Ref, Black Ref and Color Range,\n\
163 940,64,897, indicating 10 bit standard Video Range\n\
164 -y <white-ref>[,<black-ref>[,<color-range>]]\n\
165 - Same as -Y but White Ref, Black Ref and Color Range are\n\
166 set from the given argument\n\
167 -z - Fail if j2c inputs have unequal parameters (default)\n\
168 -Z - Ignore unequal parameters in j2c inputs\n\
170 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
171 o All option arguments must be separated from the option by whitespace.\n\n");
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;
219 ui32_t cdci_BlackRefLevel;
220 ui32_t cdci_WhiteRefLevel;
221 ui32_t cdci_ColorRange;
223 //new attributes for AS-02 support
224 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
225 ui32_t partition_space; //Shim parameter partition_spacing
228 bool set_video_ref(const std::string& arg)
230 std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
232 switch ( ref_tokens.size() )
235 cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
236 ref_tokens.pop_back();
238 cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
239 ref_tokens.pop_back();
241 cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
245 fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
249 if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
251 fprintf(stderr, "Unexpected CDCI video referece levels.\n");
259 CommandOptions(int argc, const char** argv) :
260 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
261 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
262 no_write_flag(false), version_flag(false), help_flag(false),
263 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
264 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
265 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
266 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
267 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
268 mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897)
270 memset(key_value, 0, KeyLen);
271 memset(key_id_value, 0, UUIDlen);
273 for ( int i = 1; i < argc; i++ )
276 if ( (strcmp( argv[i], "-help") == 0) )
282 if ( argv[i][0] == '-'
283 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
286 switch ( argv[i][1] )
289 TEST_EXTRA_ARG(i, 'A');
290 if ( ! DecodeRational(argv[i], aspect_ratio) )
292 fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
298 asset_id_flag = true;
299 TEST_EXTRA_ARG(i, 'a');
302 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
304 if ( length != UUIDlen )
306 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
313 TEST_EXTRA_ARG(i, 'b');
314 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
317 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
321 TEST_EXTRA_ARG(i, 'C');
322 if ( ! channel_assignment.DecodeHex(argv[i]) )
324 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
330 TEST_EXTRA_ARG(i, 'D');
331 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
335 TEST_EXTRA_ARG(i, 'd');
336 duration = Kumu::xabs(strtol(argv[i], 0, 10));
339 case 'E': encrypt_header_flag = false; break;
340 case 'e': encrypt_header_flag = true; break;
343 TEST_EXTRA_ARG(i, 'F');
344 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
345 if ( field_dominance > 1 )
347 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
352 case 'h': help_flag = true; break;
356 use_cdci_descriptor = true;
361 TEST_EXTRA_ARG(i, 'j');
364 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
366 if ( length != UUIDlen )
368 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
374 case 'k': key_flag = true;
375 TEST_EXTRA_ARG(i, 'k');
378 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
380 if ( length != KeyLen )
382 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
388 case 'M': write_hmac = false; break;
391 TEST_EXTRA_ARG(i, 'm');
392 if ( ! mca_config.DecodeString(argv[i]) )
399 TEST_EXTRA_ARG(i, 'p');
400 if ( ! picture_coding.DecodeHex(argv[i]) )
402 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
408 TEST_EXTRA_ARG(i, 'r');
409 if ( ! DecodeRational(argv[i], edit_rate) )
411 fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
418 use_cdci_descriptor = false;
422 TEST_EXTRA_ARG(i, 's');
423 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
427 TEST_EXTRA_ARG(i, 't');
428 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
432 TEST_EXTRA_ARG(i, 'T');
433 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
436 case 'u': show_ul_values_flag = true; break;
437 case 'V': version_flag = true; break;
438 case 'v': verbose_flag = true; break;
439 case 'W': no_write_flag = true; break;
442 TEST_EXTRA_ARG(i, 'x');
443 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
447 TEST_EXTRA_ARG(i, 'X');
448 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
452 use_cdci_descriptor = true;
453 // default 10 bit video range YUV, ref levels already set
457 // Use values provded as argument, sharp tool, be careful
458 use_cdci_descriptor = true;
459 TEST_EXTRA_ARG(i, 'y');
460 if ( ! set_video_ref(argv[i]) )
466 case 'Z': j2c_pedantic = false; break;
467 case 'z': j2c_pedantic = true; break;
470 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
477 if ( argv[i][0] != '-' )
479 filenames.push_back(argv[i]);
483 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
489 if ( help_flag || version_flag )
492 if ( filenames.size() < 2 )
494 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
498 out_file = filenames.back();
499 filenames.pop_back();
501 if ( ! picture_coding.HasValue() )
503 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
511 //------------------------------------------------------------------------------------------
515 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
516 const ASDCP::Dictionary& dict,
517 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
518 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
520 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
523 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
524 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
527 write_JP2K_file(CommandOptions& Options)
529 AESEncContext* Context = 0;
530 HMACContext* HMAC = 0;
531 AS_02::JP2K::MXFWriter Writer;
532 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
533 JP2K::SequenceParser Parser;
534 byte_t IV_buf[CBC_BLOCK_SIZE];
535 Kumu::FortunaRNG RNG;
536 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
537 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
539 // set up essence parser
540 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
543 if ( ASDCP_SUCCESS(result) )
545 ASDCP::JP2K::PictureDescriptor PDesc;
546 Parser.FillPictureDescriptor(PDesc);
547 PDesc.EditRate = Options.edit_rate;
549 if ( Options.verbose_flag )
551 fprintf(stderr, "JPEG 2000 pictures\n");
552 fputs("PictureDescriptor:\n", stderr);
553 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
554 JP2K::PictureDescriptorDump(PDesc);
557 if ( Options.use_cdci_descriptor )
559 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
560 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
562 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
563 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
564 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
566 if ( ASDCP_SUCCESS(result) )
568 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
569 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
570 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
571 tmp_dscr->ComponentDepth = Options.component_depth;
572 tmp_dscr->FrameLayout = Options.frame_layout;
573 tmp_dscr->AspectRatio = Options.aspect_ratio;
574 tmp_dscr->FieldDominance = Options.field_dominance;
575 tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
576 tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
577 tmp_dscr->ColorRange = Options.cdci_ColorRange;
578 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
583 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
584 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
586 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
587 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
588 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
590 if ( ASDCP_SUCCESS(result) )
592 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
593 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
594 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
595 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
600 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
602 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
603 Info.LabelSetType = LS_MXF_SMPTE;
605 if ( Options.asset_id_flag )
606 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
608 Kumu::GenRandomUUID(Info.AssetUUID);
610 // configure encryption
611 if( Options.key_flag )
613 Kumu::GenRandomUUID(Info.ContextID);
614 Info.EncryptedEssence = true;
616 if ( Options.key_id_flag )
618 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
622 create_random_uuid(Info.CryptographicKeyID);
625 Context = new AESEncContext;
626 result = Context->InitKey(Options.key_value);
628 if ( ASDCP_SUCCESS(result) )
629 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
631 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
633 Info.UsesHMAC = true;
634 HMAC = new HMACContext;
635 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
639 if ( ASDCP_SUCCESS(result) )
641 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
642 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
646 if ( ASDCP_SUCCESS(result) )
649 result = Parser.Reset();
651 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
653 result = Parser.ReadFrame(FrameBuffer);
655 if ( ASDCP_SUCCESS(result) )
657 if ( Options.verbose_flag )
658 FrameBuffer.Dump(stderr, Options.fb_dump_size);
660 if ( Options.encrypt_header_flag )
661 FrameBuffer.PlaintextOffset(0);
664 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
666 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
668 // The Writer class will forward the last block of ciphertext
669 // to the encryption context for use as the IV for the next
670 // frame. If you want to use non-sequitur IV values, un-comment
671 // the following line of code.
672 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
673 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
677 if ( result == RESULT_ENDOFFILE )
681 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
682 result = Writer.Finalize();
687 //------------------------------------------------------------------------------------------
691 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
692 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
695 write_PCM_file(CommandOptions& Options)
697 AESEncContext* Context = 0;
698 HMACContext* HMAC = 0;
699 PCMParserList Parser;
700 AS_02::PCM::MXFWriter Writer;
701 PCM::FrameBuffer FrameBuffer;
702 byte_t IV_buf[CBC_BLOCK_SIZE];
703 Kumu::FortunaRNG RNG;
704 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
706 // set up essence parser
707 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
710 if ( ASDCP_SUCCESS(result) )
712 ASDCP::PCM::AudioDescriptor ADesc;
713 Parser.FillAudioDescriptor(ADesc);
715 ADesc.EditRate = Options.edit_rate;
716 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
718 if ( Options.verbose_flag )
721 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
722 ADesc.AudioSamplingRate.Quotient() / 1000.0,
723 RationalToString(Options.edit_rate, buf, 64),
724 PCM::CalcSamplesPerFrame(ADesc));
725 fputs("AudioDescriptor:\n", stderr);
726 PCM::AudioDescriptorDump(ADesc);
729 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
731 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
733 if ( Options.mca_config.empty() )
735 essence_descriptor->ChannelAssignment = Options.channel_assignment;
739 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
741 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
742 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
746 // this is the d-cinema MCA label, what is the one for IMF?
747 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
751 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
753 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
754 Info.LabelSetType = LS_MXF_SMPTE;
756 if ( Options.asset_id_flag )
757 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
759 Kumu::GenRandomUUID(Info.AssetUUID);
761 // configure encryption
762 if( Options.key_flag )
764 Kumu::GenRandomUUID(Info.ContextID);
765 Info.EncryptedEssence = true;
767 if ( Options.key_id_flag )
769 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
773 create_random_uuid(Info.CryptographicKeyID);
776 Context = new AESEncContext;
777 result = Context->InitKey(Options.key_value);
779 if ( ASDCP_SUCCESS(result) )
780 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
782 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
784 Info.UsesHMAC = true;
785 HMAC = new HMACContext;
786 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
790 if ( ASDCP_SUCCESS(result) )
792 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
793 Options.mca_config, Options.edit_rate);
797 if ( ASDCP_SUCCESS(result) )
799 result = Parser.Reset();
802 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
804 result = Parser.ReadFrame(FrameBuffer);
806 if ( ASDCP_SUCCESS(result) )
808 if ( Options.verbose_flag )
809 FrameBuffer.Dump(stderr, Options.fb_dump_size);
811 if ( ! Options.no_write_flag )
813 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
815 // The Writer class will forward the last block of ciphertext
816 // to the encryption context for use as the IV for the next
817 // frame. If you want to use non-sequitur IV values, un-comment
818 // the following line of code.
819 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
820 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
825 if ( result == RESULT_ENDOFFILE )
829 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
830 result = Writer.Finalize();
838 //------------------------------------------------------------------------------------------
842 // Write one or more plaintext timed text streams to a plaintext AS-02 file
843 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
846 write_timed_text_file(CommandOptions& Options)
848 AESEncContext* Context = 0;
849 HMACContext* HMAC = 0;
850 AS_02::TimedText::ST2052_TextParser Parser;
851 AS_02::TimedText::MXFWriter Writer;
852 TimedText::FrameBuffer FrameBuffer;
853 TimedText::TimedTextDescriptor TDesc;
854 byte_t IV_buf[CBC_BLOCK_SIZE];
855 Kumu::FortunaRNG RNG;
857 // set up essence parser
858 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
861 if ( ASDCP_SUCCESS(result) )
863 Parser.FillTimedTextDescriptor(TDesc);
864 TDesc.EditRate = Options.edit_rate;
865 TDesc.ContainerDuration = Options.duration;
866 FrameBuffer.Capacity(Options.fb_size);
868 if ( Options.verbose_flag )
870 fputs("IMF Timed-Text Descriptor:\n", stderr);
871 TimedText::DescriptorDump(TDesc);
875 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
877 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
878 Info.LabelSetType = LS_MXF_SMPTE;
880 if ( Options.asset_id_flag )
881 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
883 Kumu::GenRandomUUID(Info.AssetUUID);
885 // configure encryption
886 if( Options.key_flag )
888 Kumu::GenRandomUUID(Info.ContextID);
889 Info.EncryptedEssence = true;
891 if ( Options.key_id_flag )
893 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
897 create_random_uuid(Info.CryptographicKeyID);
900 Context = new AESEncContext;
901 result = Context->InitKey(Options.key_value);
903 if ( ASDCP_SUCCESS(result) )
904 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
906 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
908 Info.UsesHMAC = true;
909 HMAC = new HMACContext;
910 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
914 if ( ASDCP_SUCCESS(result) )
915 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
918 if ( ASDCP_FAILURE(result) )
922 TimedText::ResourceList_t::const_iterator ri;
924 result = Parser.ReadTimedTextResource(XMLDoc);
926 if ( ASDCP_SUCCESS(result) )
927 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
929 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
931 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
933 if ( ASDCP_SUCCESS(result) )
935 if ( Options.verbose_flag )
936 FrameBuffer.Dump(stderr, Options.fb_dump_size);
938 if ( ! Options.no_write_flag )
940 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
942 // The Writer class will forward the last block of ciphertext
943 // to the encryption context for use as the IV for the next
944 // frame. If you want to use non-sequitur IV values, un-comment
945 // the following line of code.
946 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
947 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
951 if ( result == RESULT_ENDOFFILE )
955 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
956 result = Writer.Finalize();
963 main(int argc, const char** argv)
965 Result_t result = RESULT_OK;
967 g_dict = &ASDCP::DefaultSMPTEDict();
970 CommandOptions Options(argc, argv);
972 if ( Options.version_flag )
975 if ( Options.help_flag )
978 if ( Options.show_ul_values_flag )
980 g_dict->Dump(stdout);
983 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
986 if ( Options.error_flag )
988 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
992 EssenceType_t EssenceType;
993 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
995 if ( ASDCP_SUCCESS(result) )
997 switch ( EssenceType )
1000 result = write_JP2K_file(Options);
1003 case ESS_PCM_24b_48k:
1004 case ESS_PCM_24b_96k:
1005 result = write_PCM_file(Options);
1008 case ESS_TIMED_TEXT:
1009 result = write_timed_text_file(Options);
1013 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1014 Options.filenames.front().c_str());
1019 if ( ASDCP_FAILURE(result) )
1021 fputs("Program stopped on error.\n", stderr);
1023 if ( result != RESULT_FAIL )
1025 fputs(result, stderr);
1026 fputc('\n', stderr);
1037 // end as-02-wrap.cpp