2 Copyright (c) 2003-2008, 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-test.cpp
29 \brief AS-DCP file manipulation utility
31 This program provides command line access to the major features of the asdcplib
32 library, and serves as a library unit test which provides the functionality of
33 the supported use cases.
35 For more information about asdcplib, please refer to the header file AS_DCP.h
37 WARNING: While the asdcplib library attempts to provide a complete and secure
38 implementation of the cryptographic features of the AS-DCP file formats, this
39 unit test program is NOT secure and is therefore NOT SUITABLE FOR USE in a
40 production environment without some modification.
42 In particular, this program uses weak IV generation and externally generated
43 plaintext keys. These shortcomings exist because cryptographic-quality
44 random number generation and key management are outside the scope of the
45 asdcplib library. Developers using asdcplib for commercial implementations
46 claiming SMPTE conformance are expected to provide proper implementations of
50 #include <KM_fileio.h>
52 #include <PCMParserList.h>
53 #include <WavFileWriter.h>
56 #include <openssl/sha.h>
61 using namespace ASDCP;
63 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
65 //------------------------------------------------------------------------------------------
67 // command line option parser class
69 static const char* PROGRAM_NAME = "asdcp-test"; // program name for messages
70 const ui32_t MAX_IN_FILES = 16; // maximum number of input files handled by
71 // the command option parser
73 // local program identification info written to file headers
74 class MyInfo : public WriterInfo
79 static byte_t default_ProductUUID_Data[UUIDlen] =
80 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
81 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
83 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
84 CompanyName = "WidgetCo";
85 ProductName = "asdcp-test";
86 ProductVersion = ASDCP::Version();
92 // Increment the iterator, test for an additional non-option command line argument.
93 // Causes the caller to return if there are no remaining arguments or if the next
94 // argument begins with '-'.
95 #define TEST_EXTRA_ARG(i,c) if ( ++i >= argc || argv[(i)][0] == '-' ) \
97 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
102 banner(FILE* stream = stdout)
105 %s (asdcplib %s)\n\n\
106 Copyright (c) 2003-2008 John Hurst\n\n\
107 asdcplib may be copied only under the terms of the license found at\n\
108 the top of every file in the asdcplib distribution kit.\n\n\
109 Specify the -h (help) option for further information about %s\n\n",
110 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
115 usage(FILE* stream = stdout)
118 USAGE: %s -c <output-file> [-3] [-b <buffer-size>] [-d <duration>] [-e|-E]\n\
119 [-f <start-frame>] [-j <key-id-string>] [-k <key-string>] [-L] [-M]\n\
120 [-p <frame-rate>] [-R] [-s <num>] [-v] [-W]\n\
121 <input-file> [<input-file-2> ...]\n\
123 %s [-h|-help] [-V]\n\
125 %s -i [-H] [-n] [-v] <input-file>\n\
129 %s -G [-v] <input-file>\n\
131 %s -t <input-file>\n\
133 %s -x <file-prefix> [-3] [-b <buffer-size>] [-d <duration>]\n\
134 [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <num>] [-S|-1]\n\
135 [-v] [-W] [-w] <input-file>\n\
136 \n", PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
140 -3 - With -c, create a stereoscopic image file. Expects two\n\
141 directories of JP2K codestreams (directories must have\n\
142 an equal number of frames; left eye is first).\n\
143 - With -x, force stereoscopic interpretation of a JP2K\n\
145 -c <output-file> - Create an AS-DCP track file from input(s)\n\
146 -g - Generate a random 16 byte value to stdout\n\
147 -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\
148 -h | -help - Show help\n\
149 -i - Show file info\n\
150 -t - Calculate message digest of input file\n\
151 -U - Dump UL catalog to stdout\n\
152 -u - Generate a random UUID value to stdout\n\
153 -V - Show version information\n\
154 -x <root-name> - Extract essence from AS-DCP file to named file(s)\n\
159 -e - Encrypt MPEG or JP2K headers (default)\n\
160 -E - Do not encrypt MPEG or JP2K headers\n\
161 -j <key-id-str> - Write key ID instead of creating a random value\n\
162 -k <key-string> - Use key for ciphertext operations\n\
163 -m - verify HMAC values when reading\n\
164 -M - Do not create HMAC values when writing\n\
168 Read/Write Options:\n\
169 -b <buffer-size> - Specify size in bytes of picture frame buffer.\n\
170 Defaults to 4,194,304 (4MB)\n\
171 -d <duration> - Number of frames to process, default all\n\
172 -f <start-frame> - Starting frame number, default 0\n\
173 -L - Write SMPTE UL values instead of MXF Interop\n\
174 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
175 Use one of [23|24|48], 24 is default\n\
176 -R - Repeat the first frame over the entire file (picture\n\
177 essence only, requires -c, -d)\n\
178 -S - Split Wave essence to stereo WAV files during extract.\n\
179 Default is multichannel WAV\n\
180 -1 - Split Wave essence to mono WAV files during extract.\n\
181 Default is multichannel WAV\n\
182 -W - Read input file only, do not write source file\n\
183 -w <width> - Width of numeric element in a series of frame file names\n\
184 (use with -x, default 6).\n\
189 -H - Show MXF header metadata, used with option -i\n\
190 -n - Show index, used with option -i\n\
193 -s <num> - Number of bytes of frame buffer to be dumped as hex to\n\
194 stderr, used with option -v\n\
195 -v - Verbose, prints informative messages to stderr\n\
197 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
198 o All option arguments must be separated from the option by whitespace.\n\
199 o An argument of \"23\" to the -p option will be interpreted\n\
200 as 23000/1001 fps.\n\
227 bool error_flag; // true if the given options are in error or not complete
228 bool key_flag; // true if an encryption key was given
229 bool key_id_flag; // true if a key ID was given
230 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
231 bool write_hmac; // true if HMAC values are to be generated and written
232 bool read_hmac; // true if HMAC values are to be validated
233 bool split_wav; // true if PCM is to be extracted to stereo WAV files
234 bool mono_wav; // true if PCM is to be extracted to mono WAV files
235 bool verbose_flag; // true if the verbose option was selected
236 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
237 bool showindex_flag; // true if index is to be displayed
238 bool showheader_flag; // true if MXF file header is to be displayed
239 bool no_write_flag; // true if no output files are to be written
240 bool version_flag; // true if the version display option was selected
241 bool help_flag; // true if the help display option was selected
242 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
243 ui32_t number_width; // number of digits in a serialized filename (for JPEG extract)
244 ui32_t start_frame; // frame number to begin processing
245 ui32_t duration; // number of frames to be processed
246 bool duration_flag; // true if duration argument given
247 bool do_repeat; // if true and -c -d, repeat first input frame
248 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
249 ui32_t picture_rate; // fps of picture when wrapping PCM
250 ui32_t fb_size; // size of picture frame buffer
251 ui32_t file_count; // number of elements in filenames[]
252 const char* file_root; // filename pre for files written by the extract mode
253 const char* out_file; // name of mxf file created by create mode
254 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
255 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
256 const char* filenames[MAX_IN_FILES]; // list of filenames to be processed
259 Rational PictureRate()
261 if ( picture_rate == 23 ) return EditRate_23_98;
262 if ( picture_rate == 48 ) return EditRate_48;
267 const char* szPictureRate()
269 if ( picture_rate == 23 ) return "23.976";
270 if ( picture_rate == 48 ) return "48";
275 CommandOptions(int argc, const char** argv) :
276 mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), encrypt_header_flag(true),
277 write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false),
278 verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false),
279 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
280 number_width(6), start_frame(0),
281 duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false),
282 picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0)
284 memset(key_value, 0, KeyLen);
285 memset(key_id_value, 0, UUIDlen);
287 for ( int i = 1; i < argc; i++ )
290 if ( (strcmp( argv[i], "-help") == 0) )
296 if ( argv[i][0] == '-'
297 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
300 switch ( argv[i][1] )
302 case '1': mono_wav = true; break;
303 case '2': split_wav = true; break;
304 case '3': stereo_image_flag = true; break;
307 TEST_EXTRA_ARG(i, 'b');
308 fb_size = abs(atoi(argv[i]));
311 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
316 TEST_EXTRA_ARG(i, 'c');
322 TEST_EXTRA_ARG(i, 'd');
323 duration_flag = true;
324 duration = abs(atoi(argv[i]));
327 case 'E': encrypt_header_flag = false; break;
328 case 'e': encrypt_header_flag = true; break;
331 TEST_EXTRA_ARG(i, 'f');
332 start_frame = abs(atoi(argv[i]));
335 case 'G': mode = MMT_GOP_START; break;
336 case 'g': mode = MMT_GEN_KEY; break;
337 case 'H': showheader_flag = true; break;
338 case 'h': help_flag = true; break;
339 case 'i': mode = MMT_INFO; 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 case 'L': use_smpte_labels = true; break;
371 case 'M': write_hmac = false; break;
372 case 'm': read_hmac = true; break;
373 case 'n': showindex_flag = true; break;
376 TEST_EXTRA_ARG(i, 'p');
377 picture_rate = abs(atoi(argv[i]));
380 case 'R': do_repeat = true; break;
381 case 'S': split_wav = true; break;
384 TEST_EXTRA_ARG(i, 's');
385 fb_dump_size = abs(atoi(argv[i]));
388 case 't': mode = MMT_DIGEST; break;
389 case 'U': mode = MMT_UL_LIST; break;
390 case 'u': mode = MMT_GEN_ID; break;
391 case 'V': version_flag = true; break;
392 case 'v': verbose_flag = true; break;
393 case 'W': no_write_flag = true; break;
396 TEST_EXTRA_ARG(i, 'w');
397 number_width = abs(atoi(argv[i]));
401 TEST_EXTRA_ARG(i, 'x');
407 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
414 if ( argv[i][0] != '-' )
416 filenames[file_count++] = argv[i];
420 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
424 if ( file_count >= MAX_IN_FILES )
426 fprintf(stderr, "Filename lists exceeds maximum list size: %u\n", MAX_IN_FILES);
432 if ( help_flag || version_flag )
435 if ( ( mode == MMT_INFO
436 || mode == MMT_CREATE
437 || mode == MMT_EXTRACT
438 || mode == MMT_GOP_START
439 || mode == MMT_DIGEST ) && file_count == 0 )
441 fputs("Option requires at least one filename argument.\n", stderr);
445 if ( mode == MMT_NONE && ! help_flag && ! version_flag )
447 fputs("No operation selected (use one of -[gGcitux] or -h for help).\n", stderr);
455 //------------------------------------------------------------------------------------------
458 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
459 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
462 write_MPEG2_file(CommandOptions& Options)
464 AESEncContext* Context = 0;
465 HMACContext* HMAC = 0;
466 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
467 MPEG2::Parser Parser;
468 MPEG2::MXFWriter Writer;
469 MPEG2::VideoDescriptor VDesc;
470 byte_t IV_buf[CBC_BLOCK_SIZE];
471 Kumu::FortunaRNG RNG;
473 // set up essence parser
474 Result_t result = Parser.OpenRead(Options.filenames[0]);
477 if ( ASDCP_SUCCESS(result) )
479 Parser.FillVideoDescriptor(VDesc);
481 if ( Options.verbose_flag )
483 fputs("MPEG-2 Pictures\n", stderr);
484 fputs("VideoDescriptor:\n", stderr);
485 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
486 MPEG2::VideoDescriptorDump(VDesc);
490 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
492 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
493 Kumu::GenRandomUUID(Info.AssetUUID);
495 if ( Options.use_smpte_labels )
497 Info.LabelSetType = LS_MXF_SMPTE;
498 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
501 // configure encryption
502 if( Options.key_flag )
504 Kumu::GenRandomUUID(Info.ContextID);
505 Info.EncryptedEssence = true;
507 if ( Options.key_id_flag )
508 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
510 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
512 Context = new AESEncContext;
513 result = Context->InitKey(Options.key_value);
515 if ( ASDCP_SUCCESS(result) )
516 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
518 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
520 Info.UsesHMAC = true;
521 HMAC = new HMACContext;
522 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
526 if ( ASDCP_SUCCESS(result) )
527 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
530 if ( ASDCP_SUCCESS(result) )
531 // loop through the frames
533 result = Parser.Reset();
536 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
538 if ( ! Options.do_repeat || duration == 1 )
540 result = Parser.ReadFrame(FrameBuffer);
542 if ( ASDCP_SUCCESS(result) )
544 if ( Options.verbose_flag )
545 FrameBuffer.Dump(stderr, Options.fb_dump_size);
547 if ( Options.encrypt_header_flag )
548 FrameBuffer.PlaintextOffset(0);
552 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
554 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
556 // The Writer class will forward the last block of ciphertext
557 // to the encryption context for use as the IV for the next
558 // frame. If you want to use non-sequitur IV values, un-comment
559 // the following line of code.
560 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
561 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
565 if ( result == RESULT_ENDOFFILE )
569 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
570 result = Writer.Finalize();
575 // Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file
576 // Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
577 // Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
580 read_MPEG2_file(CommandOptions& Options)
582 AESDecContext* Context = 0;
583 HMACContext* HMAC = 0;
584 MPEG2::MXFReader Reader;
585 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
586 Kumu::FileWriter OutFile;
587 ui32_t frame_count = 0;
589 Result_t result = Reader.OpenRead(Options.filenames[0]);
591 if ( ASDCP_SUCCESS(result) )
593 MPEG2::VideoDescriptor VDesc;
594 Reader.FillVideoDescriptor(VDesc);
595 frame_count = VDesc.ContainerDuration;
597 if ( Options.verbose_flag )
599 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
600 MPEG2::VideoDescriptorDump(VDesc);
604 if ( ASDCP_SUCCESS(result) )
607 snprintf(filename, 256, "%s.ves", Options.file_root);
608 result = OutFile.OpenWrite(filename);
611 if ( ASDCP_SUCCESS(result) && Options.key_flag )
613 Context = new AESDecContext;
614 result = Context->InitKey(Options.key_value);
616 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
619 Reader.FillWriterInfo(Info);
623 HMAC = new HMACContext;
624 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
628 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
633 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
634 if ( last_frame > frame_count )
635 last_frame = frame_count;
637 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
639 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
641 if ( ASDCP_SUCCESS(result) )
643 if ( Options.verbose_flag )
644 FrameBuffer.Dump(stderr, Options.fb_dump_size);
646 ui32_t write_count = 0;
647 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
657 gop_start_test(CommandOptions& Options)
659 using namespace ASDCP::MPEG2;
662 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
663 ui32_t frame_count = 0;
665 Result_t result = Reader.OpenRead(Options.filenames[0]);
667 if ( ASDCP_SUCCESS(result) )
669 MPEG2::VideoDescriptor VDesc;
670 Reader.FillVideoDescriptor(VDesc);
671 frame_count = VDesc.ContainerDuration;
673 if ( Options.verbose_flag )
675 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
676 MPEG2::VideoDescriptorDump(VDesc);
680 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
681 if ( last_frame > frame_count )
682 last_frame = frame_count;
684 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
686 result = Reader.ReadFrameGOPStart(i, FrameBuffer);
688 if ( ASDCP_SUCCESS(result) )
690 if ( Options.verbose_flag )
691 FrameBuffer.Dump(stderr, Options.fb_dump_size);
693 if ( FrameBuffer.FrameType() != FRAME_I )
694 fprintf(stderr, "Expecting an I frame, got %c\n", FrameTypeChar(FrameBuffer.FrameType()));
696 fprintf(stderr, "Requested frame %u, got %u\n", i, FrameBuffer.FrameNumber());
703 //------------------------------------------------------------------------------------------
706 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
707 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
710 write_JP2K_S_file(CommandOptions& Options)
712 AESEncContext* Context = 0;
713 HMACContext* HMAC = 0;
714 JP2K::MXFSWriter Writer;
715 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
716 JP2K::PictureDescriptor PDesc;
717 JP2K::SequenceParser ParserLeft, ParserRight;
718 byte_t IV_buf[CBC_BLOCK_SIZE];
719 Kumu::FortunaRNG RNG;
721 if ( Options.file_count != 2 )
723 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
727 // set up essence parser
728 Result_t result = ParserLeft.OpenRead(Options.filenames[0]);
730 if ( ASDCP_SUCCESS(result) )
731 result = ParserRight.OpenRead(Options.filenames[1]);
734 if ( ASDCP_SUCCESS(result) )
736 ParserLeft.FillPictureDescriptor(PDesc);
737 PDesc.EditRate = Options.PictureRate();
739 if ( Options.verbose_flag )
741 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
742 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
743 JP2K::PictureDescriptorDump(PDesc);
747 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
749 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
750 Kumu::GenRandomUUID(Info.AssetUUID);
752 if ( Options.use_smpte_labels )
754 Info.LabelSetType = LS_MXF_SMPTE;
755 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
758 // configure encryption
759 if( Options.key_flag )
761 Kumu::GenRandomUUID(Info.ContextID);
762 Info.EncryptedEssence = true;
764 if ( Options.key_id_flag )
765 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
767 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
769 Context = new AESEncContext;
770 result = Context->InitKey(Options.key_value);
772 if ( ASDCP_SUCCESS(result) )
773 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
775 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
777 Info.UsesHMAC = true;
778 HMAC = new HMACContext;
779 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
783 if ( ASDCP_SUCCESS(result) )
784 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
787 if ( ASDCP_SUCCESS(result) )
790 result = ParserLeft.Reset();
791 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
793 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
795 result = ParserLeft.ReadFrame(FrameBuffer);
797 if ( ASDCP_SUCCESS(result) )
799 if ( Options.verbose_flag )
800 FrameBuffer.Dump(stderr, Options.fb_dump_size);
802 if ( Options.encrypt_header_flag )
803 FrameBuffer.PlaintextOffset(0);
806 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
807 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
809 if ( ASDCP_SUCCESS(result) )
810 result = ParserRight.ReadFrame(FrameBuffer);
812 if ( ASDCP_SUCCESS(result) )
814 if ( Options.verbose_flag )
815 FrameBuffer.Dump(stderr, Options.fb_dump_size);
817 if ( Options.encrypt_header_flag )
818 FrameBuffer.PlaintextOffset(0);
821 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
822 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
825 if ( result == RESULT_ENDOFFILE )
829 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
830 result = Writer.Finalize();
835 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a plaintext ASDCP file
836 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
837 // Read one or more ciphertext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
839 read_JP2K_S_file(CommandOptions& Options)
841 AESDecContext* Context = 0;
842 HMACContext* HMAC = 0;
843 JP2K::MXFSReader Reader;
844 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
845 ui32_t frame_count = 0;
847 Result_t result = Reader.OpenRead(Options.filenames[0]);
849 if ( ASDCP_SUCCESS(result) )
851 JP2K::PictureDescriptor PDesc;
852 Reader.FillPictureDescriptor(PDesc);
854 frame_count = PDesc.ContainerDuration;
856 if ( Options.verbose_flag )
858 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
859 JP2K::PictureDescriptorDump(PDesc);
863 if ( ASDCP_SUCCESS(result) && Options.key_flag )
865 Context = new AESDecContext;
866 result = Context->InitKey(Options.key_value);
868 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
871 Reader.FillWriterInfo(Info);
875 HMAC = new HMACContext;
876 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
880 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
885 const int filename_max = 1024;
886 char filename[filename_max];
887 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
888 if ( last_frame > frame_count )
889 last_frame = frame_count;
891 char left_format[64]; char right_format[64];
892 snprintf(left_format, 64, "%%s%%0%duL.j2c", Options.number_width);
893 snprintf(right_format, 64, "%%s%%0%duR.j2c", Options.number_width);
895 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
897 result = Reader.ReadFrame(i, JP2K::SP_LEFT, FrameBuffer, Context, HMAC);
899 if ( ASDCP_SUCCESS(result) )
901 Kumu::FileWriter OutFile;
903 snprintf(filename, filename_max, left_format, Options.file_root, i);
904 result = OutFile.OpenWrite(filename);
906 if ( ASDCP_SUCCESS(result) )
907 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
909 if ( Options.verbose_flag )
910 FrameBuffer.Dump(stderr, Options.fb_dump_size);
913 if ( ASDCP_SUCCESS(result) )
914 result = Reader.ReadFrame(i, JP2K::SP_RIGHT, FrameBuffer, Context, HMAC);
916 if ( ASDCP_SUCCESS(result) )
918 Kumu::FileWriter OutFile;
920 snprintf(filename, filename_max, right_format, Options.file_root, i);
921 result = OutFile.OpenWrite(filename);
923 if ( ASDCP_SUCCESS(result) )
924 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
933 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
934 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
937 write_JP2K_file(CommandOptions& Options)
939 AESEncContext* Context = 0;
940 HMACContext* HMAC = 0;
941 JP2K::MXFWriter Writer;
942 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
943 JP2K::PictureDescriptor PDesc;
944 JP2K::SequenceParser Parser;
945 byte_t IV_buf[CBC_BLOCK_SIZE];
946 Kumu::FortunaRNG RNG;
948 // set up essence parser
949 Result_t result = Parser.OpenRead(Options.filenames[0]);
952 if ( ASDCP_SUCCESS(result) )
954 Parser.FillPictureDescriptor(PDesc);
955 PDesc.EditRate = Options.PictureRate();
957 if ( Options.verbose_flag )
959 fprintf(stderr, "JPEG 2000 pictures\n");
960 fputs("PictureDescriptor:\n", stderr);
961 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
962 JP2K::PictureDescriptorDump(PDesc);
966 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
968 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
969 Kumu::GenRandomUUID(Info.AssetUUID);
971 if ( Options.use_smpte_labels )
973 Info.LabelSetType = LS_MXF_SMPTE;
974 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
977 // configure encryption
978 if( Options.key_flag )
980 Kumu::GenRandomUUID(Info.ContextID);
981 Info.EncryptedEssence = true;
983 if ( Options.key_id_flag )
984 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
986 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
988 Context = new AESEncContext;
989 result = Context->InitKey(Options.key_value);
991 if ( ASDCP_SUCCESS(result) )
992 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
994 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
996 Info.UsesHMAC = true;
997 HMAC = new HMACContext;
998 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1002 if ( ASDCP_SUCCESS(result) )
1003 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
1006 if ( ASDCP_SUCCESS(result) )
1008 ui32_t duration = 0;
1009 result = Parser.Reset();
1011 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1013 if ( ! Options.do_repeat || duration == 1 )
1015 result = Parser.ReadFrame(FrameBuffer);
1017 if ( ASDCP_SUCCESS(result) )
1019 if ( Options.verbose_flag )
1020 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1022 if ( Options.encrypt_header_flag )
1023 FrameBuffer.PlaintextOffset(0);
1027 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1029 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1031 // The Writer class will forward the last block of ciphertext
1032 // to the encryption context for use as the IV for the next
1033 // frame. If you want to use non-sequitur IV values, un-comment
1034 // the following line of code.
1035 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1036 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1040 if ( result == RESULT_ENDOFFILE )
1044 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1045 result = Writer.Finalize();
1050 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
1051 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
1052 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
1055 read_JP2K_file(CommandOptions& Options)
1057 AESDecContext* Context = 0;
1058 HMACContext* HMAC = 0;
1059 JP2K::MXFReader Reader;
1060 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
1061 ui32_t frame_count = 0;
1063 Result_t result = Reader.OpenRead(Options.filenames[0]);
1065 if ( ASDCP_SUCCESS(result) )
1067 JP2K::PictureDescriptor PDesc;
1068 Reader.FillPictureDescriptor(PDesc);
1070 frame_count = PDesc.ContainerDuration;
1072 if ( Options.verbose_flag )
1074 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1075 JP2K::PictureDescriptorDump(PDesc);
1079 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1081 Context = new AESDecContext;
1082 result = Context->InitKey(Options.key_value);
1084 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1087 Reader.FillWriterInfo(Info);
1089 if ( Info.UsesHMAC )
1091 HMAC = new HMACContext;
1092 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1096 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1101 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
1102 if ( last_frame > frame_count )
1103 last_frame = frame_count;
1105 char name_format[64];
1106 snprintf(name_format, 64, "%%s%%0%du.j2c", Options.number_width);
1108 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1110 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1112 if ( ASDCP_SUCCESS(result) )
1114 Kumu::FileWriter OutFile;
1117 snprintf(filename, 256, name_format, Options.file_root, i);
1118 result = OutFile.OpenWrite(filename);
1120 if ( ASDCP_SUCCESS(result) )
1121 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
1123 if ( Options.verbose_flag )
1124 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1131 //------------------------------------------------------------------------------------------
1135 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
1136 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
1139 write_PCM_file(CommandOptions& Options)
1141 AESEncContext* Context = 0;
1142 HMACContext* HMAC = 0;
1143 PCMParserList Parser;
1144 PCM::MXFWriter Writer;
1145 PCM::FrameBuffer FrameBuffer;
1146 PCM::AudioDescriptor ADesc;
1147 Rational PictureRate = Options.PictureRate();
1148 byte_t IV_buf[CBC_BLOCK_SIZE];
1149 Kumu::FortunaRNG RNG;
1151 // set up essence parser
1152 Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate);
1154 // set up MXF writer
1155 if ( ASDCP_SUCCESS(result) )
1157 Parser.FillAudioDescriptor(ADesc);
1159 ADesc.SampleRate = PictureRate;
1160 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1162 if ( Options.verbose_flag )
1164 fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n",
1165 Options.szPictureRate(),
1166 PCM::CalcSamplesPerFrame(ADesc));
1167 fputs("AudioDescriptor:\n", stderr);
1168 PCM::AudioDescriptorDump(ADesc);
1172 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1174 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1175 Kumu::GenRandomUUID(Info.AssetUUID);
1177 if ( Options.use_smpte_labels )
1179 Info.LabelSetType = LS_MXF_SMPTE;
1180 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1183 // configure encryption
1184 if( Options.key_flag )
1186 Kumu::GenRandomUUID(Info.ContextID);
1187 Info.EncryptedEssence = true;
1189 if ( Options.key_id_flag )
1190 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1192 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1194 Context = new AESEncContext;
1195 result = Context->InitKey(Options.key_value);
1197 if ( ASDCP_SUCCESS(result) )
1198 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1200 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1202 Info.UsesHMAC = true;
1203 HMAC = new HMACContext;
1204 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1208 if ( ASDCP_SUCCESS(result) )
1209 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1212 if ( ASDCP_SUCCESS(result) )
1214 result = Parser.Reset();
1215 ui32_t duration = 0;
1217 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1219 result = Parser.ReadFrame(FrameBuffer);
1221 if ( ASDCP_SUCCESS(result) )
1223 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1225 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1226 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1227 result = RESULT_ENDOFFILE;
1231 if ( Options.verbose_flag )
1232 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1234 if ( ! Options.no_write_flag )
1236 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1238 // The Writer class will forward the last block of ciphertext
1239 // to the encryption context for use as the IV for the next
1240 // frame. If you want to use non-sequitur IV values, un-comment
1241 // the following line of code.
1242 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1243 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1248 if ( result == RESULT_ENDOFFILE )
1252 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1253 result = Writer.Finalize();
1258 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
1259 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
1260 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
1263 read_PCM_file(CommandOptions& Options)
1265 AESDecContext* Context = 0;
1266 HMACContext* HMAC = 0;
1267 PCM::MXFReader Reader;
1268 PCM::FrameBuffer FrameBuffer;
1269 WavFileWriter OutWave;
1270 PCM::AudioDescriptor ADesc;
1271 ui32_t last_frame = 0;
1273 Result_t result = Reader.OpenRead(Options.filenames[0]);
1275 if ( ASDCP_SUCCESS(result) )
1277 Reader.FillAudioDescriptor(ADesc);
1279 if ( ADesc.SampleRate != EditRate_23_98
1280 && ADesc.SampleRate != EditRate_24
1281 && ADesc.SampleRate != EditRate_48 )
1282 ADesc.SampleRate = Options.PictureRate();
1284 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1286 if ( Options.verbose_flag )
1287 PCM::AudioDescriptorDump(ADesc);
1290 if ( ASDCP_SUCCESS(result) )
1292 last_frame = ADesc.ContainerDuration;
1294 if ( Options.duration > 0 && Options.duration < last_frame )
1295 last_frame = Options.duration;
1297 if ( Options.start_frame > 0 )
1299 if ( Options.start_frame > ADesc.ContainerDuration )
1301 fprintf(stderr, "Start value greater than file duration.\n");
1305 last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
1308 ADesc.ContainerDuration = last_frame - Options.start_frame;
1309 OutWave.OpenWrite(ADesc, Options.file_root,
1310 ( Options.split_wav ? WavFileWriter::ST_STEREO :
1311 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
1314 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1316 Context = new AESDecContext;
1317 result = Context->InitKey(Options.key_value);
1319 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1322 Reader.FillWriterInfo(Info);
1324 if ( Info.UsesHMAC )
1326 HMAC = new HMACContext;
1327 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1331 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1336 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1338 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1340 if ( ASDCP_SUCCESS(result) )
1342 if ( Options.verbose_flag )
1343 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1345 result = OutWave.WriteFrame(FrameBuffer);
1353 //------------------------------------------------------------------------------------------
1354 // TimedText essence
1357 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1358 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1361 write_timed_text_file(CommandOptions& Options)
1363 AESEncContext* Context = 0;
1364 HMACContext* HMAC = 0;
1365 TimedText::DCSubtitleParser Parser;
1366 TimedText::MXFWriter Writer;
1367 TimedText::FrameBuffer FrameBuffer;
1368 TimedText::TimedTextDescriptor TDesc;
1369 byte_t IV_buf[CBC_BLOCK_SIZE];
1370 Kumu::FortunaRNG RNG;
1372 // set up essence parser
1373 Result_t result = Parser.OpenRead(Options.filenames[0]);
1375 // set up MXF writer
1376 if ( ASDCP_SUCCESS(result) )
1378 Parser.FillDescriptor(TDesc);
1379 FrameBuffer.Capacity(2*Kumu::Megabyte);
1381 if ( Options.verbose_flag )
1383 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1384 TimedText::DescriptorDump(TDesc);
1388 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1390 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1391 Kumu::GenRandomUUID(Info.AssetUUID);
1393 if ( Options.use_smpte_labels )
1395 Info.LabelSetType = LS_MXF_SMPTE;
1396 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1399 // configure encryption
1400 if( Options.key_flag )
1402 Kumu::GenRandomUUID(Info.ContextID);
1403 Info.EncryptedEssence = true;
1405 if ( Options.key_id_flag )
1406 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1408 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1410 Context = new AESEncContext;
1411 result = Context->InitKey(Options.key_value);
1413 if ( ASDCP_SUCCESS(result) )
1414 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1416 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1418 Info.UsesHMAC = true;
1419 HMAC = new HMACContext;
1420 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1424 if ( ASDCP_SUCCESS(result) )
1425 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1428 if ( ASDCP_FAILURE(result) )
1432 TimedText::ResourceList_t::const_iterator ri;
1434 result = Parser.ReadTimedTextResource(XMLDoc);
1436 if ( ASDCP_SUCCESS(result) )
1437 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1439 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1441 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1443 if ( ASDCP_SUCCESS(result) )
1445 if ( Options.verbose_flag )
1446 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1448 if ( ! Options.no_write_flag )
1450 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1452 // The Writer class will forward the last block of ciphertext
1453 // to the encryption context for use as the IV for the next
1454 // frame. If you want to use non-sequitur IV values, un-comment
1455 // the following line of code.
1456 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1457 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1461 if ( result == RESULT_ENDOFFILE )
1465 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1466 result = Writer.Finalize();
1472 // Read one or more timed text streams from a plaintext ASDCP file
1473 // Read one or more timed text streams from a ciphertext ASDCP file
1474 // Read one or more timed text streams from a ciphertext ASDCP file
1477 read_timed_text_file(CommandOptions& Options)
1479 AESDecContext* Context = 0;
1480 HMACContext* HMAC = 0;
1481 TimedText::MXFReader Reader;
1482 TimedText::FrameBuffer FrameBuffer;
1483 TimedText::TimedTextDescriptor TDesc;
1485 Result_t result = Reader.OpenRead(Options.filenames[0]);
1487 if ( ASDCP_SUCCESS(result) )
1489 Reader.FillDescriptor(TDesc);
1490 FrameBuffer.Capacity(2*Kumu::Megabyte);
1492 if ( Options.verbose_flag )
1493 TimedText::DescriptorDump(TDesc);
1496 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1498 Context = new AESDecContext;
1499 result = Context->InitKey(Options.key_value);
1501 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1504 Reader.FillWriterInfo(Info);
1506 if ( Info.UsesHMAC )
1508 HMAC = new HMACContext;
1509 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1513 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1518 if ( ASDCP_FAILURE(result) )
1522 TimedText::ResourceList_t::const_iterator ri;
1524 result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
1526 // do something with the XML here
1527 fprintf(stderr, "XMLDoc size: %lu\n", XMLDoc.size());
1529 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1531 result = Reader.ReadAncillaryResource((*ri).ResourceID, FrameBuffer, Context, HMAC);
1533 if ( ASDCP_SUCCESS(result) )
1535 // if ( Options.verbose_flag )
1536 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1538 // do something with the resource data here
1545 //------------------------------------------------------------------------------------------
1549 // These classes wrap the irregular names in the asdcplib API
1550 // so that I can use a template to simplify the implementation
1551 // of show_file_info()
1553 class MyVideoDescriptor : public MPEG2::VideoDescriptor
1556 void FillDescriptor(MPEG2::MXFReader& Reader) {
1557 Reader.FillVideoDescriptor(*this);
1560 void Dump(FILE* stream) {
1561 MPEG2::VideoDescriptorDump(*this, stream);
1565 class MyPictureDescriptor : public JP2K::PictureDescriptor
1568 void FillDescriptor(JP2K::MXFReader& Reader) {
1569 Reader.FillPictureDescriptor(*this);
1572 void Dump(FILE* stream) {
1573 JP2K::PictureDescriptorDump(*this, stream);
1577 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
1580 void FillDescriptor(JP2K::MXFSReader& Reader) {
1581 Reader.FillPictureDescriptor(*this);
1584 void Dump(FILE* stream) {
1585 JP2K::PictureDescriptorDump(*this, stream);
1589 class MyAudioDescriptor : public PCM::AudioDescriptor
1592 void FillDescriptor(PCM::MXFReader& Reader) {
1593 Reader.FillAudioDescriptor(*this);
1596 void Dump(FILE* stream) {
1597 PCM::AudioDescriptorDump(*this, stream);
1601 class MyTextDescriptor : public TimedText::TimedTextDescriptor
1604 void FillDescriptor(TimedText::MXFReader& Reader) {
1605 Reader.FillDescriptor(*this);
1608 void Dump(FILE* stream) {
1609 TimedText::DescriptorDump(*this, stream);
1613 // MSVC didn't like the function template, so now it's a static class method
1614 template<class ReaderT, class DescriptorT>
1615 class FileInfoWrapper
1619 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
1621 assert(type_string);
1625 Result_t result = RESULT_OK;
1627 if ( Options.verbose_flag || Options.showheader_flag )
1630 result = Reader.OpenRead(Options.filenames[0]);
1632 if ( ASDCP_SUCCESS(result) )
1634 fprintf(stdout, "File essence type is %s.\n", type_string);
1636 if ( Options.showheader_flag )
1637 Reader.DumpHeaderMetadata(stream);
1640 Reader.FillWriterInfo(WI);
1641 WriterInfoDump(WI, stream);
1644 Desc.FillDescriptor(Reader);
1647 if ( Options.showindex_flag )
1648 Reader.DumpIndex(stream);
1650 else if ( result == RESULT_FORMAT && Options.showheader_flag )
1652 Reader.DumpHeaderMetadata(stream);
1660 // Read header metadata from an ASDCP file
1663 show_file_info(CommandOptions& Options)
1665 EssenceType_t EssenceType;
1666 Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1668 if ( ASDCP_FAILURE(result) )
1671 if ( EssenceType == ESS_MPEG2_VES )
1672 result = FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options, "MPEG2 video");
1674 else if ( EssenceType == ESS_PCM_24b_48k )
1675 result = FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options, "PCM audio");
1677 else if ( EssenceType == ESS_JPEG_2000 )
1679 if ( Options.stereo_image_flag )
1680 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1681 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1684 result = FileInfoWrapper<ASDCP::JP2K::MXFReader,
1685 MyPictureDescriptor>::file_info(Options, "JPEG 2000 pictures");
1687 else if ( EssenceType == ESS_JPEG_2000_S )
1688 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1689 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1691 else if ( EssenceType == ESS_TIMED_TEXT )
1692 result = FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options, "Timed Text");
1696 fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]);
1697 Kumu::FileReader Reader;
1698 MXF::OPAtomHeader TestHeader;
1700 result = Reader.OpenRead(Options.filenames[0]);
1702 if ( ASDCP_SUCCESS(result) )
1703 result = TestHeader.InitFromFile(Reader); // test UL and OP
1705 if ( ASDCP_SUCCESS(result) )
1707 TestHeader.Partition::Dump();
1709 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1712 fputs("File contains no Identification object.\n", stdout);
1714 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1717 fputs("File contains no SourcePackage object.\n", stdout);
1721 fputs("File is not MXF.\n", stdout);
1731 digest_file(const char* filename)
1733 using namespace Kumu;
1735 ASDCP_TEST_NULL_STR(filename);
1739 ByteString Buf(8192);
1741 Result_t result = Reader.OpenRead(filename);
1743 while ( ASDCP_SUCCESS(result) )
1745 ui32_t read_count = 0;
1746 result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count);
1748 if ( result == RESULT_ENDOFFILE )
1754 if ( ASDCP_SUCCESS(result) )
1755 SHA1_Update(&Ctx, Buf.Data(), read_count);
1758 if ( ASDCP_SUCCESS(result) )
1760 const ui32_t sha_len = 20;
1761 byte_t bin_buf[sha_len];
1763 SHA1_Final(bin_buf, &Ctx);
1765 fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename);
1773 main(int argc, const char** argv)
1775 Result_t result = RESULT_OK;
1777 CommandOptions Options(argc, argv);
1779 if ( Options.version_flag )
1782 if ( Options.help_flag )
1785 if ( Options.version_flag || Options.help_flag )
1788 if ( Options.error_flag )
1790 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1794 if ( Options.mode == MMT_INFO )
1796 result = show_file_info(Options);
1798 else if ( Options.mode == MMT_GOP_START )
1800 result = gop_start_test(Options);
1802 else if ( Options.mode == MMT_GEN_KEY )
1804 Kumu::FortunaRNG RNG;
1805 byte_t bin_buf[KeyLen];
1807 RNG.FillRandom(bin_buf, KeyLen);
1808 printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 64));
1810 else if ( Options.mode == MMT_GEN_ID )
1813 Kumu::GenRandomValue(TmpID);
1814 printf("%s\n", TmpID.EncodeHex(str_buf, 64));
1816 else if ( Options.mode == MMT_DIGEST )
1818 for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ )
1819 result = digest_file(Options.filenames[i]);
1821 else if ( Options.mode == MMT_UL_LIST )
1823 MDD_t di = (MDD_t)0;
1825 while ( di < MDD_Max )
1827 MDDEntry TmpType = Dict::Type(di);
1828 UL TmpUL(TmpType.ul);
1829 fprintf(stdout, "%s: %s\n", TmpUL.EncodeString(str_buf, 64), TmpType.name);
1830 di = (MDD_t)(di + 1);
1833 else if ( Options.mode == MMT_EXTRACT )
1835 EssenceType_t EssenceType;
1836 result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1838 if ( ASDCP_SUCCESS(result) )
1840 switch ( EssenceType )
1843 result = read_MPEG2_file(Options);
1847 if ( Options.stereo_image_flag )
1848 result = read_JP2K_S_file(Options);
1850 result = read_JP2K_file(Options);
1853 case ESS_JPEG_2000_S:
1854 result = read_JP2K_S_file(Options);
1857 case ESS_PCM_24b_48k:
1858 result = read_PCM_file(Options);
1861 case ESS_TIMED_TEXT:
1862 result = read_timed_text_file(Options);
1866 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]);
1871 else if ( Options.mode == MMT_CREATE )
1873 if ( Options.do_repeat && ! Options.duration_flag )
1875 fputs("Option -R requires -d <duration>\n", stderr);
1879 EssenceType_t EssenceType;
1880 result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType);
1882 if ( ASDCP_SUCCESS(result) )
1884 switch ( EssenceType )
1887 result = write_MPEG2_file(Options);
1891 if ( Options.stereo_image_flag )
1892 result = write_JP2K_S_file(Options);
1895 result = write_JP2K_file(Options);
1899 case ESS_PCM_24b_48k:
1900 result = write_PCM_file(Options);
1903 case ESS_TIMED_TEXT:
1904 result = write_timed_text_file(Options);
1908 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1909 Options.filenames[0]);
1916 fprintf(stderr, "Unhandled mode: %d.\n", Options.mode);
1920 if ( ASDCP_FAILURE(result) )
1922 fputs("Program stopped on error.\n", stderr);
1924 if ( result == RESULT_SFORMAT )
1926 fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
1928 else if ( result != RESULT_FAIL )
1930 fputs(result, stderr);
1931 fputc('\n', stderr);
1942 // end asdcp-test.cpp