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), 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),
302 channel_fmt(PCM::CF_NONE),
303 show_ul_values_flag(false),
304 dolby_atmos_sync_flag(false),
305 ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties
306 use_interop_sound_wtf(false),
309 memset(key_value, 0, KeyLen);
310 memset(key_id_value, 0, UUIDlen);
312 for ( int i = 1; i < argc; i++ )
315 if ( (strcmp( argv[i], "-help") == 0) )
321 if ( argv[i][0] == '-'
322 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
325 switch ( argv[i][1] )
327 case '3': stereo_image_flag = true; break;
330 TEST_EXTRA_ARG(i, 'A');
331 if ( ! aux_data_coding.DecodeHex(argv[i]) )
333 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
339 asset_id_flag = true;
340 TEST_EXTRA_ARG(i, 'a');
343 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
345 if ( length != UUIDlen )
347 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
354 TEST_EXTRA_ARG(i, 'b');
355 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
358 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
363 TEST_EXTRA_ARG(i, 'C');
364 if ( ! channel_assignment.DecodeHex(argv[i]) )
366 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
372 TEST_EXTRA_ARG(i, 'd');
373 duration = Kumu::xabs(strtol(argv[i], 0, 10));
376 case 'E': encrypt_header_flag = false; break;
377 case 'e': encrypt_header_flag = true; break;
380 TEST_EXTRA_ARG(i, 'f');
381 start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
384 case 'g': write_partial_pcm_flag = true; break;
385 case 'h': help_flag = true; break;
387 case 'j': key_id_flag = true;
388 TEST_EXTRA_ARG(i, 'j');
391 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
393 if ( length != UUIDlen )
395 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
401 case 'k': key_flag = true;
402 TEST_EXTRA_ARG(i, 'k');
405 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
407 if ( length != KeyLen )
409 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
416 TEST_EXTRA_ARG(i, 'l');
417 channel_fmt = decode_channel_fmt(argv[i]);
420 case 'L': use_smpte_labels = true; break;
421 case 'M': write_hmac = false; break;
424 TEST_EXTRA_ARG(i, 'm');
425 if ( ! mca_config.DecodeString(argv[i]) )
432 TEST_EXTRA_ARG(i, 'P');
433 if ( ! picture_coding.DecodeHex(argv[i]) )
435 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
441 TEST_EXTRA_ARG(i, 'p');
442 picture_rate = Kumu::xabs(strtol(argv[i], 0, 10));
445 case 's': dolby_atmos_sync_flag = true; break;
446 case 'u': show_ul_values_flag = true; break;
447 case 'V': version_flag = true; break;
448 case 'v': verbose_flag = true; break;
449 case 'w': use_interop_sound_wtf = true; break;
450 case 'W': no_write_flag = true; break;
451 case 'Z': j2c_pedantic = false; break;
452 case 'z': j2c_pedantic = true; break;
455 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
462 if ( argv[i][0] != '-' )
464 filenames.push_back(argv[i]);
468 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
474 if ( help_flag || version_flag )
477 if ( filenames.size() < 2 )
479 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
483 out_file = filenames.back();
484 filenames.pop_back();
489 //------------------------------------------------------------------------------------------
492 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
493 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
496 write_MPEG2_file(CommandOptions& Options)
498 AESEncContext* Context = 0;
499 HMACContext* HMAC = 0;
500 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
501 MPEG2::Parser Parser;
502 MPEG2::MXFWriter Writer;
503 MPEG2::VideoDescriptor VDesc;
504 byte_t IV_buf[CBC_BLOCK_SIZE];
505 Kumu::FortunaRNG RNG;
507 // set up essence parser
508 Result_t result = Parser.OpenRead(Options.filenames.front());
511 if ( ASDCP_SUCCESS(result) )
513 Parser.FillVideoDescriptor(VDesc);
515 if ( Options.verbose_flag )
517 fputs("MPEG-2 Pictures\n", stderr);
518 fputs("VideoDescriptor:\n", stderr);
519 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
520 MPEG2::VideoDescriptorDump(VDesc);
524 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
526 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
527 if ( Options.asset_id_flag )
528 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
530 Kumu::GenRandomUUID(Info.AssetUUID);
532 if ( Options.use_smpte_labels )
534 Info.LabelSetType = LS_MXF_SMPTE;
535 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
538 // configure encryption
539 if( Options.key_flag )
541 Kumu::GenRandomUUID(Info.ContextID);
542 Info.EncryptedEssence = true;
544 if ( Options.key_id_flag )
546 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
550 create_random_uuid(Info.CryptographicKeyID);
553 Context = new AESEncContext;
554 result = Context->InitKey(Options.key_value);
556 if ( ASDCP_SUCCESS(result) )
557 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
559 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
561 Info.UsesHMAC = true;
562 HMAC = new HMACContext;
563 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
567 if ( ASDCP_SUCCESS(result) )
568 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
571 if ( ASDCP_SUCCESS(result) )
572 // loop through the frames
574 result = Parser.Reset();
577 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
579 result = Parser.ReadFrame(FrameBuffer);
581 if ( ASDCP_SUCCESS(result) )
583 if ( Options.verbose_flag )
584 FrameBuffer.Dump(stderr, Options.fb_dump_size);
586 if ( Options.encrypt_header_flag )
587 FrameBuffer.PlaintextOffset(0);
590 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
592 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
594 // The Writer class will forward the last block of ciphertext
595 // to the encryption context for use as the IV for the next
596 // frame. If you want to use non-sequitur IV values, un-comment
597 // the following line of code.
598 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
599 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
603 if ( result == RESULT_ENDOFFILE )
607 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
608 result = Writer.Finalize();
614 //------------------------------------------------------------------------------------------
616 // return false if an error is discovered
618 check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc)
620 Rational rate = Options.PictureRate();
621 if ( rate != EditRate_96 && rate != EditRate_100 && rate != EditRate_120 )
624 if ( PDesc.StoredWidth > 2048 )
626 fprintf(stderr, "P-HFR files currently limited to 2K.\n");
630 if ( ! Options.use_smpte_labels )
632 fprintf(stderr, "P-HFR files must be written using SMPTE labels. Use option '-L'.\n");
636 // do not set the label if the user has already done so
637 if ( ! Options.picture_coding.HasValue() )
638 Options.picture_coding = UL(P_HFR_UL_2K);
643 //------------------------------------------------------------------------------------------
646 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
647 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
650 write_JP2K_S_file(CommandOptions& Options)
652 AESEncContext* Context = 0;
653 HMACContext* HMAC = 0;
654 JP2K::MXFSWriter Writer;
655 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
656 JP2K::PictureDescriptor PDesc;
657 JP2K::SequenceParser ParserLeft, ParserRight;
658 byte_t IV_buf[CBC_BLOCK_SIZE];
659 Kumu::FortunaRNG RNG;
661 if ( Options.filenames.size() != 2 )
663 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
667 // set up essence parser
668 Result_t result = ParserLeft.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
670 if ( ASDCP_SUCCESS(result) )
672 Options.filenames.pop_front();
673 result = ParserRight.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
677 if ( ASDCP_SUCCESS(result) )
679 ParserLeft.FillPictureDescriptor(PDesc);
680 PDesc.EditRate = Options.PictureRate();
682 if ( Options.verbose_flag )
684 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
685 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
686 JP2K::PictureDescriptorDump(PDesc);
690 if ( ! check_phfr_params(Options, PDesc) )
693 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
695 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
696 if ( Options.asset_id_flag )
697 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
699 Kumu::GenRandomUUID(Info.AssetUUID);
701 if ( Options.use_smpte_labels )
703 Info.LabelSetType = LS_MXF_SMPTE;
704 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
707 // configure encryption
708 if( Options.key_flag )
710 Kumu::GenRandomUUID(Info.ContextID);
711 Info.EncryptedEssence = true;
713 if ( Options.key_id_flag )
715 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
719 create_random_uuid(Info.CryptographicKeyID);
722 Context = new AESEncContext;
723 result = Context->InitKey(Options.key_value);
725 if ( ASDCP_SUCCESS(result) )
726 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
728 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
730 Info.UsesHMAC = true;
731 HMAC = new HMACContext;
732 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
736 if ( ASDCP_SUCCESS(result) )
737 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
739 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
741 MXF::RGBAEssenceDescriptor *descriptor = 0;
742 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
743 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
744 descriptor->PictureEssenceCoding = Options.picture_coding;
748 if ( ASDCP_SUCCESS(result) )
751 result = ParserLeft.Reset();
752 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
754 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
756 result = ParserLeft.ReadFrame(FrameBuffer);
758 if ( ASDCP_SUCCESS(result) )
760 if ( Options.verbose_flag )
761 FrameBuffer.Dump(stderr, Options.fb_dump_size);
763 if ( Options.encrypt_header_flag )
764 FrameBuffer.PlaintextOffset(0);
767 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
768 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
770 if ( ASDCP_SUCCESS(result) )
771 result = ParserRight.ReadFrame(FrameBuffer);
773 if ( ASDCP_SUCCESS(result) )
775 if ( Options.verbose_flag )
776 FrameBuffer.Dump(stderr, Options.fb_dump_size);
778 if ( Options.encrypt_header_flag )
779 FrameBuffer.PlaintextOffset(0);
782 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
783 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
786 if ( result == RESULT_ENDOFFILE )
790 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
791 result = Writer.Finalize();
796 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
797 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
800 write_JP2K_file(CommandOptions& Options)
802 AESEncContext* Context = 0;
803 HMACContext* HMAC = 0;
804 JP2K::MXFWriter Writer;
805 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
806 JP2K::PictureDescriptor PDesc;
807 JP2K::SequenceParser Parser;
808 byte_t IV_buf[CBC_BLOCK_SIZE];
809 Kumu::FortunaRNG RNG;
811 // set up essence parser
812 Result_t result = Parser.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
815 if ( ASDCP_SUCCESS(result) )
817 Parser.FillPictureDescriptor(PDesc);
818 PDesc.EditRate = Options.PictureRate();
820 if ( Options.verbose_flag )
822 fprintf(stderr, "JPEG 2000 pictures\n");
823 fputs("PictureDescriptor:\n", stderr);
824 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
825 JP2K::PictureDescriptorDump(PDesc);
829 if ( ! check_phfr_params(Options, PDesc) )
832 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
834 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
835 if ( Options.asset_id_flag )
836 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
838 Kumu::GenRandomUUID(Info.AssetUUID);
840 if ( Options.use_smpte_labels )
842 Info.LabelSetType = LS_MXF_SMPTE;
843 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
846 // configure encryption
847 if( Options.key_flag )
849 Kumu::GenRandomUUID(Info.ContextID);
850 Info.EncryptedEssence = true;
852 if ( Options.key_id_flag )
854 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
858 create_random_uuid(Info.CryptographicKeyID);
861 Context = new AESEncContext;
862 result = Context->InitKey(Options.key_value);
864 if ( ASDCP_SUCCESS(result) )
865 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
867 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
869 Info.UsesHMAC = true;
870 HMAC = new HMACContext;
871 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
875 if ( ASDCP_SUCCESS(result) )
876 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
878 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
880 MXF::RGBAEssenceDescriptor *descriptor = 0;
881 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
882 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
883 descriptor->PictureEssenceCoding = Options.picture_coding;
887 if ( ASDCP_SUCCESS(result) )
890 result = Parser.Reset();
892 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
894 result = Parser.ReadFrame(FrameBuffer);
896 if ( ASDCP_SUCCESS(result) )
898 if ( Options.verbose_flag )
899 FrameBuffer.Dump(stderr, Options.fb_dump_size);
901 if ( Options.encrypt_header_flag )
902 FrameBuffer.PlaintextOffset(0);
905 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
907 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
909 // The Writer class will forward the last block of ciphertext
910 // to the encryption context for use as the IV for the next
911 // frame. If you want to use non-sequitur IV values, un-comment
912 // the following line of code.
913 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
914 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
918 if ( result == RESULT_ENDOFFILE )
922 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
923 result = Writer.Finalize();
928 //------------------------------------------------------------------------------------------
932 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
933 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
936 write_PCM_file(CommandOptions& Options)
938 AESEncContext* Context = 0;
939 HMACContext* HMAC = 0;
940 PCMParserList Parser;
941 PCM::MXFWriter Writer;
942 PCM::FrameBuffer FrameBuffer;
943 PCM::AudioDescriptor ADesc;
944 Rational PictureRate = Options.PictureRate();
945 byte_t IV_buf[CBC_BLOCK_SIZE];
946 Kumu::FortunaRNG RNG;
948 // set up essence parser
949 Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
952 if ( ASDCP_SUCCESS(result) )
954 Parser.FillAudioDescriptor(ADesc);
956 ADesc.EditRate = PictureRate;
957 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
958 ADesc.ChannelFormat = Options.channel_fmt;
960 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE && Options.mca_config.empty() )
962 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see options -C, -l and -m)\n");
965 if ( Options.verbose_flag )
967 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
968 ADesc.AudioSamplingRate.Quotient() / 1000.0,
969 Options.szPictureRate(),
970 PCM::CalcSamplesPerFrame(ADesc));
971 fputs("AudioDescriptor:\n", stderr);
972 PCM::AudioDescriptorDump(ADesc);
976 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
978 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
979 if ( Options.asset_id_flag )
980 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
982 Kumu::GenRandomUUID(Info.AssetUUID);
984 if ( Options.use_smpte_labels )
986 Info.LabelSetType = LS_MXF_SMPTE;
987 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
990 // configure encryption
991 if( Options.key_flag )
993 Kumu::GenRandomUUID(Info.ContextID);
994 Info.EncryptedEssence = true;
996 if ( Options.key_id_flag )
998 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1002 create_random_uuid(Info.CryptographicKeyID);
1005 Context = new AESEncContext;
1006 result = Context->InitKey(Options.key_value);
1008 if ( ASDCP_SUCCESS(result) )
1009 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1011 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1013 Info.UsesHMAC = true;
1014 HMAC = new HMACContext;
1015 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1019 if ( ASDCP_SUCCESS(result) )
1020 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1022 if ( ASDCP_SUCCESS(result)
1023 && ( Options.channel_assignment.HasValue()
1024 || ! Options.mca_config.empty() ) )
1026 MXF::WaveAudioDescriptor *essence_descriptor = 0;
1027 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_WaveAudioDescriptor),
1028 reinterpret_cast<MXF::InterchangeObject**>(&essence_descriptor));
1029 assert(essence_descriptor);
1031 if ( Options.mca_config.empty() )
1033 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1037 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1039 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1040 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1044 if ( Options.channel_assignment.HasValue() )
1046 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1048 else if ( Options.use_interop_sound_wtf )
1050 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_4_WTF);
1054 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
1057 // add descriptors to the essence_descriptor and header
1058 ASDCP::MXF::InterchangeObject_list_t::iterator i;
1059 for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
1061 if ( (*i)->GetUL() != UL(g_dict->ul(MDD_AudioChannelLabelSubDescriptor))
1062 && (*i)->GetUL() != UL(g_dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
1063 && (*i)->GetUL() != UL(g_dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
1065 fprintf(stderr, "Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
1069 Writer.OP1aHeader().AddChildObject(*i);
1070 essence_descriptor->SubDescriptors.push_back((*i)->InstanceUID);
1071 *i = 0; // parent will only free the ones we don't keep
1077 if ( ASDCP_SUCCESS(result) )
1079 result = Parser.Reset();
1080 ui32_t duration = 0;
1082 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1084 result = Parser.ReadFrame(FrameBuffer);
1086 if ( ASDCP_SUCCESS(result) )
1088 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1090 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1091 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1093 if ( Options.write_partial_pcm_flag )
1095 result = RESULT_ENDOFFILE;
1100 if ( Options.verbose_flag )
1101 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1103 if ( ! Options.no_write_flag )
1105 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1107 // The Writer class will forward the last block of ciphertext
1108 // to the encryption context for use as the IV for the next
1109 // frame. If you want to use non-sequitur IV values, un-comment
1110 // the following line of code.
1111 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1112 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1117 if ( result == RESULT_ENDOFFILE )
1121 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1122 result = Writer.Finalize();
1127 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a plaintext ASDCP file
1128 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a ciphertext ASDCP file
1131 write_PCM_with_ATMOS_sync_file(CommandOptions& Options)
1133 AESEncContext* Context = 0;
1134 HMACContext* HMAC = 0;
1135 PCM::MXFWriter Writer;
1136 PCM::FrameBuffer FrameBuffer;
1137 PCM::AudioDescriptor ADesc;
1138 Rational PictureRate = Options.PictureRate();
1139 byte_t IV_buf[CBC_BLOCK_SIZE];
1140 Kumu::FortunaRNG RNG;
1142 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1143 if ( Options.asset_id_flag )
1144 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1146 Kumu::GenRandomUUID(Info.AssetUUID);
1147 AtmosSyncChannelMixer Mixer(Info.AssetUUID);
1149 // set up essence parser
1150 Result_t result = Mixer.OpenRead(Options.filenames, PictureRate);
1152 // set up MXF writer
1153 if ( ASDCP_SUCCESS(result) )
1155 Mixer.FillAudioDescriptor(ADesc);
1157 ADesc.EditRate = PictureRate;
1158 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1159 ADesc.ChannelFormat = PCM::CF_CFG_4;
1161 if ( Options.verbose_flag )
1163 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1164 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1165 Options.szPictureRate(),
1166 PCM::CalcSamplesPerFrame(ADesc));
1167 fputs("AudioDescriptor:\n", stderr);
1168 PCM::AudioDescriptorDump(ADesc);
1172 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1174 Info.LabelSetType = LS_MXF_SMPTE;
1175 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1177 // configure encryption
1178 if( Options.key_flag )
1180 Kumu::GenRandomUUID(Info.ContextID);
1181 Info.EncryptedEssence = true;
1183 if ( Options.key_id_flag )
1185 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1189 create_random_uuid(Info.CryptographicKeyID);
1192 Context = new AESEncContext;
1193 result = Context->InitKey(Options.key_value);
1195 if ( ASDCP_SUCCESS(result) )
1196 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1198 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1200 Info.UsesHMAC = true;
1201 HMAC = new HMACContext;
1202 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1206 if ( ASDCP_SUCCESS(result) )
1207 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1210 if ( ASDCP_SUCCESS(result) )
1212 result = Mixer.Reset();
1213 ui32_t duration = 0;
1215 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1217 result = Mixer.ReadFrame(FrameBuffer);
1219 if ( ASDCP_SUCCESS(result) )
1221 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1223 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1224 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1226 if ( Options.write_partial_pcm_flag )
1228 result = RESULT_ENDOFFILE;
1233 if ( Options.verbose_flag )
1234 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1236 if ( ! Options.no_write_flag )
1238 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1240 // The Writer class will forward the last block of ciphertext
1241 // to the encryption context for use as the IV for the next
1242 // frame. If you want to use non-sequitur IV values, un-comment
1243 // the following line of code.
1244 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1245 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1250 if ( result == RESULT_ENDOFFILE )
1254 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1255 result = Writer.Finalize();
1261 //------------------------------------------------------------------------------------------
1262 // TimedText essence
1265 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1266 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1269 write_timed_text_file(CommandOptions& Options)
1271 AESEncContext* Context = 0;
1272 HMACContext* HMAC = 0;
1273 TimedText::DCSubtitleParser Parser;
1274 TimedText::MXFWriter Writer;
1275 TimedText::FrameBuffer FrameBuffer;
1276 TimedText::TimedTextDescriptor TDesc;
1277 byte_t IV_buf[CBC_BLOCK_SIZE];
1278 Kumu::FortunaRNG RNG;
1280 // set up essence parser
1281 Result_t result = Parser.OpenRead(Options.filenames.front());
1283 // set up MXF writer
1284 if ( ASDCP_SUCCESS(result) )
1286 Parser.FillTimedTextDescriptor(TDesc);
1287 FrameBuffer.Capacity(Options.fb_size);
1289 if ( Options.verbose_flag )
1291 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1292 TimedText::DescriptorDump(TDesc);
1296 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1298 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1299 if ( Options.asset_id_flag )
1300 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1302 Kumu::GenRandomUUID(Info.AssetUUID);
1304 // 428-7 IN 429-5 always uses SMPTE labels
1305 Info.LabelSetType = LS_MXF_SMPTE;
1306 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1308 // configure encryption
1309 if( Options.key_flag )
1311 Kumu::GenRandomUUID(Info.ContextID);
1312 Info.EncryptedEssence = true;
1314 if ( Options.key_id_flag )
1316 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1320 create_random_uuid(Info.CryptographicKeyID);
1323 Context = new AESEncContext;
1324 result = Context->InitKey(Options.key_value);
1326 if ( ASDCP_SUCCESS(result) )
1327 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1329 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1331 Info.UsesHMAC = true;
1332 HMAC = new HMACContext;
1333 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1337 if ( ASDCP_SUCCESS(result) )
1338 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1341 if ( ASDCP_FAILURE(result) )
1345 TimedText::ResourceList_t::const_iterator ri;
1347 result = Parser.ReadTimedTextResource(XMLDoc);
1349 if ( ASDCP_SUCCESS(result) )
1350 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1352 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1354 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1356 if ( ASDCP_SUCCESS(result) )
1358 if ( Options.verbose_flag )
1359 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1361 if ( ! Options.no_write_flag )
1363 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1365 // The Writer class will forward the last block of ciphertext
1366 // to the encryption context for use as the IV for the next
1367 // frame. If you want to use non-sequitur IV values, un-comment
1368 // the following line of code.
1369 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1370 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1374 if ( result == RESULT_ENDOFFILE )
1378 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1379 result = Writer.Finalize();
1384 // Write one or more plaintext Dolby ATMOS bytestreams to a plaintext ASDCP file
1385 // Write one or more plaintext Dolby ATMOS bytestreams to a ciphertext ASDCP file
1388 write_dolby_atmos_file(CommandOptions& Options)
1390 AESEncContext* Context = 0;
1391 HMACContext* HMAC = 0;
1392 ATMOS::MXFWriter Writer;
1393 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1394 ATMOS::AtmosDescriptor ADesc;
1395 DCData::SequenceParser Parser;
1396 byte_t IV_buf[CBC_BLOCK_SIZE];
1397 Kumu::FortunaRNG RNG;
1399 // set up essence parser
1400 Result_t result = Parser.OpenRead(Options.filenames.front());
1402 // set up MXF writer
1403 if ( ASDCP_SUCCESS(result) )
1405 Parser.FillDCDataDescriptor(ADesc);
1406 ADesc.EditRate = Options.PictureRate();
1407 // TODO: fill AtmosDescriptor
1408 ADesc.FirstFrame = Options.ffoa;
1409 ADesc.MaxChannelCount = Options.max_channel_count;
1410 ADesc.MaxObjectCount = Options.max_object_count;
1411 Kumu::GenRandomUUID(ADesc.AtmosID);
1412 ADesc.AtmosVersion = 1;
1413 if ( Options.verbose_flag )
1415 fprintf(stderr, "Dolby ATMOS Data\n");
1416 fputs("AtmosDescriptor:\n", stderr);
1417 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1418 ATMOS::AtmosDescriptorDump(ADesc);
1422 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1424 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1425 if ( Options.asset_id_flag )
1426 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1428 Kumu::GenRandomUUID(Info.AssetUUID);
1430 Info.LabelSetType = LS_MXF_SMPTE;
1432 // configure encryption
1433 if( Options.key_flag )
1435 Kumu::GenRandomUUID(Info.ContextID);
1436 Info.EncryptedEssence = true;
1438 if ( Options.key_id_flag )
1440 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1444 create_random_uuid(Info.CryptographicKeyID);
1447 Context = new AESEncContext;
1448 result = Context->InitKey(Options.key_value);
1450 if ( ASDCP_SUCCESS(result) )
1451 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1453 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1455 Info.UsesHMAC = true;
1456 HMAC = new HMACContext;
1457 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1461 if ( ASDCP_SUCCESS(result) )
1462 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1465 if ( ASDCP_SUCCESS(result) )
1467 ui32_t duration = 0;
1468 result = Parser.Reset();
1470 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1472 result = Parser.ReadFrame(FrameBuffer);
1474 if ( ASDCP_SUCCESS(result) )
1476 if ( Options.verbose_flag )
1477 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1479 if ( Options.encrypt_header_flag )
1480 FrameBuffer.PlaintextOffset(0);
1483 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1485 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1487 // The Writer class will forward the last block of ciphertext
1488 // to the encryption context for use as the IV for the next
1489 // frame. If you want to use non-sequitur IV values, un-comment
1490 // the following line of code.
1491 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1492 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1496 if ( result == RESULT_ENDOFFILE )
1500 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1501 result = Writer.Finalize();
1506 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a plaintext ASDCP file
1507 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a ciphertext ASDCP file
1510 write_aux_data_file(CommandOptions& Options)
1512 AESEncContext* Context = 0;
1513 HMACContext* HMAC = 0;
1514 DCData::MXFWriter Writer;
1515 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1516 DCData::DCDataDescriptor DDesc;
1517 DCData::SequenceParser Parser;
1518 byte_t IV_buf[CBC_BLOCK_SIZE];
1519 Kumu::FortunaRNG RNG;
1521 // set up essence parser
1522 Result_t result = Parser.OpenRead(Options.filenames.front());
1524 // set up MXF writer
1525 if ( ASDCP_SUCCESS(result) )
1527 Parser.FillDCDataDescriptor(DDesc);
1528 memcpy(DDesc.DataEssenceCoding, Options.aux_data_coding.Value(), Options.aux_data_coding.Size());
1529 DDesc.EditRate = Options.PictureRate();
1531 if ( Options.verbose_flag )
1533 fprintf(stderr, "Aux Data\n");
1534 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1535 DCData::DCDataDescriptorDump(DDesc);
1539 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1541 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1542 if ( Options.asset_id_flag )
1543 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1545 Kumu::GenRandomUUID(Info.AssetUUID);
1547 Info.LabelSetType = LS_MXF_SMPTE;
1549 // configure encryption
1550 if( Options.key_flag )
1552 Kumu::GenRandomUUID(Info.ContextID);
1553 Info.EncryptedEssence = true;
1555 if ( Options.key_id_flag )
1557 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1561 create_random_uuid(Info.CryptographicKeyID);
1564 Context = new AESEncContext;
1565 result = Context->InitKey(Options.key_value);
1567 if ( ASDCP_SUCCESS(result) )
1568 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1570 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1572 Info.UsesHMAC = true;
1573 HMAC = new HMACContext;
1574 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1578 if ( ASDCP_SUCCESS(result) )
1579 result = Writer.OpenWrite(Options.out_file, Info, DDesc);
1582 if ( ASDCP_SUCCESS(result) )
1584 ui32_t duration = 0;
1585 result = Parser.Reset();
1587 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1589 result = Parser.ReadFrame(FrameBuffer);
1591 if ( ASDCP_SUCCESS(result) )
1593 if ( Options.verbose_flag )
1594 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1596 if ( Options.encrypt_header_flag )
1597 FrameBuffer.PlaintextOffset(0);
1600 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1602 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1604 // The Writer class will forward the last block of ciphertext
1605 // to the encryption context for use as the IV for the next
1606 // frame. If you want to use non-sequitur IV values, un-comment
1607 // the following line of code.
1608 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1609 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1613 if ( result == RESULT_ENDOFFILE )
1617 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1618 result = Writer.Finalize();
1625 main(int argc, const char** argv)
1627 Result_t result = RESULT_OK;
1629 g_dict = &ASDCP::DefaultSMPTEDict();
1631 CommandOptions Options(argc, argv);
1633 if ( Options.version_flag )
1636 if ( Options.help_flag )
1639 if ( Options.show_ul_values_flag )
1641 if ( Options.use_smpte_labels )
1642 DefaultSMPTEDict().Dump(stdout);
1644 DefaultInteropDict().Dump(stdout);
1647 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1650 if ( Options.error_flag )
1652 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1656 EssenceType_t EssenceType;
1657 result = ASDCP::RawEssenceType(Options.filenames.front(), EssenceType);
1659 if ( ASDCP_SUCCESS(result) )
1661 switch ( EssenceType )
1664 result = write_MPEG2_file(Options);
1668 if ( Options.stereo_image_flag )
1670 result = write_JP2K_S_file(Options);
1674 result = write_JP2K_file(Options);
1678 case ESS_PCM_24b_48k:
1679 case ESS_PCM_24b_96k:
1680 if ( Options.dolby_atmos_sync_flag )
1682 result = write_PCM_with_ATMOS_sync_file(Options);
1686 result = write_PCM_file(Options);
1690 case ESS_TIMED_TEXT:
1691 result = write_timed_text_file(Options);
1694 case ESS_DCDATA_DOLBY_ATMOS:
1695 result = write_dolby_atmos_file(Options);
1698 case ESS_DCDATA_UNKNOWN:
1699 if ( ! Options.aux_data_coding.HasValue() )
1701 fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n");
1706 result = write_aux_data_file(Options);
1711 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1712 Options.filenames.front().c_str());
1717 if ( ASDCP_FAILURE(result) )
1719 fputs("Program stopped on error.\n", stderr);
1721 if ( result != RESULT_FAIL )
1723 fputs(result, stderr);
1724 fputc('\n', stderr);
1735 // end asdcp-wrap.cpp