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 -U <UL> - Set DataEssenceCoding UL value in an Aux Data file\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), uses\n\
163 default values for White Ref, Black Ref and Color Range,\n\
164 940,64,897, indicating 10 bit standard Video Range\n\
165 -y <white-ref>[,<black-ref>[,<color-range>]]\n\
166 - Same as -Y but White Ref, Black Ref and Color Range are\n\
167 set from the given argument\n\
168 -z - Fail if j2c inputs have unequal parameters (default)\n\
169 -Z - Ignore unequal parameters in j2c inputs\n\
171 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
172 o All option arguments must be separated from the option by whitespace.\n\n");
183 bool error_flag; // true if the given options are in error or not complete
184 bool key_flag; // true if an encryption key was given
185 bool asset_id_flag; // true if an asset ID was given
186 bool encrypt_header_flag; // true if j2c headers are to be encrypted
187 bool write_hmac; // true if HMAC values are to be generated and written
188 bool verbose_flag; // true if the verbose option was selected
189 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
190 bool no_write_flag; // true if no output files are to be written
191 bool version_flag; // true if the version display option was selected
192 bool help_flag; // true if the help display option was selected
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;
220 ui32_t cdci_BlackRefLevel;
221 ui32_t cdci_WhiteRefLevel;
222 ui32_t cdci_ColorRange;
224 //new attributes for AS-02 support
225 AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
226 ui32_t partition_space; //Shim parameter partition_spacing
232 bool set_video_ref(const std::string& arg)
234 std::list<std::string> ref_tokens = Kumu::km_token_split(arg, ",");
236 switch ( ref_tokens.size() )
239 cdci_ColorRange = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
240 ref_tokens.pop_back();
242 cdci_BlackRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
243 ref_tokens.pop_back();
245 cdci_WhiteRefLevel = Kumu::xabs(strtol(ref_tokens.back().c_str(), 0, 10));
249 fprintf(stderr, "Expecting <white-ref>[,<black-ref>[,<color-range>]]\n");
253 if ( cdci_WhiteRefLevel > 65535 || cdci_BlackRefLevel > 65535 || cdci_ColorRange > 65535 )
255 fprintf(stderr, "Unexpected CDCI video referece levels.\n");
263 CommandOptions(int argc, const char** argv) :
264 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
265 encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
266 no_write_flag(false), version_flag(false), help_flag(false),
267 duration(0xffffffff), j2c_pedantic(true), use_cdci_descriptor(false), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
268 show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
269 mca_config(g_dict), rgba_MaxRef(1023), rgba_MinRef(0),
270 horizontal_subsampling(2), vertical_subsampling(2), component_depth(10),
271 frame_layout(0), aspect_ratio(ASDCP::Rational(4,3)), field_dominance(0),
272 mxf_header_size(16384), cdci_WhiteRefLevel(940), cdci_BlackRefLevel(64), cdci_ColorRange(897)
274 memset(key_value, 0, KeyLen);
275 memset(key_id_value, 0, UUIDlen);
277 for ( int i = 1; i < argc; i++ )
280 if ( (strcmp( argv[i], "-help") == 0) )
286 if ( argv[i][0] == '-'
287 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
290 switch ( argv[i][1] )
293 TEST_EXTRA_ARG(i, 'A');
294 if ( ! DecodeRational(argv[i], aspect_ratio) )
296 fprintf(stderr, "Error decoding aspect ratio value: %s\n", argv[i]);
302 asset_id_flag = true;
303 TEST_EXTRA_ARG(i, 'a');
306 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
308 if ( length != UUIDlen )
310 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
317 TEST_EXTRA_ARG(i, 'b');
318 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
321 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
325 TEST_EXTRA_ARG(i, 'C');
326 if ( ! channel_assignment.DecodeHex(argv[i]) )
328 fprintf(stderr, "Error decoding ChannelAssignment UL value: %s\n", argv[i]);
334 TEST_EXTRA_ARG(i, 'D');
335 component_depth = Kumu::xabs(strtol(argv[i], 0, 10));
339 TEST_EXTRA_ARG(i, 'd');
340 duration = Kumu::xabs(strtol(argv[i], 0, 10));
343 case 'E': encrypt_header_flag = false; break;
344 case 'e': encrypt_header_flag = true; break;
347 TEST_EXTRA_ARG(i, 'F');
348 field_dominance = Kumu::xabs(strtol(argv[i], 0, 10));
349 if ( field_dominance > 1 )
351 fprintf(stderr, "Field dominance value must be \"0\" or \"1\"\n");
356 case 'h': help_flag = true; break;
360 use_cdci_descriptor = true;
365 TEST_EXTRA_ARG(i, 'j');
368 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
370 if ( length != UUIDlen )
372 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
378 case 'k': key_flag = true;
379 TEST_EXTRA_ARG(i, 'k');
382 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
384 if ( length != KeyLen )
386 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
392 case 'M': write_hmac = false; break;
395 TEST_EXTRA_ARG(i, 'm');
396 if ( ! mca_config.DecodeString(argv[i]) )
403 TEST_EXTRA_ARG(i, 'p');
404 if ( ! picture_coding.DecodeHex(argv[i]) )
406 fprintf(stderr, "Error decoding PictureEssenceCoding UL value: %s\n", argv[i]);
412 TEST_EXTRA_ARG(i, 'r');
413 if ( ! DecodeRational(argv[i], edit_rate) )
415 fprintf(stderr, "Error decoding edit rate value: %s\n", argv[i]);
422 use_cdci_descriptor = false;
426 TEST_EXTRA_ARG(i, 's');
427 partition_space = Kumu::xabs(strtol(argv[i], 0, 10));
431 TEST_EXTRA_ARG(i, 't');
432 rgba_MinRef = Kumu::xabs(strtol(argv[i], 0, 10));
436 TEST_EXTRA_ARG(i, 'T');
437 rgba_MaxRef = Kumu::xabs(strtol(argv[i], 0, 10));
440 case 'u': show_ul_values_flag = true; break;
443 TEST_EXTRA_ARG(i, 'U');
444 if ( ! aux_data_coding.DecodeHex(argv[i]) )
446 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
451 case 'V': version_flag = true; break;
452 case 'v': verbose_flag = true; break;
453 case 'W': no_write_flag = true; break;
456 TEST_EXTRA_ARG(i, 'x');
457 horizontal_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
461 TEST_EXTRA_ARG(i, 'X');
462 vertical_subsampling = Kumu::xabs(strtol(argv[i], 0, 10));
466 use_cdci_descriptor = true;
467 // default 10 bit video range YUV, ref levels already set
471 // Use values provded as argument, sharp tool, be careful
472 use_cdci_descriptor = true;
473 TEST_EXTRA_ARG(i, 'y');
474 if ( ! set_video_ref(argv[i]) )
480 case 'Z': j2c_pedantic = false; break;
481 case 'z': j2c_pedantic = true; break;
484 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
491 if ( argv[i][0] != '-' )
493 filenames.push_back(argv[i]);
497 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
503 if ( help_flag || version_flag )
506 if ( filenames.size() < 2 )
508 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
512 out_file = filenames.back();
513 filenames.pop_back();
515 if ( ! picture_coding.HasValue() )
517 picture_coding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
525 //------------------------------------------------------------------------------------------
529 Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
530 const ASDCP::Dictionary& dict,
531 ASDCP::MXF::GenericPictureEssenceDescriptor& GenericPictureEssenceDescriptor,
532 ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor);
534 Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
537 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
538 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
541 write_JP2K_file(CommandOptions& Options)
543 AESEncContext* Context = 0;
544 HMACContext* HMAC = 0;
545 AS_02::JP2K::MXFWriter Writer;
546 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
547 JP2K::SequenceParser Parser;
548 byte_t IV_buf[CBC_BLOCK_SIZE];
549 Kumu::FortunaRNG RNG;
550 ASDCP::MXF::FileDescriptor *essence_descriptor = 0;
551 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
553 // set up essence parser
554 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
557 if ( ASDCP_SUCCESS(result) )
559 ASDCP::JP2K::PictureDescriptor PDesc;
560 Parser.FillPictureDescriptor(PDesc);
561 PDesc.EditRate = Options.edit_rate;
563 if ( Options.verbose_flag )
565 fprintf(stderr, "JPEG 2000 pictures\n");
566 fputs("PictureDescriptor:\n", stderr);
567 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
568 JP2K::PictureDescriptorDump(PDesc);
571 if ( Options.use_cdci_descriptor )
573 ASDCP::MXF::CDCIEssenceDescriptor* tmp_dscr = new ASDCP::MXF::CDCIEssenceDescriptor(g_dict);
574 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
576 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
577 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
578 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
580 if ( ASDCP_SUCCESS(result) )
582 tmp_dscr->PictureEssenceCoding = Options.picture_coding;
583 tmp_dscr->HorizontalSubsampling = Options.horizontal_subsampling;
584 tmp_dscr->VerticalSubsampling = Options.vertical_subsampling;
585 tmp_dscr->ComponentDepth = Options.component_depth;
586 tmp_dscr->FrameLayout = Options.frame_layout;
587 tmp_dscr->AspectRatio = Options.aspect_ratio;
588 tmp_dscr->FieldDominance = Options.field_dominance;
589 tmp_dscr->WhiteReflevel = Options.cdci_WhiteRefLevel;
590 tmp_dscr->BlackRefLevel = Options.cdci_BlackRefLevel;
591 tmp_dscr->ColorRange = Options.cdci_ColorRange;
592 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
597 ASDCP::MXF::RGBAEssenceDescriptor* tmp_dscr = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
598 essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
600 result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict,
601 *static_cast<ASDCP::MXF::GenericPictureEssenceDescriptor*>(tmp_dscr),
602 *static_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
604 if ( ASDCP_SUCCESS(result) )
606 tmp_dscr->PictureEssenceCoding = UL(g_dict->ul(MDD_JP2KEssenceCompression_BroadcastProfile_1));
607 tmp_dscr->ComponentMaxRef = Options.rgba_MaxRef;
608 tmp_dscr->ComponentMinRef = Options.rgba_MinRef;
609 essence_descriptor = static_cast<ASDCP::MXF::FileDescriptor*>(tmp_dscr);
614 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
616 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
617 Info.LabelSetType = LS_MXF_SMPTE;
619 if ( Options.asset_id_flag )
620 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
622 Kumu::GenRandomUUID(Info.AssetUUID);
624 // configure encryption
625 if( Options.key_flag )
627 Kumu::GenRandomUUID(Info.ContextID);
628 Info.EncryptedEssence = true;
630 if ( Options.key_id_flag )
632 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
636 create_random_uuid(Info.CryptographicKeyID);
639 Context = new AESEncContext;
640 result = Context->InitKey(Options.key_value);
642 if ( ASDCP_SUCCESS(result) )
643 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
645 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
647 Info.UsesHMAC = true;
648 HMAC = new HMACContext;
649 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
653 if ( ASDCP_SUCCESS(result) )
655 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor, essence_sub_descriptors,
656 Options.edit_rate, Options.mxf_header_size, Options.index_strategy, Options.partition_space);
660 if ( ASDCP_SUCCESS(result) )
663 result = Parser.Reset();
665 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
667 result = Parser.ReadFrame(FrameBuffer);
669 if ( ASDCP_SUCCESS(result) )
671 if ( Options.verbose_flag )
672 FrameBuffer.Dump(stderr, Options.fb_dump_size);
674 if ( Options.encrypt_header_flag )
675 FrameBuffer.PlaintextOffset(0);
678 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
680 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
682 // The Writer class will forward the last block of ciphertext
683 // to the encryption context for use as the IV for the next
684 // frame. If you want to use non-sequitur IV values, un-comment
685 // the following line of code.
686 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
687 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
691 if ( result == RESULT_ENDOFFILE )
695 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
696 result = Writer.Finalize();
701 //------------------------------------------------------------------------------------------
705 // Write one or more plaintext PCM audio streams to a plaintext AS-02 file
706 // Write one or more plaintext PCM audio streams to a ciphertext AS-02 file
709 write_PCM_file(CommandOptions& Options)
711 AESEncContext* Context = 0;
712 HMACContext* HMAC = 0;
713 PCMParserList Parser;
714 AS_02::PCM::MXFWriter Writer;
715 PCM::FrameBuffer FrameBuffer;
716 byte_t IV_buf[CBC_BLOCK_SIZE];
717 Kumu::FortunaRNG RNG;
718 ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
720 // set up essence parser
721 Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
724 if ( ASDCP_SUCCESS(result) )
726 ASDCP::PCM::AudioDescriptor ADesc;
727 Parser.FillAudioDescriptor(ADesc);
729 ADesc.EditRate = Options.edit_rate;
730 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
732 if ( Options.verbose_flag )
735 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
736 ADesc.AudioSamplingRate.Quotient() / 1000.0,
737 RationalToString(Options.edit_rate, buf, 64),
738 PCM::CalcSamplesPerFrame(ADesc));
739 fputs("AudioDescriptor:\n", stderr);
740 PCM::AudioDescriptorDump(ADesc);
743 essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
745 result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
747 if ( Options.mca_config.empty() )
749 essence_descriptor->ChannelAssignment = Options.channel_assignment;
753 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
755 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
756 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
760 // this is the d-cinema MCA label, what is the one for IMF?
761 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_IMFAudioChannelCfg_MCA);
765 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
767 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
768 Info.LabelSetType = LS_MXF_SMPTE;
770 if ( Options.asset_id_flag )
771 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
773 Kumu::GenRandomUUID(Info.AssetUUID);
775 // configure encryption
776 if( Options.key_flag )
778 Kumu::GenRandomUUID(Info.ContextID);
779 Info.EncryptedEssence = true;
781 if ( Options.key_id_flag )
783 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
787 create_random_uuid(Info.CryptographicKeyID);
790 Context = new AESEncContext;
791 result = Context->InitKey(Options.key_value);
793 if ( ASDCP_SUCCESS(result) )
794 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
796 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
798 Info.UsesHMAC = true;
799 HMAC = new HMACContext;
800 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
804 if ( ASDCP_SUCCESS(result) )
806 result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
807 Options.mca_config, Options.edit_rate);
811 if ( ASDCP_SUCCESS(result) )
813 result = Parser.Reset();
816 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
818 result = Parser.ReadFrame(FrameBuffer);
820 if ( ASDCP_SUCCESS(result) )
822 if ( Options.verbose_flag )
823 FrameBuffer.Dump(stderr, Options.fb_dump_size);
825 if ( ! Options.no_write_flag )
827 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
829 // The Writer class will forward the last block of ciphertext
830 // to the encryption context for use as the IV for the next
831 // frame. If you want to use non-sequitur IV values, un-comment
832 // the following line of code.
833 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
834 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
839 if ( result == RESULT_ENDOFFILE )
843 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
844 result = Writer.Finalize();
852 //------------------------------------------------------------------------------------------
856 // Write one or more plaintext timed text streams to a plaintext AS-02 file
857 // Write one or more plaintext timed text streams to a ciphertext AS-02 file
860 write_timed_text_file(CommandOptions& Options)
862 AESEncContext* Context = 0;
863 HMACContext* HMAC = 0;
864 AS_02::TimedText::ST2052_TextParser Parser;
865 AS_02::TimedText::MXFWriter Writer;
866 TimedText::FrameBuffer FrameBuffer;
867 TimedText::TimedTextDescriptor TDesc;
868 byte_t IV_buf[CBC_BLOCK_SIZE];
869 Kumu::FortunaRNG RNG;
871 // set up essence parser
872 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
875 if ( ASDCP_SUCCESS(result) )
877 Parser.FillTimedTextDescriptor(TDesc);
878 TDesc.EditRate = Options.edit_rate;
879 TDesc.ContainerDuration = Options.duration;
880 FrameBuffer.Capacity(Options.fb_size);
882 if ( Options.verbose_flag )
884 fputs("IMF Timed-Text Descriptor:\n", stderr);
885 TimedText::DescriptorDump(TDesc);
889 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
891 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
892 Info.LabelSetType = LS_MXF_SMPTE;
894 if ( Options.asset_id_flag )
895 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
897 Kumu::GenRandomUUID(Info.AssetUUID);
899 // configure encryption
900 if( Options.key_flag )
902 Kumu::GenRandomUUID(Info.ContextID);
903 Info.EncryptedEssence = true;
905 if ( Options.key_id_flag )
907 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
911 create_random_uuid(Info.CryptographicKeyID);
914 Context = new AESEncContext;
915 result = Context->InitKey(Options.key_value);
917 if ( ASDCP_SUCCESS(result) )
918 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
920 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
922 Info.UsesHMAC = true;
923 HMAC = new HMACContext;
924 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
928 if ( ASDCP_SUCCESS(result) )
929 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
932 if ( ASDCP_FAILURE(result) )
936 TimedText::ResourceList_t::const_iterator ri;
938 result = Parser.ReadTimedTextResource(XMLDoc);
940 if ( ASDCP_SUCCESS(result) )
941 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
943 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
945 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
947 if ( ASDCP_SUCCESS(result) )
949 if ( Options.verbose_flag )
950 FrameBuffer.Dump(stderr, Options.fb_dump_size);
952 if ( ! Options.no_write_flag )
954 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
956 // The Writer class will forward the last block of ciphertext
957 // to the encryption context for use as the IV for the next
958 // frame. If you want to use non-sequitur IV values, un-comment
959 // the following line of code.
960 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
961 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
965 if ( result == RESULT_ENDOFFILE )
969 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
970 result = Writer.Finalize();
976 // Write one or more plaintext Aux Data bytestreams to a plaintext AS-02 file
977 // Write one or more plaintext Aux Data bytestreams to a ciphertext AS-02 file
980 write_aux_data_file(CommandOptions& Options)
982 AESEncContext* Context = 0;
983 HMACContext* HMAC = 0;
984 AS_02::AuxData::MXFWriter Writer;
985 DCData::FrameBuffer FrameBuffer(Options.fb_size);
986 DCData::SequenceParser Parser;
987 byte_t IV_buf[CBC_BLOCK_SIZE];
988 Kumu::FortunaRNG RNG;
990 // set up essence parser
991 Result_t result = Parser.OpenRead(Options.filenames.front());
994 if ( ASDCP_SUCCESS(result) )
997 if ( Options.verbose_flag )
999 fprintf(stderr, "Aux Data\n");
1000 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1004 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1006 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1007 if ( Options.asset_id_flag )
1008 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1010 Kumu::GenRandomUUID(Info.AssetUUID);
1012 Info.LabelSetType = LS_MXF_SMPTE;
1014 // configure encryption
1015 if( Options.key_flag )
1017 Kumu::GenRandomUUID(Info.ContextID);
1018 Info.EncryptedEssence = true;
1020 if ( Options.key_id_flag )
1022 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1026 create_random_uuid(Info.CryptographicKeyID);
1029 Context = new AESEncContext;
1030 result = Context->InitKey(Options.key_value);
1032 if ( ASDCP_SUCCESS(result) )
1033 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1035 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1037 Info.UsesHMAC = true;
1038 HMAC = new HMACContext;
1039 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1043 if ( ASDCP_SUCCESS(result) )
1045 ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptor_list; // empty for now
1046 ASDCP::MXF::DCDataDescriptor *essence_descriptor = new ASDCP::MXF::DCDataDescriptor(g_dict);
1047 essence_descriptor->SampleRate = Options.edit_rate;
1048 essence_descriptor->ContainerDuration = 0;
1049 essence_descriptor->DataEssenceCoding = Options.aux_data_coding;
1051 result = Writer.OpenWrite(Options.out_file, Info, essence_descriptor,
1052 essence_sub_descriptor_list, Options.edit_rate);
1056 if ( ASDCP_SUCCESS(result) )
1058 ui32_t duration = 0;
1059 result = Parser.Reset();
1061 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1063 result = Parser.ReadFrame(FrameBuffer);
1065 if ( ASDCP_SUCCESS(result) )
1067 if ( Options.verbose_flag )
1068 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1070 if ( Options.encrypt_header_flag )
1071 FrameBuffer.PlaintextOffset(0);
1074 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1076 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1078 // The Writer class will forward the last block of ciphertext
1079 // to the encryption context for use as the IV for the next
1080 // frame. If you want to use non-sequitur IV values, un-comment
1081 // the following line of code.
1082 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1083 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1087 if ( result == RESULT_ENDOFFILE )
1091 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1092 result = Writer.Finalize();
1100 main(int argc, const char** argv)
1102 Result_t result = RESULT_OK;
1104 g_dict = &ASDCP::DefaultSMPTEDict();
1107 CommandOptions Options(argc, argv);
1109 if ( Options.version_flag )
1112 if ( Options.help_flag )
1115 if ( Options.show_ul_values_flag )
1117 g_dict->Dump(stdout);
1120 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1123 if ( Options.error_flag )
1125 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1129 EssenceType_t EssenceType;
1130 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1132 if ( ASDCP_SUCCESS(result) )
1134 switch ( EssenceType )
1137 result = write_JP2K_file(Options);
1140 case ESS_PCM_24b_48k:
1141 case ESS_PCM_24b_96k:
1142 result = write_PCM_file(Options);
1145 case ESS_TIMED_TEXT:
1146 result = write_timed_text_file(Options);
1149 case ESS_DCDATA_UNKNOWN:
1150 if ( ! Options.aux_data_coding.HasValue() )
1152 fprintf(stderr, "Option \"-U <UL>\" is required for Aux Data essence.\n");
1157 result = write_aux_data_file(Options);
1162 fprintf(stderr, "%s: Unknown file type, not AS-02-compatible essence.\n",
1163 Options.filenames.front().c_str());
1168 if ( ASDCP_FAILURE(result) )
1170 fputs("Program stopped on error.\n", stderr);
1172 if ( result != RESULT_FAIL )
1174 fputs(result, stderr);
1175 fputc('\n', stderr);
1186 // end as-02-wrap.cpp