2 Copyright (c) 2003-2015, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file asdcp-wrap.cpp
29 \brief AS-DCP file manipulation utility
31 This program wraps d-cinema essence (picture, sound or text) into an AS-DCP
34 For more information about asdcplib, please refer to the header file AS_DCP.h
36 WARNING: While the asdcplib library attempts to provide a complete and secure
37 implementation of the cryptographic features of the AS-DCP file formats, this
38 unit test program is NOT secure and is therefore NOT SUITABLE FOR USE in a
39 production environment without some modification.
41 In particular, this program uses weak IV generation and externally generated
42 plaintext keys. These shortcomings exist because cryptographic-quality
43 random number generation and key management are outside the scope of the
44 asdcplib library. Developers using asdcplib for commercial implementations
45 claiming SMPTE conformance are expected to provide proper implementations of
49 #include <KM_fileio.h>
51 #include <AtmosSyncChannel_Mixer.h>
53 #include <PCMParserList.h>
56 using namespace ASDCP;
58 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
60 const byte_t P_HFR_UL_2K[16] = {
61 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
62 0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
65 const ASDCP::Dictionary *g_dict = 0;
67 //------------------------------------------------------------------------------------------
69 // command line option parser class
71 static const char* PROGRAM_NAME = "asdcp-wrap"; // program name for messages
73 // local program identification info written to file headers
74 class MyInfo : public WriterInfo
79 static byte_t default_ProductUUID_Data[UUIDlen] =
80 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
81 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
83 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
84 CompanyName = "WidgetCo";
85 ProductName = "asdcp-wrap";
86 ProductVersion = ASDCP::Version();
92 // Increment the iterator, test for an additional non-option command line argument.
93 // Causes the caller to return if there are no remaining arguments or if the next
94 // argument begins with '-'.
95 #define TEST_EXTRA_ARG(i,c) \
96 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
97 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
103 create_random_uuid(byte_t* uuidbuf)
106 GenRandomValue(tmp_id);
107 memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
112 banner(FILE* stream = stdout)
115 %s (asdcplib %s)\n\n\
116 Copyright (c) 2003-2015 John Hurst\n\n\
117 asdcplib may be copied only under the terms of the license found at\n\
118 the top of every file in the asdcplib distribution kit.\n\n\
119 Specify the -h (help) option for further information about %s\n\n",
120 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
125 usage(FILE* stream = stdout)
128 USAGE: %s [-h|-help] [-V]\n\
130 %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
131 [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
132 [-l <label>] [-L] [-M] [-m <expr>] [-p <frame-rate>] [-s] [-v]\n\
133 [-W] [-z|-Z] <input-file>+ <output-file>\n\n",
134 PROGRAM_NAME, PROGRAM_NAME);
138 -3 - Create a stereoscopic image file. Expects two\n\
139 directories of JP2K codestreams (directories must have\n\
140 an equal number of frames; the left eye is first)\n\
141 -A <UL> - Set DataEssenceCoding UL value in an Aux Data file\n\
142 -C <UL> - Set ChannelAssignment UL value in a PCM file\n\
143 -h | -help - Show help\n\
144 -V - Show version information\n\
145 -e - Encrypt MPEG or JP2K headers (default)\n\
146 -E - Do not encrypt MPEG or JP2K headers\n\
147 -j <key-id-str> - Write key ID instead of creating a random value\n\
148 -k <key-string> - Use key for ciphertext operations\n\
149 -M - Do not create HMAC values when writing\n\
150 -m <expr> - Write MCA labels using <expr>. Example:\n\
151 51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
152 Note: The symbol '-' may be used for an unlabeled\n\
153 channel, but not within a soundfield.\n\
154 -a <UUID> - Specify the Asset ID of the file\n\
155 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
156 Defaults to 4,194,304 (4MB)\n\
157 -d <duration> - Number of frames to process, default all\n\
158 -f <start-frame> - Starting frame number, default 0\n\
159 -l <label> - Use given channel format label when writing MXF sound\n\
160 files. SMPTE 429-2 labels: '5.1', '6.1', '7.1',\n\
162 Default is no label (valid for Interop only).\n\
163 -L - Write SMPTE UL values instead of MXF Interop\n\
164 -P <UL> - Set PictureEssenceCoding UL value in a JP2K file\n\
165 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
166 Use one of [23|24|25|30|48|50|60], 24 is default\n\
167 -s - Insert a Dolby Atmos synchronization channel when\n\
168 wrapping PCM. This implies a -L option(SMPTE ULs) and \n\
169 will overide -C and -l options with Configuration 4 \n\
170 Channel Assigment and no format label respectively. \n\
171 -v - Verbose, prints informative messages to stderr\n\
172 -W - Read input file only, do not write source file\n\
173 -z - Fail if j2c inputs have unequal parameters (default)\n\
174 -Z - Ignore unequal parameters in j2c inputs\n\
176 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
177 o All option arguments must be separated from the option by whitespace.\n\
178 o An argument of \"23\" to the -p option will be interpreted\n\
179 as 24000/1001 fps.\n\
185 decode_channel_fmt(const std::string& label_name)
187 if ( label_name == "5.1" )
188 return PCM::CF_CFG_1;
190 else if ( label_name == "6.1" )
191 return PCM::CF_CFG_2;
193 else if ( label_name == "7.1" )
194 return PCM::CF_CFG_3;
196 else if ( label_name == "WTF" )
197 return PCM::CF_CFG_4;
199 else if ( label_name == "7.1DS" )
200 return PCM::CF_CFG_5;
202 fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
203 fprintf(stderr, "Expecting '5.1', '6.1', '7.1', '7.1DS' or 'WTF'\n");
214 bool error_flag; // true if the given options are in error or not complete
215 bool key_flag; // true if an encryption key was given
216 bool asset_id_flag; // true if an asset ID was given
217 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
218 bool write_hmac; // true if HMAC values are to be generated and written
219 bool verbose_flag; // true if the verbose option was selected
220 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
221 bool no_write_flag; // true if no output files are to be written
222 bool version_flag; // true if the version display option was selected
223 bool help_flag; // true if the help display option was selected
224 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
225 bool write_partial_pcm_flag; // if true, write the last frame of PCM input even when it is incomplete
226 ui32_t start_frame; // frame number to begin processing
227 ui32_t duration; // number of frames to be processed
228 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
229 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
230 ui32_t picture_rate; // fps of picture when wrapping PCM
231 ui32_t fb_size; // size of picture frame buffer
232 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
233 bool key_id_flag; // true if a key ID was given
234 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
235 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
236 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
237 std::string out_file; //
238 bool show_ul_values_flag; /// if true, dump the UL table before going to work.
239 Kumu::PathList_t filenames; // list of filenames to be processed
240 UL channel_assignment;
243 bool dolby_atmos_sync_flag; // if true, insert a Dolby Atmos Synchronization channel.
244 ui32_t ffoa; /// first frame of action for atmos wrapping
245 ui32_t max_channel_count; /// max channel count for atmos wrapping
246 ui32_t max_object_count; /// max object count for atmos wrapping
247 ASDCP::MXF::ASDCP_MCAConfigParser mca_config;
250 Rational PictureRate()
252 if ( picture_rate == 16 ) return EditRate_16;
253 if ( picture_rate == 18 ) return EditRate_18;
254 if ( picture_rate == 20 ) return EditRate_20;
255 if ( picture_rate == 22 ) return EditRate_22;
256 if ( picture_rate == 23 ) return EditRate_23_98;
257 if ( picture_rate == 24 ) return EditRate_24;
258 if ( picture_rate == 25 ) return EditRate_25;
259 if ( picture_rate == 30 ) return EditRate_30;
260 if ( picture_rate == 48 ) return EditRate_48;
261 if ( picture_rate == 50 ) return EditRate_50;
262 if ( picture_rate == 60 ) return EditRate_60;
263 if ( picture_rate == 96 ) return EditRate_96;
264 if ( picture_rate == 100 ) return EditRate_100;
265 if ( picture_rate == 120 ) return EditRate_120;
270 const char* szPictureRate()
272 if ( picture_rate == 16 ) return "16";
273 if ( picture_rate == 18 ) return "18.182";
274 if ( picture_rate == 20 ) return "20";
275 if ( picture_rate == 22 ) return "21.818";
276 if ( picture_rate == 23 ) return "23.976";
277 if ( picture_rate == 24 ) return "24";
278 if ( picture_rate == 25 ) return "25";
279 if ( picture_rate == 30 ) return "30";
280 if ( picture_rate == 48 ) return "48";
281 if ( picture_rate == 50 ) return "50";
282 if ( picture_rate == 60 ) return "60";
283 if ( picture_rate == 96 ) return "96";
284 if ( picture_rate == 100 ) return "100";
285 if ( picture_rate == 120 ) return "120";
290 CommandOptions(int argc, const char** argv) :
291 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
292 encrypt_header_flag(true), write_hmac(true),
293 verbose_flag(false), fb_dump_size(0),
294 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
295 write_partial_pcm_flag(false), start_frame(0),
296 duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
297 fb_size(FRAME_BUFFER_SIZE),
298 channel_fmt(PCM::CF_NONE),
299 ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties
300 dolby_atmos_sync_flag(false),
301 show_ul_values_flag(false),
304 memset(key_value, 0, KeyLen);
305 memset(key_id_value, 0, UUIDlen);
307 for ( int i = 1; i < argc; i++ )
310 if ( (strcmp( argv[i], "-help") == 0) )
316 if ( argv[i][0] == '-'
317 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
320 switch ( argv[i][1] )
322 case '3': stereo_image_flag = true; break;
325 TEST_EXTRA_ARG(i, 'A');
326 if ( ! aux_data_coding.DecodeHex(argv[i]) )
328 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
334 asset_id_flag = true;
335 TEST_EXTRA_ARG(i, 'a');
338 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
340 if ( length != UUIDlen )
342 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
349 TEST_EXTRA_ARG(i, 'b');
350 fb_size = abs(atoi(argv[i]));
353 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
358 TEST_EXTRA_ARG(i, 'C');
359 if ( ! channel_assignment.DecodeHex(argv[i]) )
361 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
367 TEST_EXTRA_ARG(i, 'd');
368 duration = abs(atoi(argv[i]));
371 case 'E': encrypt_header_flag = false; break;
372 case 'e': encrypt_header_flag = true; break;
375 TEST_EXTRA_ARG(i, 'f');
376 start_frame = abs(atoi(argv[i]));
379 case 'g': write_partial_pcm_flag = true; break;
380 case 'h': help_flag = true; break;
382 case 'j': key_id_flag = true;
383 TEST_EXTRA_ARG(i, 'j');
386 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
388 if ( length != UUIDlen )
390 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
396 case 'k': key_flag = true;
397 TEST_EXTRA_ARG(i, 'k');
400 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
402 if ( length != KeyLen )
404 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
411 TEST_EXTRA_ARG(i, 'l');
412 channel_fmt = decode_channel_fmt(argv[i]);
415 case 'L': use_smpte_labels = true; break;
416 case 'M': write_hmac = false; break;
419 TEST_EXTRA_ARG(i, 'm');
420 if ( ! mca_config.DecodeString(argv[i]) )
427 TEST_EXTRA_ARG(i, 'P');
428 if ( ! picture_coding.DecodeHex(argv[i]) )
430 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
436 TEST_EXTRA_ARG(i, 'p');
437 picture_rate = abs(atoi(argv[i]));
440 case 's': dolby_atmos_sync_flag = true; break;
441 case 'u': show_ul_values_flag = true; break;
442 case 'V': version_flag = true; break;
443 case 'v': verbose_flag = true; break;
444 case 'W': no_write_flag = true; break;
445 case 'Z': j2c_pedantic = false; break;
446 case 'z': j2c_pedantic = true; break;
449 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
456 if ( argv[i][0] != '-' )
458 filenames.push_back(argv[i]);
462 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
468 if ( help_flag || version_flag )
471 if ( filenames.size() < 2 )
473 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
477 out_file = filenames.back();
478 filenames.pop_back();
483 //------------------------------------------------------------------------------------------
486 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
487 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
490 write_MPEG2_file(CommandOptions& Options)
492 AESEncContext* Context = 0;
493 HMACContext* HMAC = 0;
494 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
495 MPEG2::Parser Parser;
496 MPEG2::MXFWriter Writer;
497 MPEG2::VideoDescriptor VDesc;
498 byte_t IV_buf[CBC_BLOCK_SIZE];
499 Kumu::FortunaRNG RNG;
501 // set up essence parser
502 Result_t result = Parser.OpenRead(Options.filenames.front());
505 if ( ASDCP_SUCCESS(result) )
507 Parser.FillVideoDescriptor(VDesc);
509 if ( Options.verbose_flag )
511 fputs("MPEG-2 Pictures\n", stderr);
512 fputs("VideoDescriptor:\n", stderr);
513 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
514 MPEG2::VideoDescriptorDump(VDesc);
518 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
520 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
521 if ( Options.asset_id_flag )
522 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
524 Kumu::GenRandomUUID(Info.AssetUUID);
526 if ( Options.use_smpte_labels )
528 Info.LabelSetType = LS_MXF_SMPTE;
529 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
532 // configure encryption
533 if( Options.key_flag )
535 Kumu::GenRandomUUID(Info.ContextID);
536 Info.EncryptedEssence = true;
538 if ( Options.key_id_flag )
540 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
544 create_random_uuid(Info.CryptographicKeyID);
547 Context = new AESEncContext;
548 result = Context->InitKey(Options.key_value);
550 if ( ASDCP_SUCCESS(result) )
551 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
553 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
555 Info.UsesHMAC = true;
556 HMAC = new HMACContext;
557 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
561 if ( ASDCP_SUCCESS(result) )
562 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
565 if ( ASDCP_SUCCESS(result) )
566 // loop through the frames
568 result = Parser.Reset();
571 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
573 result = Parser.ReadFrame(FrameBuffer);
575 if ( ASDCP_SUCCESS(result) )
577 if ( Options.verbose_flag )
578 FrameBuffer.Dump(stderr, Options.fb_dump_size);
580 if ( Options.encrypt_header_flag )
581 FrameBuffer.PlaintextOffset(0);
584 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
586 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
588 // The Writer class will forward the last block of ciphertext
589 // to the encryption context for use as the IV for the next
590 // frame. If you want to use non-sequitur IV values, un-comment
591 // the following line of code.
592 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
593 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
597 if ( result == RESULT_ENDOFFILE )
601 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
602 result = Writer.Finalize();
608 //------------------------------------------------------------------------------------------
610 // return false if an error is discovered
612 check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc)
614 Rational rate = Options.PictureRate();
615 if ( rate != EditRate_96 && rate != EditRate_100 && rate != EditRate_120 )
618 if ( PDesc.StoredWidth > 2048 )
620 fprintf(stderr, "P-HFR files currently limited to 2K.\n");
624 if ( ! Options.use_smpte_labels )
626 fprintf(stderr, "P-HFR files must be written using SMPTE labels. Use option '-L'.\n");
630 // do not set the label if the user has already done so
631 if ( ! Options.picture_coding.HasValue() )
632 Options.picture_coding = UL(P_HFR_UL_2K);
637 //------------------------------------------------------------------------------------------
640 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
641 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
644 write_JP2K_S_file(CommandOptions& Options)
646 AESEncContext* Context = 0;
647 HMACContext* HMAC = 0;
648 JP2K::MXFSWriter Writer;
649 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
650 JP2K::PictureDescriptor PDesc;
651 JP2K::SequenceParser ParserLeft, ParserRight;
652 byte_t IV_buf[CBC_BLOCK_SIZE];
653 Kumu::FortunaRNG RNG;
655 if ( Options.filenames.size() != 2 )
657 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
661 // set up essence parser
662 Result_t result = ParserLeft.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
664 if ( ASDCP_SUCCESS(result) )
666 Options.filenames.pop_front();
667 result = ParserRight.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
671 if ( ASDCP_SUCCESS(result) )
673 ParserLeft.FillPictureDescriptor(PDesc);
674 PDesc.EditRate = Options.PictureRate();
676 if ( Options.verbose_flag )
678 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
679 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
680 JP2K::PictureDescriptorDump(PDesc);
684 if ( ! check_phfr_params(Options, PDesc) )
687 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
689 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
690 if ( Options.asset_id_flag )
691 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
693 Kumu::GenRandomUUID(Info.AssetUUID);
695 if ( Options.use_smpte_labels )
697 Info.LabelSetType = LS_MXF_SMPTE;
698 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
701 // configure encryption
702 if( Options.key_flag )
704 Kumu::GenRandomUUID(Info.ContextID);
705 Info.EncryptedEssence = true;
707 if ( Options.key_id_flag )
709 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
713 create_random_uuid(Info.CryptographicKeyID);
716 Context = new AESEncContext;
717 result = Context->InitKey(Options.key_value);
719 if ( ASDCP_SUCCESS(result) )
720 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
722 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
724 Info.UsesHMAC = true;
725 HMAC = new HMACContext;
726 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
730 if ( ASDCP_SUCCESS(result) )
731 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
733 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
735 MXF::RGBAEssenceDescriptor *descriptor = 0;
736 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
737 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
738 descriptor->PictureEssenceCoding = Options.picture_coding;
742 if ( ASDCP_SUCCESS(result) )
745 result = ParserLeft.Reset();
746 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
748 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
750 result = ParserLeft.ReadFrame(FrameBuffer);
752 if ( ASDCP_SUCCESS(result) )
754 if ( Options.verbose_flag )
755 FrameBuffer.Dump(stderr, Options.fb_dump_size);
757 if ( Options.encrypt_header_flag )
758 FrameBuffer.PlaintextOffset(0);
761 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
762 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
764 if ( ASDCP_SUCCESS(result) )
765 result = ParserRight.ReadFrame(FrameBuffer);
767 if ( ASDCP_SUCCESS(result) )
769 if ( Options.verbose_flag )
770 FrameBuffer.Dump(stderr, Options.fb_dump_size);
772 if ( Options.encrypt_header_flag )
773 FrameBuffer.PlaintextOffset(0);
776 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
777 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
780 if ( result == RESULT_ENDOFFILE )
784 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
785 result = Writer.Finalize();
790 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
791 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
794 write_JP2K_file(CommandOptions& Options)
796 AESEncContext* Context = 0;
797 HMACContext* HMAC = 0;
798 JP2K::MXFWriter Writer;
799 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
800 JP2K::PictureDescriptor PDesc;
801 JP2K::SequenceParser Parser;
802 byte_t IV_buf[CBC_BLOCK_SIZE];
803 Kumu::FortunaRNG RNG;
805 // set up essence parser
806 Result_t result = Parser.OpenRead(Options.filenames.front(), Options.j2c_pedantic);
809 if ( ASDCP_SUCCESS(result) )
811 Parser.FillPictureDescriptor(PDesc);
812 PDesc.EditRate = Options.PictureRate();
814 if ( Options.verbose_flag )
816 fprintf(stderr, "JPEG 2000 pictures\n");
817 fputs("PictureDescriptor:\n", stderr);
818 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
819 JP2K::PictureDescriptorDump(PDesc);
823 if ( ! check_phfr_params(Options, PDesc) )
826 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
828 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
829 if ( Options.asset_id_flag )
830 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
832 Kumu::GenRandomUUID(Info.AssetUUID);
834 if ( Options.use_smpte_labels )
836 Info.LabelSetType = LS_MXF_SMPTE;
837 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
840 // configure encryption
841 if( Options.key_flag )
843 Kumu::GenRandomUUID(Info.ContextID);
844 Info.EncryptedEssence = true;
846 if ( Options.key_id_flag )
848 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
852 create_random_uuid(Info.CryptographicKeyID);
855 Context = new AESEncContext;
856 result = Context->InitKey(Options.key_value);
858 if ( ASDCP_SUCCESS(result) )
859 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
861 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
863 Info.UsesHMAC = true;
864 HMAC = new HMACContext;
865 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
869 if ( ASDCP_SUCCESS(result) )
870 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
872 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
874 MXF::RGBAEssenceDescriptor *descriptor = 0;
875 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
876 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
877 descriptor->PictureEssenceCoding = Options.picture_coding;
881 if ( ASDCP_SUCCESS(result) )
884 result = Parser.Reset();
886 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
888 result = Parser.ReadFrame(FrameBuffer);
890 if ( ASDCP_SUCCESS(result) )
892 if ( Options.verbose_flag )
893 FrameBuffer.Dump(stderr, Options.fb_dump_size);
895 if ( Options.encrypt_header_flag )
896 FrameBuffer.PlaintextOffset(0);
899 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
901 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
903 // The Writer class will forward the last block of ciphertext
904 // to the encryption context for use as the IV for the next
905 // frame. If you want to use non-sequitur IV values, un-comment
906 // the following line of code.
907 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
908 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
912 if ( result == RESULT_ENDOFFILE )
916 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
917 result = Writer.Finalize();
922 //------------------------------------------------------------------------------------------
926 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
927 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
930 write_PCM_file(CommandOptions& Options)
932 AESEncContext* Context = 0;
933 HMACContext* HMAC = 0;
934 PCMParserList Parser;
935 PCM::MXFWriter Writer;
936 PCM::FrameBuffer FrameBuffer;
937 PCM::AudioDescriptor ADesc;
938 Rational PictureRate = Options.PictureRate();
939 byte_t IV_buf[CBC_BLOCK_SIZE];
940 Kumu::FortunaRNG RNG;
942 // set up essence parser
943 Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
946 if ( ASDCP_SUCCESS(result) )
948 Parser.FillAudioDescriptor(ADesc);
950 ADesc.EditRate = PictureRate;
951 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
952 ADesc.ChannelFormat = Options.channel_fmt;
954 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE && Options.mca_config.empty() )
956 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see options -C, -l and -m)\n");
959 if ( Options.verbose_flag )
961 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
962 ADesc.AudioSamplingRate.Quotient() / 1000.0,
963 Options.szPictureRate(),
964 PCM::CalcSamplesPerFrame(ADesc));
965 fputs("AudioDescriptor:\n", stderr);
966 PCM::AudioDescriptorDump(ADesc);
970 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
972 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
973 if ( Options.asset_id_flag )
974 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
976 Kumu::GenRandomUUID(Info.AssetUUID);
978 if ( Options.use_smpte_labels )
980 Info.LabelSetType = LS_MXF_SMPTE;
981 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
984 // configure encryption
985 if( Options.key_flag )
987 Kumu::GenRandomUUID(Info.ContextID);
988 Info.EncryptedEssence = true;
990 if ( Options.key_id_flag )
992 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
996 create_random_uuid(Info.CryptographicKeyID);
999 Context = new AESEncContext;
1000 result = Context->InitKey(Options.key_value);
1002 if ( ASDCP_SUCCESS(result) )
1003 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1005 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1007 Info.UsesHMAC = true;
1008 HMAC = new HMACContext;
1009 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1013 if ( ASDCP_SUCCESS(result) )
1014 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1016 if ( ASDCP_SUCCESS(result)
1017 && ( Options.channel_assignment.HasValue()
1018 || ! Options.mca_config.empty() ) )
1020 MXF::WaveAudioDescriptor *essence_descriptor = 0;
1021 Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_WaveAudioDescriptor),
1022 reinterpret_cast<MXF::InterchangeObject**>(&essence_descriptor));
1023 assert(essence_descriptor);
1025 if ( Options.mca_config.empty() )
1027 essence_descriptor->ChannelAssignment = Options.channel_assignment;
1031 if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
1033 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
1034 Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
1038 essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
1040 // add descriptors to the essence_descriptor and header
1041 ASDCP::MXF::InterchangeObject_list_t::iterator i;
1042 for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
1044 if ( (*i)->GetUL() != UL(g_dict->ul(MDD_AudioChannelLabelSubDescriptor))
1045 && (*i)->GetUL() != UL(g_dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
1046 && (*i)->GetUL() != UL(g_dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
1048 fprintf(stderr, "Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
1052 Writer.OP1aHeader().AddChildObject(*i);
1053 essence_descriptor->SubDescriptors.push_back((*i)->InstanceUID);
1054 *i = 0; // parent will only free the ones we don't keep
1060 if ( ASDCP_SUCCESS(result) )
1062 result = Parser.Reset();
1063 ui32_t duration = 0;
1065 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1067 result = Parser.ReadFrame(FrameBuffer);
1069 if ( ASDCP_SUCCESS(result) )
1071 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1073 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1074 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1076 if ( Options.write_partial_pcm_flag )
1078 result = RESULT_ENDOFFILE;
1083 if ( Options.verbose_flag )
1084 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1086 if ( ! Options.no_write_flag )
1088 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1090 // The Writer class will forward the last block of ciphertext
1091 // to the encryption context for use as the IV for the next
1092 // frame. If you want to use non-sequitur IV values, un-comment
1093 // the following line of code.
1094 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1095 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1100 if ( result == RESULT_ENDOFFILE )
1104 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1105 result = Writer.Finalize();
1110 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a plaintext ASDCP file
1111 // Mix one or more plaintext PCM audio streams with a Dolby Atmos Synchronization channel and write them to a ciphertext ASDCP file
1114 write_PCM_with_ATMOS_sync_file(CommandOptions& Options)
1116 AESEncContext* Context = 0;
1117 HMACContext* HMAC = 0;
1118 PCM::MXFWriter Writer;
1119 PCM::FrameBuffer FrameBuffer;
1120 PCM::AudioDescriptor ADesc;
1121 Rational PictureRate = Options.PictureRate();
1122 byte_t IV_buf[CBC_BLOCK_SIZE];
1123 Kumu::FortunaRNG RNG;
1125 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1126 if ( Options.asset_id_flag )
1127 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1129 Kumu::GenRandomUUID(Info.AssetUUID);
1130 AtmosSyncChannelMixer Mixer(Info.AssetUUID);
1132 // set up essence parser
1133 Result_t result = Mixer.OpenRead(Options.filenames, PictureRate);
1135 // set up MXF writer
1136 if ( ASDCP_SUCCESS(result) )
1138 Mixer.FillAudioDescriptor(ADesc);
1140 ADesc.EditRate = PictureRate;
1141 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1142 ADesc.ChannelFormat = PCM::CF_CFG_4;
1144 if ( Options.verbose_flag )
1146 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1147 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1148 Options.szPictureRate(),
1149 PCM::CalcSamplesPerFrame(ADesc));
1150 fputs("AudioDescriptor:\n", stderr);
1151 PCM::AudioDescriptorDump(ADesc);
1155 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1157 Info.LabelSetType = LS_MXF_SMPTE;
1158 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1160 // configure encryption
1161 if( Options.key_flag )
1163 Kumu::GenRandomUUID(Info.ContextID);
1164 Info.EncryptedEssence = true;
1166 if ( Options.key_id_flag )
1168 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1172 create_random_uuid(Info.CryptographicKeyID);
1175 Context = new AESEncContext;
1176 result = Context->InitKey(Options.key_value);
1178 if ( ASDCP_SUCCESS(result) )
1179 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1181 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1183 Info.UsesHMAC = true;
1184 HMAC = new HMACContext;
1185 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1189 if ( ASDCP_SUCCESS(result) )
1190 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1193 if ( ASDCP_SUCCESS(result) )
1195 result = Mixer.Reset();
1196 ui32_t duration = 0;
1198 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1200 result = Mixer.ReadFrame(FrameBuffer);
1202 if ( ASDCP_SUCCESS(result) )
1204 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1206 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1207 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1209 if ( Options.write_partial_pcm_flag )
1211 result = RESULT_ENDOFFILE;
1216 if ( Options.verbose_flag )
1217 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1219 if ( ! Options.no_write_flag )
1221 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1223 // The Writer class will forward the last block of ciphertext
1224 // to the encryption context for use as the IV for the next
1225 // frame. If you want to use non-sequitur IV values, un-comment
1226 // the following line of code.
1227 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1228 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1233 if ( result == RESULT_ENDOFFILE )
1237 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1238 result = Writer.Finalize();
1244 //------------------------------------------------------------------------------------------
1245 // TimedText essence
1248 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1249 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1252 write_timed_text_file(CommandOptions& Options)
1254 AESEncContext* Context = 0;
1255 HMACContext* HMAC = 0;
1256 TimedText::DCSubtitleParser Parser;
1257 TimedText::MXFWriter Writer;
1258 TimedText::FrameBuffer FrameBuffer;
1259 TimedText::TimedTextDescriptor TDesc;
1260 byte_t IV_buf[CBC_BLOCK_SIZE];
1261 Kumu::FortunaRNG RNG;
1263 // set up essence parser
1264 Result_t result = Parser.OpenRead(Options.filenames.front());
1266 // set up MXF writer
1267 if ( ASDCP_SUCCESS(result) )
1269 Parser.FillTimedTextDescriptor(TDesc);
1270 FrameBuffer.Capacity(Options.fb_size);
1272 if ( Options.verbose_flag )
1274 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1275 TimedText::DescriptorDump(TDesc);
1279 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1281 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1282 if ( Options.asset_id_flag )
1283 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1285 Kumu::GenRandomUUID(Info.AssetUUID);
1287 // 428-7 IN 429-5 always uses SMPTE labels
1288 Info.LabelSetType = LS_MXF_SMPTE;
1289 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1291 // configure encryption
1292 if( Options.key_flag )
1294 Kumu::GenRandomUUID(Info.ContextID);
1295 Info.EncryptedEssence = true;
1297 if ( Options.key_id_flag )
1299 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1303 create_random_uuid(Info.CryptographicKeyID);
1306 Context = new AESEncContext;
1307 result = Context->InitKey(Options.key_value);
1309 if ( ASDCP_SUCCESS(result) )
1310 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1312 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1314 Info.UsesHMAC = true;
1315 HMAC = new HMACContext;
1316 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1320 if ( ASDCP_SUCCESS(result) )
1321 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1324 if ( ASDCP_FAILURE(result) )
1328 TimedText::ResourceList_t::const_iterator ri;
1330 result = Parser.ReadTimedTextResource(XMLDoc);
1332 if ( ASDCP_SUCCESS(result) )
1333 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1335 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1337 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1339 if ( ASDCP_SUCCESS(result) )
1341 if ( Options.verbose_flag )
1342 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1344 if ( ! Options.no_write_flag )
1346 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1348 // The Writer class will forward the last block of ciphertext
1349 // to the encryption context for use as the IV for the next
1350 // frame. If you want to use non-sequitur IV values, un-comment
1351 // the following line of code.
1352 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1353 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1357 if ( result == RESULT_ENDOFFILE )
1361 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1362 result = Writer.Finalize();
1367 // Write one or more plaintext Dolby ATMOS bytestreams to a plaintext ASDCP file
1368 // Write one or more plaintext Dolby ATMOS bytestreams to a ciphertext ASDCP file
1371 write_dolby_atmos_file(CommandOptions& Options)
1373 AESEncContext* Context = 0;
1374 HMACContext* HMAC = 0;
1375 ATMOS::MXFWriter Writer;
1376 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1377 ATMOS::AtmosDescriptor ADesc;
1378 DCData::SequenceParser Parser;
1379 byte_t IV_buf[CBC_BLOCK_SIZE];
1380 Kumu::FortunaRNG RNG;
1382 // set up essence parser
1383 Result_t result = Parser.OpenRead(Options.filenames.front());
1385 // set up MXF writer
1386 if ( ASDCP_SUCCESS(result) )
1388 Parser.FillDCDataDescriptor(ADesc);
1389 ADesc.EditRate = Options.PictureRate();
1390 // TODO: fill AtmosDescriptor
1391 ADesc.FirstFrame = Options.ffoa;
1392 ADesc.MaxChannelCount = Options.max_channel_count;
1393 ADesc.MaxObjectCount = Options.max_object_count;
1394 Kumu::GenRandomUUID(ADesc.AtmosID);
1395 ADesc.AtmosVersion = 1;
1396 if ( Options.verbose_flag )
1398 fprintf(stderr, "Dolby ATMOS Data\n");
1399 fputs("AtmosDescriptor:\n", stderr);
1400 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1401 ATMOS::AtmosDescriptorDump(ADesc);
1405 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1407 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1408 if ( Options.asset_id_flag )
1409 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1411 Kumu::GenRandomUUID(Info.AssetUUID);
1413 Info.LabelSetType = LS_MXF_SMPTE;
1415 // configure encryption
1416 if( Options.key_flag )
1418 Kumu::GenRandomUUID(Info.ContextID);
1419 Info.EncryptedEssence = true;
1421 if ( Options.key_id_flag )
1423 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1427 create_random_uuid(Info.CryptographicKeyID);
1430 Context = new AESEncContext;
1431 result = Context->InitKey(Options.key_value);
1433 if ( ASDCP_SUCCESS(result) )
1434 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1436 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1438 Info.UsesHMAC = true;
1439 HMAC = new HMACContext;
1440 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1444 if ( ASDCP_SUCCESS(result) )
1445 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1448 if ( ASDCP_SUCCESS(result) )
1450 ui32_t duration = 0;
1451 result = Parser.Reset();
1453 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1455 result = Parser.ReadFrame(FrameBuffer);
1457 if ( ASDCP_SUCCESS(result) )
1459 if ( Options.verbose_flag )
1460 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1462 if ( Options.encrypt_header_flag )
1463 FrameBuffer.PlaintextOffset(0);
1466 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1468 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1470 // The Writer class will forward the last block of ciphertext
1471 // to the encryption context for use as the IV for the next
1472 // frame. If you want to use non-sequitur IV values, un-comment
1473 // the following line of code.
1474 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1475 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1479 if ( result == RESULT_ENDOFFILE )
1483 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1484 result = Writer.Finalize();
1489 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a plaintext ASDCP file
1490 // Write one or more plaintext Aux Data (ST 429-14) bytestreams to a ciphertext ASDCP file
1493 write_aux_data_file(CommandOptions& Options)
1495 AESEncContext* Context = 0;
1496 HMACContext* HMAC = 0;
1497 DCData::MXFWriter Writer;
1498 DCData::FrameBuffer FrameBuffer(Options.fb_size);
1499 DCData::DCDataDescriptor DDesc;
1500 DCData::SequenceParser Parser;
1501 byte_t IV_buf[CBC_BLOCK_SIZE];
1502 Kumu::FortunaRNG RNG;
1504 // set up essence parser
1505 Result_t result = Parser.OpenRead(Options.filenames.front());
1507 // set up MXF writer
1508 if ( ASDCP_SUCCESS(result) )
1510 Parser.FillDCDataDescriptor(DDesc);
1511 memcpy(DDesc.DataEssenceCoding, Options.aux_data_coding.Value(), Options.aux_data_coding.Size());
1512 DDesc.EditRate = Options.PictureRate();
1514 if ( Options.verbose_flag )
1516 fprintf(stderr, "Aux Data\n");
1517 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1518 DCData::DCDataDescriptorDump(DDesc);
1522 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1524 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1525 if ( Options.asset_id_flag )
1526 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1528 Kumu::GenRandomUUID(Info.AssetUUID);
1530 Info.LabelSetType = LS_MXF_SMPTE;
1532 // configure encryption
1533 if( Options.key_flag )
1535 Kumu::GenRandomUUID(Info.ContextID);
1536 Info.EncryptedEssence = true;
1538 if ( Options.key_id_flag )
1540 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1544 create_random_uuid(Info.CryptographicKeyID);
1547 Context = new AESEncContext;
1548 result = Context->InitKey(Options.key_value);
1550 if ( ASDCP_SUCCESS(result) )
1551 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1553 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1555 Info.UsesHMAC = true;
1556 HMAC = new HMACContext;
1557 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1561 if ( ASDCP_SUCCESS(result) )
1562 result = Writer.OpenWrite(Options.out_file, Info, DDesc);
1565 if ( ASDCP_SUCCESS(result) )
1567 ui32_t duration = 0;
1568 result = Parser.Reset();
1570 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1572 result = Parser.ReadFrame(FrameBuffer);
1574 if ( ASDCP_SUCCESS(result) )
1576 if ( Options.verbose_flag )
1577 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1579 if ( Options.encrypt_header_flag )
1580 FrameBuffer.PlaintextOffset(0);
1583 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1585 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1587 // The Writer class will forward the last block of ciphertext
1588 // to the encryption context for use as the IV for the next
1589 // frame. If you want to use non-sequitur IV values, un-comment
1590 // the following line of code.
1591 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1592 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1596 if ( result == RESULT_ENDOFFILE )
1600 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1601 result = Writer.Finalize();
1608 main(int argc, const char** argv)
1610 Result_t result = RESULT_OK;
1612 g_dict = &ASDCP::DefaultSMPTEDict();
1614 CommandOptions Options(argc, argv);
1616 if ( Options.version_flag )
1619 if ( Options.help_flag )
1622 if ( Options.show_ul_values_flag )
1624 if ( Options.use_smpte_labels )
1625 DefaultSMPTEDict().Dump(stdout);
1627 DefaultInteropDict().Dump(stdout);
1630 if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
1633 if ( Options.error_flag )
1635 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1639 EssenceType_t EssenceType;
1640 result = ASDCP::RawEssenceType(Options.filenames.front(), EssenceType);
1642 if ( ASDCP_SUCCESS(result) )
1644 switch ( EssenceType )
1647 result = write_MPEG2_file(Options);
1651 if ( Options.stereo_image_flag )
1653 result = write_JP2K_S_file(Options);
1657 result = write_JP2K_file(Options);
1661 case ESS_PCM_24b_48k:
1662 case ESS_PCM_24b_96k:
1663 if ( Options.dolby_atmos_sync_flag )
1665 result = write_PCM_with_ATMOS_sync_file(Options);
1669 result = write_PCM_file(Options);
1673 case ESS_TIMED_TEXT:
1674 result = write_timed_text_file(Options);
1677 case ESS_DCDATA_DOLBY_ATMOS:
1678 result = write_dolby_atmos_file(Options);
1681 case ESS_DCDATA_UNKNOWN:
1682 if ( ! Options.aux_data_coding.HasValue() )
1684 fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n");
1689 result = write_aux_data_file(Options);
1694 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1695 Options.filenames.front().c_str());
1700 if ( ASDCP_FAILURE(result) )
1702 fputs("Program stopped on error.\n", stderr);
1704 if ( result != RESULT_FAIL )
1706 fputs(result, stderr);
1707 fputc('\n', stderr);
1718 // end asdcp-wrap.cpp