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* PACKAGE = "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";
88 snprintf(s_buf, 128, "%u.%u.%u", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR);
89 ProductVersion = s_buf;
95 // Increment the iterator, test for an additional non-option command line argument.
96 // Causes the caller to return if there are no remaining arguments or if the next
97 // argument begins with '-'.
98 #define TEST_EXTRA_ARG(i,c) if ( ++i >= argc || argv[(i)][0] == '-' ) \
100 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
105 banner(FILE* stream = stdout)
108 %s (asdcplib %s)\n\n\
109 Copyright (c) 2003-2008 John Hurst\n\n\
110 asdcplib may be copied only under the terms of the license found at\n\
111 the top of every file in the asdcplib distribution kit.\n\n\
112 Specify the -h (help) option for further information about %s\n\n",
113 PACKAGE, ASDCP::Version(), PACKAGE);
118 usage(FILE* stream = stdout)
121 USAGE: %s -c <output-file> [-3] [-b <buffer-size>] [-d <duration>] [-e|-E]\n\
122 [-f <start-frame>] [-j <key-id-string>] [-k <key-string>] [-L] [-M]\n\
123 [-p <frame-rate>] [-R] [-s <num>] [-v] [-W]\n\
124 <input-file> [<input-file-2> ...]\n\
126 %s [-h|-help] [-V]\n\
128 %s -i [-H] [-n] [-v] <input-file>\n\
132 %s -G [-v] <input-file>\n\
134 %s -t <input-file>\n\
136 %s -x <file-prefix> [-3] [-b <buffer-size>] [-d <duration>]\n\
137 [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <num>] [-S|-1]\n\
138 [-v] [-W] <input-file>\n\
139 \n", PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE);
143 -3 - With -c, create a stereoscopic image file. Expects two\n\
144 directories of JP2K codestreams (directories must have\n\
145 an equal number of frames; left eye is first).\n\
146 - With -x, force stereoscopic interpretation of a JP2K\n\
148 -c <output-file> - Create an AS-DCP track file from input(s)\n\
149 -g - Generate a random 16 byte value to stdout\n\
150 -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\
151 -h | -help - Show help\n\
152 -i - Show file info\n\
153 -t - Calculate message digest of input file\n\
154 -U - Dump UL catalog to stdout\n\
155 -u - Generate a random UUID value to stdout\n\
156 -V - Show version information\n\
157 -x <root-name> - Extract essence from AS-DCP file to named file(s)\n\
162 -e - Encrypt MPEG or JP2K headers (default)\n\
163 -E - Do not encrypt MPEG or JP2K headers\n\
164 -j <key-id-str> - Write key ID instead of creating a random value\n\
165 -k <key-string> - Use key for ciphertext operations\n\
166 -m - verify HMAC values when reading\n\
167 -M - Do not create HMAC values when writing\n\
171 Read/Write Options:\n\
172 -b <buffer-size> - Specify size in bytes of picture frame buffer.\n\
173 Defaults to 4,194,304 (4MB)\n\
174 -d <duration> - Number of frames to process, default all\n\
175 -f <start-frame> - Starting frame number, default 0\n\
176 -L - Write SMPTE UL values instead of MXF Interop\n\
177 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
178 Use one of [23|24|48], 24 is default\n\
179 -R - Repeat the first frame over the entire file (picture\n\
180 essence only, requires -c, -d)\n\
181 -S - Split Wave essence to stereo WAV files during extract.\n\
182 Default is multichannel WAV\n\
183 -1 - Split Wave essence to mono WAV files during extract.\n\
184 Default is multichannel WAV\n\
185 -W - Read input file only, do not write source file\n\
190 -H - Show MXF header metadata, used with option -i\n\
191 -n - Show index, used with option -i\n\
194 -s <num> - Number of bytes of frame buffer to be dumped as hex to\n\
195 stderr, used with option -v\n\
196 -v - Verbose, prints informative messages to stderr\n\
198 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
199 o All option arguments must be separated from the option by whitespace.\n\
200 o An argument of \"23\" to the -p option will be interpreted\n\
201 as 23000/1001 fps.\n\
228 bool error_flag; // true if the given options are in error or not complete
229 bool key_flag; // true if an encryption key was given
230 bool key_id_flag; // true if a key ID was given
231 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
232 bool write_hmac; // true if HMAC values are to be generated and written
233 bool read_hmac; // true if HMAC values are to be validated
234 bool split_wav; // true if PCM is to be extracted to stereo WAV files
235 bool mono_wav; // true if PCM is to be extracted to mono WAV files
236 bool verbose_flag; // true if the verbose option was selected
237 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
238 bool showindex_flag; // true if index is to be displayed
239 bool showheader_flag; // true if MXF file header is to be displayed
240 bool no_write_flag; // true if no output files are to be written
241 bool version_flag; // true if the version display option was selected
242 bool help_flag; // true if the help display option was selected
243 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
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), start_frame(0),
280 duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false),
281 picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0)
283 memset(key_value, 0, KeyLen);
284 memset(key_id_value, 0, UUIDlen);
286 for ( int i = 1; i < argc; i++ )
289 if ( (strcmp( argv[i], "-help") == 0) )
295 if ( argv[i][0] == '-'
296 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
299 switch ( argv[i][1] )
301 case '1': mono_wav = true; break;
302 case '2': split_wav = true; break;
303 case '3': stereo_image_flag = true; break;
304 case 'i': mode = MMT_INFO; break;
305 case 'G': mode = MMT_GOP_START; break;
306 case 'W': no_write_flag = true; break;
307 case 'n': showindex_flag = true; break;
308 case 'H': showheader_flag = true; break;
309 case 'R': do_repeat = true; break;
310 case 'S': split_wav = true; break;
311 case 'V': version_flag = true; break;
312 case 'h': help_flag = true; break;
313 case 'v': verbose_flag = true; break;
314 case 'g': mode = MMT_GEN_KEY; break;
315 case 'U': mode = MMT_UL_LIST; break;
316 case 'u': mode = MMT_GEN_ID; break;
317 case 'e': encrypt_header_flag = true; break;
318 case 'E': encrypt_header_flag = false; break;
319 case 'M': write_hmac = false; break;
320 case 'm': read_hmac = true; break;
321 case 'L': use_smpte_labels = true; break;
324 TEST_EXTRA_ARG(i, 'c');
330 TEST_EXTRA_ARG(i, 'x');
335 case 'k': key_flag = true;
336 TEST_EXTRA_ARG(i, 'k');
339 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
341 if ( length != KeyLen )
343 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
349 case 'j': key_id_flag = true;
350 TEST_EXTRA_ARG(i, 'j');
353 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
355 if ( length != UUIDlen )
357 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
364 TEST_EXTRA_ARG(i, 'f');
365 start_frame = abs(atoi(argv[i]));
369 TEST_EXTRA_ARG(i, 'd');
370 duration_flag = true;
371 duration = abs(atoi(argv[i]));
375 TEST_EXTRA_ARG(i, 'p');
376 picture_rate = abs(atoi(argv[i]));
380 TEST_EXTRA_ARG(i, 's');
381 fb_dump_size = abs(atoi(argv[i]));
384 case 't': mode = MMT_DIGEST; break;
387 TEST_EXTRA_ARG(i, 'b');
388 fb_size = abs(atoi(argv[i]));
391 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
396 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
403 if ( argv[i][0] != '-' )
405 filenames[file_count++] = argv[i];
409 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
413 if ( file_count >= MAX_IN_FILES )
415 fprintf(stderr, "Filename lists exceeds maximum list size: %u\n", MAX_IN_FILES);
421 if ( help_flag || version_flag )
424 if ( ( mode == MMT_INFO
425 || mode == MMT_CREATE
426 || mode == MMT_EXTRACT
427 || mode == MMT_GOP_START
428 || mode == MMT_DIGEST ) && file_count == 0 )
430 fputs("Option requires at least one filename argument.\n", stderr);
434 if ( mode == MMT_NONE && ! help_flag && ! version_flag )
436 fputs("No operation selected (use one of -[gGcitux] or -h for help).\n", stderr);
444 //------------------------------------------------------------------------------------------
447 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
448 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
451 write_MPEG2_file(CommandOptions& Options)
453 AESEncContext* Context = 0;
454 HMACContext* HMAC = 0;
455 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
456 MPEG2::Parser Parser;
457 MPEG2::MXFWriter Writer;
458 MPEG2::VideoDescriptor VDesc;
459 byte_t IV_buf[CBC_BLOCK_SIZE];
460 Kumu::FortunaRNG RNG;
462 // set up essence parser
463 Result_t result = Parser.OpenRead(Options.filenames[0]);
466 if ( ASDCP_SUCCESS(result) )
468 Parser.FillVideoDescriptor(VDesc);
470 if ( Options.verbose_flag )
472 fputs("MPEG-2 Pictures\n", stderr);
473 fputs("VideoDescriptor:\n", stderr);
474 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
475 MPEG2::VideoDescriptorDump(VDesc);
479 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
481 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
482 Kumu::GenRandomUUID(Info.AssetUUID);
484 if ( Options.use_smpte_labels )
486 Info.LabelSetType = LS_MXF_SMPTE;
487 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
490 // configure encryption
491 if( Options.key_flag )
493 Kumu::GenRandomUUID(Info.ContextID);
494 Info.EncryptedEssence = true;
496 if ( Options.key_id_flag )
497 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
499 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
501 Context = new AESEncContext;
502 result = Context->InitKey(Options.key_value);
504 if ( ASDCP_SUCCESS(result) )
505 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
507 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
509 Info.UsesHMAC = true;
510 HMAC = new HMACContext;
511 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
515 if ( ASDCP_SUCCESS(result) )
516 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
519 if ( ASDCP_SUCCESS(result) )
520 // loop through the frames
522 result = Parser.Reset();
525 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
527 if ( ! Options.do_repeat || duration == 1 )
529 result = Parser.ReadFrame(FrameBuffer);
531 if ( ASDCP_SUCCESS(result) )
533 if ( Options.verbose_flag )
534 FrameBuffer.Dump(stderr, Options.fb_dump_size);
536 if ( Options.encrypt_header_flag )
537 FrameBuffer.PlaintextOffset(0);
541 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
543 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
545 // The Writer class will forward the last block of ciphertext
546 // to the encryption context for use as the IV for the next
547 // frame. If you want to use non-sequitur IV values, un-comment
548 // the following line of code.
549 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
550 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
554 if ( result == RESULT_ENDOFFILE )
558 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
559 result = Writer.Finalize();
564 // Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file
565 // Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
566 // Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
569 read_MPEG2_file(CommandOptions& Options)
571 AESDecContext* Context = 0;
572 HMACContext* HMAC = 0;
573 MPEG2::MXFReader Reader;
574 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
575 Kumu::FileWriter OutFile;
576 ui32_t frame_count = 0;
578 Result_t result = Reader.OpenRead(Options.filenames[0]);
580 if ( ASDCP_SUCCESS(result) )
582 MPEG2::VideoDescriptor VDesc;
583 Reader.FillVideoDescriptor(VDesc);
584 frame_count = VDesc.ContainerDuration;
586 if ( Options.verbose_flag )
588 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
589 MPEG2::VideoDescriptorDump(VDesc);
593 if ( ASDCP_SUCCESS(result) )
596 snprintf(filename, 256, "%s.ves", Options.file_root);
597 result = OutFile.OpenWrite(filename);
600 if ( ASDCP_SUCCESS(result) && Options.key_flag )
602 Context = new AESDecContext;
603 result = Context->InitKey(Options.key_value);
605 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
608 Reader.FillWriterInfo(Info);
612 HMAC = new HMACContext;
613 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
617 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
622 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
623 if ( last_frame > frame_count )
624 last_frame = frame_count;
626 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
628 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
630 if ( ASDCP_SUCCESS(result) )
632 if ( Options.verbose_flag )
633 FrameBuffer.Dump(stderr, Options.fb_dump_size);
635 ui32_t write_count = 0;
636 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
646 gop_start_test(CommandOptions& Options)
648 using namespace ASDCP::MPEG2;
651 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
652 ui32_t frame_count = 0;
654 Result_t result = Reader.OpenRead(Options.filenames[0]);
656 if ( ASDCP_SUCCESS(result) )
658 MPEG2::VideoDescriptor VDesc;
659 Reader.FillVideoDescriptor(VDesc);
660 frame_count = VDesc.ContainerDuration;
662 if ( Options.verbose_flag )
664 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
665 MPEG2::VideoDescriptorDump(VDesc);
669 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
670 if ( last_frame > frame_count )
671 last_frame = frame_count;
673 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
675 result = Reader.ReadFrameGOPStart(i, FrameBuffer);
677 if ( ASDCP_SUCCESS(result) )
679 if ( Options.verbose_flag )
680 FrameBuffer.Dump(stderr, Options.fb_dump_size);
682 if ( FrameBuffer.FrameType() != FRAME_I )
683 fprintf(stderr, "Expecting an I frame, got %c\n", FrameTypeChar(FrameBuffer.FrameType()));
685 fprintf(stderr, "Requested frame %u, got %u\n", i, FrameBuffer.FrameNumber());
692 //------------------------------------------------------------------------------------------
695 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
696 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
699 write_JP2K_S_file(CommandOptions& Options)
701 AESEncContext* Context = 0;
702 HMACContext* HMAC = 0;
703 JP2K::MXFSWriter Writer;
704 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
705 JP2K::PictureDescriptor PDesc;
706 JP2K::SequenceParser ParserLeft, ParserRight;
707 byte_t IV_buf[CBC_BLOCK_SIZE];
708 Kumu::FortunaRNG RNG;
710 if ( Options.file_count != 2 )
712 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
716 // set up essence parser
717 Result_t result = ParserLeft.OpenRead(Options.filenames[0]);
719 if ( ASDCP_SUCCESS(result) )
720 result = ParserRight.OpenRead(Options.filenames[1]);
723 if ( ASDCP_SUCCESS(result) )
725 ParserLeft.FillPictureDescriptor(PDesc);
726 PDesc.EditRate = Options.PictureRate();
728 if ( Options.verbose_flag )
730 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
731 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
732 JP2K::PictureDescriptorDump(PDesc);
736 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
738 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
739 Kumu::GenRandomUUID(Info.AssetUUID);
741 if ( Options.use_smpte_labels )
743 Info.LabelSetType = LS_MXF_SMPTE;
744 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
747 // configure encryption
748 if( Options.key_flag )
750 Kumu::GenRandomUUID(Info.ContextID);
751 Info.EncryptedEssence = true;
753 if ( Options.key_id_flag )
754 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
756 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
758 Context = new AESEncContext;
759 result = Context->InitKey(Options.key_value);
761 if ( ASDCP_SUCCESS(result) )
762 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
764 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
766 Info.UsesHMAC = true;
767 HMAC = new HMACContext;
768 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
772 if ( ASDCP_SUCCESS(result) )
773 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
776 if ( ASDCP_SUCCESS(result) )
779 result = ParserLeft.Reset();
780 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
782 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
784 result = ParserLeft.ReadFrame(FrameBuffer);
786 if ( ASDCP_SUCCESS(result) )
788 if ( Options.verbose_flag )
789 FrameBuffer.Dump(stderr, Options.fb_dump_size);
791 if ( Options.encrypt_header_flag )
792 FrameBuffer.PlaintextOffset(0);
795 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
796 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
798 if ( ASDCP_SUCCESS(result) )
799 result = ParserRight.ReadFrame(FrameBuffer);
801 if ( ASDCP_SUCCESS(result) )
803 if ( Options.verbose_flag )
804 FrameBuffer.Dump(stderr, Options.fb_dump_size);
806 if ( Options.encrypt_header_flag )
807 FrameBuffer.PlaintextOffset(0);
810 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
811 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
814 if ( result == RESULT_ENDOFFILE )
818 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
819 result = Writer.Finalize();
824 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a plaintext ASDCP file
825 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
826 // Read one or more ciphertext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
828 read_JP2K_S_file(CommandOptions& Options)
830 AESDecContext* Context = 0;
831 HMACContext* HMAC = 0;
832 JP2K::MXFSReader Reader;
833 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
834 ui32_t frame_count = 0;
836 Result_t result = Reader.OpenRead(Options.filenames[0]);
838 if ( ASDCP_SUCCESS(result) )
840 JP2K::PictureDescriptor PDesc;
841 Reader.FillPictureDescriptor(PDesc);
843 frame_count = PDesc.ContainerDuration;
845 if ( Options.verbose_flag )
847 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
848 JP2K::PictureDescriptorDump(PDesc);
852 if ( ASDCP_SUCCESS(result) && Options.key_flag )
854 Context = new AESDecContext;
855 result = Context->InitKey(Options.key_value);
857 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
860 Reader.FillWriterInfo(Info);
864 HMAC = new HMACContext;
865 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
869 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
874 const int filename_max = 1024;
875 char filename[filename_max];
876 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
877 if ( last_frame > frame_count )
878 last_frame = frame_count;
880 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
882 result = Reader.ReadFrame(i, JP2K::SP_LEFT, FrameBuffer, Context, HMAC);
884 if ( ASDCP_SUCCESS(result) )
886 Kumu::FileWriter OutFile;
888 snprintf(filename, filename_max, "%s%06uL.j2c", Options.file_root, i);
889 result = OutFile.OpenWrite(filename);
891 if ( ASDCP_SUCCESS(result) )
892 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
894 if ( Options.verbose_flag )
895 FrameBuffer.Dump(stderr, Options.fb_dump_size);
898 if ( ASDCP_SUCCESS(result) )
899 result = Reader.ReadFrame(i, JP2K::SP_RIGHT, FrameBuffer, Context, HMAC);
901 if ( ASDCP_SUCCESS(result) )
903 Kumu::FileWriter OutFile;
905 snprintf(filename, filename_max, "%s%06uR.j2c", Options.file_root, i);
906 result = OutFile.OpenWrite(filename);
908 if ( ASDCP_SUCCESS(result) )
909 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
918 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
919 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
922 write_JP2K_file(CommandOptions& Options)
924 AESEncContext* Context = 0;
925 HMACContext* HMAC = 0;
926 JP2K::MXFWriter Writer;
927 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
928 JP2K::PictureDescriptor PDesc;
929 JP2K::SequenceParser Parser;
930 byte_t IV_buf[CBC_BLOCK_SIZE];
931 Kumu::FortunaRNG RNG;
933 // set up essence parser
934 Result_t result = Parser.OpenRead(Options.filenames[0]);
937 if ( ASDCP_SUCCESS(result) )
939 Parser.FillPictureDescriptor(PDesc);
940 PDesc.EditRate = Options.PictureRate();
942 if ( Options.verbose_flag )
944 fprintf(stderr, "JPEG 2000 pictures\n");
945 fputs("PictureDescriptor:\n", stderr);
946 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
947 JP2K::PictureDescriptorDump(PDesc);
951 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
953 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
954 Kumu::GenRandomUUID(Info.AssetUUID);
956 if ( Options.use_smpte_labels )
958 Info.LabelSetType = LS_MXF_SMPTE;
959 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
962 // configure encryption
963 if( Options.key_flag )
965 Kumu::GenRandomUUID(Info.ContextID);
966 Info.EncryptedEssence = true;
968 if ( Options.key_id_flag )
969 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
971 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
973 Context = new AESEncContext;
974 result = Context->InitKey(Options.key_value);
976 if ( ASDCP_SUCCESS(result) )
977 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
979 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
981 Info.UsesHMAC = true;
982 HMAC = new HMACContext;
983 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
987 if ( ASDCP_SUCCESS(result) )
988 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
991 if ( ASDCP_SUCCESS(result) )
994 result = Parser.Reset();
996 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
998 if ( ! Options.do_repeat || duration == 1 )
1000 result = Parser.ReadFrame(FrameBuffer);
1002 if ( ASDCP_SUCCESS(result) )
1004 if ( Options.verbose_flag )
1005 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1007 if ( Options.encrypt_header_flag )
1008 FrameBuffer.PlaintextOffset(0);
1012 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1014 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1016 // The Writer class will forward the last block of ciphertext
1017 // to the encryption context for use as the IV for the next
1018 // frame. If you want to use non-sequitur IV values, un-comment
1019 // the following line of code.
1020 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1021 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1025 if ( result == RESULT_ENDOFFILE )
1029 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1030 result = Writer.Finalize();
1035 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
1036 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
1037 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
1040 read_JP2K_file(CommandOptions& Options)
1042 AESDecContext* Context = 0;
1043 HMACContext* HMAC = 0;
1044 JP2K::MXFReader Reader;
1045 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
1046 ui32_t frame_count = 0;
1048 Result_t result = Reader.OpenRead(Options.filenames[0]);
1050 if ( ASDCP_SUCCESS(result) )
1052 JP2K::PictureDescriptor PDesc;
1053 Reader.FillPictureDescriptor(PDesc);
1055 frame_count = PDesc.ContainerDuration;
1057 if ( Options.verbose_flag )
1059 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1060 JP2K::PictureDescriptorDump(PDesc);
1064 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1066 Context = new AESDecContext;
1067 result = Context->InitKey(Options.key_value);
1069 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1072 Reader.FillWriterInfo(Info);
1074 if ( Info.UsesHMAC )
1076 HMAC = new HMACContext;
1077 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1081 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1086 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
1087 if ( last_frame > frame_count )
1088 last_frame = frame_count;
1090 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1092 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1094 if ( ASDCP_SUCCESS(result) )
1096 Kumu::FileWriter OutFile;
1099 snprintf(filename, 256, "%s%06u.j2c", Options.file_root, i);
1100 result = OutFile.OpenWrite(filename);
1102 if ( ASDCP_SUCCESS(result) )
1103 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
1105 if ( Options.verbose_flag )
1106 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1113 //------------------------------------------------------------------------------------------
1117 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
1118 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
1121 write_PCM_file(CommandOptions& Options)
1123 AESEncContext* Context = 0;
1124 HMACContext* HMAC = 0;
1125 PCMParserList Parser;
1126 PCM::MXFWriter Writer;
1127 PCM::FrameBuffer FrameBuffer;
1128 PCM::AudioDescriptor ADesc;
1129 Rational PictureRate = Options.PictureRate();
1130 byte_t IV_buf[CBC_BLOCK_SIZE];
1131 Kumu::FortunaRNG RNG;
1133 // set up essence parser
1134 Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate);
1136 // set up MXF writer
1137 if ( ASDCP_SUCCESS(result) )
1139 Parser.FillAudioDescriptor(ADesc);
1141 ADesc.SampleRate = PictureRate;
1142 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1144 if ( Options.verbose_flag )
1146 fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n",
1147 Options.szPictureRate(),
1148 PCM::CalcSamplesPerFrame(ADesc));
1149 fputs("AudioDescriptor:\n", stderr);
1150 PCM::AudioDescriptorDump(ADesc);
1154 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1156 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1157 Kumu::GenRandomUUID(Info.AssetUUID);
1159 if ( Options.use_smpte_labels )
1161 Info.LabelSetType = LS_MXF_SMPTE;
1162 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1165 // configure encryption
1166 if( Options.key_flag )
1168 Kumu::GenRandomUUID(Info.ContextID);
1169 Info.EncryptedEssence = true;
1171 if ( Options.key_id_flag )
1172 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1174 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1176 Context = new AESEncContext;
1177 result = Context->InitKey(Options.key_value);
1179 if ( ASDCP_SUCCESS(result) )
1180 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1182 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1184 Info.UsesHMAC = true;
1185 HMAC = new HMACContext;
1186 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1190 if ( ASDCP_SUCCESS(result) )
1191 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1194 if ( ASDCP_SUCCESS(result) )
1196 result = Parser.Reset();
1197 ui32_t duration = 0;
1199 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1201 result = Parser.ReadFrame(FrameBuffer);
1203 if ( ASDCP_SUCCESS(result) )
1205 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1207 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1208 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1209 result = RESULT_ENDOFFILE;
1213 if ( Options.verbose_flag )
1214 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1216 if ( ! Options.no_write_flag )
1218 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1220 // The Writer class will forward the last block of ciphertext
1221 // to the encryption context for use as the IV for the next
1222 // frame. If you want to use non-sequitur IV values, un-comment
1223 // the following line of code.
1224 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1225 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1230 if ( result == RESULT_ENDOFFILE )
1234 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1235 result = Writer.Finalize();
1240 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
1241 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
1242 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
1245 read_PCM_file(CommandOptions& Options)
1247 AESDecContext* Context = 0;
1248 HMACContext* HMAC = 0;
1249 PCM::MXFReader Reader;
1250 PCM::FrameBuffer FrameBuffer;
1251 WavFileWriter OutWave;
1252 PCM::AudioDescriptor ADesc;
1253 ui32_t last_frame = 0;
1255 Result_t result = Reader.OpenRead(Options.filenames[0]);
1257 if ( ASDCP_SUCCESS(result) )
1259 Reader.FillAudioDescriptor(ADesc);
1261 if ( ADesc.SampleRate != EditRate_23_98
1262 && ADesc.SampleRate != EditRate_24
1263 && ADesc.SampleRate != EditRate_48 )
1264 ADesc.SampleRate = Options.PictureRate();
1266 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1268 if ( Options.verbose_flag )
1269 PCM::AudioDescriptorDump(ADesc);
1272 if ( ASDCP_SUCCESS(result) )
1274 last_frame = ADesc.ContainerDuration;
1276 if ( Options.duration > 0 && Options.duration < last_frame )
1277 last_frame = Options.duration;
1279 if ( Options.start_frame > 0 )
1281 if ( Options.start_frame > ADesc.ContainerDuration )
1283 fprintf(stderr, "Start value greater than file duration.\n");
1287 last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
1290 ADesc.ContainerDuration = last_frame - Options.start_frame;
1291 OutWave.OpenWrite(ADesc, Options.file_root,
1292 ( Options.split_wav ? WavFileWriter::ST_STEREO :
1293 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
1296 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1298 Context = new AESDecContext;
1299 result = Context->InitKey(Options.key_value);
1301 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1304 Reader.FillWriterInfo(Info);
1306 if ( Info.UsesHMAC )
1308 HMAC = new HMACContext;
1309 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1313 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1318 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1320 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1322 if ( ASDCP_SUCCESS(result) )
1324 if ( Options.verbose_flag )
1325 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1327 result = OutWave.WriteFrame(FrameBuffer);
1335 //------------------------------------------------------------------------------------------
1336 // TimedText essence
1339 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1340 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1343 write_timed_text_file(CommandOptions& Options)
1345 AESEncContext* Context = 0;
1346 HMACContext* HMAC = 0;
1347 TimedText::DCSubtitleParser Parser;
1348 TimedText::MXFWriter Writer;
1349 TimedText::FrameBuffer FrameBuffer;
1350 TimedText::TimedTextDescriptor TDesc;
1351 byte_t IV_buf[CBC_BLOCK_SIZE];
1352 Kumu::FortunaRNG RNG;
1354 // set up essence parser
1355 Result_t result = Parser.OpenRead(Options.filenames[0]);
1357 // set up MXF writer
1358 if ( ASDCP_SUCCESS(result) )
1360 Parser.FillDescriptor(TDesc);
1361 FrameBuffer.Capacity(2*Kumu::Megabyte);
1363 if ( Options.verbose_flag )
1365 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1366 TimedText::DescriptorDump(TDesc);
1370 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1372 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1373 Kumu::GenRandomUUID(Info.AssetUUID);
1375 if ( Options.use_smpte_labels )
1377 Info.LabelSetType = LS_MXF_SMPTE;
1378 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1381 // configure encryption
1382 if( Options.key_flag )
1384 Kumu::GenRandomUUID(Info.ContextID);
1385 Info.EncryptedEssence = true;
1387 if ( Options.key_id_flag )
1388 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1390 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1392 Context = new AESEncContext;
1393 result = Context->InitKey(Options.key_value);
1395 if ( ASDCP_SUCCESS(result) )
1396 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1398 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1400 Info.UsesHMAC = true;
1401 HMAC = new HMACContext;
1402 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1406 if ( ASDCP_SUCCESS(result) )
1407 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1410 if ( ASDCP_FAILURE(result) )
1414 TimedText::ResourceList_t::const_iterator ri;
1416 result = Parser.ReadTimedTextResource(XMLDoc);
1418 if ( ASDCP_SUCCESS(result) )
1419 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1421 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1423 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1425 if ( ASDCP_SUCCESS(result) )
1427 if ( Options.verbose_flag )
1428 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1430 if ( ! Options.no_write_flag )
1432 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1434 // The Writer class will forward the last block of ciphertext
1435 // to the encryption context for use as the IV for the next
1436 // frame. If you want to use non-sequitur IV values, un-comment
1437 // the following line of code.
1438 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1439 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1443 if ( result == RESULT_ENDOFFILE )
1447 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1448 result = Writer.Finalize();
1454 // Read one or more timed text streams from a plaintext ASDCP file
1455 // Read one or more timed text streams from a ciphertext ASDCP file
1456 // Read one or more timed text streams from a ciphertext ASDCP file
1459 read_timed_text_file(CommandOptions& Options)
1461 AESDecContext* Context = 0;
1462 HMACContext* HMAC = 0;
1463 TimedText::MXFReader Reader;
1464 TimedText::FrameBuffer FrameBuffer;
1465 TimedText::TimedTextDescriptor TDesc;
1467 Result_t result = Reader.OpenRead(Options.filenames[0]);
1469 if ( ASDCP_SUCCESS(result) )
1471 Reader.FillDescriptor(TDesc);
1472 FrameBuffer.Capacity(2*Kumu::Megabyte);
1474 if ( Options.verbose_flag )
1475 TimedText::DescriptorDump(TDesc);
1478 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1480 Context = new AESDecContext;
1481 result = Context->InitKey(Options.key_value);
1483 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1486 Reader.FillWriterInfo(Info);
1488 if ( Info.UsesHMAC )
1490 HMAC = new HMACContext;
1491 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1495 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1500 if ( ASDCP_FAILURE(result) )
1504 TimedText::ResourceList_t::const_iterator ri;
1506 result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
1508 // do something with the XML here
1509 fprintf(stderr, "XMLDoc size: %lu\n", XMLDoc.size());
1511 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1513 result = Reader.ReadAncillaryResource((*ri).ResourceID, FrameBuffer, Context, HMAC);
1515 if ( ASDCP_SUCCESS(result) )
1517 // if ( Options.verbose_flag )
1518 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1520 // do something with the resource data here
1527 //------------------------------------------------------------------------------------------
1531 // These classes wrap the irregular names in the asdcplib API
1532 // so that I can use a template to simplify the implementation
1533 // of show_file_info()
1535 class MyVideoDescriptor : public MPEG2::VideoDescriptor
1538 void FillDescriptor(MPEG2::MXFReader& Reader) {
1539 Reader.FillVideoDescriptor(*this);
1542 void Dump(FILE* stream) {
1543 MPEG2::VideoDescriptorDump(*this, stream);
1547 class MyPictureDescriptor : public JP2K::PictureDescriptor
1550 void FillDescriptor(JP2K::MXFReader& Reader) {
1551 Reader.FillPictureDescriptor(*this);
1554 void Dump(FILE* stream) {
1555 JP2K::PictureDescriptorDump(*this, stream);
1559 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
1562 void FillDescriptor(JP2K::MXFSReader& Reader) {
1563 Reader.FillPictureDescriptor(*this);
1566 void Dump(FILE* stream) {
1567 JP2K::PictureDescriptorDump(*this, stream);
1571 class MyAudioDescriptor : public PCM::AudioDescriptor
1574 void FillDescriptor(PCM::MXFReader& Reader) {
1575 Reader.FillAudioDescriptor(*this);
1578 void Dump(FILE* stream) {
1579 PCM::AudioDescriptorDump(*this, stream);
1583 class MyTextDescriptor : public TimedText::TimedTextDescriptor
1586 void FillDescriptor(TimedText::MXFReader& Reader) {
1587 Reader.FillDescriptor(*this);
1590 void Dump(FILE* stream) {
1591 TimedText::DescriptorDump(*this, stream);
1595 // MSVC didn't like the function template, so now it's a static class method
1596 template<class ReaderT, class DescriptorT>
1597 class FileInfoWrapper
1601 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
1603 assert(type_string);
1607 Result_t result = RESULT_OK;
1609 if ( Options.verbose_flag || Options.showheader_flag )
1612 result = Reader.OpenRead(Options.filenames[0]);
1614 if ( ASDCP_SUCCESS(result) )
1616 fprintf(stdout, "File essence type is %s.\n", type_string);
1618 if ( Options.showheader_flag )
1619 Reader.DumpHeaderMetadata(stream);
1622 Reader.FillWriterInfo(WI);
1623 WriterInfoDump(WI, stream);
1626 Desc.FillDescriptor(Reader);
1629 if ( Options.showindex_flag )
1630 Reader.DumpIndex(stream);
1632 else if ( result == RESULT_FORMAT && Options.showheader_flag )
1634 Reader.DumpHeaderMetadata(stream);
1642 // Read header metadata from an ASDCP file
1645 show_file_info(CommandOptions& Options)
1647 EssenceType_t EssenceType;
1648 Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1650 if ( ASDCP_FAILURE(result) )
1653 if ( EssenceType == ESS_MPEG2_VES )
1654 result = FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options, "MPEG2 video");
1656 else if ( EssenceType == ESS_PCM_24b_48k )
1657 result = FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options, "PCM audio");
1659 else if ( EssenceType == ESS_JPEG_2000 )
1661 if ( Options.stereo_image_flag )
1662 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1663 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1666 result = FileInfoWrapper<ASDCP::JP2K::MXFReader,
1667 MyPictureDescriptor>::file_info(Options, "JPEG 2000 pictures");
1669 else if ( EssenceType == ESS_JPEG_2000_S )
1670 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1671 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1673 else if ( EssenceType == ESS_TIMED_TEXT )
1674 result = FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options, "Timed Text");
1678 fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]);
1679 Kumu::FileReader Reader;
1680 MXF::OPAtomHeader TestHeader;
1682 result = Reader.OpenRead(Options.filenames[0]);
1684 if ( ASDCP_SUCCESS(result) )
1685 result = TestHeader.InitFromFile(Reader); // test UL and OP
1687 if ( ASDCP_SUCCESS(result) )
1689 TestHeader.Partition::Dump();
1691 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1694 fputs("File contains no Identification object.\n", stdout);
1696 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1699 fputs("File contains no SourcePackage object.\n", stdout);
1703 fputs("File is not MXF.\n", stdout);
1713 digest_file(const char* filename)
1715 using namespace Kumu;
1717 ASDCP_TEST_NULL_STR(filename);
1721 ByteString Buf(8192);
1723 Result_t result = Reader.OpenRead(filename);
1725 while ( ASDCP_SUCCESS(result) )
1727 ui32_t read_count = 0;
1728 result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count);
1730 if ( result == RESULT_ENDOFFILE )
1736 if ( ASDCP_SUCCESS(result) )
1737 SHA1_Update(&Ctx, Buf.Data(), read_count);
1740 if ( ASDCP_SUCCESS(result) )
1742 const ui32_t sha_len = 20;
1743 byte_t bin_buf[sha_len];
1745 SHA1_Final(bin_buf, &Ctx);
1747 fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename);
1755 main(int argc, const char** argv)
1757 Result_t result = RESULT_OK;
1759 CommandOptions Options(argc, argv);
1761 if ( Options.version_flag )
1764 if ( Options.help_flag )
1767 if ( Options.version_flag || Options.help_flag )
1770 if ( Options.error_flag )
1772 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PACKAGE);
1776 if ( Options.mode == MMT_INFO )
1778 result = show_file_info(Options);
1780 else if ( Options.mode == MMT_GOP_START )
1782 result = gop_start_test(Options);
1784 else if ( Options.mode == MMT_GEN_KEY )
1786 Kumu::FortunaRNG RNG;
1787 byte_t bin_buf[KeyLen];
1789 RNG.FillRandom(bin_buf, KeyLen);
1790 printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 64));
1792 else if ( Options.mode == MMT_GEN_ID )
1795 Kumu::GenRandomValue(TmpID);
1796 printf("%s\n", TmpID.EncodeHex(str_buf, 64));
1798 else if ( Options.mode == MMT_DIGEST )
1800 for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ )
1801 result = digest_file(Options.filenames[i]);
1803 else if ( Options.mode == MMT_UL_LIST )
1805 MDD_t di = (MDD_t)0;
1807 while ( di < MDD_Max )
1809 MDDEntry TmpType = Dict::Type(di);
1810 UL TmpUL(TmpType.ul);
1811 fprintf(stdout, "%s: %s\n", TmpUL.EncodeString(str_buf, 64), TmpType.name);
1812 di = (MDD_t)(di + 1);
1815 else if ( Options.mode == MMT_EXTRACT )
1817 EssenceType_t EssenceType;
1818 result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1820 if ( ASDCP_SUCCESS(result) )
1822 switch ( EssenceType )
1825 result = read_MPEG2_file(Options);
1829 if ( Options.stereo_image_flag )
1830 result = read_JP2K_S_file(Options);
1832 result = read_JP2K_file(Options);
1835 case ESS_JPEG_2000_S:
1836 result = read_JP2K_S_file(Options);
1839 case ESS_PCM_24b_48k:
1840 result = read_PCM_file(Options);
1843 case ESS_TIMED_TEXT:
1844 result = read_timed_text_file(Options);
1848 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]);
1853 else if ( Options.mode == MMT_CREATE )
1855 if ( Options.do_repeat && ! Options.duration_flag )
1857 fputs("Option -R requires -d <duration>\n", stderr);
1861 EssenceType_t EssenceType;
1862 result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType);
1864 if ( ASDCP_SUCCESS(result) )
1866 switch ( EssenceType )
1869 result = write_MPEG2_file(Options);
1873 if ( Options.stereo_image_flag )
1874 result = write_JP2K_S_file(Options);
1877 result = write_JP2K_file(Options);
1881 case ESS_PCM_24b_48k:
1882 result = write_PCM_file(Options);
1885 case ESS_TIMED_TEXT:
1886 result = write_timed_text_file(Options);
1890 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1891 Options.filenames[0]);
1898 fprintf(stderr, "Unhandled mode: %d.\n", Options.mode);
1902 if ( ASDCP_FAILURE(result) )
1904 fputs("Program stopped on error.\n", stderr);
1906 if ( result == RESULT_SFORMAT )
1908 fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
1910 else if ( result != RESULT_FAIL )
1912 fputs(result, stderr);
1913 fputc('\n', stderr);
1924 // end asdcp-test.cpp