2 Copyright (c) 2003-2018
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15 derived from this software without specific prior written permission.
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 /*! \file asdcp-wrap.cpp
30 \brief AS-DCP file manipulation utility
32 This program wraps d-cinema essence (picture, sound or text) into an AS-DCP
35 For more information about asdcplib, please refer to the header file AS_DCP.h
37 WARNING: While the asdcplib library attempts to provide a complete and secure
38 implementation of the cryptographic features of the AS-DCP file formats, this
39 unit test program is NOT secure and is therefore NOT SUITABLE FOR USE in a
40 production environment without some modification.
42 In particular, this program uses weak IV generation and externally generated
43 plaintext keys. These shortcomings exist because cryptographic-quality
44 random number generation and key management are outside the scope of the
45 asdcplib library. Developers using asdcplib for commercial implementations
46 claiming SMPTE conformance are expected to provide proper implementations of
50 #include <KM_fileio.h>
52 #include <AtmosSyncChannel_Mixer.h>
54 #include <PCMParserList.h>
57 using namespace ASDCP;
59 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
61 const byte_t P_HFR_UL_2K[16] = {
62 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
63 0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
66 const ASDCP::Dictionary *g_dict = 0;
68 //------------------------------------------------------------------------------------------
70 // command line option parser class
72 static const char* PROGRAM_NAME = "asdcp-wrap"; // program name for messages
74 // local program identification info written to file headers
75 class MyInfo : public WriterInfo
80 static byte_t default_ProductUUID_Data[UUIDlen] =
81 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
82 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
84 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
85 CompanyName = "WidgetCo";
86 ProductName = "asdcp-wrap";
87 ProductVersion = ASDCP::Version();
93 // Increment the iterator, test for an additional non-option command line argument.
94 // Causes the caller to return if there are no remaining arguments or if the next
95 // argument begins with '-'.
96 #define TEST_EXTRA_ARG(i,c) \
97 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
98 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
104 create_random_uuid(byte_t* uuidbuf)
107 GenRandomValue(tmp_id);
108 memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
113 banner(FILE* stream = stdout)
116 %s (asdcplib %s)\n\n\
117 Copyright (c) 2003-2018 John Hurst\n\n\
118 asdcplib may be copied only under the terms of the license found at\n\
119 the top of every file in the asdcplib distribution kit.\n\n\
120 Specify the -h (help) option for further information about %s\n\n",
121 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
126 usage(FILE* stream = stdout)
129 USAGE: %s [-h|-help] [-V]\n\
131 %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
132 [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
133 [-l <label>] [-L] [-M] [-m <expr>] [-p <frame-rate>] [-s] [-v]\n\
134 [-W] [-z|-Z] <input-file>+ <output-file>\n\n",
135 PROGRAM_NAME, PROGRAM_NAME);
139 -h | -help - Show help\n\
140 -V - Show version information\n\
141 -3 - Create a stereoscopic image file. Expects two\n\
142 directories of JP2K codestreams (directories must have\n\
143 an equal number of frames; the left eye is first)\n\
144 -a <UUID> - Specify the Asset ID of the file\n\
145 -A <UL> - Set DataEssenceCoding UL value in an Aux Data file\n\
146 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
147 Defaults to 4,194,304 (4MB)\n\
148 -C <UL> - Set ChannelAssignment UL value in a PCM file\n\
149 -d <duration> - Number of frames to process, default all\n\
150 -e - Encrypt MPEG or JP2K headers (default)\n\
151 -E - Do not encrypt MPEG or JP2K headers\n\
152 -f <start-frame> - Starting frame number, default 0\n\
153 -g <rfc-5646-code>\n\
154 - Create MCA labels having the given RFC 5646 language code\n\
155 (requires option \"-m\")\n\
156 -j <key-id-str> - Write key ID instead of creating a random value\n\
157 -k <key-string> - Use key for ciphertext operations\n\
158 -l <label> - Use given channel format label when writing MXF sound\n\
159 files. SMPTE 429-2 labels: '5.1', '6.1', '7.1',\n\
161 Default is no label (valid for Interop only).\n\
162 -L - Write SMPTE UL values instead of MXF Interop\n\
163 -m <expr> - Write MCA labels using <expr>. Example:\n\
164 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
165 Note: The symbol '-' may be used for an unlabeled\n\
166 channel, but not within a soundfield.\n\
167 -M - Do not create HMAC values when writing\n\
168 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
169 Use one of [23|24|25|30|48|50|60], 24 is default\n\
170 -P <UL> - Set PictureEssenceCoding UL value in a JP2K file\n\
171 -s - Insert a Dolby Atmos synchronization channel when\n\
172 wrapping PCM. This implies a -L option(SMPTE ULs) and \n\
173 will overide -C and -l options with Configuration 4 \n\
174 Channel Assigment and no format label respectively. \n\
175 -v - Verbose, prints informative messages to stderr\n\
176 -w - When writing 377-4 MCA labels, use the WTF Channel\n\
177 assignment label instead of the standard MCA label\n\
178 -W - Read input file only, do not write output file\n\
179 -z - Fail if j2c inputs have unequal parameters (default)\n\
180 -Z - Ignore unequal parameters in j2c inputs\n\
182 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
183 o All option arguments must be separated from the option by whitespace.\n\
184 o An argument of \"23\" to the -p option will be interpreted\n\
185 as 24000/1001 fps.\n\
191 decode_channel_fmt(const std::string& label_name)
193 if ( label_name == "5.1" )
194 return PCM::CF_CFG_1;
196 else if ( label_name == "6.1" )
197 return PCM::CF_CFG_2;
199 else if ( label_name == "7.1" )
200 return PCM::CF_CFG_3;
202 else if ( label_name == "WTF" )
203 return PCM::CF_CFG_4;
205 else if ( label_name == "7.1DS" )
206 return PCM::CF_CFG_5;
208 fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
209 fprintf(stderr, "Expecting '5.1', '6.1', '7.1', '7.1DS' or 'WTF'\n");
220 bool error_flag; // true if the given options are in error or not complete
221 bool key_flag; // true if an encryption key was given
222 bool asset_id_flag; // true if an asset ID was given
223 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
224 bool write_hmac; // true if HMAC values are to be generated and written
225 bool verbose_flag; // true if the verbose option was selected
226 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
227 bool no_write_flag; // true if no output files are to be written
228 bool version_flag; // true if the version display option was selected
229 bool help_flag; // true if the help display option was selected
230 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
231 bool write_partial_pcm_flag; // if true, write the last frame of PCM input even when it is incomplete
232 ui32_t start_frame; // frame number to begin processing
233 ui32_t duration; // number of frames to be processed
234 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
235 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
236 ui32_t picture_rate; // fps of picture when wrapping PCM
237 ui32_t fb_size; // size of picture frame buffer
238 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
239 bool key_id_flag; // true if a key ID was given
240 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
241 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
242 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
243 std::string out_file; //
244 bool show_ul_values_flag; /// if true, dump the UL table before going to work.
245 Kumu::PathList_t filenames; // list of filenames to be processed
246 UL channel_assignment;
249 bool dolby_atmos_sync_flag; // if true, insert a Dolby Atmos Synchronization channel.
250 ui32_t ffoa; // first frame of action for atmos wrapping
251 ui32_t max_channel_count; // max channel count for atmos wrapping
252 ui32_t max_object_count; // max object count for atmos wrapping
253 bool use_interop_sound_wtf; // make true to force WTF assignment label instead of MCA
254 ASDCP::MXF::ASDCP_MCAConfigParser mca_config;
255 std::string mca_language;
258 Rational PictureRate()
260 if ( picture_rate == 16 ) return EditRate_16;
261 if ( picture_rate == 18 ) return EditRate_18;
262 if ( picture_rate == 20 ) return EditRate_20;
263 if ( picture_rate == 22 ) return EditRate_22;
264 if ( picture_rate == 23 ) return EditRate_23_98;
265 if ( picture_rate == 24 ) return EditRate_24;
266 if ( picture_rate == 25 ) return EditRate_25;
267 if ( picture_rate == 30 ) return EditRate_30;
268 if ( picture_rate == 48 ) return EditRate_48;
269 if ( picture_rate == 50 ) return EditRate_50;
270 if ( picture_rate == 60 ) return EditRate_60;
271 if ( picture_rate == 96 ) return EditRate_96;
272 if ( picture_rate == 100 ) return EditRate_100;
273 if ( picture_rate == 120 ) return EditRate_120;
274 if ( picture_rate == 192 ) return EditRate_192;
275 if ( picture_rate == 200 ) return EditRate_200;
276 if ( picture_rate == 240 ) return EditRate_240;
281 const char* szPictureRate()
283 if ( picture_rate == 16 ) return "16";
284 if ( picture_rate == 18 ) return "18.182";
285 if ( picture_rate == 20 ) return "20";
286 if ( picture_rate == 22 ) return "21.818";
287 if ( picture_rate == 23 ) return "23.976";
288 if ( picture_rate == 24 ) return "24";
289 if ( picture_rate == 25 ) return "25";
290 if ( picture_rate == 30 ) return "30";
291 if ( picture_rate == 48 ) return "48";
292 if ( picture_rate == 50 ) return "50";
293 if ( picture_rate == 60 ) return "60";
294 if ( picture_rate == 96 ) return "96";
295 if ( picture_rate == 100 ) return "100";
296 if ( picture_rate == 120 ) return "120";
297 if ( picture_rate == 192 ) return "192";
298 if ( picture_rate == 200 ) return "200";
299 if ( picture_rate == 240 ) return "240";
304 CommandOptions(int argc, const char** argv) :
305 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
306 encrypt_header_flag(true), write_hmac(true),
307 verbose_flag(false), fb_dump_size(0),
308 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
309 write_partial_pcm_flag(false), start_frame(0),
310 duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
311 fb_size(FRAME_BUFFER_SIZE),
312 channel_fmt(PCM::CF_NONE),
313 ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties
314 dolby_atmos_sync_flag(false),
315 show_ul_values_flag(false),
317 use_interop_sound_wtf(false)
319 memset(key_value, 0, KeyLen);
320 memset(key_id_value, 0, UUIDlen);
321 std::string mca_config_str;
323 for ( int i = 1; i < argc; i++ )
326 if ( (strcmp( argv[i], "-help") == 0) )
332 if ( argv[i][0] == '-'
333 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
336 switch ( argv[i][1] )
338 case '3': stereo_image_flag = true; break;
341 TEST_EXTRA_ARG(i, 'A');
342 if ( ! aux_data_coding.DecodeHex(argv[i]) )
344 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
350 asset_id_flag = true;
351 TEST_EXTRA_ARG(i, 'a');
354 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
356 if ( length != UUIDlen )
358 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
365 TEST_EXTRA_ARG(i, 'b');
366 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
369 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
374 TEST_EXTRA_ARG(i, 'C');
375 if ( ! channel_assignment.DecodeHex(argv[i]) )
377 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
383 TEST_EXTRA_ARG(i, 'd');
384 duration = Kumu::xabs(strtol(argv[i], 0, 10));
387 case 'E': encrypt_header_flag = false; break;
388 case 'e': encrypt_header_flag = true; break;
391 TEST_EXTRA_ARG(i, 'f');
392 start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
396 TEST_EXTRA_ARG(i, 'g');
397 mca_language = argv[i];
400 case 'h': help_flag = true; break;
402 case 'j': key_id_flag = true;
403 TEST_EXTRA_ARG(i, 'j');
406 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
408 if ( length != UUIDlen )
410 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
416 case 'k': key_flag = true;
417 TEST_EXTRA_ARG(i, 'k');
420 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
422 if ( length != KeyLen )
424 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
431 TEST_EXTRA_ARG(i, 'l');
432 channel_fmt = decode_channel_fmt(argv[i]);
435 case 'L': use_smpte_labels = true; break;
436 case 'M': write_hmac = false; break;
439 TEST_EXTRA_ARG(i, 'm');
440 mca_config_str = argv[i];
444 TEST_EXTRA_ARG(i, 'P');
445 if ( ! picture_coding.DecodeHex(argv[i]) )
447 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
453 TEST_EXTRA_ARG(i, 'p');
454 picture_rate = Kumu::xabs(strtol(argv[i], 0, 10));
457 case 's': dolby_atmos_sync_flag = true; break;
458 case 'u': show_ul_values_flag = true; break;
459 case 'V': version_flag = true; break;
460 case 'v': verbose_flag = true; break;
461 case 'w': use_interop_sound_wtf = true; break;
462 case 'W': no_write_flag = true; break;
463 case 'x': write_partial_pcm_flag = true; break;
464 case 'Z': j2c_pedantic = false; break;
465 case 'z': j2c_pedantic = true; break;
468 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
475 if ( argv[i][0] != '-' )
477 filenames.push_back(argv[i]);
481 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
487 if ( ! mca_config_str.empty() )
489 if ( mca_language.empty() )
491 if ( ! mca_config.DecodeString(mca_config_str) )
498 if ( ! mca_config.DecodeString(mca_config_str, mca_language) )
505 if ( help_flag || version_flag )
510 if ( filenames.size() < 2 )
512 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
516 out_file = filenames.back();
517 filenames.pop_back();
522 //------------------------------------------------------------------------------------------
525 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
526 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
529 write_MPEG2_file(CommandOptions& Options)
531 AESEncContext* Context = 0;
532 HMACContext* HMAC = 0;
533 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
534 MPEG2::Parser Parser;
535 MPEG2::MXFWriter Writer;
536 MPEG2::VideoDescriptor VDesc;
537 byte_t IV_buf[CBC_BLOCK_SIZE];
538 Kumu::FortunaRNG RNG;
540 // set up essence parser
541 Result_t result = Parser.OpenRead(Options.filenames.front());
544 if ( ASDCP_SUCCESS(result) )
546 Parser.FillVideoDescriptor(VDesc);
548 if ( Options.verbose_flag )
550 fputs("MPEG-2 Pictures\n", stderr);
551 fputs("VideoDescriptor:\n", stderr);
552 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
553 MPEG2::VideoDescriptorDump(VDesc);
557 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
559 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
560 if ( Options.asset_id_flag )
561 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
563 Kumu::GenRandomUUID(Info.AssetUUID);
565 if ( Options.use_smpte_labels )
567 Info.LabelSetType = LS_MXF_SMPTE;
568 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
571 // configure encryption
572 if( Options.key_flag )
574 Kumu::GenRandomUUID(Info.ContextID);
575 Info.EncryptedEssence = true;
577 if ( Options.key_id_flag )
579 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
583 create_random_uuid(Info.CryptographicKeyID);
586 Context = new AESEncContext;
587 result = Context->InitKey(Options.key_value);
589 if ( ASDCP_SUCCESS(result) )
590 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
592 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
594 Info.UsesHMAC = true;
595 HMAC = new HMACContext;
596 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
600 if ( ASDCP_SUCCESS(result) )
601 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
604 if ( ASDCP_SUCCESS(result) )
605 // loop through the frames
607 result = Parser.Reset();
610 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
612 result = Parser.ReadFrame(FrameBuffer);
614 if ( ASDCP_SUCCESS(result) )
616 if ( Options.verbose_flag )
617 FrameBuffer.Dump(stderr, Options.fb_dump_size);
619 if ( Options.encrypt_header_flag )
620 FrameBuffer.PlaintextOffset(0);
623 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
625 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
627 // The Writer class will forward the last block of ciphertext
628 // to the encryption context for use as the IV for the next
629 // frame. If you want to use non-sequitur IV values, un-comment
630 // the following line of code.
631 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
632 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
636 if ( result == RESULT_ENDOFFILE )
640 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
641 result = Writer.Finalize();
647 //------------------------------------------------------------------------------------------
649 // return false if an error is discovered
651 check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc)
653 Rational rate = Options.PictureRate();
654 if ( rate != EditRate_96 && rate != EditRate_100 && rate != EditRate_120
655 && rate != EditRate_192 && rate != EditRate_200 && rate != EditRate_240 )
658 if ( PDesc.StoredWidth > 2048 )
660 fprintf(stderr, "P-HFR files currently limited to 2K.\n");
664 if ( ! Options.use_smpte_labels )
666 fprintf(stderr, "P-HFR files must be written using SMPTE labels. Use option '-L'.\n");
670 // do not set the label if the user has already done so
671 if ( ! Options.picture_coding.HasValue() )
672 Options.picture_coding = UL(P_HFR_UL_2K);
677 //------------------------------------------------------------------------------------------
680 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
681 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
684 write_JP2K_S_file(CommandOptions& Options)
686 AESEncContext* Context = 0;
687 HMACContext* HMAC = 0;
688 JP2K::MXFSWriter Writer;
689 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
690 JP2K::PictureDescriptor PDesc;
691 JP2K::SequenceParser ParserLeft, ParserRight;
692 byte_t IV_buf[CBC_BLOCK_SIZE];
693 Kumu::FortunaRNG RNG;
695 if ( Options.filenames.size() != 2 )
697 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
701 // set up essence parser
702 Result_t result = ParserLeft.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
704 if ( ASDCP_SUCCESS(result) )
706 Options.filenames.pop_front();
707 result = ParserRight.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
711 if ( ASDCP_SUCCESS(result) )
713 ParserLeft.FillPictureDescriptor(PDesc);
714 PDesc.EditRate = Options.PictureRate();
716 if ( Options.verbose_flag )
718 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
719 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
720 JP2K::PictureDescriptorDump(PDesc);
724 if ( ! check_phfr_params(Options, PDesc) )
727 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
729 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
730 if ( Options.asset_id_flag )
731 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
733 Kumu::GenRandomUUID(Info.AssetUUID);
735 if ( Options.use_smpte_labels )
737 Info.LabelSetType = LS_MXF_SMPTE;
738 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
741 // configure encryption
742 if( Options.key_flag )
744 Kumu::GenRandomUUID(Info.ContextID);
745 Info.EncryptedEssence = true;
747 if ( Options.key_id_flag )
749 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
753 create_random_uuid(Info.CryptographicKeyID);
756 Context = new AESEncContext;
757 result = Context->InitKey(Options.key_value);
759 if ( ASDCP_SUCCESS(result) )
760 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
762 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
764 Info.UsesHMAC = true;
765 HMAC = new HMACContext;
766 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
770 if ( ASDCP_SUCCESS(result) )
771 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
773 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
775 MXF::RGBAEssenceDescriptor *descriptor = 0;
776 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
777 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
778 descriptor->PictureEssenceCoding = Options.picture_coding;
782 if ( ASDCP_SUCCESS(result) )
785 result = ParserLeft.Reset();
786 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
788 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
790 result = ParserLeft.ReadFrame(FrameBuffer);
792 if ( ASDCP_SUCCESS(result) )
794 if ( Options.verbose_flag )
795 FrameBuffer.Dump(stderr, Options.fb_dump_size);
797 if ( Options.encrypt_header_flag )
798 FrameBuffer.PlaintextOffset(0);
801 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
802 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
804 if ( ASDCP_SUCCESS(result) )
805 result = ParserRight.ReadFrame(FrameBuffer);
807 if ( ASDCP_SUCCESS(result) )
809 if ( Options.verbose_flag )
810 FrameBuffer.Dump(stderr, Options.fb_dump_size);
812 if ( Options.encrypt_header_flag )
813 FrameBuffer.PlaintextOffset(0);
816 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
817 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
820 if ( result == RESULT_ENDOFFILE )
824 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
825 result = Writer.Finalize();
830 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
831 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
834 write_JP2K_file(CommandOptions& Options)
836 AESEncContext* Context = 0;
837 HMACContext* HMAC = 0;
838 JP2K::MXFWriter Writer;
839 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
840 JP2K::PictureDescriptor PDesc;
841 JP2K::SequenceParser Parser;
842 byte_t IV_buf[CBC_BLOCK_SIZE];
843 Kumu::FortunaRNG RNG;
845 // set up essence parser
846 Result_t result = Parser.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
849 if ( ASDCP_SUCCESS(result) )
851 Parser.FillPictureDescriptor(PDesc);
852 PDesc.EditRate = Options.PictureRate();
854 if ( Options.verbose_flag )
856 fprintf(stderr, "JPEG 2000 pictures\n");
857 fputs("PictureDescriptor:\n", stderr);
858 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
859 JP2K::PictureDescriptorDump(PDesc);
863 if ( ! check_phfr_params(Options, PDesc) )
866 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
868 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
869 if ( Options.asset_id_flag )
870 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
872 Kumu::GenRandomUUID(Info.AssetUUID);
874 if ( Options.use_smpte_labels )
876 Info.LabelSetType = LS_MXF_SMPTE;
877 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
880 // configure encryption
881 if( Options.key_flag )
883 Kumu::GenRandomUUID(Info.ContextID);
884 Info.EncryptedEssence = true;
886 if ( Options.key_id_flag )
888 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
892 create_random_uuid(Info.CryptographicKeyID);
895 Context = new AESEncContext;
896 result = Context->InitKey(Options.key_value);
898 if ( ASDCP_SUCCESS(result) )
899 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
901 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
903 Info.UsesHMAC = true;
904 HMAC = new HMACContext;
905 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
909 if ( ASDCP_SUCCESS(result) )
910 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
912 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
914 MXF::RGBAEssenceDescriptor *descriptor = 0;
915 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
916 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
917 descriptor->PictureEssenceCoding = Options.picture_coding;
921 if ( ASDCP_SUCCESS(result) )
924 result = Parser.Reset();
926 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
928 result = Parser.ReadFrame(FrameBuffer);
930 if ( ASDCP_SUCCESS(result) )
932 if ( Options.verbose_flag )
933 FrameBuffer.Dump(stderr, Options.fb_dump_size);
935 if ( Options.encrypt_header_flag )
936 FrameBuffer.PlaintextOffset(0);
939 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
941 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
943 // The Writer class will forward the last block of ciphertext
944 // to the encryption context for use as the IV for the next
945 // frame. If you want to use non-sequitur IV values, un-comment
946 // the following line of code.
947 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
948 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
952 if ( result == RESULT_ENDOFFILE )
956 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
957 result = Writer.Finalize();
962 //------------------------------------------------------------------------------------------
966 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
967 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
970 write_PCM_file(CommandOptions& Options)
972 AESEncContext* Context = 0;
973 HMACContext* HMAC = 0;
974 PCMParserList Parser;
975 PCM::MXFWriter Writer;
976 PCM::FrameBuffer FrameBuffer;
977 PCM::AudioDescriptor ADesc;
978 Rational PictureRate = Options.PictureRate();
979 byte_t IV_buf[CBC_BLOCK_SIZE];
980 Kumu::FortunaRNG RNG;
982 // set up essence parser
983 Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
986 if ( ASDCP_SUCCESS(result) )
988 Parser.FillAudioDescriptor(ADesc);
990 ADesc.EditRate = PictureRate;
991 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
992 ADesc.ChannelFormat = Options.channel_fmt;
994 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE && Options.mca_config.empty() )
996 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see options -C, -l and -m)\n");
999 if ( Options.verbose_flag )
1001 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1002 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1003 Options.szPictureRate(),
1004 PCM::CalcSamplesPerFrame(ADesc));
1005 fputs("AudioDescriptor:\n", stderr);
1006 PCM::AudioDescriptorDump(ADesc);
1010 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1012 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1013 if ( Options.asset_id_flag )
1014 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1016 Kumu::GenRandomUUID(Info.AssetUUID);
1018 if ( Options.use_smpte_labels )
1020 Info.LabelSetType = LS_MXF_SMPTE;
1021 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1024 // configure encryption
1025 if( Options.key_flag )
1027 Kumu::GenRandomUUID(Info.ContextID);
1028 Info.EncryptedEssence = true;
1030 if ( Options.key_id_flag )
1032 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1036 create_random_uuid(Info.CryptographicKeyID);
1039 Context = new AESEncContext;
1040 result = Context->InitKey(Options.key_value);
1042 if ( ASDCP_SUCCESS(result) )
1043 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1045 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1047 Info.UsesHMAC = true;
1048 HMAC = new HMACContext;
1049 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1053 if ( ASDCP_SUCCESS(result) )
1054 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1056 if ( ASDCP_SUCCESS(result)
1057 && ( Options.channel_assignment.HasValue()
1058 || ! Options.mca_config.empty() ) )
1060 MXF::WaveAudioDescriptor *essence_descriptor = 0;
1061 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_WaveAudioDescriptor),
1062 reinterpret_cast<MXF::InterchangeObject**>(&essence_descriptor));
1063 assert(essence_descriptor);
1065 if ( Options.mca_config.empty() )
1067 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1071 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1073 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1074 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1078 if ( Options.channel_assignment.HasValue() )
1080 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1082 else if ( Options.use_interop_sound_wtf )
1084 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_4_WTF);
1088 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
1091 // add descriptors to the essence_descriptor and header
1092 ASDCP::MXF::InterchangeObject_list_t::iterator i;
1093 for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
1095 if ( (*i)->GetUL() != UL(g_dict->ul(MDD_AudioChannelLabelSubDescriptor))
1096 && (*i)->GetUL() != UL(g_dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
1097 && (*i)->GetUL() != UL(g_dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
1099 fprintf(stderr, "Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
1103 Writer.OP1aHeader().AddChildObject(*i);
1104 essence_descriptor->SubDescriptors.push_back((*i)->InstanceUID);
1105 *i = 0; // parent will only free the ones we don't keep
1111 if ( ASDCP_SUCCESS(result) )
1113 result = Parser.Reset();
1114 ui32_t duration = 0;
1116 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1118 result = Parser.ReadFrame(FrameBuffer);
1120 if ( ASDCP_SUCCESS(result) )
1122 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1124 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1125 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1127 if ( Options.write_partial_pcm_flag )
1129 result = RESULT_ENDOFFILE;
1134 if ( Options.verbose_flag )
1135 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1137 if ( ! Options.no_write_flag )
1139 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1141 // The Writer class will forward the last block of ciphertext
1142 // to the encryption context for use as the IV for the next
1143 // frame. If you want to use non-sequitur IV values, un-comment
1144 // the following line of code.
1145 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1146 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1151 if ( result == RESULT_ENDOFFILE )
1155 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1156 result = Writer.Finalize();
1161 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a plaintext ASDCP file
1162 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a ciphertext ASDCP file
1165 write_PCM_with_ATMOS_sync_file(CommandOptions& Options)
1167 AESEncContext* Context = 0;
1168 HMACContext* HMAC = 0;
1169 PCM::MXFWriter Writer;
1170 PCM::FrameBuffer FrameBuffer;
1171 PCM::AudioDescriptor ADesc;
1172 Rational PictureRate = Options.PictureRate();
1173 byte_t IV_buf[CBC_BLOCK_SIZE];
1174 Kumu::FortunaRNG RNG;
1176 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1177 if ( Options.asset_id_flag )
1178 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1180 Kumu::GenRandomUUID(Info.AssetUUID);
1181 AtmosSyncChannelMixer Mixer(Info.AssetUUID);
1183 // set up essence parser
1184 Result_t result = Mixer.OpenRead(Options.filenames, PictureRate);
1186 // set up MXF writer
1187 if ( ASDCP_SUCCESS(result) )
1189 if ( Mixer.ChannelCount() % 2 != 0 )
1191 result = Mixer.AppendSilenceChannels(1);
1194 Mixer.FillAudioDescriptor(ADesc);
1196 ADesc.EditRate = PictureRate;
1197 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1198 ADesc.ChannelFormat = PCM::CF_CFG_4;
1200 if ( Options.verbose_flag )
1202 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1203 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1204 Options.szPictureRate(),
1205 PCM::CalcSamplesPerFrame(ADesc));
1206 fputs("AudioDescriptor:\n", stderr);
1207 PCM::AudioDescriptorDump(ADesc);
1211 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1213 Info.LabelSetType = LS_MXF_SMPTE;
1214 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1216 // configure encryption
1217 if( Options.key_flag )
1219 Kumu::GenRandomUUID(Info.ContextID);
1220 Info.EncryptedEssence = true;
1222 if ( Options.key_id_flag )
1224 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1228 create_random_uuid(Info.CryptographicKeyID);
1231 Context = new AESEncContext;
1232 result = Context->InitKey(Options.key_value);
1234 if ( ASDCP_SUCCESS(result) )
1235 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1237 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1239 Info.UsesHMAC = true;
1240 HMAC = new HMACContext;
1241 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1245 if ( ASDCP_SUCCESS(result) )
1246 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1249 if ( ASDCP_SUCCESS(result) )
1251 result = Mixer.Reset();
1252 ui32_t duration = 0;
1254 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1256 result = Mixer.ReadFrame(FrameBuffer);
1258 if ( ASDCP_SUCCESS(result) )
1260 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1262 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1263 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1265 if ( Options.write_partial_pcm_flag )
1267 result = RESULT_ENDOFFILE;
1272 if ( Options.verbose_flag )
1273 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1275 if ( ! Options.no_write_flag )
1277 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1279 // The Writer class will forward the last block of ciphertext
1280 // to the encryption context for use as the IV for the next
1281 // frame. If you want to use non-sequitur IV values, un-comment
1282 // the following line of code.
1283 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1284 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1289 if ( result == RESULT_ENDOFFILE )
1293 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1294 result = Writer.Finalize();
1300 //------------------------------------------------------------------------------------------
1301 // TimedText essence
1304 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1305 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1308 write_timed_text_file(CommandOptions& Options)
1310 AESEncContext* Context = 0;
1311 HMACContext* HMAC = 0;
1312 TimedText::DCSubtitleParser Parser;
1313 TimedText::MXFWriter Writer;
1314 TimedText::FrameBuffer FrameBuffer;
1315 TimedText::TimedTextDescriptor TDesc;
1316 byte_t IV_buf[CBC_BLOCK_SIZE];
1317 Kumu::FortunaRNG RNG;
1319 // set up essence parser
1320 Result_t result = Parser.OpenRead(Options.filenames.front());
1322 // set up MXF writer
1323 if ( ASDCP_SUCCESS(result) )
1325 Parser.FillTimedTextDescriptor(TDesc);
1326 FrameBuffer.Capacity(Options.fb_size);
1328 if ( Options.verbose_flag )
1330 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1331 TimedText::DescriptorDump(TDesc);
1335 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1337 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1338 if ( Options.asset_id_flag )
1339 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1341 Kumu::GenRandomUUID(Info.AssetUUID);
1343 // 428-7 IN 429-5 always uses SMPTE labels
1344 Info.LabelSetType = LS_MXF_SMPTE;
1345 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1347 // configure encryption
1348 if( Options.key_flag )
1350 Kumu::GenRandomUUID(Info.ContextID);
1351 Info.EncryptedEssence = true;
1353 if ( Options.key_id_flag )
1355 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1359 create_random_uuid(Info.CryptographicKeyID);
1362 Context = new AESEncContext;
1363 result = Context->InitKey(Options.key_value);
1365 if ( ASDCP_SUCCESS(result) )
1366 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1368 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1370 Info.UsesHMAC = true;
1371 HMAC = new HMACContext;
1372 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1376 if ( ASDCP_SUCCESS(result) )
1377 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1380 if ( ASDCP_FAILURE(result) )
1384 TimedText::ResourceList_t::const_iterator ri;
1386 result = Parser.ReadTimedTextResource(XMLDoc);
1388 if ( ASDCP_SUCCESS(result) )
1389 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1391 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1393 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1395 if ( ASDCP_SUCCESS(result) )
1397 if ( Options.verbose_flag )
1398 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1400 if ( ! Options.no_write_flag )
1402 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1404 // The Writer class will forward the last block of ciphertext
1405 // to the encryption context for use as the IV for the next
1406 // frame. If you want to use non-sequitur IV values, un-comment
1407 // the following line of code.
1408 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1409 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1413 if ( result == RESULT_ENDOFFILE )
1417 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1418 result = Writer.Finalize();
1423 // Write one or more plaintext Dolby ATMOS bytestreams to a plaintext ASDCP file
1424 // Write one or more plaintext Dolby ATMOS bytestreams to a ciphertext ASDCP file
1427 write_dolby_atmos_file(CommandOptions& Options)
1429 AESEncContext* Context = 0;
1430 HMACContext* HMAC = 0;
1431 ATMOS::MXFWriter Writer;
1432 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1433 ATMOS::AtmosDescriptor ADesc;
1434 DCData::SequenceParser Parser;
1435 byte_t IV_buf[CBC_BLOCK_SIZE];
1436 Kumu::FortunaRNG RNG;
1438 // set up essence parser
1439 Result_t result = Parser.OpenRead(Options.filenames.front());
1441 // set up MXF writer
1442 if ( ASDCP_SUCCESS(result) )
1444 Parser.FillDCDataDescriptor(ADesc);
1445 ADesc.EditRate = Options.PictureRate();
1446 // TODO: fill AtmosDescriptor
1447 ADesc.FirstFrame = Options.ffoa;
1448 ADesc.MaxChannelCount = Options.max_channel_count;
1449 ADesc.MaxObjectCount = Options.max_object_count;
1450 Kumu::GenRandomUUID(ADesc.AtmosID);
1451 ADesc.AtmosVersion = 1;
1452 if ( Options.verbose_flag )
1454 fprintf(stderr, "Dolby ATMOS Data\n");
1455 fputs("AtmosDescriptor:\n", stderr);
1456 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1457 ATMOS::AtmosDescriptorDump(ADesc);
1461 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1463 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1464 if ( Options.asset_id_flag )
1465 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1467 Kumu::GenRandomUUID(Info.AssetUUID);
1469 Info.LabelSetType = LS_MXF_SMPTE;
1471 // configure encryption
1472 if( Options.key_flag )
1474 Kumu::GenRandomUUID(Info.ContextID);
1475 Info.EncryptedEssence = true;
1477 if ( Options.key_id_flag )
1479 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1483 create_random_uuid(Info.CryptographicKeyID);
1486 Context = new AESEncContext;
1487 result = Context->InitKey(Options.key_value);
1489 if ( ASDCP_SUCCESS(result) )
1490 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1492 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1494 Info.UsesHMAC = true;
1495 HMAC = new HMACContext;
1496 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1500 if ( ASDCP_SUCCESS(result) )
1501 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1504 if ( ASDCP_SUCCESS(result) )
1506 ui32_t duration = 0;
1507 result = Parser.Reset();
1509 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1511 result = Parser.ReadFrame(FrameBuffer);
1513 if ( ASDCP_SUCCESS(result) )
1515 if ( Options.verbose_flag )
1516 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1518 if ( Options.encrypt_header_flag )
1519 FrameBuffer.PlaintextOffset(0);
1522 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1524 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1526 // The Writer class will forward the last block of ciphertext
1527 // to the encryption context for use as the IV for the next
1528 // frame. If you want to use non-sequitur IV values, un-comment
1529 // the following line of code.
1530 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1531 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1535 if ( result == RESULT_ENDOFFILE )
1539 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1540 result = Writer.Finalize();
1545 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a plaintext ASDCP file
1546 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a ciphertext ASDCP file
1549 write_aux_data_file(CommandOptions& Options)
1551 AESEncContext* Context = 0;
1552 HMACContext* HMAC = 0;
1553 DCData::MXFWriter Writer;
1554 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1555 DCData::DCDataDescriptor DDesc;
1556 DCData::SequenceParser Parser;
1557 byte_t IV_buf[CBC_BLOCK_SIZE];
1558 Kumu::FortunaRNG RNG;
1560 // set up essence parser
1561 Result_t result = Parser.OpenRead(Options.filenames.front());
1563 // set up MXF writer
1564 if ( ASDCP_SUCCESS(result) )
1566 Parser.FillDCDataDescriptor(DDesc);
1567 memcpy(DDesc.DataEssenceCoding, Options.aux_data_coding.Value(), Options.aux_data_coding.Size());
1568 DDesc.EditRate = Options.PictureRate();
1570 if ( Options.verbose_flag )
1572 fprintf(stderr, "Aux Data\n");
1573 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1574 DCData::DCDataDescriptorDump(DDesc);
1578 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1580 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1581 if ( Options.asset_id_flag )
1582 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1584 Kumu::GenRandomUUID(Info.AssetUUID);
1586 Info.LabelSetType = LS_MXF_SMPTE;
1588 // configure encryption
1589 if( Options.key_flag )
1591 Kumu::GenRandomUUID(Info.ContextID);
1592 Info.EncryptedEssence = true;
1594 if ( Options.key_id_flag )
1596 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1600 create_random_uuid(Info.CryptographicKeyID);
1603 Context = new AESEncContext;
1604 result = Context->InitKey(Options.key_value);
1606 if ( ASDCP_SUCCESS(result) )
1607 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1609 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1611 Info.UsesHMAC = true;
1612 HMAC = new HMACContext;
1613 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1617 if ( ASDCP_SUCCESS(result) )
1618 result = Writer.OpenWrite(Options.out_file, Info, DDesc);
1621 if ( ASDCP_SUCCESS(result) )
1623 ui32_t duration = 0;
1624 result = Parser.Reset();
1626 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1628 result = Parser.ReadFrame(FrameBuffer);
1630 if ( ASDCP_SUCCESS(result) )
1632 if ( Options.verbose_flag )
1633 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1635 if ( Options.encrypt_header_flag )
1636 FrameBuffer.PlaintextOffset(0);
1639 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1641 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1643 // The Writer class will forward the last block of ciphertext
1644 // to the encryption context for use as the IV for the next
1645 // frame. If you want to use non-sequitur IV values, un-comment
1646 // the following line of code.
1647 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1648 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1652 if ( result == RESULT_ENDOFFILE )
1656 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1657 result = Writer.Finalize();
1664 main(int argc, const char** argv)
1666 Result_t result = RESULT_OK;
1668 g_dict = &ASDCP::DefaultSMPTEDict();
1670 CommandOptions Options(argc, argv);
1672 if ( Options.version_flag )
1675 if ( Options.help_flag )
1678 if ( Options.show_ul_values_flag )
1680 if ( Options.use_smpte_labels )
1681 DefaultSMPTEDict().Dump(stdout);
1683 DefaultInteropDict().Dump(stdout);
1686 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1689 if ( Options.error_flag )
1691 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1695 EssenceType_t EssenceType;
1696 result = ASDCP::RawEssenceType(Options.filenames.front(), EssenceType);
1698 if ( ASDCP_SUCCESS(result) )
1700 switch ( EssenceType )
1703 result = write_MPEG2_file(Options);
1707 if ( Options.stereo_image_flag )
1709 result = write_JP2K_S_file(Options);
1713 result = write_JP2K_file(Options);
1717 case ESS_PCM_24b_48k:
1718 case ESS_PCM_24b_96k:
1719 if ( Options.dolby_atmos_sync_flag )
1721 result = write_PCM_with_ATMOS_sync_file(Options);
1725 result = write_PCM_file(Options);
1729 case ESS_TIMED_TEXT:
1730 result = write_timed_text_file(Options);
1733 case ESS_DCDATA_DOLBY_ATMOS:
1734 result = write_dolby_atmos_file(Options);
1737 case ESS_DCDATA_UNKNOWN:
1738 if ( ! Options.aux_data_coding.HasValue() )
1740 fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n");
1745 result = write_aux_data_file(Options);
1750 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1751 Options.filenames.front().c_str());
1756 if ( ASDCP_FAILURE(result) )
1758 fputs("Program stopped on error.\n", stderr);
1760 if ( result != RESULT_FAIL )
1762 fputs(result, stderr);
1763 fputc('\n', stderr);
1774 // end asdcp-wrap.cpp