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
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 if ( Mixer.ChannelCount() % 2 != 0 )
1156 result = Mixer.AppendSilenceChannels(1);
1159 Mixer.FillAudioDescriptor(ADesc);
1161 ADesc.EditRate = PictureRate;
1162 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1163 ADesc.ChannelFormat = PCM::CF_CFG_4;
1165 if ( Options.verbose_flag )
1167 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1168 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1169 Options.szPictureRate(),
1170 PCM::CalcSamplesPerFrame(ADesc));
1171 fputs("AudioDescriptor:\n", stderr);
1172 PCM::AudioDescriptorDump(ADesc);
1176 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1178 Info.LabelSetType = LS_MXF_SMPTE;
1179 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1181 // configure encryption
1182 if( Options.key_flag )
1184 Kumu::GenRandomUUID(Info.ContextID);
1185 Info.EncryptedEssence = true;
1187 if ( Options.key_id_flag )
1189 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1193 create_random_uuid(Info.CryptographicKeyID);
1196 Context = new AESEncContext;
1197 result = Context->InitKey(Options.key_value);
1199 if ( ASDCP_SUCCESS(result) )
1200 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1202 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1204 Info.UsesHMAC = true;
1205 HMAC = new HMACContext;
1206 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1210 if ( ASDCP_SUCCESS(result) )
1211 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1214 if ( ASDCP_SUCCESS(result) )
1216 result = Mixer.Reset();
1217 ui32_t duration = 0;
1219 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1221 result = Mixer.ReadFrame(FrameBuffer);
1223 if ( ASDCP_SUCCESS(result) )
1225 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1227 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1228 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1230 if ( Options.write_partial_pcm_flag )
1232 result = RESULT_ENDOFFILE;
1237 if ( Options.verbose_flag )
1238 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1240 if ( ! Options.no_write_flag )
1242 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1244 // The Writer class will forward the last block of ciphertext
1245 // to the encryption context for use as the IV for the next
1246 // frame. If you want to use non-sequitur IV values, un-comment
1247 // the following line of code.
1248 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1249 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1254 if ( result == RESULT_ENDOFFILE )
1258 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1259 result = Writer.Finalize();
1265 //------------------------------------------------------------------------------------------
1266 // TimedText essence
1269 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1270 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1273 write_timed_text_file(CommandOptions& Options)
1275 AESEncContext* Context = 0;
1276 HMACContext* HMAC = 0;
1277 TimedText::DCSubtitleParser Parser;
1278 TimedText::MXFWriter Writer;
1279 TimedText::FrameBuffer FrameBuffer;
1280 TimedText::TimedTextDescriptor TDesc;
1281 byte_t IV_buf[CBC_BLOCK_SIZE];
1282 Kumu::FortunaRNG RNG;
1284 // set up essence parser
1285 Result_t result = Parser.OpenRead(Options.filenames.front());
1287 // set up MXF writer
1288 if ( ASDCP_SUCCESS(result) )
1290 Parser.FillTimedTextDescriptor(TDesc);
1291 FrameBuffer.Capacity(Options.fb_size);
1293 if ( Options.verbose_flag )
1295 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1296 TimedText::DescriptorDump(TDesc);
1300 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1302 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1303 if ( Options.asset_id_flag )
1304 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1306 Kumu::GenRandomUUID(Info.AssetUUID);
1308 // 428-7 IN 429-5 always uses SMPTE labels
1309 Info.LabelSetType = LS_MXF_SMPTE;
1310 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1312 // configure encryption
1313 if( Options.key_flag )
1315 Kumu::GenRandomUUID(Info.ContextID);
1316 Info.EncryptedEssence = true;
1318 if ( Options.key_id_flag )
1320 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1324 create_random_uuid(Info.CryptographicKeyID);
1327 Context = new AESEncContext;
1328 result = Context->InitKey(Options.key_value);
1330 if ( ASDCP_SUCCESS(result) )
1331 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1333 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1335 Info.UsesHMAC = true;
1336 HMAC = new HMACContext;
1337 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1341 if ( ASDCP_SUCCESS(result) )
1342 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1345 if ( ASDCP_FAILURE(result) )
1349 TimedText::ResourceList_t::const_iterator ri;
1351 result = Parser.ReadTimedTextResource(XMLDoc);
1353 if ( ASDCP_SUCCESS(result) )
1354 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1356 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1358 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1360 if ( ASDCP_SUCCESS(result) )
1362 if ( Options.verbose_flag )
1363 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1365 if ( ! Options.no_write_flag )
1367 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1369 // The Writer class will forward the last block of ciphertext
1370 // to the encryption context for use as the IV for the next
1371 // frame. If you want to use non-sequitur IV values, un-comment
1372 // the following line of code.
1373 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1374 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1378 if ( result == RESULT_ENDOFFILE )
1382 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1383 result = Writer.Finalize();
1388 // Write one or more plaintext Dolby ATMOS bytestreams to a plaintext ASDCP file
1389 // Write one or more plaintext Dolby ATMOS bytestreams to a ciphertext ASDCP file
1392 write_dolby_atmos_file(CommandOptions& Options)
1394 AESEncContext* Context = 0;
1395 HMACContext* HMAC = 0;
1396 ATMOS::MXFWriter Writer;
1397 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1398 ATMOS::AtmosDescriptor ADesc;
1399 DCData::SequenceParser Parser;
1400 byte_t IV_buf[CBC_BLOCK_SIZE];
1401 Kumu::FortunaRNG RNG;
1403 // set up essence parser
1404 Result_t result = Parser.OpenRead(Options.filenames.front());
1406 // set up MXF writer
1407 if ( ASDCP_SUCCESS(result) )
1409 Parser.FillDCDataDescriptor(ADesc);
1410 ADesc.EditRate = Options.PictureRate();
1411 // TODO: fill AtmosDescriptor
1412 ADesc.FirstFrame = Options.ffoa;
1413 ADesc.MaxChannelCount = Options.max_channel_count;
1414 ADesc.MaxObjectCount = Options.max_object_count;
1415 Kumu::GenRandomUUID(ADesc.AtmosID);
1416 ADesc.AtmosVersion = 1;
1417 if ( Options.verbose_flag )
1419 fprintf(stderr, "Dolby ATMOS Data\n");
1420 fputs("AtmosDescriptor:\n", stderr);
1421 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1422 ATMOS::AtmosDescriptorDump(ADesc);
1426 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1428 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1429 if ( Options.asset_id_flag )
1430 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1432 Kumu::GenRandomUUID(Info.AssetUUID);
1434 Info.LabelSetType = LS_MXF_SMPTE;
1436 // configure encryption
1437 if( Options.key_flag )
1439 Kumu::GenRandomUUID(Info.ContextID);
1440 Info.EncryptedEssence = true;
1442 if ( Options.key_id_flag )
1444 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1448 create_random_uuid(Info.CryptographicKeyID);
1451 Context = new AESEncContext;
1452 result = Context->InitKey(Options.key_value);
1454 if ( ASDCP_SUCCESS(result) )
1455 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1457 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1459 Info.UsesHMAC = true;
1460 HMAC = new HMACContext;
1461 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1465 if ( ASDCP_SUCCESS(result) )
1466 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1469 if ( ASDCP_SUCCESS(result) )
1471 ui32_t duration = 0;
1472 result = Parser.Reset();
1474 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1476 result = Parser.ReadFrame(FrameBuffer);
1478 if ( ASDCP_SUCCESS(result) )
1480 if ( Options.verbose_flag )
1481 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1483 if ( Options.encrypt_header_flag )
1484 FrameBuffer.PlaintextOffset(0);
1487 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1489 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1491 // The Writer class will forward the last block of ciphertext
1492 // to the encryption context for use as the IV for the next
1493 // frame. If you want to use non-sequitur IV values, un-comment
1494 // the following line of code.
1495 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1496 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1500 if ( result == RESULT_ENDOFFILE )
1504 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1505 result = Writer.Finalize();
1510 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a plaintext ASDCP file
1511 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a ciphertext ASDCP file
1514 write_aux_data_file(CommandOptions& Options)
1516 AESEncContext* Context = 0;
1517 HMACContext* HMAC = 0;
1518 DCData::MXFWriter Writer;
1519 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1520 DCData::DCDataDescriptor DDesc;
1521 DCData::SequenceParser Parser;
1522 byte_t IV_buf[CBC_BLOCK_SIZE];
1523 Kumu::FortunaRNG RNG;
1525 // set up essence parser
1526 Result_t result = Parser.OpenRead(Options.filenames.front());
1528 // set up MXF writer
1529 if ( ASDCP_SUCCESS(result) )
1531 Parser.FillDCDataDescriptor(DDesc);
1532 memcpy(DDesc.DataEssenceCoding, Options.aux_data_coding.Value(), Options.aux_data_coding.Size());
1533 DDesc.EditRate = Options.PictureRate();
1535 if ( Options.verbose_flag )
1537 fprintf(stderr, "Aux Data\n");
1538 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1539 DCData::DCDataDescriptorDump(DDesc);
1543 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1545 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1546 if ( Options.asset_id_flag )
1547 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1549 Kumu::GenRandomUUID(Info.AssetUUID);
1551 Info.LabelSetType = LS_MXF_SMPTE;
1553 // configure encryption
1554 if( Options.key_flag )
1556 Kumu::GenRandomUUID(Info.ContextID);
1557 Info.EncryptedEssence = true;
1559 if ( Options.key_id_flag )
1561 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1565 create_random_uuid(Info.CryptographicKeyID);
1568 Context = new AESEncContext;
1569 result = Context->InitKey(Options.key_value);
1571 if ( ASDCP_SUCCESS(result) )
1572 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1574 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1576 Info.UsesHMAC = true;
1577 HMAC = new HMACContext;
1578 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1582 if ( ASDCP_SUCCESS(result) )
1583 result = Writer.OpenWrite(Options.out_file, Info, DDesc);
1586 if ( ASDCP_SUCCESS(result) )
1588 ui32_t duration = 0;
1589 result = Parser.Reset();
1591 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1593 result = Parser.ReadFrame(FrameBuffer);
1595 if ( ASDCP_SUCCESS(result) )
1597 if ( Options.verbose_flag )
1598 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1600 if ( Options.encrypt_header_flag )
1601 FrameBuffer.PlaintextOffset(0);
1604 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1606 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1608 // The Writer class will forward the last block of ciphertext
1609 // to the encryption context for use as the IV for the next
1610 // frame. If you want to use non-sequitur IV values, un-comment
1611 // the following line of code.
1612 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1613 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1617 if ( result == RESULT_ENDOFFILE )
1621 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1622 result = Writer.Finalize();
1629 main(int argc, const char** argv)
1631 Result_t result = RESULT_OK;
1633 g_dict = &ASDCP::DefaultSMPTEDict();
1635 CommandOptions Options(argc, argv);
1637 if ( Options.version_flag )
1640 if ( Options.help_flag )
1643 if ( Options.show_ul_values_flag )
1645 if ( Options.use_smpte_labels )
1646 DefaultSMPTEDict().Dump(stdout);
1648 DefaultInteropDict().Dump(stdout);
1651 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1654 if ( Options.error_flag )
1656 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1660 EssenceType_t EssenceType;
1661 result = ASDCP::RawEssenceType(Options.filenames.front(), EssenceType);
1663 if ( ASDCP_SUCCESS(result) )
1665 switch ( EssenceType )
1668 result = write_MPEG2_file(Options);
1672 if ( Options.stereo_image_flag )
1674 result = write_JP2K_S_file(Options);
1678 result = write_JP2K_file(Options);
1682 case ESS_PCM_24b_48k:
1683 case ESS_PCM_24b_96k:
1684 if ( Options.dolby_atmos_sync_flag )
1686 result = write_PCM_with_ATMOS_sync_file(Options);
1690 result = write_PCM_file(Options);
1694 case ESS_TIMED_TEXT:
1695 result = write_timed_text_file(Options);
1698 case ESS_DCDATA_DOLBY_ATMOS:
1699 result = write_dolby_atmos_file(Options);
1702 case ESS_DCDATA_UNKNOWN:
1703 if ( ! Options.aux_data_coding.HasValue() )
1705 fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n");
1710 result = write_aux_data_file(Options);
1715 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1716 Options.filenames.front().c_str());
1721 if ( ASDCP_FAILURE(result) )
1723 fputs("Program stopped on error.\n", stderr);
1725 if ( result != RESULT_FAIL )
1727 fputs(result, stderr);
1728 fputc('\n', stderr);
1739 // end asdcp-wrap.cpp