2 Copyright (c) 2003-2007, 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
53 #include <KM_fileio.h>
55 #include <PCMParserList.h>
56 #include <WavFileWriter.h>
59 #include <openssl/sha.h>
61 using namespace ASDCP;
63 const ui32_t FRAME_BUFFER_SIZE = 4*1024*1024;
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-2006 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> [-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 - Create a stereoscopic image file. Expects two dir-\n\
144 ectories of JP2K codestreams (directories must have\n\
145 an equal number of frames; left eye is first).\n\
146 -c <output-file> - Create an AS-DCP track file from input(s)\n\
147 -g - Generate a random 16 byte value to stdout\n\
148 -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\
149 -h | -help - Show help\n\
150 -i - Show file info\n\
151 -t - Calculate message digest of input file\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\
187 -H - Show MXF header metadata, used with option -i\n\
188 -n - Show index, used with option -i\n\
191 -s <num> - Number of bytes of frame buffer to be dumped as hex to\n\
192 stderr, used with option -v\n\
193 -v - Verbose, prints informative messages to stderr\n\
195 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
196 o All option arguments must be separated from the option by whitespace.\n\
197 o An argument of \"23\" to the -p option will be interpreted\n\
198 as 23000/1001 fps.\n\
224 bool error_flag; // true if the given options are in error or not complete
225 bool key_flag; // true if an encryption key was given
226 bool key_id_flag; // true if a key ID was given
227 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
228 bool write_hmac; // true if HMAC values are to be generated and written
229 bool read_hmac; // true if HMAC values are to be validated
230 bool split_wav; // true if PCM is to be extracted to stereo WAV files
231 bool mono_wav; // true if PCM is to be extracted to mono WAV files
232 bool verbose_flag; // true if the verbose option was selected
233 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
234 bool showindex_flag; // true if index is to be displayed
235 bool showheader_flag; // true if MXF file header is to be displayed
236 bool no_write_flag; // true if no output files are to be written
237 bool version_flag; // true if the version display option was selected
238 bool help_flag; // true if the help display option was selected
239 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
240 ui32_t start_frame; // frame number to begin processing
241 ui32_t duration; // number of frames to be processed
242 bool duration_flag; // true if duration argument given
243 bool do_repeat; // if true and -c -d, repeat first input frame
244 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
245 ui32_t picture_rate; // fps of picture when wrapping PCM
246 ui32_t fb_size; // size of picture frame buffer
247 ui32_t file_count; // number of elements in filenames[]
248 const char* file_root; // filename pre for files written by the extract mode
249 const char* out_file; // name of mxf file created by create mode
250 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
251 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
252 const char* filenames[MAX_IN_FILES]; // list of filenames to be processed
255 Rational PictureRate()
257 if ( picture_rate == 23 ) return EditRate_23_98;
258 if ( picture_rate == 48 ) return EditRate_48;
263 const char* szPictureRate()
265 if ( picture_rate == 23 ) return "23.976";
266 if ( picture_rate == 48 ) return "48";
271 CommandOptions(int argc, const char** argv) :
272 mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), encrypt_header_flag(true),
273 write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false),
274 verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false),
275 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false), start_frame(0),
276 duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false),
277 picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0)
279 memset(key_value, 0, KeyLen);
280 memset(key_id_value, 0, UUIDlen);
282 for ( int i = 1; i < argc; i++ )
285 if ( (strcmp( argv[i], "-help") == 0) )
291 if ( argv[i][0] == '-'
292 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
295 switch ( argv[i][1] )
297 case '1': mono_wav = true; break;
298 case '2': split_wav = true; break;
299 case '3': stereo_image_flag = true; break;
300 case 'i': mode = MMT_INFO; break;
301 case 'G': mode = MMT_GOP_START; break;
302 case 'W': no_write_flag = true; break;
303 case 'n': showindex_flag = true; break;
304 case 'H': showheader_flag = true; break;
305 case 'R': do_repeat = true; break;
306 case 'S': split_wav = true; break;
307 case 'V': version_flag = true; break;
308 case 'h': help_flag = true; break;
309 case 'v': verbose_flag = true; break;
310 case 'g': mode = MMT_GEN_KEY; break;
311 case 'u': mode = MMT_GEN_ID; break;
312 case 'e': encrypt_header_flag = true; break;
313 case 'E': encrypt_header_flag = false; break;
314 case 'M': write_hmac = false; break;
315 case 'm': read_hmac = true; break;
316 case 'L': use_smpte_labels = true; break;
319 TEST_EXTRA_ARG(i, 'c');
325 TEST_EXTRA_ARG(i, 'x');
330 case 'k': key_flag = true;
331 TEST_EXTRA_ARG(i, 'k');
334 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
336 if ( length != KeyLen )
338 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
344 case 'j': key_id_flag = true;
345 TEST_EXTRA_ARG(i, 'j');
348 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
350 if ( length != UUIDlen )
352 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
359 TEST_EXTRA_ARG(i, 'f');
360 start_frame = abs(atoi(argv[i]));
364 TEST_EXTRA_ARG(i, 'd');
365 duration_flag = true;
366 duration = abs(atoi(argv[i]));
370 TEST_EXTRA_ARG(i, 'p');
371 picture_rate = abs(atoi(argv[i]));
375 TEST_EXTRA_ARG(i, 's');
376 fb_dump_size = abs(atoi(argv[i]));
379 case 't': mode = MMT_DIGEST; break;
382 TEST_EXTRA_ARG(i, 'b');
383 fb_size = abs(atoi(argv[i]));
386 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
391 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
398 if ( argv[i][0] != '-' )
400 filenames[file_count++] = argv[i];
404 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
408 if ( file_count >= MAX_IN_FILES )
410 fprintf(stderr, "Filename lists exceeds maximum list size: %u\n", MAX_IN_FILES);
416 if ( help_flag || version_flag )
419 if ( ( mode == MMT_INFO
420 || mode == MMT_CREATE
421 || mode == MMT_EXTRACT
422 || mode == MMT_GOP_START
423 || mode == MMT_DIGEST ) && file_count == 0 )
425 fputs("Option requires at least one filename argument.\n", stderr);
429 if ( mode == MMT_NONE && ! help_flag && ! version_flag )
431 fputs("No operation selected (use one of -[gGcitux] or -h for help).\n", stderr);
439 //------------------------------------------------------------------------------------------
442 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
443 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
446 write_MPEG2_file(CommandOptions& Options)
448 AESEncContext* Context = 0;
449 HMACContext* HMAC = 0;
450 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
451 MPEG2::Parser Parser;
452 MPEG2::MXFWriter Writer;
453 MPEG2::VideoDescriptor VDesc;
454 byte_t IV_buf[CBC_BLOCK_SIZE];
455 Kumu::FortunaRNG RNG;
457 // set up essence parser
458 Result_t result = Parser.OpenRead(Options.filenames[0]);
461 if ( ASDCP_SUCCESS(result) )
463 Parser.FillVideoDescriptor(VDesc);
465 if ( Options.verbose_flag )
467 fputs("MPEG-2 Pictures\n", stderr);
468 fputs("VideoDescriptor:\n", stderr);
469 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
470 MPEG2::VideoDescriptorDump(VDesc);
474 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
476 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
477 Kumu::GenRandomUUID(Info.AssetUUID);
479 if ( Options.use_smpte_labels )
481 Info.LabelSetType = LS_MXF_SMPTE;
482 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
485 // configure encryption
486 if( Options.key_flag )
488 Kumu::GenRandomUUID(Info.ContextID);
489 Info.EncryptedEssence = true;
491 if ( Options.key_id_flag )
492 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
494 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
496 Context = new AESEncContext;
497 result = Context->InitKey(Options.key_value);
499 if ( ASDCP_SUCCESS(result) )
500 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
502 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
504 Info.UsesHMAC = true;
505 HMAC = new HMACContext;
506 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
510 if ( ASDCP_SUCCESS(result) )
511 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
514 if ( ASDCP_SUCCESS(result) )
515 // loop through the frames
517 result = Parser.Reset();
520 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
522 if ( ! Options.do_repeat || duration == 1 )
524 result = Parser.ReadFrame(FrameBuffer);
526 if ( ASDCP_SUCCESS(result) )
528 if ( Options.verbose_flag )
529 FrameBuffer.Dump(stderr, Options.fb_dump_size);
531 if ( Options.encrypt_header_flag )
532 FrameBuffer.PlaintextOffset(0);
536 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
538 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
540 // The Writer class will forward the last block of ciphertext
541 // to the encryption context for use as the IV for the next
542 // frame. If you want to use non-sequitur IV values, un-comment
543 // the following line of code.
544 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
545 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
549 if ( result == RESULT_ENDOFFILE )
553 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
554 result = Writer.Finalize();
559 // Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file
560 // Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
561 // Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
564 read_MPEG2_file(CommandOptions& Options)
566 AESDecContext* Context = 0;
567 HMACContext* HMAC = 0;
568 MPEG2::MXFReader Reader;
569 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
570 Kumu::FileWriter OutFile;
571 ui32_t frame_count = 0;
573 Result_t result = Reader.OpenRead(Options.filenames[0]);
575 if ( ASDCP_SUCCESS(result) )
577 MPEG2::VideoDescriptor VDesc;
578 Reader.FillVideoDescriptor(VDesc);
579 frame_count = VDesc.ContainerDuration;
581 if ( Options.verbose_flag )
583 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
584 MPEG2::VideoDescriptorDump(VDesc);
588 if ( ASDCP_SUCCESS(result) )
591 snprintf(filename, 256, "%s.ves", Options.file_root);
592 result = OutFile.OpenWrite(filename);
595 if ( ASDCP_SUCCESS(result) && Options.key_flag )
597 Context = new AESDecContext;
598 result = Context->InitKey(Options.key_value);
600 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
603 Reader.FillWriterInfo(Info);
607 HMAC = new HMACContext;
608 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
612 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
617 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
618 if ( last_frame > frame_count )
619 last_frame = frame_count;
621 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
623 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
625 if ( ASDCP_SUCCESS(result) )
627 if ( Options.verbose_flag )
628 FrameBuffer.Dump(stderr, Options.fb_dump_size);
630 ui32_t write_count = 0;
631 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
641 gop_start_test(CommandOptions& Options)
643 using namespace ASDCP::MPEG2;
646 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
647 ui32_t frame_count = 0;
649 Result_t result = Reader.OpenRead(Options.filenames[0]);
651 if ( ASDCP_SUCCESS(result) )
653 MPEG2::VideoDescriptor VDesc;
654 Reader.FillVideoDescriptor(VDesc);
655 frame_count = VDesc.ContainerDuration;
657 if ( Options.verbose_flag )
659 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
660 MPEG2::VideoDescriptorDump(VDesc);
664 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
665 if ( last_frame > frame_count )
666 last_frame = frame_count;
668 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
670 result = Reader.ReadFrameGOPStart(i, FrameBuffer);
672 if ( ASDCP_SUCCESS(result) )
674 if ( Options.verbose_flag )
675 FrameBuffer.Dump(stderr, Options.fb_dump_size);
677 if ( FrameBuffer.FrameType() != FRAME_I )
678 fprintf(stderr, "Expecting an I frame, got %c\n", FrameTypeChar(FrameBuffer.FrameType()));
680 fprintf(stderr, "Requested frame %u, got %u\n", i, FrameBuffer.FrameNumber());
687 //------------------------------------------------------------------------------------------
690 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
691 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
694 write_JP2K_S_file(CommandOptions& Options)
696 AESEncContext* Context = 0;
697 HMACContext* HMAC = 0;
698 JP2K::MXFSWriter Writer;
699 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
700 JP2K::PictureDescriptor PDesc;
701 JP2K::SequenceParser ParserLeft, ParserRight;
702 byte_t IV_buf[CBC_BLOCK_SIZE];
703 Kumu::FortunaRNG RNG;
705 if ( Options.file_count != 2 )
707 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
711 // set up essence parser
712 Result_t result = ParserLeft.OpenRead(Options.filenames[0]);
714 if ( ASDCP_SUCCESS(result) )
715 result = ParserRight.OpenRead(Options.filenames[1]);
718 if ( ASDCP_SUCCESS(result) )
720 ParserLeft.FillPictureDescriptor(PDesc);
721 PDesc.EditRate = Options.PictureRate();
723 if ( Options.verbose_flag )
725 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
726 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
727 JP2K::PictureDescriptorDump(PDesc);
731 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
733 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
734 Kumu::GenRandomUUID(Info.AssetUUID);
736 if ( Options.use_smpte_labels )
738 Info.LabelSetType = LS_MXF_SMPTE;
739 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
742 // configure encryption
743 if( Options.key_flag )
745 Kumu::GenRandomUUID(Info.ContextID);
746 Info.EncryptedEssence = true;
748 if ( Options.key_id_flag )
749 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
751 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
753 Context = new AESEncContext;
754 result = Context->InitKey(Options.key_value);
756 if ( ASDCP_SUCCESS(result) )
757 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
759 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
761 Info.UsesHMAC = true;
762 HMAC = new HMACContext;
763 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
767 if ( ASDCP_SUCCESS(result) )
768 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
771 if ( ASDCP_SUCCESS(result) )
774 result = ParserLeft.Reset();
775 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
777 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
779 result = ParserLeft.ReadFrame(FrameBuffer);
781 if ( ASDCP_SUCCESS(result) )
783 if ( Options.verbose_flag )
784 FrameBuffer.Dump(stderr, Options.fb_dump_size);
786 if ( Options.encrypt_header_flag )
787 FrameBuffer.PlaintextOffset(0);
790 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
791 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
793 if ( ASDCP_SUCCESS(result) )
794 result = ParserRight.ReadFrame(FrameBuffer);
796 if ( ASDCP_SUCCESS(result) )
798 if ( Options.verbose_flag )
799 FrameBuffer.Dump(stderr, Options.fb_dump_size);
801 if ( Options.encrypt_header_flag )
802 FrameBuffer.PlaintextOffset(0);
805 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
806 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
809 if ( result == RESULT_ENDOFFILE )
813 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
814 result = Writer.Finalize();
819 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a plaintext ASDCP file
820 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
821 // Read one or more ciphertext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
823 read_JP2K_S_file(CommandOptions& Options)
825 AESDecContext* Context = 0;
826 HMACContext* HMAC = 0;
827 JP2K::MXFSReader Reader;
828 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
829 ui32_t frame_count = 0;
831 Result_t result = Reader.OpenRead(Options.filenames[0]);
833 if ( ASDCP_SUCCESS(result) )
835 JP2K::PictureDescriptor PDesc;
836 Reader.FillPictureDescriptor(PDesc);
838 frame_count = PDesc.ContainerDuration;
840 if ( Options.verbose_flag )
842 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
843 JP2K::PictureDescriptorDump(PDesc);
847 if ( ASDCP_SUCCESS(result) && Options.key_flag )
849 Context = new AESDecContext;
850 result = Context->InitKey(Options.key_value);
852 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
855 Reader.FillWriterInfo(Info);
859 HMAC = new HMACContext;
860 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
864 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
869 const int filename_max = 1024;
870 char filename[filename_max];
871 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
872 if ( last_frame > frame_count )
873 last_frame = frame_count;
875 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
877 result = Reader.ReadFrame(i, JP2K::SP_LEFT, FrameBuffer, Context, HMAC);
879 if ( ASDCP_SUCCESS(result) )
881 Kumu::FileWriter OutFile;
883 snprintf(filename, filename_max, "%s%06uL.j2c", Options.file_root, i);
884 result = OutFile.OpenWrite(filename);
886 if ( ASDCP_SUCCESS(result) )
887 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
889 if ( Options.verbose_flag )
890 FrameBuffer.Dump(stderr, Options.fb_dump_size);
893 if ( ASDCP_SUCCESS(result) )
894 result = Reader.ReadFrame(i, JP2K::SP_RIGHT, FrameBuffer, Context, HMAC);
896 if ( ASDCP_SUCCESS(result) )
898 Kumu::FileWriter OutFile;
900 snprintf(filename, filename_max, "%s%06uR.j2c", Options.file_root, i);
901 result = OutFile.OpenWrite(filename);
903 if ( ASDCP_SUCCESS(result) )
904 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
913 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
914 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
917 write_JP2K_file(CommandOptions& Options)
919 AESEncContext* Context = 0;
920 HMACContext* HMAC = 0;
921 JP2K::MXFWriter Writer;
922 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
923 JP2K::PictureDescriptor PDesc;
924 JP2K::SequenceParser Parser;
925 byte_t IV_buf[CBC_BLOCK_SIZE];
926 Kumu::FortunaRNG RNG;
928 // set up essence parser
929 Result_t result = Parser.OpenRead(Options.filenames[0]);
932 if ( ASDCP_SUCCESS(result) )
934 Parser.FillPictureDescriptor(PDesc);
935 PDesc.EditRate = Options.PictureRate();
937 if ( Options.verbose_flag )
939 fprintf(stderr, "JPEG 2000 pictures\n");
940 fputs("PictureDescriptor:\n", stderr);
941 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
942 JP2K::PictureDescriptorDump(PDesc);
946 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
948 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
949 Kumu::GenRandomUUID(Info.AssetUUID);
951 if ( Options.use_smpte_labels )
953 Info.LabelSetType = LS_MXF_SMPTE;
954 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
957 // configure encryption
958 if( Options.key_flag )
960 Kumu::GenRandomUUID(Info.ContextID);
961 Info.EncryptedEssence = true;
963 if ( Options.key_id_flag )
964 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
966 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
968 Context = new AESEncContext;
969 result = Context->InitKey(Options.key_value);
971 if ( ASDCP_SUCCESS(result) )
972 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
974 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
976 Info.UsesHMAC = true;
977 HMAC = new HMACContext;
978 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
982 if ( ASDCP_SUCCESS(result) )
983 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
986 if ( ASDCP_SUCCESS(result) )
989 result = Parser.Reset();
991 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
993 if ( ! Options.do_repeat || duration == 1 )
995 result = Parser.ReadFrame(FrameBuffer);
997 if ( ASDCP_SUCCESS(result) )
999 if ( Options.verbose_flag )
1000 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1002 if ( Options.encrypt_header_flag )
1003 FrameBuffer.PlaintextOffset(0);
1007 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1009 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1011 // The Writer class will forward the last block of ciphertext
1012 // to the encryption context for use as the IV for the next
1013 // frame. If you want to use non-sequitur IV values, un-comment
1014 // the following line of code.
1015 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1016 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1020 if ( result == RESULT_ENDOFFILE )
1024 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1025 result = Writer.Finalize();
1030 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
1031 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
1032 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
1035 read_JP2K_file(CommandOptions& Options)
1037 AESDecContext* Context = 0;
1038 HMACContext* HMAC = 0;
1039 JP2K::MXFReader Reader;
1040 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
1041 ui32_t frame_count = 0;
1043 Result_t result = Reader.OpenRead(Options.filenames[0]);
1045 if ( ASDCP_SUCCESS(result) )
1047 JP2K::PictureDescriptor PDesc;
1048 Reader.FillPictureDescriptor(PDesc);
1050 frame_count = PDesc.ContainerDuration;
1052 if ( Options.verbose_flag )
1054 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1055 JP2K::PictureDescriptorDump(PDesc);
1059 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1061 Context = new AESDecContext;
1062 result = Context->InitKey(Options.key_value);
1064 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1067 Reader.FillWriterInfo(Info);
1069 if ( Info.UsesHMAC )
1071 HMAC = new HMACContext;
1072 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1076 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1081 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
1082 if ( last_frame > frame_count )
1083 last_frame = frame_count;
1085 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1087 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1089 if ( ASDCP_SUCCESS(result) )
1091 Kumu::FileWriter OutFile;
1094 snprintf(filename, 256, "%s%06u.j2c", Options.file_root, i);
1095 result = OutFile.OpenWrite(filename);
1097 if ( ASDCP_SUCCESS(result) )
1098 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
1100 if ( Options.verbose_flag )
1101 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1108 //------------------------------------------------------------------------------------------
1112 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
1113 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
1116 write_PCM_file(CommandOptions& Options)
1118 AESEncContext* Context = 0;
1119 HMACContext* HMAC = 0;
1120 PCMParserList Parser;
1121 PCM::MXFWriter Writer;
1122 PCM::FrameBuffer FrameBuffer;
1123 PCM::AudioDescriptor ADesc;
1124 Rational PictureRate = Options.PictureRate();
1125 byte_t IV_buf[CBC_BLOCK_SIZE];
1126 Kumu::FortunaRNG RNG;
1128 // set up essence parser
1129 Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate);
1131 // set up MXF writer
1132 if ( ASDCP_SUCCESS(result) )
1134 Parser.FillAudioDescriptor(ADesc);
1136 ADesc.SampleRate = PictureRate;
1137 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1139 if ( Options.verbose_flag )
1141 fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n",
1142 Options.szPictureRate(),
1143 PCM::CalcSamplesPerFrame(ADesc));
1144 fputs("AudioDescriptor:\n", stderr);
1145 PCM::AudioDescriptorDump(ADesc);
1149 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1151 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1152 Kumu::GenRandomUUID(Info.AssetUUID);
1154 if ( Options.use_smpte_labels )
1156 Info.LabelSetType = LS_MXF_SMPTE;
1157 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1160 // configure encryption
1161 if( Options.key_flag )
1163 Kumu::GenRandomUUID(Info.ContextID);
1164 Info.EncryptedEssence = true;
1166 if ( Options.key_id_flag )
1167 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1169 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1171 Context = new AESEncContext;
1172 result = Context->InitKey(Options.key_value);
1174 if ( ASDCP_SUCCESS(result) )
1175 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1177 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1179 Info.UsesHMAC = true;
1180 HMAC = new HMACContext;
1181 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1185 if ( ASDCP_SUCCESS(result) )
1186 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1189 if ( ASDCP_SUCCESS(result) )
1191 result = Parser.Reset();
1192 ui32_t duration = 0;
1194 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1196 result = Parser.ReadFrame(FrameBuffer);
1198 if ( ASDCP_SUCCESS(result) )
1200 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1202 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1203 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1204 result = RESULT_ENDOFFILE;
1208 if ( Options.verbose_flag )
1209 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1211 if ( ! Options.no_write_flag )
1213 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1215 // The Writer class will forward the last block of ciphertext
1216 // to the encryption context for use as the IV for the next
1217 // frame. If you want to use non-sequitur IV values, un-comment
1218 // the following line of code.
1219 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1220 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1225 if ( result == RESULT_ENDOFFILE )
1229 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1230 result = Writer.Finalize();
1235 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
1236 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
1237 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
1240 read_PCM_file(CommandOptions& Options)
1242 AESDecContext* Context = 0;
1243 HMACContext* HMAC = 0;
1244 PCM::MXFReader Reader;
1245 PCM::FrameBuffer FrameBuffer;
1246 WavFileWriter OutWave;
1247 PCM::AudioDescriptor ADesc;
1248 ui32_t last_frame = 0;
1250 Result_t result = Reader.OpenRead(Options.filenames[0]);
1252 if ( ASDCP_SUCCESS(result) )
1254 Reader.FillAudioDescriptor(ADesc);
1256 if ( ADesc.SampleRate != EditRate_23_98
1257 && ADesc.SampleRate != EditRate_24
1258 && ADesc.SampleRate != EditRate_48 )
1259 ADesc.SampleRate = Options.PictureRate();
1261 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1263 if ( Options.verbose_flag )
1264 PCM::AudioDescriptorDump(ADesc);
1267 if ( ASDCP_SUCCESS(result) )
1269 last_frame = ADesc.ContainerDuration;
1271 if ( Options.duration > 0 && Options.duration < last_frame )
1272 last_frame = Options.duration;
1274 if ( Options.start_frame > 0 )
1276 if ( Options.start_frame > ADesc.ContainerDuration )
1278 fprintf(stderr, "Start value greater than file duration.\n");
1282 last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
1285 ADesc.ContainerDuration = last_frame - Options.start_frame;
1286 OutWave.OpenWrite(ADesc, Options.file_root,
1287 ( Options.split_wav ? WavFileWriter::ST_STEREO :
1288 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
1291 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1293 Context = new AESDecContext;
1294 result = Context->InitKey(Options.key_value);
1296 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1299 Reader.FillWriterInfo(Info);
1301 if ( Info.UsesHMAC )
1303 HMAC = new HMACContext;
1304 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1308 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1313 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1315 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1317 if ( ASDCP_SUCCESS(result) )
1319 if ( Options.verbose_flag )
1320 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1322 result = OutWave.WriteFrame(FrameBuffer);
1330 //------------------------------------------------------------------------------------------
1331 // TimedText essence
1334 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1335 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1338 write_timed_text_file(CommandOptions& Options)
1340 AESEncContext* Context = 0;
1341 HMACContext* HMAC = 0;
1342 TimedText::DCSubtitleParser Parser;
1343 TimedText::MXFWriter Writer;
1344 TimedText::FrameBuffer FrameBuffer;
1345 TimedText::TimedTextDescriptor TDesc;
1346 byte_t IV_buf[CBC_BLOCK_SIZE];
1347 Kumu::FortunaRNG RNG;
1349 // set up essence parser
1350 Result_t result = Parser.OpenRead(Options.filenames[0]);
1352 // set up MXF writer
1353 if ( ASDCP_SUCCESS(result) )
1355 Parser.FillDescriptor(TDesc);
1356 FrameBuffer.Capacity(2*Kumu::Megabyte);
1358 if ( Options.verbose_flag )
1360 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1361 TimedText::DescriptorDump(TDesc);
1365 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1367 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1368 Kumu::GenRandomUUID(Info.AssetUUID);
1370 if ( Options.use_smpte_labels )
1372 Info.LabelSetType = LS_MXF_SMPTE;
1373 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1376 // configure encryption
1377 if( Options.key_flag )
1379 Kumu::GenRandomUUID(Info.ContextID);
1380 Info.EncryptedEssence = true;
1382 if ( Options.key_id_flag )
1383 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1385 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1387 Context = new AESEncContext;
1388 result = Context->InitKey(Options.key_value);
1390 if ( ASDCP_SUCCESS(result) )
1391 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1393 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1395 Info.UsesHMAC = true;
1396 HMAC = new HMACContext;
1397 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1401 if ( ASDCP_SUCCESS(result) )
1402 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1405 if ( ASDCP_FAILURE(result) )
1409 TimedText::ResourceList_t::const_iterator ri;
1411 result = Parser.ReadTimedTextResource(XMLDoc);
1413 if ( ASDCP_SUCCESS(result) )
1414 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1416 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1418 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1420 if ( ASDCP_SUCCESS(result) )
1422 if ( Options.verbose_flag )
1423 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1425 if ( ! Options.no_write_flag )
1427 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1429 // The Writer class will forward the last block of ciphertext
1430 // to the encryption context for use as the IV for the next
1431 // frame. If you want to use non-sequitur IV values, un-comment
1432 // the following line of code.
1433 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1434 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1438 if ( result == RESULT_ENDOFFILE )
1442 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1443 result = Writer.Finalize();
1449 // Read one or more timed text streams from a plaintext ASDCP file
1450 // Read one or more timed text streams from a ciphertext ASDCP file
1451 // Read one or more timed text streams from a ciphertext ASDCP file
1454 read_timed_text_file(CommandOptions& Options)
1456 AESDecContext* Context = 0;
1457 HMACContext* HMAC = 0;
1458 TimedText::MXFReader Reader;
1459 TimedText::FrameBuffer FrameBuffer;
1460 TimedText::TimedTextDescriptor TDesc;
1462 Result_t result = Reader.OpenRead(Options.filenames[0]);
1464 if ( ASDCP_SUCCESS(result) )
1466 Reader.FillDescriptor(TDesc);
1467 FrameBuffer.Capacity(2*Kumu::Megabyte);
1469 if ( Options.verbose_flag )
1470 TimedText::DescriptorDump(TDesc);
1473 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1475 Context = new AESDecContext;
1476 result = Context->InitKey(Options.key_value);
1478 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1481 Reader.FillWriterInfo(Info);
1483 if ( Info.UsesHMAC )
1485 HMAC = new HMACContext;
1486 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1490 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1495 if ( ASDCP_FAILURE(result) )
1499 TimedText::ResourceList_t::const_iterator ri;
1501 result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
1503 // do something with the XML here
1504 fprintf(stderr, "XMLDoc size: %lu\n", XMLDoc.size());
1506 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1508 result = Reader.ReadAncillaryResource((*ri).ResourceID, FrameBuffer, Context, HMAC);
1510 if ( ASDCP_SUCCESS(result) )
1512 // if ( Options.verbose_flag )
1513 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1515 // do something with the resource data here
1522 //------------------------------------------------------------------------------------------
1526 // These classes wrap the irregular names in the asdcplib API
1527 // so that I can use a template to simplify the implementation
1528 // of show_file_info()
1530 class MyVideoDescriptor : public MPEG2::VideoDescriptor
1533 void FillDescriptor(MPEG2::MXFReader& Reader) {
1534 Reader.FillVideoDescriptor(*this);
1537 void Dump(FILE* stream) {
1538 MPEG2::VideoDescriptorDump(*this, stream);
1542 class MyPictureDescriptor : public JP2K::PictureDescriptor
1545 void FillDescriptor(JP2K::MXFReader& Reader) {
1546 Reader.FillPictureDescriptor(*this);
1549 void Dump(FILE* stream) {
1550 JP2K::PictureDescriptorDump(*this, stream);
1554 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
1557 void FillDescriptor(JP2K::MXFSReader& Reader) {
1558 Reader.FillPictureDescriptor(*this);
1561 void Dump(FILE* stream) {
1562 JP2K::PictureDescriptorDump(*this, stream);
1566 class MyAudioDescriptor : public PCM::AudioDescriptor
1569 void FillDescriptor(PCM::MXFReader& Reader) {
1570 Reader.FillAudioDescriptor(*this);
1573 void Dump(FILE* stream) {
1574 PCM::AudioDescriptorDump(*this, stream);
1578 class MyTextDescriptor : public TimedText::TimedTextDescriptor
1581 void FillDescriptor(TimedText::MXFReader& Reader) {
1582 Reader.FillDescriptor(*this);
1585 void Dump(FILE* stream) {
1586 TimedText::DescriptorDump(*this, stream);
1590 // MSVC didn't like the function template, so now it's a static class method
1591 template<class ReaderT, class DescriptorT>
1592 class FileInfoWrapper
1596 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
1598 assert(type_string);
1602 if ( Options.verbose_flag || Options.showheader_flag )
1605 Result_t result = Reader.OpenRead(Options.filenames[0]);
1607 if ( ASDCP_SUCCESS(result) )
1609 fprintf(stdout, "File essence type is %s.\n", type_string);
1611 if ( Options.showheader_flag )
1612 Reader.DumpHeaderMetadata(stream);
1615 Reader.FillWriterInfo(WI);
1616 WriterInfoDump(WI, stream);
1619 Desc.FillDescriptor(Reader);
1622 if ( Options.showindex_flag )
1623 Reader.DumpIndex(stream);
1625 else if ( result == RESULT_FORMAT && Options.showheader_flag )
1627 Reader.DumpHeaderMetadata(stream);
1633 // Read header metadata from an ASDCP file
1636 show_file_info(CommandOptions& Options)
1638 EssenceType_t EssenceType;
1639 Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1641 if ( ASDCP_FAILURE(result) )
1644 if ( EssenceType == ESS_MPEG2_VES )
1645 FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options, "MPEG2 video");
1647 else if ( EssenceType == ESS_PCM_24b_48k )
1648 FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options, "PCM audio");
1650 else if ( EssenceType == ESS_JPEG_2000 )
1652 if ( Options.stereo_image_flag )
1653 FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1654 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1657 FileInfoWrapper<ASDCP::JP2K::MXFReader,
1658 MyPictureDescriptor>::file_info(Options, "JPEG 2000 pictures");
1660 else if ( EssenceType == ESS_JPEG_2000_S )
1661 FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1662 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1664 else if ( EssenceType == ESS_TIMED_TEXT )
1665 FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options, "Timed Text");
1669 fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]);
1670 Kumu::FileReader Reader;
1671 MXF::OPAtomHeader TestHeader;
1673 result = Reader.OpenRead(Options.filenames[0]);
1675 if ( ASDCP_SUCCESS(result) )
1676 result = TestHeader.InitFromFile(Reader); // test UL and OP
1678 if ( ASDCP_SUCCESS(result) )
1680 TestHeader.Partition::Dump();
1682 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1685 fputs("File contains no Identification object.\n", stdout);
1687 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1690 fputs("File contains no SourcePackage object.\n", stdout);
1694 fputs("File is not MXF.\n", stdout);
1704 digest_file(const char* filename)
1706 using namespace Kumu;
1708 ASDCP_TEST_NULL_STR(filename);
1712 ByteString Buf(8192);
1714 Result_t result = Reader.OpenRead(filename);
1716 while ( ASDCP_SUCCESS(result) )
1718 ui32_t read_count = 0;
1719 result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count);
1721 if ( result == RESULT_ENDOFFILE )
1727 if ( ASDCP_SUCCESS(result) )
1728 SHA1_Update(&Ctx, Buf.Data(), read_count);
1731 if ( ASDCP_SUCCESS(result) )
1733 const ui32_t sha_len = 20;
1734 byte_t bin_buf[sha_len];
1736 SHA1_Final(bin_buf, &Ctx);
1738 fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename);
1746 main(int argc, const char** argv)
1748 Result_t result = RESULT_OK;
1749 CommandOptions Options(argc, argv);
1751 if ( Options.version_flag )
1754 if ( Options.help_flag )
1757 if ( Options.version_flag || Options.help_flag )
1760 if ( Options.error_flag )
1762 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PACKAGE);
1766 if ( Options.mode == MMT_INFO )
1768 result = show_file_info(Options);
1770 else if ( Options.mode == MMT_GOP_START )
1772 result = gop_start_test(Options);
1774 else if ( Options.mode == MMT_GEN_KEY )
1776 Kumu::FortunaRNG RNG;
1777 byte_t bin_buf[KeyLen];
1780 RNG.FillRandom(bin_buf, KeyLen);
1781 printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 40));
1783 else if ( Options.mode == MMT_GEN_ID )
1786 Kumu::GenRandomValue(TmpID);
1788 printf("%s\n", TmpID.EncodeHex(str_buf, 40));
1790 else if ( Options.mode == MMT_DIGEST )
1792 for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ )
1793 result = digest_file(Options.filenames[i]);
1795 else if ( Options.mode == MMT_EXTRACT )
1797 EssenceType_t EssenceType;
1798 result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1800 if ( ASDCP_SUCCESS(result) )
1802 switch ( EssenceType )
1805 result = read_MPEG2_file(Options);
1809 if ( Options.stereo_image_flag )
1810 result = read_JP2K_S_file(Options);
1812 result = read_JP2K_file(Options);
1815 case ESS_JPEG_2000_S:
1816 result = read_JP2K_S_file(Options);
1819 case ESS_PCM_24b_48k:
1820 result = read_PCM_file(Options);
1823 case ESS_TIMED_TEXT:
1824 result = read_timed_text_file(Options);
1828 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]);
1833 else if ( Options.mode == MMT_CREATE )
1835 if ( Options.do_repeat && ! Options.duration_flag )
1837 fputs("Option -R requires -d <duration>\n", stderr);
1841 EssenceType_t EssenceType;
1842 result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType);
1844 if ( ASDCP_SUCCESS(result) )
1846 switch ( EssenceType )
1849 result = write_MPEG2_file(Options);
1853 if ( Options.stereo_image_flag )
1854 result = write_JP2K_S_file(Options);
1857 result = write_JP2K_file(Options);
1861 case ESS_PCM_24b_48k:
1862 result = write_PCM_file(Options);
1865 case ESS_TIMED_TEXT:
1866 result = write_timed_text_file(Options);
1870 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1871 Options.filenames[0]);
1877 if ( ASDCP_FAILURE(result) )
1879 fputs("Program stopped on error.\n", stderr);
1881 if ( result != RESULT_FAIL )
1883 fputs(result, stderr);
1884 fputc('\n', stderr);
1895 // end asdcp-test.cpp