2 Copyright (c) 2003-2015, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file asdcp-wrap.cpp
28 \version $Id: asdcp-wrap.cpp,v 1.23 2015/10/14 16:48:22 jhurst Exp $
29 \brief AS-DCP file manipulation utility
31 This program wraps d-cinema essence (picture, sound or text) into an AS-DCP
34 For more information about asdcplib, please refer to the header file AS_DCP.h
36 WARNING: While the asdcplib library attempts to provide a complete and secure
37 implementation of the cryptographic features of the AS-DCP file formats, this
38 unit test program is NOT secure and is therefore NOT SUITABLE FOR USE in a
39 production environment without some modification.
41 In particular, this program uses weak IV generation and externally generated
42 plaintext keys. These shortcomings exist because cryptographic-quality
43 random number generation and key management are outside the scope of the
44 asdcplib library. Developers using asdcplib for commercial implementations
45 claiming SMPTE conformance are expected to provide proper implementations of
49 #include <KM_fileio.h>
51 #include <AtmosSyncChannel_Mixer.h>
53 #include <PCMParserList.h>
56 using namespace ASDCP;
58 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
60 const byte_t P_HFR_UL_2K[16] = {
61 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
62 0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
65 const ASDCP::Dictionary *g_dict = 0;
67 //------------------------------------------------------------------------------------------
69 // command line option parser class
71 static const char* PROGRAM_NAME = "asdcp-wrap"; // program name for messages
73 // local program identification info written to file headers
74 class MyInfo : public WriterInfo
79 static byte_t default_ProductUUID_Data[UUIDlen] =
80 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
81 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
83 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
84 CompanyName = "WidgetCo";
85 ProductName = "asdcp-wrap";
86 ProductVersion = ASDCP::Version();
92 // Increment the iterator, test for an additional non-option command line argument.
93 // Causes the caller to return if there are no remaining arguments or if the next
94 // argument begins with '-'.
95 #define TEST_EXTRA_ARG(i,c) \
96 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
97 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
103 create_random_uuid(byte_t* uuidbuf)
106 GenRandomValue(tmp_id);
107 memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
112 banner(FILE* stream = stdout)
115 %s (asdcplib %s)\n\n\
116 Copyright (c) 2003-2015 John Hurst\n\n\
117 asdcplib may be copied only under the terms of the license found at\n\
118 the top of every file in the asdcplib distribution kit.\n\n\
119 Specify the -h (help) option for further information about %s\n\n",
120 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
125 usage(FILE* stream = stdout)
128 USAGE: %s [-h|-help] [-V]\n\
130 %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
131 [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
132 [-l <label>] [-L] [-M] [-m <expr>] [-p <frame-rate>] [-s] [-v]\n\
133 [-W] [-z|-Z] <input-file>+ <output-file>\n\n",
134 PROGRAM_NAME, PROGRAM_NAME);
138 -3 - Create a stereoscopic image file. Expects two\n\
139 directories of JP2K codestreams (directories must have\n\
140 an equal number of frames; the left eye is first)\n\
141 -A <UL> - Set DataEssenceCoding UL value in an Aux Data file\n\
142 -C <UL> - Set ChannelAssignment UL value in a PCM file\n\
143 -h | -help - Show help\n\
144 -V - Show version information\n\
145 -e - Encrypt MPEG or JP2K headers (default)\n\
146 -E - Do not encrypt MPEG or JP2K headers\n\
147 -j <key-id-str> - Write key ID instead of creating a random value\n\
148 -k <key-string> - Use key for ciphertext operations\n\
149 -M - Do not create HMAC values when writing\n\
150 -m <expr> - Write MCA labels using <expr>. Example:\n\
151 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
152 Note: The symbol '-' may be used for an unlabeled\n\
153 channel, but not within a soundfield.\n\
154 -a <UUID> - Specify the Asset ID of the file\n\
155 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
156 Defaults to 4,194,304 (4MB)\n\
157 -d <duration> - Number of frames to process, default all\n\
158 -f <start-frame> - Starting frame number, default 0\n\
159 -l <label> - Use given channel format label when writing MXF sound\n\
160 files. SMPTE 429-2 labels: '5.1', '6.1', '7.1',\n\
162 Default is no label (valid for Interop only).\n\
163 -L - Write SMPTE UL values instead of MXF Interop\n\
164 -P <UL> - Set PictureEssenceCoding UL value in a JP2K file\n\
165 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
166 Use one of [23|24|25|30|48|50|60], 24 is default\n\
167 -s - Insert a Dolby Atmos synchronization channel when\n\
168 wrapping PCM. This implies a -L option(SMPTE ULs) and \n\
169 will overide -C and -l options with Configuration 4 \n\
170 Channel Assigment and no format label respectively. \n\
171 -v - Verbose, prints informative messages to stderr\n\
172 -w - When writing 377-4 MCA labels, use the WTF Channel\n\
173 assignment label instead of the standard MCA label\n\
174 -W - Read input file only, do not write output file\n\
175 -z - Fail if j2c inputs have unequal parameters (default)\n\
176 -Z - Ignore unequal parameters in j2c inputs\n\
178 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
179 o All option arguments must be separated from the option by whitespace.\n\
180 o An argument of \"23\" to the -p option will be interpreted\n\
181 as 24000/1001 fps.\n\
187 decode_channel_fmt(const std::string& label_name)
189 if ( label_name == "5.1" )
190 return PCM::CF_CFG_1;
192 else if ( label_name == "6.1" )
193 return PCM::CF_CFG_2;
195 else if ( label_name == "7.1" )
196 return PCM::CF_CFG_3;
198 else if ( label_name == "WTF" )
199 return PCM::CF_CFG_4;
201 else if ( label_name == "7.1DS" )
202 return PCM::CF_CFG_5;
204 fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
205 fprintf(stderr, "Expecting '5.1', '6.1', '7.1', '7.1DS' or 'WTF'\n");
216 bool error_flag; // true if the given options are in error or not complete
217 bool key_flag; // true if an encryption key was given
218 bool asset_id_flag; // true if an asset ID was given
219 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
220 bool write_hmac; // true if HMAC values are to be generated and written
221 bool verbose_flag; // true if the verbose option was selected
222 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
223 bool no_write_flag; // true if no output files are to be written
224 bool version_flag; // true if the version display option was selected
225 bool help_flag; // true if the help display option was selected
226 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
227 bool write_partial_pcm_flag; // if true, write the last frame of PCM input even when it is incomplete
228 ui32_t start_frame; // frame number to begin processing
229 ui32_t duration; // number of frames to be processed
230 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
231 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
232 ui32_t picture_rate; // fps of picture when wrapping PCM
233 ui32_t fb_size; // size of picture frame buffer
234 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
235 bool key_id_flag; // true if a key ID was given
236 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
237 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
238 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
239 std::string out_file; //
240 bool show_ul_values_flag; /// if true, dump the UL table before going to work.
241 Kumu::PathList_t filenames; // list of filenames to be processed
242 UL channel_assignment;
245 bool dolby_atmos_sync_flag; // if true, insert a Dolby Atmos Synchronization channel.
246 ui32_t ffoa; // first frame of action for atmos wrapping
247 ui32_t max_channel_count; // max channel count for atmos wrapping
248 ui32_t max_object_count; // max object count for atmos wrapping
249 bool use_interop_sound_wtf; // make true to force WTF assignment label instead of MCA
250 ASDCP::MXF::ASDCP_MCAConfigParser mca_config;
253 Rational PictureRate()
255 if ( picture_rate == 16 ) return EditRate_16;
256 if ( picture_rate == 18 ) return EditRate_18;
257 if ( picture_rate == 20 ) return EditRate_20;
258 if ( picture_rate == 22 ) return EditRate_22;
259 if ( picture_rate == 23 ) return EditRate_23_98;
260 if ( picture_rate == 24 ) return EditRate_24;
261 if ( picture_rate == 25 ) return EditRate_25;
262 if ( picture_rate == 30 ) return EditRate_30;
263 if ( picture_rate == 48 ) return EditRate_48;
264 if ( picture_rate == 50 ) return EditRate_50;
265 if ( picture_rate == 60 ) return EditRate_60;
266 if ( picture_rate == 96 ) return EditRate_96;
267 if ( picture_rate == 100 ) return EditRate_100;
268 if ( picture_rate == 120 ) return EditRate_120;
273 const char* szPictureRate()
275 if ( picture_rate == 16 ) return "16";
276 if ( picture_rate == 18 ) return "18.182";
277 if ( picture_rate == 20 ) return "20";
278 if ( picture_rate == 22 ) return "21.818";
279 if ( picture_rate == 23 ) return "23.976";
280 if ( picture_rate == 24 ) return "24";
281 if ( picture_rate == 25 ) return "25";
282 if ( picture_rate == 30 ) return "30";
283 if ( picture_rate == 48 ) return "48";
284 if ( picture_rate == 50 ) return "50";
285 if ( picture_rate == 60 ) return "60";
286 if ( picture_rate == 96 ) return "96";
287 if ( picture_rate == 100 ) return "100";
288 if ( picture_rate == 120 ) return "120";
293 CommandOptions(int argc, const char** argv) :
294 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
295 encrypt_header_flag(true), write_hmac(true),
296 verbose_flag(false), fb_dump_size(0),
297 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
298 write_partial_pcm_flag(false), start_frame(0),
299 duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
300 fb_size(FRAME_BUFFER_SIZE),
301 channel_fmt(PCM::CF_NONE),
302 ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties
303 dolby_atmos_sync_flag(false),
304 show_ul_values_flag(false),
306 use_interop_sound_wtf(false)
308 memset(key_value, 0, KeyLen);
309 memset(key_id_value, 0, UUIDlen);
311 for ( int i = 1; i < argc; i++ )
314 if ( (strcmp( argv[i], "-help") == 0) )
320 if ( argv[i][0] == '-'
321 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
324 switch ( argv[i][1] )
326 case '3': stereo_image_flag = true; break;
329 TEST_EXTRA_ARG(i, 'A');
330 if ( ! aux_data_coding.DecodeHex(argv[i]) )
332 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
338 asset_id_flag = true;
339 TEST_EXTRA_ARG(i, 'a');
342 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
344 if ( length != UUIDlen )
346 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
353 TEST_EXTRA_ARG(i, 'b');
354 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
357 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
362 TEST_EXTRA_ARG(i, 'C');
363 if ( ! channel_assignment.DecodeHex(argv[i]) )
365 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
371 TEST_EXTRA_ARG(i, 'd');
372 duration = Kumu::xabs(strtol(argv[i], 0, 10));
375 case 'E': encrypt_header_flag = false; break;
376 case 'e': encrypt_header_flag = true; break;
379 TEST_EXTRA_ARG(i, 'f');
380 start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
383 case 'g': write_partial_pcm_flag = true; break;
384 case 'h': help_flag = true; break;
386 case 'j': key_id_flag = true;
387 TEST_EXTRA_ARG(i, 'j');
390 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
392 if ( length != UUIDlen )
394 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
400 case 'k': key_flag = true;
401 TEST_EXTRA_ARG(i, 'k');
404 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
406 if ( length != KeyLen )
408 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
415 TEST_EXTRA_ARG(i, 'l');
416 channel_fmt = decode_channel_fmt(argv[i]);
419 case 'L': use_smpte_labels = true; break;
420 case 'M': write_hmac = false; break;
423 TEST_EXTRA_ARG(i, 'm');
424 if ( ! mca_config.DecodeString(argv[i]) )
431 TEST_EXTRA_ARG(i, 'P');
432 if ( ! picture_coding.DecodeHex(argv[i]) )
434 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
440 TEST_EXTRA_ARG(i, 'p');
441 picture_rate = Kumu::xabs(strtol(argv[i], 0, 10));
444 case 's': dolby_atmos_sync_flag = true; break;
445 case 'u': show_ul_values_flag = true; break;
446 case 'V': version_flag = true; break;
447 case 'v': verbose_flag = true; break;
448 case 'w': use_interop_sound_wtf = true; break;
449 case 'W': no_write_flag = true; break;
450 case 'Z': j2c_pedantic = false; break;
451 case 'z': j2c_pedantic = true; break;
454 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
461 if ( argv[i][0] != '-' )
463 filenames.push_back(argv[i]);
467 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
473 if ( help_flag || version_flag )
476 if ( filenames.size() < 2 )
478 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
482 out_file = filenames.back();
483 filenames.pop_back();
488 //------------------------------------------------------------------------------------------
491 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
492 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
495 write_MPEG2_file(CommandOptions& Options)
497 AESEncContext* Context = 0;
498 HMACContext* HMAC = 0;
499 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
500 MPEG2::Parser Parser;
501 MPEG2::MXFWriter Writer;
502 MPEG2::VideoDescriptor VDesc;
503 byte_t IV_buf[CBC_BLOCK_SIZE];
504 Kumu::FortunaRNG RNG;
506 // set up essence parser
507 Result_t result = Parser.OpenRead(Options.filenames.front());
510 if ( ASDCP_SUCCESS(result) )
512 Parser.FillVideoDescriptor(VDesc);
514 if ( Options.verbose_flag )
516 fputs("MPEG-2 Pictures\n", stderr);
517 fputs("VideoDescriptor:\n", stderr);
518 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
519 MPEG2::VideoDescriptorDump(VDesc);
523 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
525 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
526 if ( Options.asset_id_flag )
527 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
529 Kumu::GenRandomUUID(Info.AssetUUID);
531 if ( Options.use_smpte_labels )
533 Info.LabelSetType = LS_MXF_SMPTE;
534 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
537 // configure encryption
538 if( Options.key_flag )
540 Kumu::GenRandomUUID(Info.ContextID);
541 Info.EncryptedEssence = true;
543 if ( Options.key_id_flag )
545 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
549 create_random_uuid(Info.CryptographicKeyID);
552 Context = new AESEncContext;
553 result = Context->InitKey(Options.key_value);
555 if ( ASDCP_SUCCESS(result) )
556 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
558 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
560 Info.UsesHMAC = true;
561 HMAC = new HMACContext;
562 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
566 if ( ASDCP_SUCCESS(result) )
567 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
570 if ( ASDCP_SUCCESS(result) )
571 // loop through the frames
573 result = Parser.Reset();
576 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
578 result = Parser.ReadFrame(FrameBuffer);
580 if ( ASDCP_SUCCESS(result) )
582 if ( Options.verbose_flag )
583 FrameBuffer.Dump(stderr, Options.fb_dump_size);
585 if ( Options.encrypt_header_flag )
586 FrameBuffer.PlaintextOffset(0);
589 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
591 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
593 // The Writer class will forward the last block of ciphertext
594 // to the encryption context for use as the IV for the next
595 // frame. If you want to use non-sequitur IV values, un-comment
596 // the following line of code.
597 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
598 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
602 if ( result == RESULT_ENDOFFILE )
606 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
607 result = Writer.Finalize();
613 //------------------------------------------------------------------------------------------
615 // return false if an error is discovered
617 check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc)
619 Rational rate = Options.PictureRate();
620 if ( rate != EditRate_96 && rate != EditRate_100 && rate != EditRate_120 )
623 if ( PDesc.StoredWidth > 2048 )
625 fprintf(stderr, "P-HFR files currently limited to 2K.\n");
629 if ( ! Options.use_smpte_labels )
631 fprintf(stderr, "P-HFR files must be written using SMPTE labels. Use option '-L'.\n");
635 // do not set the label if the user has already done so
636 if ( ! Options.picture_coding.HasValue() )
637 Options.picture_coding = UL(P_HFR_UL_2K);
642 //------------------------------------------------------------------------------------------
645 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
646 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
649 write_JP2K_S_file(CommandOptions& Options)
651 AESEncContext* Context = 0;
652 HMACContext* HMAC = 0;
653 JP2K::MXFSWriter Writer;
654 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
655 JP2K::PictureDescriptor PDesc;
656 JP2K::SequenceParser ParserLeft, ParserRight;
657 byte_t IV_buf[CBC_BLOCK_SIZE];
658 Kumu::FortunaRNG RNG;
660 if ( Options.filenames.size() != 2 )
662 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
666 // set up essence parser
667 Result_t result = ParserLeft.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
669 if ( ASDCP_SUCCESS(result) )
671 Options.filenames.pop_front();
672 result = ParserRight.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
676 if ( ASDCP_SUCCESS(result) )
678 ParserLeft.FillPictureDescriptor(PDesc);
679 PDesc.EditRate = Options.PictureRate();
681 if ( Options.verbose_flag )
683 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
684 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
685 JP2K::PictureDescriptorDump(PDesc);
689 if ( ! check_phfr_params(Options, PDesc) )
692 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
694 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
695 if ( Options.asset_id_flag )
696 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
698 Kumu::GenRandomUUID(Info.AssetUUID);
700 if ( Options.use_smpte_labels )
702 Info.LabelSetType = LS_MXF_SMPTE;
703 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
706 // configure encryption
707 if( Options.key_flag )
709 Kumu::GenRandomUUID(Info.ContextID);
710 Info.EncryptedEssence = true;
712 if ( Options.key_id_flag )
714 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
718 create_random_uuid(Info.CryptographicKeyID);
721 Context = new AESEncContext;
722 result = Context->InitKey(Options.key_value);
724 if ( ASDCP_SUCCESS(result) )
725 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
727 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
729 Info.UsesHMAC = true;
730 HMAC = new HMACContext;
731 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
735 if ( ASDCP_SUCCESS(result) )
736 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
738 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
740 MXF::RGBAEssenceDescriptor *descriptor = 0;
741 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
742 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
743 descriptor->PictureEssenceCoding = Options.picture_coding;
747 if ( ASDCP_SUCCESS(result) )
750 result = ParserLeft.Reset();
751 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
753 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
755 result = ParserLeft.ReadFrame(FrameBuffer);
757 if ( ASDCP_SUCCESS(result) )
759 if ( Options.verbose_flag )
760 FrameBuffer.Dump(stderr, Options.fb_dump_size);
762 if ( Options.encrypt_header_flag )
763 FrameBuffer.PlaintextOffset(0);
766 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
767 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
769 if ( ASDCP_SUCCESS(result) )
770 result = ParserRight.ReadFrame(FrameBuffer);
772 if ( ASDCP_SUCCESS(result) )
774 if ( Options.verbose_flag )
775 FrameBuffer.Dump(stderr, Options.fb_dump_size);
777 if ( Options.encrypt_header_flag )
778 FrameBuffer.PlaintextOffset(0);
781 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
782 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
785 if ( result == RESULT_ENDOFFILE )
789 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
790 result = Writer.Finalize();
795 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
796 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
799 write_JP2K_file(CommandOptions& Options)
801 AESEncContext* Context = 0;
802 HMACContext* HMAC = 0;
803 JP2K::MXFWriter Writer;
804 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
805 JP2K::PictureDescriptor PDesc;
806 JP2K::SequenceParser Parser;
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(), Options.j2c_pedantic);
814 if ( ASDCP_SUCCESS(result) )
816 Parser.FillPictureDescriptor(PDesc);
817 PDesc.EditRate = Options.PictureRate();
819 if ( Options.verbose_flag )
821 fprintf(stderr, "JPEG 2000 pictures\n");
822 fputs("PictureDescriptor:\n", stderr);
823 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
824 JP2K::PictureDescriptorDump(PDesc);
828 if ( ! check_phfr_params(Options, PDesc) )
831 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
833 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
834 if ( Options.asset_id_flag )
835 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
837 Kumu::GenRandomUUID(Info.AssetUUID);
839 if ( Options.use_smpte_labels )
841 Info.LabelSetType = LS_MXF_SMPTE;
842 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
845 // configure encryption
846 if( Options.key_flag )
848 Kumu::GenRandomUUID(Info.ContextID);
849 Info.EncryptedEssence = true;
851 if ( Options.key_id_flag )
853 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
857 create_random_uuid(Info.CryptographicKeyID);
860 Context = new AESEncContext;
861 result = Context->InitKey(Options.key_value);
863 if ( ASDCP_SUCCESS(result) )
864 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
866 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
868 Info.UsesHMAC = true;
869 HMAC = new HMACContext;
870 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
874 if ( ASDCP_SUCCESS(result) )
875 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
877 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
879 MXF::RGBAEssenceDescriptor *descriptor = 0;
880 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
881 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
882 descriptor->PictureEssenceCoding = Options.picture_coding;
886 if ( ASDCP_SUCCESS(result) )
889 result = Parser.Reset();
891 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
893 result = Parser.ReadFrame(FrameBuffer);
895 if ( ASDCP_SUCCESS(result) )
897 if ( Options.verbose_flag )
898 FrameBuffer.Dump(stderr, Options.fb_dump_size);
900 if ( Options.encrypt_header_flag )
901 FrameBuffer.PlaintextOffset(0);
904 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
906 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
908 // The Writer class will forward the last block of ciphertext
909 // to the encryption context for use as the IV for the next
910 // frame. If you want to use non-sequitur IV values, un-comment
911 // the following line of code.
912 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
913 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
917 if ( result == RESULT_ENDOFFILE )
921 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
922 result = Writer.Finalize();
927 //------------------------------------------------------------------------------------------
931 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
932 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
935 write_PCM_file(CommandOptions& Options)
937 AESEncContext* Context = 0;
938 HMACContext* HMAC = 0;
939 PCMParserList Parser;
940 PCM::MXFWriter Writer;
941 PCM::FrameBuffer FrameBuffer;
942 PCM::AudioDescriptor ADesc;
943 Rational PictureRate = Options.PictureRate();
944 byte_t IV_buf[CBC_BLOCK_SIZE];
945 Kumu::FortunaRNG RNG;
947 // set up essence parser
948 Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
951 if ( ASDCP_SUCCESS(result) )
953 Parser.FillAudioDescriptor(ADesc);
955 ADesc.EditRate = PictureRate;
956 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
957 ADesc.ChannelFormat = Options.channel_fmt;
959 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE && Options.mca_config.empty() )
961 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see options -C, -l and -m)\n");
964 if ( Options.verbose_flag )
966 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
967 ADesc.AudioSamplingRate.Quotient() / 1000.0,
968 Options.szPictureRate(),
969 PCM::CalcSamplesPerFrame(ADesc));
970 fputs("AudioDescriptor:\n", stderr);
971 PCM::AudioDescriptorDump(ADesc);
975 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
977 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
978 if ( Options.asset_id_flag )
979 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
981 Kumu::GenRandomUUID(Info.AssetUUID);
983 if ( Options.use_smpte_labels )
985 Info.LabelSetType = LS_MXF_SMPTE;
986 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
989 // configure encryption
990 if( Options.key_flag )
992 Kumu::GenRandomUUID(Info.ContextID);
993 Info.EncryptedEssence = true;
995 if ( Options.key_id_flag )
997 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1001 create_random_uuid(Info.CryptographicKeyID);
1004 Context = new AESEncContext;
1005 result = Context->InitKey(Options.key_value);
1007 if ( ASDCP_SUCCESS(result) )
1008 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1010 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1012 Info.UsesHMAC = true;
1013 HMAC = new HMACContext;
1014 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1018 if ( ASDCP_SUCCESS(result) )
1019 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1021 if ( ASDCP_SUCCESS(result)
1022 && ( Options.channel_assignment.HasValue()
1023 || ! Options.mca_config.empty() ) )
1025 MXF::WaveAudioDescriptor *essence_descriptor = 0;
1026 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_WaveAudioDescriptor),
1027 reinterpret_cast<MXF::InterchangeObject**>(&essence_descriptor));
1028 assert(essence_descriptor);
1030 if ( Options.mca_config.empty() )
1032 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1036 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1038 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1039 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1043 if ( Options.channel_assignment.HasValue() )
1045 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1047 else if ( Options.use_interop_sound_wtf )
1049 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_4_WTF);
1053 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
1056 // add descriptors to the essence_descriptor and header
1057 ASDCP::MXF::InterchangeObject_list_t::iterator i;
1058 for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
1060 if ( (*i)->GetUL() != UL(g_dict->ul(MDD_AudioChannelLabelSubDescriptor))
1061 && (*i)->GetUL() != UL(g_dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
1062 && (*i)->GetUL() != UL(g_dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
1064 fprintf(stderr, "Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
1068 Writer.OP1aHeader().AddChildObject(*i);
1069 essence_descriptor->SubDescriptors.push_back((*i)->InstanceUID);
1070 *i = 0; // parent will only free the ones we don't keep
1076 if ( ASDCP_SUCCESS(result) )
1078 result = Parser.Reset();
1079 ui32_t duration = 0;
1081 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1083 result = Parser.ReadFrame(FrameBuffer);
1085 if ( ASDCP_SUCCESS(result) )
1087 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1089 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1090 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1092 if ( Options.write_partial_pcm_flag )
1094 result = RESULT_ENDOFFILE;
1099 if ( Options.verbose_flag )
1100 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1102 if ( ! Options.no_write_flag )
1104 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1106 // The Writer class will forward the last block of ciphertext
1107 // to the encryption context for use as the IV for the next
1108 // frame. If you want to use non-sequitur IV values, un-comment
1109 // the following line of code.
1110 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1111 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1116 if ( result == RESULT_ENDOFFILE )
1120 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1121 result = Writer.Finalize();
1126 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a plaintext ASDCP file
1127 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a ciphertext ASDCP file
1130 write_PCM_with_ATMOS_sync_file(CommandOptions& Options)
1132 AESEncContext* Context = 0;
1133 HMACContext* HMAC = 0;
1134 PCM::MXFWriter Writer;
1135 PCM::FrameBuffer FrameBuffer;
1136 PCM::AudioDescriptor ADesc;
1137 Rational PictureRate = Options.PictureRate();
1138 byte_t IV_buf[CBC_BLOCK_SIZE];
1139 Kumu::FortunaRNG RNG;
1141 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1142 if ( Options.asset_id_flag )
1143 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1145 Kumu::GenRandomUUID(Info.AssetUUID);
1146 AtmosSyncChannelMixer Mixer(Info.AssetUUID);
1148 // set up essence parser
1149 Result_t result = Mixer.OpenRead(Options.filenames, PictureRate);
1151 // set up MXF writer
1152 if ( ASDCP_SUCCESS(result) )
1154 Mixer.FillAudioDescriptor(ADesc);
1156 ADesc.EditRate = PictureRate;
1157 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1158 ADesc.ChannelFormat = PCM::CF_CFG_4;
1160 if ( Options.verbose_flag )
1162 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1163 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1164 Options.szPictureRate(),
1165 PCM::CalcSamplesPerFrame(ADesc));
1166 fputs("AudioDescriptor:\n", stderr);
1167 PCM::AudioDescriptorDump(ADesc);
1171 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1173 Info.LabelSetType = LS_MXF_SMPTE;
1174 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1176 // configure encryption
1177 if( Options.key_flag )
1179 Kumu::GenRandomUUID(Info.ContextID);
1180 Info.EncryptedEssence = true;
1182 if ( Options.key_id_flag )
1184 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1188 create_random_uuid(Info.CryptographicKeyID);
1191 Context = new AESEncContext;
1192 result = Context->InitKey(Options.key_value);
1194 if ( ASDCP_SUCCESS(result) )
1195 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1197 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1199 Info.UsesHMAC = true;
1200 HMAC = new HMACContext;
1201 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1205 if ( ASDCP_SUCCESS(result) )
1206 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1209 if ( ASDCP_SUCCESS(result) )
1211 result = Mixer.Reset();
1212 ui32_t duration = 0;
1214 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1216 result = Mixer.ReadFrame(FrameBuffer);
1218 if ( ASDCP_SUCCESS(result) )
1220 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1222 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1223 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1225 if ( Options.write_partial_pcm_flag )
1227 result = RESULT_ENDOFFILE;
1232 if ( Options.verbose_flag )
1233 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1235 if ( ! Options.no_write_flag )
1237 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1239 // The Writer class will forward the last block of ciphertext
1240 // to the encryption context for use as the IV for the next
1241 // frame. If you want to use non-sequitur IV values, un-comment
1242 // the following line of code.
1243 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1244 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1249 if ( result == RESULT_ENDOFFILE )
1253 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1254 result = Writer.Finalize();
1260 //------------------------------------------------------------------------------------------
1261 // TimedText essence
1264 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1265 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1268 write_timed_text_file(CommandOptions& Options)
1270 AESEncContext* Context = 0;
1271 HMACContext* HMAC = 0;
1272 TimedText::DCSubtitleParser Parser;
1273 TimedText::MXFWriter Writer;
1274 TimedText::FrameBuffer FrameBuffer;
1275 TimedText::TimedTextDescriptor TDesc;
1276 byte_t IV_buf[CBC_BLOCK_SIZE];
1277 Kumu::FortunaRNG RNG;
1279 // set up essence parser
1280 Result_t result = Parser.OpenRead(Options.filenames.front());
1282 // set up MXF writer
1283 if ( ASDCP_SUCCESS(result) )
1285 Parser.FillTimedTextDescriptor(TDesc);
1286 FrameBuffer.Capacity(Options.fb_size);
1288 if ( Options.verbose_flag )
1290 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1291 TimedText::DescriptorDump(TDesc);
1295 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1297 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1298 if ( Options.asset_id_flag )
1299 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1301 Kumu::GenRandomUUID(Info.AssetUUID);
1303 // 428-7 IN 429-5 always uses SMPTE labels
1304 Info.LabelSetType = LS_MXF_SMPTE;
1305 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1307 // configure encryption
1308 if( Options.key_flag )
1310 Kumu::GenRandomUUID(Info.ContextID);
1311 Info.EncryptedEssence = true;
1313 if ( Options.key_id_flag )
1315 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1319 create_random_uuid(Info.CryptographicKeyID);
1322 Context = new AESEncContext;
1323 result = Context->InitKey(Options.key_value);
1325 if ( ASDCP_SUCCESS(result) )
1326 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1328 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1330 Info.UsesHMAC = true;
1331 HMAC = new HMACContext;
1332 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1336 if ( ASDCP_SUCCESS(result) )
1337 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1340 if ( ASDCP_FAILURE(result) )
1344 TimedText::ResourceList_t::const_iterator ri;
1346 result = Parser.ReadTimedTextResource(XMLDoc);
1348 if ( ASDCP_SUCCESS(result) )
1349 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1351 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1353 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1355 if ( ASDCP_SUCCESS(result) )
1357 if ( Options.verbose_flag )
1358 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1360 if ( ! Options.no_write_flag )
1362 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1364 // The Writer class will forward the last block of ciphertext
1365 // to the encryption context for use as the IV for the next
1366 // frame. If you want to use non-sequitur IV values, un-comment
1367 // the following line of code.
1368 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1369 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1373 if ( result == RESULT_ENDOFFILE )
1377 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1378 result = Writer.Finalize();
1383 // Write one or more plaintext Dolby ATMOS bytestreams to a plaintext ASDCP file
1384 // Write one or more plaintext Dolby ATMOS bytestreams to a ciphertext ASDCP file
1387 write_dolby_atmos_file(CommandOptions& Options)
1389 AESEncContext* Context = 0;
1390 HMACContext* HMAC = 0;
1391 ATMOS::MXFWriter Writer;
1392 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1393 ATMOS::AtmosDescriptor ADesc;
1394 DCData::SequenceParser Parser;
1395 byte_t IV_buf[CBC_BLOCK_SIZE];
1396 Kumu::FortunaRNG RNG;
1398 // set up essence parser
1399 Result_t result = Parser.OpenRead(Options.filenames.front());
1401 // set up MXF writer
1402 if ( ASDCP_SUCCESS(result) )
1404 Parser.FillDCDataDescriptor(ADesc);
1405 ADesc.EditRate = Options.PictureRate();
1406 // TODO: fill AtmosDescriptor
1407 ADesc.FirstFrame = Options.ffoa;
1408 ADesc.MaxChannelCount = Options.max_channel_count;
1409 ADesc.MaxObjectCount = Options.max_object_count;
1410 Kumu::GenRandomUUID(ADesc.AtmosID);
1411 ADesc.AtmosVersion = 1;
1412 if ( Options.verbose_flag )
1414 fprintf(stderr, "Dolby ATMOS Data\n");
1415 fputs("AtmosDescriptor:\n", stderr);
1416 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1417 ATMOS::AtmosDescriptorDump(ADesc);
1421 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1423 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1424 if ( Options.asset_id_flag )
1425 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1427 Kumu::GenRandomUUID(Info.AssetUUID);
1429 Info.LabelSetType = LS_MXF_SMPTE;
1431 // configure encryption
1432 if( Options.key_flag )
1434 Kumu::GenRandomUUID(Info.ContextID);
1435 Info.EncryptedEssence = true;
1437 if ( Options.key_id_flag )
1439 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1443 create_random_uuid(Info.CryptographicKeyID);
1446 Context = new AESEncContext;
1447 result = Context->InitKey(Options.key_value);
1449 if ( ASDCP_SUCCESS(result) )
1450 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1452 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1454 Info.UsesHMAC = true;
1455 HMAC = new HMACContext;
1456 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1460 if ( ASDCP_SUCCESS(result) )
1461 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1464 if ( ASDCP_SUCCESS(result) )
1466 ui32_t duration = 0;
1467 result = Parser.Reset();
1469 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1471 result = Parser.ReadFrame(FrameBuffer);
1473 if ( ASDCP_SUCCESS(result) )
1475 if ( Options.verbose_flag )
1476 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1478 if ( Options.encrypt_header_flag )
1479 FrameBuffer.PlaintextOffset(0);
1482 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1484 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1486 // The Writer class will forward the last block of ciphertext
1487 // to the encryption context for use as the IV for the next
1488 // frame. If you want to use non-sequitur IV values, un-comment
1489 // the following line of code.
1490 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1491 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1495 if ( result == RESULT_ENDOFFILE )
1499 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1500 result = Writer.Finalize();
1505 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a plaintext ASDCP file
1506 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a ciphertext ASDCP file
1509 write_aux_data_file(CommandOptions& Options)
1511 AESEncContext* Context = 0;
1512 HMACContext* HMAC = 0;
1513 DCData::MXFWriter Writer;
1514 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1515 DCData::DCDataDescriptor DDesc;
1516 DCData::SequenceParser Parser;
1517 byte_t IV_buf[CBC_BLOCK_SIZE];
1518 Kumu::FortunaRNG RNG;
1520 // set up essence parser
1521 Result_t result = Parser.OpenRead(Options.filenames.front());
1523 // set up MXF writer
1524 if ( ASDCP_SUCCESS(result) )
1526 Parser.FillDCDataDescriptor(DDesc);
1527 memcpy(DDesc.DataEssenceCoding, Options.aux_data_coding.Value(), Options.aux_data_coding.Size());
1528 DDesc.EditRate = Options.PictureRate();
1530 if ( Options.verbose_flag )
1532 fprintf(stderr, "Aux Data\n");
1533 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1534 DCData::DCDataDescriptorDump(DDesc);
1538 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1540 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1541 if ( Options.asset_id_flag )
1542 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1544 Kumu::GenRandomUUID(Info.AssetUUID);
1546 Info.LabelSetType = LS_MXF_SMPTE;
1548 // configure encryption
1549 if( Options.key_flag )
1551 Kumu::GenRandomUUID(Info.ContextID);
1552 Info.EncryptedEssence = true;
1554 if ( Options.key_id_flag )
1556 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1560 create_random_uuid(Info.CryptographicKeyID);
1563 Context = new AESEncContext;
1564 result = Context->InitKey(Options.key_value);
1566 if ( ASDCP_SUCCESS(result) )
1567 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1569 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1571 Info.UsesHMAC = true;
1572 HMAC = new HMACContext;
1573 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1577 if ( ASDCP_SUCCESS(result) )
1578 result = Writer.OpenWrite(Options.out_file, Info, DDesc);
1581 if ( ASDCP_SUCCESS(result) )
1583 ui32_t duration = 0;
1584 result = Parser.Reset();
1586 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1588 result = Parser.ReadFrame(FrameBuffer);
1590 if ( ASDCP_SUCCESS(result) )
1592 if ( Options.verbose_flag )
1593 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1595 if ( Options.encrypt_header_flag )
1596 FrameBuffer.PlaintextOffset(0);
1599 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1601 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1603 // The Writer class will forward the last block of ciphertext
1604 // to the encryption context for use as the IV for the next
1605 // frame. If you want to use non-sequitur IV values, un-comment
1606 // the following line of code.
1607 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1608 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1612 if ( result == RESULT_ENDOFFILE )
1616 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1617 result = Writer.Finalize();
1624 main(int argc, const char** argv)
1626 Result_t result = RESULT_OK;
1628 g_dict = &ASDCP::DefaultSMPTEDict();
1630 CommandOptions Options(argc, argv);
1632 if ( Options.version_flag )
1635 if ( Options.help_flag )
1638 if ( Options.show_ul_values_flag )
1640 if ( Options.use_smpte_labels )
1641 DefaultSMPTEDict().Dump(stdout);
1643 DefaultInteropDict().Dump(stdout);
1646 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1649 if ( Options.error_flag )
1651 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1655 EssenceType_t EssenceType;
1656 result = ASDCP::RawEssenceType(Options.filenames.front(), EssenceType);
1658 if ( ASDCP_SUCCESS(result) )
1660 switch ( EssenceType )
1663 result = write_MPEG2_file(Options);
1667 if ( Options.stereo_image_flag )
1669 result = write_JP2K_S_file(Options);
1673 result = write_JP2K_file(Options);
1677 case ESS_PCM_24b_48k:
1678 case ESS_PCM_24b_96k:
1679 if ( Options.dolby_atmos_sync_flag )
1681 result = write_PCM_with_ATMOS_sync_file(Options);
1685 result = write_PCM_file(Options);
1689 case ESS_TIMED_TEXT:
1690 result = write_timed_text_file(Options);
1693 case ESS_DCDATA_DOLBY_ATMOS:
1694 result = write_dolby_atmos_file(Options);
1697 case ESS_DCDATA_UNKNOWN:
1698 if ( ! Options.aux_data_coding.HasValue() )
1700 fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n");
1705 result = write_aux_data_file(Options);
1710 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1711 Options.filenames.front().c_str());
1716 if ( ASDCP_FAILURE(result) )
1718 fputs("Program stopped on error.\n", stderr);
1720 if ( result != RESULT_FAIL )
1722 fputs(result, stderr);
1723 fputc('\n', stderr);
1734 // end asdcp-wrap.cpp