2 Copyright (c) 2003-2012, 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) in t 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>
52 #include <PCMParserList.h>
55 using namespace ASDCP;
57 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
59 //------------------------------------------------------------------------------------------
61 // command line option parser class
63 static const char* PROGRAM_NAME = "asdcp-wrap"; // program name for messages
65 // local program identification info written to file headers
66 class MyInfo : public WriterInfo
71 static byte_t default_ProductUUID_Data[UUIDlen] =
72 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
73 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
75 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
76 CompanyName = "WidgetCo";
77 ProductName = "asdcp-wrap";
78 ProductVersion = ASDCP::Version();
84 // Increment the iterator, test for an additional non-option command line argument.
85 // Causes the caller to return if there are no remaining arguments or if the next
86 // argument begins with '-'.
87 #define TEST_EXTRA_ARG(i,c) \
88 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
89 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
95 banner(FILE* stream = stdout)
99 Copyright (c) 2003-2012 John Hurst\n\n\
100 asdcplib may be copied only under the terms of the license found at\n\
101 the top of every file in the asdcplib distribution kit.\n\n\
102 Specify the -h (help) option for further information about %s\n\n",
103 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
108 usage(FILE* stream = stdout)
111 USAGE: %s [-h|-help] [-V]\n\
113 %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>] [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>] [-l <label>] [-L] [-M] [-p <frame-rate>] [-s <num>] [-v] [-W] [-z|-Z] <input-file>+ <output-file>\n\n",
114 PROGRAM_NAME, PROGRAM_NAME);
118 -3 - With -c, create a stereoscopic image file. Expects two\n\
119 directories of JP2K codestreams (directories must have\n\
120 an equal number of frames; left eye is first).\n\
121 - With -x, force stereoscopic interpretation of a JP2K\n\
123 -C <UL> - Set ChannelAssignment UL value\n\
124 -h | -help - Show help\n\
125 -V - Show version information\n\
126 -e - Encrypt MPEG or JP2K headers (default)\n\
127 -E - Do not encrypt MPEG or JP2K headers\n\
128 -j <key-id-str> - Write key ID instead of creating a random value\n\
129 -k <key-string> - Use key for ciphertext operations\n\
130 -M - Do not create HMAC values when writing\n\
131 -a <UUID> - Specify the Asset ID of a file (with -c)\n\
132 -b <buffer-size> - Specify size in bytes of picture frame buffer.\n\
133 Defaults to 4,194,304 (4MB)\n\
134 -d <duration> - Number of frames to process, default all\n\
135 -f <start-frame> - Starting frame number, default 0\n\
136 -l <label> - Use given channel format label when writing MXF sound\n\
137 files. SMPTE 429-2 labels: '5.1', '6.1', '7.1', '7.1DS', 'WTF'.\n\
138 Default is no label (valid for Interop only).\n\
139 -L - Write SMPTE UL values instead of MXF Interop\n\
140 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
141 Use one of [23|24|25|30|48|50|60], 24 is default\n\
142 -v - Verbose, prints informative messages to stderr\n\
143 -W - Read input file only, do not write source file\n\
144 -z - Fail if j2c inputs have unequal parameters (default)\n\
145 -Z - Ignore unequal parameters in j2c inputs\n\
147 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
148 o All option arguments must be separated from the option by whitespace.\n\
149 o An argument of \"23\" to the -p option will be interpreted\n\
150 as 24000/1001 fps.\n\
156 decode_channel_fmt(const std::string& label_name)
158 if ( label_name == "5.1" )
159 return PCM::CF_CFG_1;
161 else if ( label_name == "6.1" )
162 return PCM::CF_CFG_2;
164 else if ( label_name == "7.1" )
165 return PCM::CF_CFG_3;
167 else if ( label_name == "WTF" )
168 return PCM::CF_CFG_4;
170 else if ( label_name == "7.1DS" )
171 return PCM::CF_CFG_5;
173 fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
174 fprintf(stderr, "Expecting '5.1', '6.1', '7.1', '7.1DS' or 'WTF'\n");
185 bool error_flag; // true if the given options are in error or not complete
186 bool key_flag; // true if an encryption key was given
187 bool asset_id_flag; // true if an asset ID was given
188 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
189 bool write_hmac; // true if HMAC values are to be generated and written
190 /// bool read_hmac; // true if HMAC values are to be validated
191 /// bool split_wav; // true if PCM is to be extracted to stereo WAV files
192 /// bool mono_wav; // true if PCM is to be extracted to mono WAV files
193 bool verbose_flag; // true if the verbose option was selected
194 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
195 /// bool showindex_flag; // true if index is to be displayed
196 /// bool showheader_flag; // true if MXF file header is to be displayed
197 bool no_write_flag; // true if no output files are to be written
198 bool version_flag; // true if the version display option was selected
199 bool help_flag; // true if the help display option was selected
200 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
201 /// ui32_t number_width; // number of digits in a serialized filename (for JPEG extract)
202 ui32_t start_frame; // frame number to begin processing
203 ui32_t duration; // number of frames to be processed
204 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
205 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
206 ui32_t picture_rate; // fps of picture when wrapping PCM
207 ui32_t fb_size; // size of picture frame buffer
208 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
209 bool key_id_flag; // true if a key ID was given
210 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
211 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
212 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
213 std::string out_file; //
214 bool show_ul_values; /// if true, dump the UL table before going tp work.
215 Kumu::PathList_t filenames; // list of filenames to be processed
216 UL channel_assignment;
219 Rational PictureRate()
221 if ( picture_rate == 23 ) return EditRate_23_98;
222 if ( picture_rate == 24 ) return EditRate_24;
223 if ( picture_rate == 25 ) return EditRate_25;
224 if ( picture_rate == 30 ) return EditRate_30;
225 if ( picture_rate == 48 ) return EditRate_48;
226 if ( picture_rate == 50 ) return EditRate_50;
227 if ( picture_rate == 60 ) return EditRate_60;
228 if ( picture_rate == 96 ) return EditRate_96;
229 if ( picture_rate == 100 ) return EditRate_100;
230 if ( picture_rate == 120 ) return EditRate_120;
235 const char* szPictureRate()
237 if ( picture_rate == 23 ) return "23.976";
238 if ( picture_rate == 24 ) return "24";
239 if ( picture_rate == 25 ) return "25";
240 if ( picture_rate == 30 ) return "30";
241 if ( picture_rate == 48 ) return "48";
242 if ( picture_rate == 50 ) return "50";
243 if ( picture_rate == 60 ) return "60";
244 if ( picture_rate == 96 ) return "96";
245 if ( picture_rate == 100 ) return "100";
246 if ( picture_rate == 120 ) return "120";
251 CommandOptions(int argc, const char** argv) :
252 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
253 encrypt_header_flag(true), write_hmac(true),
254 verbose_flag(false), fb_dump_size(0),
255 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
257 duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
258 fb_size(FRAME_BUFFER_SIZE),
259 channel_fmt(PCM::CF_NONE),
260 show_ul_values(false)
262 memset(key_value, 0, KeyLen);
263 memset(key_id_value, 0, UUIDlen);
265 for ( int i = 1; i < argc; i++ )
268 if ( (strcmp( argv[i], "-help") == 0) )
274 if ( argv[i][0] == '-'
275 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
278 switch ( argv[i][1] )
280 case '3': stereo_image_flag = true; break;
283 asset_id_flag = true;
284 TEST_EXTRA_ARG(i, 'a');
287 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
289 if ( length != UUIDlen )
291 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
298 TEST_EXTRA_ARG(i, 'b');
299 fb_size = abs(atoi(argv[i]));
302 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
307 TEST_EXTRA_ARG(i, 'd');
308 duration = abs(atoi(argv[i]));
311 case 'E': encrypt_header_flag = false; break;
312 case 'e': encrypt_header_flag = true; break;
315 TEST_EXTRA_ARG(i, 'f');
316 start_frame = abs(atoi(argv[i]));
319 case 'h': help_flag = true; break;
321 case 'j': key_id_flag = true;
322 TEST_EXTRA_ARG(i, 'j');
325 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
327 if ( length != UUIDlen )
329 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
335 case 'k': key_flag = true;
336 TEST_EXTRA_ARG(i, 'k');
339 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
341 if ( length != KeyLen )
343 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
350 TEST_EXTRA_ARG(i, 'l');
351 channel_fmt = decode_channel_fmt(argv[i]);
354 case 'L': use_smpte_labels = true; break;
355 case 'M': write_hmac = false; break;
358 TEST_EXTRA_ARG(i, 'p');
359 picture_rate = abs(atoi(argv[i]));
363 TEST_EXTRA_ARG(i, 'U');
364 if ( ! channel_assignment.DecodeHex(argv[i]) )
366 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
371 case 'V': version_flag = true; break;
372 case 'v': verbose_flag = true; break;
373 case 'W': no_write_flag = true; break;
374 case 'Z': j2c_pedantic = false; break;
375 case 'z': j2c_pedantic = true; break;
378 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
385 if ( argv[i][0] != '-' )
387 filenames.push_back(argv[i]);
391 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
397 if ( help_flag || version_flag )
400 if ( filenames.size() < 2 )
402 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
406 out_file = filenames.back();
407 filenames.pop_back();
412 //------------------------------------------------------------------------------------------
415 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
416 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
419 write_MPEG2_file(CommandOptions& Options)
421 AESEncContext* Context = 0;
422 HMACContext* HMAC = 0;
423 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
424 MPEG2::Parser Parser;
425 MPEG2::MXFWriter Writer;
426 MPEG2::VideoDescriptor VDesc;
427 byte_t IV_buf[CBC_BLOCK_SIZE];
428 Kumu::FortunaRNG RNG;
430 // set up essence parser
431 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
434 if ( ASDCP_SUCCESS(result) )
436 Parser.FillVideoDescriptor(VDesc);
438 if ( Options.verbose_flag )
440 fputs("MPEG-2 Pictures\n", stderr);
441 fputs("VideoDescriptor:\n", stderr);
442 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
443 MPEG2::VideoDescriptorDump(VDesc);
447 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
449 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
450 if ( Options.asset_id_flag )
451 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
453 Kumu::GenRandomUUID(Info.AssetUUID);
455 if ( Options.use_smpte_labels )
457 Info.LabelSetType = LS_MXF_SMPTE;
458 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
461 // configure encryption
462 if( Options.key_flag )
464 Kumu::GenRandomUUID(Info.ContextID);
465 Info.EncryptedEssence = true;
467 if ( Options.key_id_flag )
468 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
470 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
472 Context = new AESEncContext;
473 result = Context->InitKey(Options.key_value);
475 if ( ASDCP_SUCCESS(result) )
476 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
478 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
480 Info.UsesHMAC = true;
481 HMAC = new HMACContext;
482 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
486 if ( ASDCP_SUCCESS(result) )
487 result = Writer.OpenWrite(Options.out_file.c_str(), Info, VDesc);
490 if ( ASDCP_SUCCESS(result) )
491 // loop through the frames
493 result = Parser.Reset();
496 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
500 result = Parser.ReadFrame(FrameBuffer);
502 if ( ASDCP_SUCCESS(result) )
504 if ( Options.verbose_flag )
505 FrameBuffer.Dump(stderr, Options.fb_dump_size);
507 if ( Options.encrypt_header_flag )
508 FrameBuffer.PlaintextOffset(0);
512 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
514 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
516 // The Writer class will forward the last block of ciphertext
517 // to the encryption context for use as the IV for the next
518 // frame. If you want to use non-sequitur IV values, un-comment
519 // the following line of code.
520 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
521 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
525 if ( result == RESULT_ENDOFFILE )
529 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
530 result = Writer.Finalize();
536 //------------------------------------------------------------------------------------------
539 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
540 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
543 write_JP2K_S_file(CommandOptions& Options)
545 AESEncContext* Context = 0;
546 HMACContext* HMAC = 0;
547 JP2K::MXFSWriter Writer;
548 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
549 JP2K::PictureDescriptor PDesc;
550 JP2K::SequenceParser ParserLeft, ParserRight;
551 byte_t IV_buf[CBC_BLOCK_SIZE];
552 Kumu::FortunaRNG RNG;
554 if ( Options.filenames.size() != 2 )
556 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
560 // set up essence parser
561 Result_t result = ParserLeft.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
563 if ( ASDCP_SUCCESS(result) )
565 Options.filenames.pop_front();
566 result = ParserRight.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
570 if ( ASDCP_SUCCESS(result) )
572 ParserLeft.FillPictureDescriptor(PDesc);
573 PDesc.EditRate = Options.PictureRate();
575 if ( Options.verbose_flag )
577 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
578 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
579 JP2K::PictureDescriptorDump(PDesc);
583 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
585 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
586 if ( Options.asset_id_flag )
587 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
589 Kumu::GenRandomUUID(Info.AssetUUID);
591 if ( Options.use_smpte_labels )
593 Info.LabelSetType = LS_MXF_SMPTE;
594 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
597 // configure encryption
598 if( Options.key_flag )
600 Kumu::GenRandomUUID(Info.ContextID);
601 Info.EncryptedEssence = true;
603 if ( Options.key_id_flag )
604 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
606 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
608 Context = new AESEncContext;
609 result = Context->InitKey(Options.key_value);
611 if ( ASDCP_SUCCESS(result) )
612 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
614 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
616 Info.UsesHMAC = true;
617 HMAC = new HMACContext;
618 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
622 if ( ASDCP_SUCCESS(result) )
623 result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
626 if ( ASDCP_SUCCESS(result) )
629 result = ParserLeft.Reset();
630 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
632 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
634 result = ParserLeft.ReadFrame(FrameBuffer);
636 if ( ASDCP_SUCCESS(result) )
638 if ( Options.verbose_flag )
639 FrameBuffer.Dump(stderr, Options.fb_dump_size);
641 if ( Options.encrypt_header_flag )
642 FrameBuffer.PlaintextOffset(0);
645 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
646 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
648 if ( ASDCP_SUCCESS(result) )
649 result = ParserRight.ReadFrame(FrameBuffer);
651 if ( ASDCP_SUCCESS(result) )
653 if ( Options.verbose_flag )
654 FrameBuffer.Dump(stderr, Options.fb_dump_size);
656 if ( Options.encrypt_header_flag )
657 FrameBuffer.PlaintextOffset(0);
660 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
661 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
664 if ( result == RESULT_ENDOFFILE )
668 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
669 result = Writer.Finalize();
674 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
675 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
678 write_JP2K_file(CommandOptions& Options)
680 AESEncContext* Context = 0;
681 HMACContext* HMAC = 0;
682 JP2K::MXFWriter Writer;
683 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
684 JP2K::PictureDescriptor PDesc;
685 JP2K::SequenceParser Parser;
686 byte_t IV_buf[CBC_BLOCK_SIZE];
687 Kumu::FortunaRNG RNG;
689 // set up essence parser
690 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
693 if ( ASDCP_SUCCESS(result) )
695 Parser.FillPictureDescriptor(PDesc);
696 PDesc.EditRate = Options.PictureRate();
698 if ( Options.verbose_flag )
700 fprintf(stderr, "JPEG 2000 pictures\n");
701 fputs("PictureDescriptor:\n", stderr);
702 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
703 JP2K::PictureDescriptorDump(PDesc);
707 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
709 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
710 if ( Options.asset_id_flag )
711 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
713 Kumu::GenRandomUUID(Info.AssetUUID);
715 if ( Options.use_smpte_labels )
717 Info.LabelSetType = LS_MXF_SMPTE;
718 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
721 // configure encryption
722 if( Options.key_flag )
724 Kumu::GenRandomUUID(Info.ContextID);
725 Info.EncryptedEssence = true;
727 if ( Options.key_id_flag )
728 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
730 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
732 Context = new AESEncContext;
733 result = Context->InitKey(Options.key_value);
735 if ( ASDCP_SUCCESS(result) )
736 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
738 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
740 Info.UsesHMAC = true;
741 HMAC = new HMACContext;
742 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
746 if ( ASDCP_SUCCESS(result) )
747 result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
750 if ( ASDCP_SUCCESS(result) )
753 result = Parser.Reset();
755 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
759 result = Parser.ReadFrame(FrameBuffer);
761 if ( ASDCP_SUCCESS(result) )
763 if ( Options.verbose_flag )
764 FrameBuffer.Dump(stderr, Options.fb_dump_size);
766 if ( Options.encrypt_header_flag )
767 FrameBuffer.PlaintextOffset(0);
771 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
773 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
775 // The Writer class will forward the last block of ciphertext
776 // to the encryption context for use as the IV for the next
777 // frame. If you want to use non-sequitur IV values, un-comment
778 // the following line of code.
779 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
780 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
784 if ( result == RESULT_ENDOFFILE )
788 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
789 result = Writer.Finalize();
794 //------------------------------------------------------------------------------------------
798 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
799 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
802 write_PCM_file(CommandOptions& Options)
804 AESEncContext* Context = 0;
805 HMACContext* HMAC = 0;
806 PCMParserList Parser;
807 PCM::MXFWriter Writer;
808 PCM::FrameBuffer FrameBuffer;
809 PCM::AudioDescriptor ADesc;
810 Rational PictureRate = Options.PictureRate();
811 byte_t IV_buf[CBC_BLOCK_SIZE];
812 Kumu::FortunaRNG RNG;
814 // set up essence parser
815 Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
818 if ( ASDCP_SUCCESS(result) )
820 Parser.FillAudioDescriptor(ADesc);
822 ADesc.EditRate = PictureRate;
823 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
824 ADesc.ChannelFormat = Options.channel_fmt;
826 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE)
828 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see option -l)\n");
831 if ( Options.verbose_flag )
833 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
834 ADesc.AudioSamplingRate.Quotient() / 1000.0,
835 Options.szPictureRate(),
836 PCM::CalcSamplesPerFrame(ADesc));
837 fputs("AudioDescriptor:\n", stderr);
838 PCM::AudioDescriptorDump(ADesc);
842 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
844 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
845 if ( Options.asset_id_flag )
846 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
848 Kumu::GenRandomUUID(Info.AssetUUID);
850 if ( Options.use_smpte_labels )
852 Info.LabelSetType = LS_MXF_SMPTE;
853 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
856 // configure encryption
857 if( Options.key_flag )
859 Kumu::GenRandomUUID(Info.ContextID);
860 Info.EncryptedEssence = true;
862 if ( Options.key_id_flag )
863 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
865 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
867 Context = new AESEncContext;
868 result = Context->InitKey(Options.key_value);
870 if ( ASDCP_SUCCESS(result) )
871 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
873 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
875 Info.UsesHMAC = true;
876 HMAC = new HMACContext;
877 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
881 if ( ASDCP_SUCCESS(result) )
882 result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
884 if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
886 MXF::WaveAudioDescriptor *descriptor = 0;
887 Writer.OPAtomHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
888 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
889 descriptor->ChannelAssignment = Options.channel_assignment;
893 if ( ASDCP_SUCCESS(result) )
895 result = Parser.Reset();
898 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
900 result = Parser.ReadFrame(FrameBuffer);
902 if ( ASDCP_SUCCESS(result) )
904 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
906 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
907 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
908 result = RESULT_ENDOFFILE;
912 if ( Options.verbose_flag )
913 FrameBuffer.Dump(stderr, Options.fb_dump_size);
915 if ( ! Options.no_write_flag )
917 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
919 // The Writer class will forward the last block of ciphertext
920 // to the encryption context for use as the IV for the next
921 // frame. If you want to use non-sequitur IV values, un-comment
922 // the following line of code.
923 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
924 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
929 if ( result == RESULT_ENDOFFILE )
933 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
934 result = Writer.Finalize();
940 //------------------------------------------------------------------------------------------
944 // Write one or more plaintext timed text streams to a plaintext ASDCP file
945 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
948 write_timed_text_file(CommandOptions& Options)
950 AESEncContext* Context = 0;
951 HMACContext* HMAC = 0;
952 TimedText::DCSubtitleParser Parser;
953 TimedText::MXFWriter Writer;
954 TimedText::FrameBuffer FrameBuffer;
955 TimedText::TimedTextDescriptor TDesc;
956 byte_t IV_buf[CBC_BLOCK_SIZE];
957 Kumu::FortunaRNG RNG;
959 // set up essence parser
960 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
963 if ( ASDCP_SUCCESS(result) )
965 Parser.FillTimedTextDescriptor(TDesc);
966 FrameBuffer.Capacity(Options.fb_size);
968 if ( Options.verbose_flag )
970 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
971 TimedText::DescriptorDump(TDesc);
975 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
977 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
978 if ( Options.asset_id_flag )
979 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
981 Kumu::GenRandomUUID(Info.AssetUUID);
983 if ( Options.use_smpte_labels )
985 Info.LabelSetType = LS_MXF_SMPTE;
986 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
989 // configure encryption
990 if( Options.key_flag )
992 Kumu::GenRandomUUID(Info.ContextID);
993 Info.EncryptedEssence = true;
995 if ( Options.key_id_flag )
996 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
998 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1000 Context = new AESEncContext;
1001 result = Context->InitKey(Options.key_value);
1003 if ( ASDCP_SUCCESS(result) )
1004 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1006 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1008 Info.UsesHMAC = true;
1009 HMAC = new HMACContext;
1010 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1014 if ( ASDCP_SUCCESS(result) )
1015 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1018 if ( ASDCP_FAILURE(result) )
1022 TimedText::ResourceList_t::const_iterator ri;
1024 result = Parser.ReadTimedTextResource(XMLDoc);
1026 if ( ASDCP_SUCCESS(result) )
1027 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1029 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1031 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1033 if ( ASDCP_SUCCESS(result) )
1035 if ( Options.verbose_flag )
1036 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1038 if ( ! Options.no_write_flag )
1040 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1042 // The Writer class will forward the last block of ciphertext
1043 // to the encryption context for use as the IV for the next
1044 // frame. If you want to use non-sequitur IV values, un-comment
1045 // the following line of code.
1046 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1047 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1051 if ( result == RESULT_ENDOFFILE )
1055 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1056 result = Writer.Finalize();
1063 main(int argc, const char** argv)
1065 Result_t result = RESULT_OK;
1067 CommandOptions Options(argc, argv);
1069 if ( Options.version_flag )
1072 if ( Options.help_flag )
1075 if ( Options.version_flag || Options.help_flag )
1078 if ( Options.error_flag )
1080 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1084 if ( Options.show_ul_values )
1086 if ( Options.use_smpte_labels )
1087 DefaultSMPTEDict().Dump(stdout);
1089 DefaultInteropDict().Dump(stdout);
1092 EssenceType_t EssenceType;
1093 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1095 if ( ASDCP_SUCCESS(result) )
1097 switch ( EssenceType )
1100 result = write_MPEG2_file(Options);
1104 if ( Options.stereo_image_flag )
1106 result = write_JP2K_S_file(Options);
1110 result = write_JP2K_file(Options);
1114 case ESS_PCM_24b_48k:
1115 case ESS_PCM_24b_96k:
1116 result = write_PCM_file(Options);
1119 case ESS_TIMED_TEXT:
1120 result = write_timed_text_file(Options);
1124 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1125 Options.filenames.front().c_str());
1130 if ( ASDCP_FAILURE(result) )
1132 fputs("Program stopped on error.\n", stderr);
1134 if ( result != RESULT_FAIL )
1136 fputs(result, stderr);
1137 fputc('\n', stderr);
1148 // end asdcp-wrap.cpp