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) 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>
52 #include <PCMParserList.h>
55 using namespace ASDCP;
57 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
59 const byte_t P_HFR_UL_2K[16] = {
60 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
61 0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
64 //------------------------------------------------------------------------------------------
66 // command line option parser class
68 static const char* PROGRAM_NAME = "asdcp-wrap"; // program name for messages
70 // local program identification info written to file headers
71 class MyInfo : public WriterInfo
76 static byte_t default_ProductUUID_Data[UUIDlen] =
77 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
78 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
80 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
81 CompanyName = "WidgetCo";
82 ProductName = "asdcp-wrap";
83 ProductVersion = ASDCP::Version();
89 // Increment the iterator, test for an additional non-option command line argument.
90 // Causes the caller to return if there are no remaining arguments or if the next
91 // argument begins with '-'.
92 #define TEST_EXTRA_ARG(i,c) \
93 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
94 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
100 banner(FILE* stream = stdout)
103 %s (asdcplib %s)\n\n\
104 Copyright (c) 2003-2012 John Hurst\n\n\
105 asdcplib may be copied only under the terms of the license found at\n\
106 the top of every file in the asdcplib distribution kit.\n\n\
107 Specify the -h (help) option for further information about %s\n\n",
108 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
113 usage(FILE* stream = stdout)
116 USAGE: %s [-h|-help] [-V]\n\
118 %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
119 [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
120 [-l <label>] [-L] [-M] [-p <frame-rate>] [-s <num>] [-v] [-W]\n\
121 [-z|-Z] <input-file>+ <output-file>\n\n",
122 PROGRAM_NAME, PROGRAM_NAME);
126 -3 - Create a stereoscopic image file. Expects two\n\
127 directories of JP2K codestreams (directories must have\n\
128 an equal number of frames; the left eye is first)\n\
129 -C <UL> - Set ChannelAssignment UL value in a PCM file\n\
130 -h | -help - Show help\n\
131 -V - Show version information\n\
132 -e - Encrypt MPEG or JP2K headers (default)\n\
133 -E - Do not encrypt MPEG or JP2K headers\n\
134 -j <key-id-str> - Write key ID instead of creating a random value\n\
135 -k <key-string> - Use key for ciphertext operations\n\
136 -M - Do not create HMAC values when writing\n\
137 -a <UUID> - Specify the Asset ID of the file\n\
138 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
139 Defaults to 4,194,304 (4MB)\n\
140 -d <duration> - Number of frames to process, default all\n\
141 -f <start-frame> - Starting frame number, default 0\n\
142 -l <label> - Use given channel format label when writing MXF sound\n\
143 files. SMPTE 429-2 labels: '5.1', '6.1', '7.1',\n\
145 Default is no label (valid for Interop only).\n\
146 -L - Write SMPTE UL values instead of MXF Interop\n\
147 -P <UL> - Set PictureEssenceCoding UL value in a JP2K file\n\
148 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
149 Use one of [23|24|25|30|48|50|60], 24 is default\n\
150 -v - Verbose, prints informative messages to stderr\n\
151 -W - Read input file only, do not write source file\n\
152 -z - Fail if j2c inputs have unequal parameters (default)\n\
153 -Z - Ignore unequal parameters in j2c inputs\n\
155 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
156 o All option arguments must be separated from the option by whitespace.\n\
157 o An argument of \"23\" to the -p option will be interpreted\n\
158 as 24000/1001 fps.\n\
164 decode_channel_fmt(const std::string& label_name)
166 if ( label_name == "5.1" )
167 return PCM::CF_CFG_1;
169 else if ( label_name == "6.1" )
170 return PCM::CF_CFG_2;
172 else if ( label_name == "7.1" )
173 return PCM::CF_CFG_3;
175 else if ( label_name == "WTF" )
176 return PCM::CF_CFG_4;
178 else if ( label_name == "7.1DS" )
179 return PCM::CF_CFG_5;
181 fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
182 fprintf(stderr, "Expecting '5.1', '6.1', '7.1', '7.1DS' or 'WTF'\n");
193 bool error_flag; // true if the given options are in error or not complete
194 bool key_flag; // true if an encryption key was given
195 bool asset_id_flag; // true if an asset ID was given
196 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
197 bool write_hmac; // true if HMAC values are to be generated and written
198 bool verbose_flag; // true if the verbose option was selected
199 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
200 bool no_write_flag; // true if no output files are to be written
201 bool version_flag; // true if the version display option was selected
202 bool help_flag; // true if the help display option was selected
203 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
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;
222 Rational PictureRate()
224 if ( picture_rate == 16 ) return EditRate_16;
225 if ( picture_rate == 18 ) return EditRate_18;
226 if ( picture_rate == 20 ) return EditRate_20;
227 if ( picture_rate == 22 ) return EditRate_22;
228 if ( picture_rate == 23 ) return EditRate_23_98;
229 if ( picture_rate == 24 ) return EditRate_24;
230 if ( picture_rate == 25 ) return EditRate_25;
231 if ( picture_rate == 30 ) return EditRate_30;
232 if ( picture_rate == 48 ) return EditRate_48;
233 if ( picture_rate == 50 ) return EditRate_50;
234 if ( picture_rate == 60 ) return EditRate_60;
235 if ( picture_rate == 96 ) return EditRate_96;
236 if ( picture_rate == 100 ) return EditRate_100;
237 if ( picture_rate == 120 ) return EditRate_120;
242 const char* szPictureRate()
244 if ( picture_rate == 16 ) return "16";
245 if ( picture_rate == 18 ) return "18.182";
246 if ( picture_rate == 20 ) return "20";
247 if ( picture_rate == 22 ) return "21.818";
248 if ( picture_rate == 23 ) return "23.976";
249 if ( picture_rate == 24 ) return "24";
250 if ( picture_rate == 25 ) return "25";
251 if ( picture_rate == 30 ) return "30";
252 if ( picture_rate == 48 ) return "48";
253 if ( picture_rate == 50 ) return "50";
254 if ( picture_rate == 60 ) return "60";
255 if ( picture_rate == 96 ) return "96";
256 if ( picture_rate == 100 ) return "100";
257 if ( picture_rate == 120 ) return "120";
262 CommandOptions(int argc, const char** argv) :
263 error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
264 encrypt_header_flag(true), write_hmac(true),
265 verbose_flag(false), fb_dump_size(0),
266 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
268 duration(0xffffffff), use_smpte_labels(false), j2c_pedantic(true),
269 fb_size(FRAME_BUFFER_SIZE),
270 channel_fmt(PCM::CF_NONE),
271 show_ul_values(false)
273 memset(key_value, 0, KeyLen);
274 memset(key_id_value, 0, UUIDlen);
276 for ( int i = 1; i < argc; i++ )
279 if ( (strcmp( argv[i], "-help") == 0) )
285 if ( argv[i][0] == '-'
286 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
289 switch ( argv[i][1] )
291 case '3': stereo_image_flag = true; break;
294 asset_id_flag = true;
295 TEST_EXTRA_ARG(i, 'a');
298 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
300 if ( length != UUIDlen )
302 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
309 TEST_EXTRA_ARG(i, 'b');
310 fb_size = abs(atoi(argv[i]));
313 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
318 TEST_EXTRA_ARG(i, 'C');
319 if ( ! channel_assignment.DecodeHex(argv[i]) )
321 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
327 TEST_EXTRA_ARG(i, 'd');
328 duration = abs(atoi(argv[i]));
331 case 'E': encrypt_header_flag = false; break;
332 case 'e': encrypt_header_flag = true; break;
335 TEST_EXTRA_ARG(i, 'f');
336 start_frame = abs(atoi(argv[i]));
339 case 'h': help_flag = true; break;
341 case 'j': key_id_flag = true;
342 TEST_EXTRA_ARG(i, 'j');
345 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
347 if ( length != UUIDlen )
349 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
355 case 'k': key_flag = true;
356 TEST_EXTRA_ARG(i, 'k');
359 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
361 if ( length != KeyLen )
363 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
370 TEST_EXTRA_ARG(i, 'l');
371 channel_fmt = decode_channel_fmt(argv[i]);
374 case 'L': use_smpte_labels = true; break;
375 case 'M': write_hmac = false; break;
378 TEST_EXTRA_ARG(i, 'P');
379 if ( ! picture_coding.DecodeHex(argv[i]) )
381 fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
387 TEST_EXTRA_ARG(i, 'p');
388 picture_rate = abs(atoi(argv[i]));
391 case 'V': version_flag = true; break;
392 case 'v': verbose_flag = true; break;
393 case 'W': no_write_flag = true; break;
394 case 'Z': j2c_pedantic = false; break;
395 case 'z': j2c_pedantic = true; break;
398 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
405 if ( argv[i][0] != '-' )
407 filenames.push_back(argv[i]);
411 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
417 if ( help_flag || version_flag )
420 if ( filenames.size() < 2 )
422 fputs("Option requires at least two filename arguments: <input-file> <output-file>\n", stderr);
426 out_file = filenames.back();
427 filenames.pop_back();
432 //------------------------------------------------------------------------------------------
435 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
436 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
439 write_MPEG2_file(CommandOptions& Options)
441 AESEncContext* Context = 0;
442 HMACContext* HMAC = 0;
443 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
444 MPEG2::Parser Parser;
445 MPEG2::MXFWriter Writer;
446 MPEG2::VideoDescriptor VDesc;
447 byte_t IV_buf[CBC_BLOCK_SIZE];
448 Kumu::FortunaRNG RNG;
450 // set up essence parser
451 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
454 if ( ASDCP_SUCCESS(result) )
456 Parser.FillVideoDescriptor(VDesc);
458 if ( Options.verbose_flag )
460 fputs("MPEG-2 Pictures\n", stderr);
461 fputs("VideoDescriptor:\n", stderr);
462 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
463 MPEG2::VideoDescriptorDump(VDesc);
467 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
469 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
470 if ( Options.asset_id_flag )
471 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
473 Kumu::GenRandomUUID(Info.AssetUUID);
475 if ( Options.use_smpte_labels )
477 Info.LabelSetType = LS_MXF_SMPTE;
478 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
481 // configure encryption
482 if( Options.key_flag )
484 Kumu::GenRandomUUID(Info.ContextID);
485 Info.EncryptedEssence = true;
487 if ( Options.key_id_flag )
488 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
490 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
492 Context = new AESEncContext;
493 result = Context->InitKey(Options.key_value);
495 if ( ASDCP_SUCCESS(result) )
496 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
498 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
500 Info.UsesHMAC = true;
501 HMAC = new HMACContext;
502 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
506 if ( ASDCP_SUCCESS(result) )
507 result = Writer.OpenWrite(Options.out_file.c_str(), Info, VDesc);
510 if ( ASDCP_SUCCESS(result) )
511 // loop through the frames
513 result = Parser.Reset();
516 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
520 result = Parser.ReadFrame(FrameBuffer);
522 if ( ASDCP_SUCCESS(result) )
524 if ( Options.verbose_flag )
525 FrameBuffer.Dump(stderr, Options.fb_dump_size);
527 if ( Options.encrypt_header_flag )
528 FrameBuffer.PlaintextOffset(0);
532 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
534 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
536 // The Writer class will forward the last block of ciphertext
537 // to the encryption context for use as the IV for the next
538 // frame. If you want to use non-sequitur IV values, un-comment
539 // the following line of code.
540 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
541 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
545 if ( result == RESULT_ENDOFFILE )
549 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
550 result = Writer.Finalize();
556 //------------------------------------------------------------------------------------------
558 // return false if an error is discovered
560 check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc)
562 Rational rate = Options.PictureRate();
563 if ( rate != EditRate_96 && rate != EditRate_100 && rate != EditRate_120 )
566 if ( PDesc.StoredWidth > 2048 )
568 fprintf(stderr, "P-HFR files currently limited to 2K.\n");
572 if ( ! Options.use_smpte_labels )
574 fprintf(stderr, "P-HFR files must be written using SMPTE labels. Use option '-L'.\n");
578 // do not set the label if the user has already done so
579 if ( ! Options.picture_coding.HasValue() )
580 Options.picture_coding = UL(P_HFR_UL_2K);
585 //------------------------------------------------------------------------------------------
588 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
589 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
592 write_JP2K_S_file(CommandOptions& Options)
594 AESEncContext* Context = 0;
595 HMACContext* HMAC = 0;
596 JP2K::MXFSWriter Writer;
597 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
598 JP2K::PictureDescriptor PDesc;
599 JP2K::SequenceParser ParserLeft, ParserRight;
600 byte_t IV_buf[CBC_BLOCK_SIZE];
601 Kumu::FortunaRNG RNG;
603 if ( Options.filenames.size() != 2 )
605 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
609 // set up essence parser
610 Result_t result = ParserLeft.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
612 if ( ASDCP_SUCCESS(result) )
614 Options.filenames.pop_front();
615 result = ParserRight.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
619 if ( ASDCP_SUCCESS(result) )
621 ParserLeft.FillPictureDescriptor(PDesc);
622 PDesc.EditRate = Options.PictureRate();
624 if ( Options.verbose_flag )
626 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
627 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
628 JP2K::PictureDescriptorDump(PDesc);
632 if ( ! check_phfr_params(Options, PDesc) )
635 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
637 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
638 if ( Options.asset_id_flag )
639 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
641 Kumu::GenRandomUUID(Info.AssetUUID);
643 if ( Options.use_smpte_labels )
645 Info.LabelSetType = LS_MXF_SMPTE;
646 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
649 // configure encryption
650 if( Options.key_flag )
652 Kumu::GenRandomUUID(Info.ContextID);
653 Info.EncryptedEssence = true;
655 if ( Options.key_id_flag )
656 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
658 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
660 Context = new AESEncContext;
661 result = Context->InitKey(Options.key_value);
663 if ( ASDCP_SUCCESS(result) )
664 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
666 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
668 Info.UsesHMAC = true;
669 HMAC = new HMACContext;
670 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
674 if ( ASDCP_SUCCESS(result) )
675 result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
677 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
679 MXF::RGBAEssenceDescriptor *descriptor = 0;
680 Writer.OPAtomHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
681 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
682 descriptor->PictureEssenceCoding = Options.picture_coding;
686 if ( ASDCP_SUCCESS(result) )
689 result = ParserLeft.Reset();
690 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
692 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
694 result = ParserLeft.ReadFrame(FrameBuffer);
696 if ( ASDCP_SUCCESS(result) )
698 if ( Options.verbose_flag )
699 FrameBuffer.Dump(stderr, Options.fb_dump_size);
701 if ( Options.encrypt_header_flag )
702 FrameBuffer.PlaintextOffset(0);
705 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
706 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
708 if ( ASDCP_SUCCESS(result) )
709 result = ParserRight.ReadFrame(FrameBuffer);
711 if ( ASDCP_SUCCESS(result) )
713 if ( Options.verbose_flag )
714 FrameBuffer.Dump(stderr, Options.fb_dump_size);
716 if ( Options.encrypt_header_flag )
717 FrameBuffer.PlaintextOffset(0);
720 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
721 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
724 if ( result == RESULT_ENDOFFILE )
728 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
729 result = Writer.Finalize();
734 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
735 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
738 write_JP2K_file(CommandOptions& Options)
740 AESEncContext* Context = 0;
741 HMACContext* HMAC = 0;
742 JP2K::MXFWriter Writer;
743 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
744 JP2K::PictureDescriptor PDesc;
745 JP2K::SequenceParser Parser;
746 byte_t IV_buf[CBC_BLOCK_SIZE];
747 Kumu::FortunaRNG RNG;
749 // set up essence parser
750 Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
753 if ( ASDCP_SUCCESS(result) )
755 Parser.FillPictureDescriptor(PDesc);
756 PDesc.EditRate = Options.PictureRate();
758 if ( Options.verbose_flag )
760 fprintf(stderr, "JPEG 2000 pictures\n");
761 fputs("PictureDescriptor:\n", stderr);
762 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
763 JP2K::PictureDescriptorDump(PDesc);
767 if ( ! check_phfr_params(Options, PDesc) )
770 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
772 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
773 if ( Options.asset_id_flag )
774 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
776 Kumu::GenRandomUUID(Info.AssetUUID);
778 if ( Options.use_smpte_labels )
780 Info.LabelSetType = LS_MXF_SMPTE;
781 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
784 // configure encryption
785 if( Options.key_flag )
787 Kumu::GenRandomUUID(Info.ContextID);
788 Info.EncryptedEssence = true;
790 if ( Options.key_id_flag )
791 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
793 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
795 Context = new AESEncContext;
796 result = Context->InitKey(Options.key_value);
798 if ( ASDCP_SUCCESS(result) )
799 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
801 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
803 Info.UsesHMAC = true;
804 HMAC = new HMACContext;
805 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
809 if ( ASDCP_SUCCESS(result) )
810 result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
812 if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
814 MXF::RGBAEssenceDescriptor *descriptor = 0;
815 Writer.OPAtomHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
816 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
817 descriptor->PictureEssenceCoding = Options.picture_coding;
821 if ( ASDCP_SUCCESS(result) )
824 result = Parser.Reset();
826 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
830 result = Parser.ReadFrame(FrameBuffer);
832 if ( ASDCP_SUCCESS(result) )
834 if ( Options.verbose_flag )
835 FrameBuffer.Dump(stderr, Options.fb_dump_size);
837 if ( Options.encrypt_header_flag )
838 FrameBuffer.PlaintextOffset(0);
842 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
844 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
846 // The Writer class will forward the last block of ciphertext
847 // to the encryption context for use as the IV for the next
848 // frame. If you want to use non-sequitur IV values, un-comment
849 // the following line of code.
850 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
851 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
855 if ( result == RESULT_ENDOFFILE )
859 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
860 result = Writer.Finalize();
865 //------------------------------------------------------------------------------------------
869 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
870 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
873 write_PCM_file(CommandOptions& Options)
875 AESEncContext* Context = 0;
876 HMACContext* HMAC = 0;
877 PCMParserList Parser;
878 PCM::MXFWriter Writer;
879 PCM::FrameBuffer FrameBuffer;
880 PCM::AudioDescriptor ADesc;
881 Rational PictureRate = Options.PictureRate();
882 byte_t IV_buf[CBC_BLOCK_SIZE];
883 Kumu::FortunaRNG RNG;
885 // set up essence parser
886 Result_t result = Parser.OpenRead(Options.filenames, PictureRate);
889 if ( ASDCP_SUCCESS(result) )
891 Parser.FillAudioDescriptor(ADesc);
893 ADesc.EditRate = PictureRate;
894 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
895 ADesc.ChannelFormat = Options.channel_fmt;
897 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE)
899 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see option -l)\n");
902 if ( Options.verbose_flag )
904 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
905 ADesc.AudioSamplingRate.Quotient() / 1000.0,
906 Options.szPictureRate(),
907 PCM::CalcSamplesPerFrame(ADesc));
908 fputs("AudioDescriptor:\n", stderr);
909 PCM::AudioDescriptorDump(ADesc);
913 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
915 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
916 if ( Options.asset_id_flag )
917 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
919 Kumu::GenRandomUUID(Info.AssetUUID);
921 if ( Options.use_smpte_labels )
923 Info.LabelSetType = LS_MXF_SMPTE;
924 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
927 // configure encryption
928 if( Options.key_flag )
930 Kumu::GenRandomUUID(Info.ContextID);
931 Info.EncryptedEssence = true;
933 if ( Options.key_id_flag )
934 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
936 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
938 Context = new AESEncContext;
939 result = Context->InitKey(Options.key_value);
941 if ( ASDCP_SUCCESS(result) )
942 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
944 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
946 Info.UsesHMAC = true;
947 HMAC = new HMACContext;
948 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
952 if ( ASDCP_SUCCESS(result) )
953 result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
955 if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
957 MXF::WaveAudioDescriptor *descriptor = 0;
958 Writer.OPAtomHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
959 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
960 descriptor->ChannelAssignment = Options.channel_assignment;
964 if ( ASDCP_SUCCESS(result) )
966 result = Parser.Reset();
969 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
971 result = Parser.ReadFrame(FrameBuffer);
973 if ( ASDCP_SUCCESS(result) )
975 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
977 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
978 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
979 result = RESULT_ENDOFFILE;
983 if ( Options.verbose_flag )
984 FrameBuffer.Dump(stderr, Options.fb_dump_size);
986 if ( ! Options.no_write_flag )
988 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
990 // The Writer class will forward the last block of ciphertext
991 // to the encryption context for use as the IV for the next
992 // frame. If you want to use non-sequitur IV values, un-comment
993 // the following line of code.
994 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
995 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1000 if ( result == RESULT_ENDOFFILE )
1004 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1005 result = Writer.Finalize();
1011 //------------------------------------------------------------------------------------------
1012 // TimedText essence
1015 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1016 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1019 write_timed_text_file(CommandOptions& Options)
1021 AESEncContext* Context = 0;
1022 HMACContext* HMAC = 0;
1023 TimedText::DCSubtitleParser Parser;
1024 TimedText::MXFWriter Writer;
1025 TimedText::FrameBuffer FrameBuffer;
1026 TimedText::TimedTextDescriptor TDesc;
1027 byte_t IV_buf[CBC_BLOCK_SIZE];
1028 Kumu::FortunaRNG RNG;
1030 // set up essence parser
1031 Result_t result = Parser.OpenRead(Options.filenames.front().c_str());
1033 // set up MXF writer
1034 if ( ASDCP_SUCCESS(result) )
1036 Parser.FillTimedTextDescriptor(TDesc);
1037 FrameBuffer.Capacity(Options.fb_size);
1039 if ( Options.verbose_flag )
1041 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1042 TimedText::DescriptorDump(TDesc);
1046 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1048 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1049 if ( Options.asset_id_flag )
1050 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1052 Kumu::GenRandomUUID(Info.AssetUUID);
1054 if ( Options.use_smpte_labels )
1056 Info.LabelSetType = LS_MXF_SMPTE;
1057 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1060 // configure encryption
1061 if( Options.key_flag )
1063 Kumu::GenRandomUUID(Info.ContextID);
1064 Info.EncryptedEssence = true;
1066 if ( Options.key_id_flag )
1067 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1069 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1071 Context = new AESEncContext;
1072 result = Context->InitKey(Options.key_value);
1074 if ( ASDCP_SUCCESS(result) )
1075 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1077 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1079 Info.UsesHMAC = true;
1080 HMAC = new HMACContext;
1081 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1085 if ( ASDCP_SUCCESS(result) )
1086 result = Writer.OpenWrite(Options.out_file.c_str(), Info, TDesc);
1089 if ( ASDCP_FAILURE(result) )
1093 TimedText::ResourceList_t::const_iterator ri;
1095 result = Parser.ReadTimedTextResource(XMLDoc);
1097 if ( ASDCP_SUCCESS(result) )
1098 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1100 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1102 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1104 if ( ASDCP_SUCCESS(result) )
1106 if ( Options.verbose_flag )
1107 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1109 if ( ! Options.no_write_flag )
1111 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1113 // The Writer class will forward the last block of ciphertext
1114 // to the encryption context for use as the IV for the next
1115 // frame. If you want to use non-sequitur IV values, un-comment
1116 // the following line of code.
1117 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1118 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1122 if ( result == RESULT_ENDOFFILE )
1126 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1127 result = Writer.Finalize();
1134 main(int argc, const char** argv)
1136 Result_t result = RESULT_OK;
1138 CommandOptions Options(argc, argv);
1140 if ( Options.version_flag )
1143 if ( Options.help_flag )
1146 if ( Options.version_flag || Options.help_flag )
1149 if ( Options.error_flag )
1151 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1155 if ( Options.show_ul_values )
1157 if ( Options.use_smpte_labels )
1158 DefaultSMPTEDict().Dump(stdout);
1160 DefaultInteropDict().Dump(stdout);
1163 EssenceType_t EssenceType;
1164 result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
1166 if ( ASDCP_SUCCESS(result) )
1168 switch ( EssenceType )
1171 result = write_MPEG2_file(Options);
1175 if ( Options.stereo_image_flag )
1177 result = write_JP2K_S_file(Options);
1181 result = write_JP2K_file(Options);
1185 case ESS_PCM_24b_48k:
1186 case ESS_PCM_24b_96k:
1187 result = write_PCM_file(Options);
1190 case ESS_TIMED_TEXT:
1191 result = write_timed_text_file(Options);
1195 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1196 Options.filenames.front().c_str());
1201 if ( ASDCP_FAILURE(result) )
1203 fputs("Program stopped on error.\n", stderr);
1205 if ( result != RESULT_FAIL )
1207 fputs(result, stderr);
1208 fputc('\n', stderr);
1219 // end asdcp-wrap.cpp