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
28 \version $Id: asdcp-wrap.cpp,v 1.5 2012/03/07 18:47:02 mikey Exp $
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>]\n\
114 [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
115 [-l <label>] [-L] [-M] [-p <frame-rate>] [-s <num>] [-v] [-W]\n\
116 [-z|-Z] <input-file>+ <output-file>\n\n",
117 PROGRAM_NAME, PROGRAM_NAME);
121 -3 - Create a stereoscopic image file. Expects two\n\
122 directories of JP2K codestreams (directories must have\n\
123 an equal number of frames; left eye is first).\n\
124 -C <UL> - Set ChannelAssignment UL value\n\
125 -h | -help - Show help\n\
126 -V - Show version information\n\
127 -e - Encrypt MPEG or JP2K headers (default)\n\
128 -E - Do not encrypt MPEG or JP2K headers\n\
129 -j <key-id-str> - Write key ID instead of creating a random value\n\
130 -k <key-string> - Use key for ciphertext operations\n\
131 -M - Do not create HMAC values when writing\n\
132 -a <UUID> - Specify the Asset ID of a file (with -c)\n\
133 -b <buffer-size> - Specify size in bytes of picture frame buffer.\n\
134 Defaults to 4,194,304 (4MB)\n\
135 -d <duration> - Number of frames to process, default all\n\
136 -f <start-frame> - Starting frame number, default 0\n\
137 -l <label> - Use given channel format label when writing MXF sound\n\
138 files. SMPTE 429-2 labels: '5.1', '6.1', '7.1',\n\
140 Default is no label (valid for Interop only).\n\
141 -L - Write SMPTE UL values instead of MXF Interop\n\
142 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
143 Use one of [23|24|25|30|48|50|60], 24 is default\n\
144 -v - Verbose, prints informative messages to stderr\n\
145 -W - Read input file only, do not write source file\n\
146 -z - Fail if j2c inputs have unequal parameters (default)\n\
147 -Z - Ignore unequal parameters in j2c inputs\n\
149 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
150 o All option arguments must be separated from the option by whitespace.\n\
151 o An argument of \"23\" to the -p option will be interpreted\n\
152 as 24000/1001 fps.\n\
158 decode_channel_fmt(const std::string& label_name)
160 if ( label_name == "5.1" )
161 return PCM::CF_CFG_1;
163 else if ( label_name == "6.1" )
164 return PCM::CF_CFG_2;
166 else if ( label_name == "7.1" )
167 return PCM::CF_CFG_3;
169 else if ( label_name == "WTF" )
170 return PCM::CF_CFG_4;
172 else if ( label_name == "7.1DS" )
173 return PCM::CF_CFG_5;
175 fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
176 fprintf(stderr, "Expecting '5.1', '6.1', '7.1', '7.1DS' or 'WTF'\n");
187 bool error_flag; // true if the given options are in error or not complete
188 bool key_flag; // true if an encryption key was given
189 bool asset_id_flag; // true if an asset ID was given
190 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
191 bool write_hmac; // true if HMAC values are to be generated and written
192 /// bool read_hmac; // true if HMAC values are to be validated
193 /// bool split_wav; // true if PCM is to be extracted to stereo WAV files
194 /// bool mono_wav; // true if PCM is to be extracted to mono WAV files
195 bool verbose_flag; // true if the verbose option was selected
196 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
197 /// bool showindex_flag; // true if index is to be displayed
198 /// bool showheader_flag; // true if MXF file header is to be displayed
199 bool no_write_flag; // true if no output files are to be written
200 bool version_flag; // true if the version display option was selected
201 bool help_flag; // true if the help display option was selected
202 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
203 /// ui32_t number_width; // number of digits in a serialized filename (for JPEG extract)
204 ui32_t start_frame; // frame number to begin processing
205 ui32_t duration; // number of frames to be processed
206 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
207 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
208 ui32_t picture_rate; // fps of picture when wrapping PCM
209 ui32_t fb_size; // size of picture frame buffer
210 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
211 bool key_id_flag; // true if a key ID was given
212 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
213 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
214 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
215 std::string out_file; //
216 bool show_ul_values; /// if true, dump the UL table before going tp work.
217 Kumu::PathList_t filenames; // list of filenames to be processed
218 UL channel_assignment;
221 Rational PictureRate()
223 if ( picture_rate == 23 ) return EditRate_23_98;
224 if ( picture_rate == 24 ) return EditRate_24;
225 if ( picture_rate == 25 ) return EditRate_25;
226 if ( picture_rate == 30 ) return EditRate_30;
227 if ( picture_rate == 48 ) return EditRate_48;
228 if ( picture_rate == 50 ) return EditRate_50;
229 if ( picture_rate == 60 ) return EditRate_60;
230 if ( picture_rate == 96 ) return EditRate_96;
231 if ( picture_rate == 100 ) return EditRate_100;
232 if ( picture_rate == 120 ) return EditRate_120;
237 const char* szPictureRate()
239 if ( picture_rate == 23 ) return "23.976";
240 if ( picture_rate == 24 ) return "24";
241 if ( picture_rate == 25 ) return "25";
242 if ( picture_rate == 30 ) return "30";
243 if ( picture_rate == 48 ) return "48";
244 if ( picture_rate == 50 ) return "50";
245 if ( picture_rate == 60 ) return "60";
246 if ( picture_rate == 96 ) return "96";
247 if ( picture_rate == 100 ) return "100";
248 if ( picture_rate == 120 ) return "120";
253 CommandOptions(int argc, const char** argv) :
254 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
255 encrypt_header_flag(true), write_hmac(true),
256 verbose_flag(false), fb_dump_size(0),
257 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
259 duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
260 fb_size(FRAME_BUFFER_SIZE),
261 channel_fmt(PCM::CF_NONE),
262 show_ul_values(false)
264 memset(key_value, 0, KeyLen);
265 memset(key_id_value, 0, UUIDlen);
267 for ( int i = 1; i < argc; i++ )
270 if ( (strcmp( argv[i], "-help") == 0) )
276 if ( argv[i][0] == '-'
277 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
280 switch ( argv[i][1] )
282 case '3': stereo_image_flag = true; break;
285 asset_id_flag = true;
286 TEST_EXTRA_ARG(i, 'a');
289 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
291 if ( length != UUIDlen )
293 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
300 TEST_EXTRA_ARG(i, 'b');
301 fb_size = abs(atoi(argv[i]));
304 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
309 TEST_EXTRA_ARG(i, 'U');
310 if ( ! channel_assignment.DecodeHex(argv[i]) )
312 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
318 TEST_EXTRA_ARG(i, 'd');
319 duration = abs(atoi(argv[i]));
322 case 'E': encrypt_header_flag = false; break;
323 case 'e': encrypt_header_flag = true; break;
326 TEST_EXTRA_ARG(i, 'f');
327 start_frame = abs(atoi(argv[i]));
330 case 'h': help_flag = true; break;
332 case 'j': key_id_flag = true;
333 TEST_EXTRA_ARG(i, 'j');
336 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
338 if ( length != UUIDlen )
340 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
346 case 'k': key_flag = true;
347 TEST_EXTRA_ARG(i, 'k');
350 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
352 if ( length != KeyLen )
354 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
361 TEST_EXTRA_ARG(i, 'l');
362 channel_fmt = decode_channel_fmt(argv[i]);
365 case 'L': use_smpte_labels = true; break;
366 case 'M': write_hmac = false; break;
369 TEST_EXTRA_ARG(i, 'p');
370 picture_rate = abs(atoi(argv[i]));
373 case 'V': version_flag = true; break;
374 case 'v': verbose_flag = true; break;
375 case 'W': no_write_flag = true; break;
376 case 'Z': j2c_pedantic = false; break;
377 case 'z': j2c_pedantic = true; break;
380 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
387 if ( argv[i][0] != '-' )
389 filenames.push_back(argv[i]);
393 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
399 if ( help_flag || version_flag )
402 if ( filenames.size() < 2 )
404 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
408 out_file = filenames.back();
409 filenames.pop_back();
414 //------------------------------------------------------------------------------------------
417 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
418 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
421 write_MPEG2_file(CommandOptions& Options)
423 AESEncContext* Context = 0;
424 HMACContext* HMAC = 0;
425 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
426 MPEG2::Parser Parser;
427 MPEG2::MXFWriter Writer;
428 MPEG2::VideoDescriptor VDesc;
429 byte_t IV_buf[CBC_BLOCK_SIZE];
430 Kumu::FortunaRNG RNG;
432 // set up essence parser
433 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
436 if ( ASDCP_SUCCESS(result) )
438 Parser.FillVideoDescriptor(VDesc);
440 if ( Options.verbose_flag )
442 fputs("MPEG-2 Pictures\n", stderr);
443 fputs("VideoDescriptor:\n", stderr);
444 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
445 MPEG2::VideoDescriptorDump(VDesc);
449 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
451 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
452 if ( Options.asset_id_flag )
453 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
455 Kumu::GenRandomUUID(Info.AssetUUID);
457 if ( Options.use_smpte_labels )
459 Info.LabelSetType = LS_MXF_SMPTE;
460 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
463 // configure encryption
464 if( Options.key_flag )
466 Kumu::GenRandomUUID(Info.ContextID);
467 Info.EncryptedEssence = true;
469 if ( Options.key_id_flag )
470 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
472 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
474 Context = new AESEncContext;
475 result = Context->InitKey(Options.key_value);
477 if ( ASDCP_SUCCESS(result) )
478 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
480 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
482 Info.UsesHMAC = true;
483 HMAC = new HMACContext;
484 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
488 if ( ASDCP_SUCCESS(result) )
489 result = Writer.OpenWrite(Options.out_file.c_str(), Info, VDesc);
492 if ( ASDCP_SUCCESS(result) )
493 // loop through the frames
495 result = Parser.Reset();
498 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
502 result = Parser.ReadFrame(FrameBuffer);
504 if ( ASDCP_SUCCESS(result) )
506 if ( Options.verbose_flag )
507 FrameBuffer.Dump(stderr, Options.fb_dump_size);
509 if ( Options.encrypt_header_flag )
510 FrameBuffer.PlaintextOffset(0);
514 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
516 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
518 // The Writer class will forward the last block of ciphertext
519 // to the encryption context for use as the IV for the next
520 // frame. If you want to use non-sequitur IV values, un-comment
521 // the following line of code.
522 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
523 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
527 if ( result == RESULT_ENDOFFILE )
531 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
532 result = Writer.Finalize();
538 //------------------------------------------------------------------------------------------
541 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
542 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
545 write_JP2K_S_file(CommandOptions& Options)
547 AESEncContext* Context = 0;
548 HMACContext* HMAC = 0;
549 JP2K::MXFSWriter Writer;
550 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
551 JP2K::PictureDescriptor PDesc;
552 JP2K::SequenceParser ParserLeft, ParserRight;
553 byte_t IV_buf[CBC_BLOCK_SIZE];
554 Kumu::FortunaRNG RNG;
556 if ( Options.filenames.size() != 2 )
558 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
562 // set up essence parser
563 Result_t result = ParserLeft.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
565 if ( ASDCP_SUCCESS(result) )
567 Options.filenames.pop_front();
568 result = ParserRight.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
572 if ( ASDCP_SUCCESS(result) )
574 ParserLeft.FillPictureDescriptor(PDesc);
575 PDesc.EditRate = Options.PictureRate();
577 if ( Options.verbose_flag )
579 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
580 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
581 JP2K::PictureDescriptorDump(PDesc);
585 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
587 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
588 if ( Options.asset_id_flag )
589 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
591 Kumu::GenRandomUUID(Info.AssetUUID);
593 if ( Options.use_smpte_labels )
595 Info.LabelSetType = LS_MXF_SMPTE;
596 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
599 // configure encryption
600 if( Options.key_flag )
602 Kumu::GenRandomUUID(Info.ContextID);
603 Info.EncryptedEssence = true;
605 if ( Options.key_id_flag )
606 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
608 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
610 Context = new AESEncContext;
611 result = Context->InitKey(Options.key_value);
613 if ( ASDCP_SUCCESS(result) )
614 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
616 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
618 Info.UsesHMAC = true;
619 HMAC = new HMACContext;
620 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
624 if ( ASDCP_SUCCESS(result) )
625 result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
628 if ( ASDCP_SUCCESS(result) )
631 result = ParserLeft.Reset();
632 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
634 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
636 result = ParserLeft.ReadFrame(FrameBuffer);
638 if ( ASDCP_SUCCESS(result) )
640 if ( Options.verbose_flag )
641 FrameBuffer.Dump(stderr, Options.fb_dump_size);
643 if ( Options.encrypt_header_flag )
644 FrameBuffer.PlaintextOffset(0);
647 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
648 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
650 if ( ASDCP_SUCCESS(result) )
651 result = ParserRight.ReadFrame(FrameBuffer);
653 if ( ASDCP_SUCCESS(result) )
655 if ( Options.verbose_flag )
656 FrameBuffer.Dump(stderr, Options.fb_dump_size);
658 if ( Options.encrypt_header_flag )
659 FrameBuffer.PlaintextOffset(0);
662 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
663 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
666 if ( result == RESULT_ENDOFFILE )
670 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
671 result = Writer.Finalize();
676 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
677 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
680 write_JP2K_file(CommandOptions& Options)
682 AESEncContext* Context = 0;
683 HMACContext* HMAC = 0;
684 JP2K::MXFWriter Writer;
685 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
686 JP2K::PictureDescriptor PDesc;
687 JP2K::SequenceParser Parser;
688 byte_t IV_buf[CBC_BLOCK_SIZE];
689 Kumu::FortunaRNG RNG;
691 // set up essence parser
692 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
695 if ( ASDCP_SUCCESS(result) )
697 Parser.FillPictureDescriptor(PDesc);
698 PDesc.EditRate = Options.PictureRate();
700 if ( Options.verbose_flag )
702 fprintf(stderr, "JPEG 2000 pictures\n");
703 fputs("PictureDescriptor:\n", stderr);
704 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
705 JP2K::PictureDescriptorDump(PDesc);
709 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
711 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
712 if ( Options.asset_id_flag )
713 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
715 Kumu::GenRandomUUID(Info.AssetUUID);
717 if ( Options.use_smpte_labels )
719 Info.LabelSetType = LS_MXF_SMPTE;
720 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
723 // configure encryption
724 if( Options.key_flag )
726 Kumu::GenRandomUUID(Info.ContextID);
727 Info.EncryptedEssence = true;
729 if ( Options.key_id_flag )
730 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
732 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
734 Context = new AESEncContext;
735 result = Context->InitKey(Options.key_value);
737 if ( ASDCP_SUCCESS(result) )
738 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
740 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
742 Info.UsesHMAC = true;
743 HMAC = new HMACContext;
744 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
748 if ( ASDCP_SUCCESS(result) )
749 result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
752 if ( ASDCP_SUCCESS(result) )
755 result = Parser.Reset();
757 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
761 result = Parser.ReadFrame(FrameBuffer);
763 if ( ASDCP_SUCCESS(result) )
765 if ( Options.verbose_flag )
766 FrameBuffer.Dump(stderr, Options.fb_dump_size);
768 if ( Options.encrypt_header_flag )
769 FrameBuffer.PlaintextOffset(0);
773 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
775 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
777 // The Writer class will forward the last block of ciphertext
778 // to the encryption context for use as the IV for the next
779 // frame. If you want to use non-sequitur IV values, un-comment
780 // the following line of code.
781 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
782 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
786 if ( result == RESULT_ENDOFFILE )
790 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
791 result = Writer.Finalize();
796 //------------------------------------------------------------------------------------------
800 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
801 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
804 write_PCM_file(CommandOptions& Options)
806 AESEncContext* Context = 0;
807 HMACContext* HMAC = 0;
808 PCMParserList Parser;
809 PCM::MXFWriter Writer;
810 PCM::FrameBuffer FrameBuffer;
811 PCM::AudioDescriptor ADesc;
812 Rational PictureRate = Options.PictureRate();
813 byte_t IV_buf[CBC_BLOCK_SIZE];
814 Kumu::FortunaRNG RNG;
816 // set up essence parser
817 Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
820 if ( ASDCP_SUCCESS(result) )
822 Parser.FillAudioDescriptor(ADesc);
824 ADesc.EditRate = PictureRate;
825 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
826 ADesc.ChannelFormat = Options.channel_fmt;
828 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE)
830 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see option -l)\n");
833 if ( Options.verbose_flag )
835 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
836 ADesc.AudioSamplingRate.Quotient() / 1000.0,
837 Options.szPictureRate(),
838 PCM::CalcSamplesPerFrame(ADesc));
839 fputs("AudioDescriptor:\n", stderr);
840 PCM::AudioDescriptorDump(ADesc);
844 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
846 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
847 if ( Options.asset_id_flag )
848 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
850 Kumu::GenRandomUUID(Info.AssetUUID);
852 if ( Options.use_smpte_labels )
854 Info.LabelSetType = LS_MXF_SMPTE;
855 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
858 // configure encryption
859 if( Options.key_flag )
861 Kumu::GenRandomUUID(Info.ContextID);
862 Info.EncryptedEssence = true;
864 if ( Options.key_id_flag )
865 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
867 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
869 Context = new AESEncContext;
870 result = Context->InitKey(Options.key_value);
872 if ( ASDCP_SUCCESS(result) )
873 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
875 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
877 Info.UsesHMAC = true;
878 HMAC = new HMACContext;
879 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
883 if ( ASDCP_SUCCESS(result) )
884 result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
886 if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
888 MXF::WaveAudioDescriptor *descriptor = 0;
889 Writer.OPAtomHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
890 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
891 descriptor->ChannelAssignment = Options.channel_assignment;
895 if ( ASDCP_SUCCESS(result) )
897 result = Parser.Reset();
900 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
902 result = Parser.ReadFrame(FrameBuffer);
904 if ( ASDCP_SUCCESS(result) )
906 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
908 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
909 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
910 result = RESULT_ENDOFFILE;
914 if ( Options.verbose_flag )
915 FrameBuffer.Dump(stderr, Options.fb_dump_size);
917 if ( ! Options.no_write_flag )
919 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
921 // The Writer class will forward the last block of ciphertext
922 // to the encryption context for use as the IV for the next
923 // frame. If you want to use non-sequitur IV values, un-comment
924 // the following line of code.
925 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
926 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
931 if ( result == RESULT_ENDOFFILE )
935 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
936 result = Writer.Finalize();
942 //------------------------------------------------------------------------------------------
946 // Write one or more plaintext timed text streams to a plaintext ASDCP file
947 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
950 write_timed_text_file(CommandOptions& Options)
952 AESEncContext* Context = 0;
953 HMACContext* HMAC = 0;
954 TimedText::DCSubtitleParser Parser;
955 TimedText::MXFWriter Writer;
956 TimedText::FrameBuffer FrameBuffer;
957 TimedText::TimedTextDescriptor TDesc;
958 byte_t IV_buf[CBC_BLOCK_SIZE];
959 Kumu::FortunaRNG RNG;
961 // set up essence parser
962 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
965 if ( ASDCP_SUCCESS(result) )
967 Parser.FillTimedTextDescriptor(TDesc);
968 FrameBuffer.Capacity(Options.fb_size);
970 if ( Options.verbose_flag )
972 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
973 TimedText::DescriptorDump(TDesc);
977 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
979 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
980 if ( Options.asset_id_flag )
981 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
983 Kumu::GenRandomUUID(Info.AssetUUID);
985 if ( Options.use_smpte_labels )
987 Info.LabelSetType = LS_MXF_SMPTE;
988 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
991 // configure encryption
992 if( Options.key_flag )
994 Kumu::GenRandomUUID(Info.ContextID);
995 Info.EncryptedEssence = true;
997 if ( Options.key_id_flag )
998 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1000 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1002 Context = new AESEncContext;
1003 result = Context->InitKey(Options.key_value);
1005 if ( ASDCP_SUCCESS(result) )
1006 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1008 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1010 Info.UsesHMAC = true;
1011 HMAC = new HMACContext;
1012 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1016 if ( ASDCP_SUCCESS(result) )
1017 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1020 if ( ASDCP_FAILURE(result) )
1024 TimedText::ResourceList_t::const_iterator ri;
1026 result = Parser.ReadTimedTextResource(XMLDoc);
1028 if ( ASDCP_SUCCESS(result) )
1029 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1031 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1033 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1035 if ( ASDCP_SUCCESS(result) )
1037 if ( Options.verbose_flag )
1038 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1040 if ( ! Options.no_write_flag )
1042 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1044 // The Writer class will forward the last block of ciphertext
1045 // to the encryption context for use as the IV for the next
1046 // frame. If you want to use non-sequitur IV values, un-comment
1047 // the following line of code.
1048 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1049 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1053 if ( result == RESULT_ENDOFFILE )
1057 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1058 result = Writer.Finalize();
1065 main(int argc, const char** argv)
1067 Result_t result = RESULT_OK;
1069 CommandOptions Options(argc, argv);
1071 if ( Options.version_flag )
1074 if ( Options.help_flag )
1077 if ( Options.version_flag || Options.help_flag )
1080 if ( Options.error_flag )
1082 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1086 if ( Options.show_ul_values )
1088 if ( Options.use_smpte_labels )
1089 DefaultSMPTEDict().Dump(stdout);
1091 DefaultInteropDict().Dump(stdout);
1094 EssenceType_t EssenceType;
1095 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1097 if ( ASDCP_SUCCESS(result) )
1099 switch ( EssenceType )
1102 result = write_MPEG2_file(Options);
1106 if ( Options.stereo_image_flag )
1108 result = write_JP2K_S_file(Options);
1112 result = write_JP2K_file(Options);
1116 case ESS_PCM_24b_48k:
1117 case ESS_PCM_24b_96k:
1118 result = write_PCM_file(Options);
1121 case ESS_TIMED_TEXT:
1122 result = write_timed_text_file(Options);
1126 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1127 Options.filenames.front().c_str());
1132 if ( ASDCP_FAILURE(result) )
1134 fputs("Program stopped on error.\n", stderr);
1136 if ( result != RESULT_FAIL )
1138 fputs(result, stderr);
1139 fputc('\n', stderr);
1150 // end asdcp-wrap.cpp