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>
60 #ifdef ASDCP_WITH_TIMED_TEXT
61 #include <AS_DCP_TimedText.h>
64 #include <openssl/sha.h>
66 using namespace ASDCP;
68 const ui32_t FRAME_BUFFER_SIZE = 4*1024*1024;
70 //------------------------------------------------------------------------------------------
72 // command line option parser class
74 static const char* PACKAGE = "asdcp-test"; // program name for messages
75 const ui32_t MAX_IN_FILES = 16; // maximum number of input files handled by
76 // the command option parser
78 // local program identification info written to file headers
79 class MyInfo : public WriterInfo
84 static byte_t default_ProductUUID_Data[UUIDlen] =
85 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
86 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
88 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
89 CompanyName = "WidgetCo";
90 ProductName = "asdcp-test";
93 snprintf(s_buf, 128, "%u.%u.%u", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR);
94 ProductVersion = s_buf;
100 // Increment the iterator, test for an additional non-option command line argument.
101 // Causes the caller to return if there are no remaining arguments or if the next
102 // argument begins with '-'.
103 #define TEST_EXTRA_ARG(i,c) if ( ++i >= argc || argv[(i)][0] == '-' ) \
105 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
110 banner(FILE* stream = stdout)
113 %s (asdcplib %s)\n\n\
114 Copyright (c) 2003-2006 John Hurst\n\n\
115 asdcplib may be copied only under the terms of the license found at\n\
116 the top of every file in the asdcplib distribution kit.\n\n\
117 Specify the -h (help) option for further information about %s\n\n",
118 PACKAGE, ASDCP::Version(), PACKAGE);
123 usage(FILE* stream = stdout)
126 USAGE: %s -c <output-file> [-3] [-b <buffer-size>] [-d <duration>] [-e|-E]\n\
127 [-f <start-frame>] [-j <key-id-string>] [-k <key-string>] [-L] [-M]\n\
128 [-p <frame-rate>] [-R] [-s <num>] [-v] [-W]\n\
129 <input-file> [<input-file-2> ...]\n\
131 %s [-h|-help] [-V]\n\
133 %s -i [-H] [-n] [-v] <input-file>\n\
137 %s -G [-v] <input-file>\n\
139 %s -t <input-file>\n\
141 %s -x <file-prefix> [-b <buffer-size>] [-d <duration>]\n\
142 [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <num>] [-S|-1]\n\
143 [-v] [-W] <input-file>\n\
144 \n", PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE);
148 -3 - Create a stereoscopic image file. Expects two dir-\n\
149 ectories of JP2K codestreams (directories must have\n\
150 an equal number of frames; left eye is first).\n\
151 -c <output-file> - Create an AS-DCP track file from input(s)\n\
152 -g - Generate a random 16 byte value to stdout\n\
153 -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\
154 -h | -help - Show help\n\
155 -i - Show file info\n\
156 -t - Calculate message digest of input file\n\
157 -u - Generate a random UUID value to stdout\n\
158 -V - Show version information\n\
159 -x <root-name> - Extract essence from AS-DCP file to named file(s)\n\
164 -e - Encrypt MPEG or JP2K headers (default)\n\
165 -E - Do not encrypt MPEG or JP2K headers\n\
166 -j <key-id-str> - Write key ID instead of creating a random value\n\
167 -k <key-string> - Use key for ciphertext operations\n\
168 -m - verify HMAC values when reading\n\
169 -M - Do not create HMAC values when writing\n\
173 Read/Write Options:\n\
174 -b <buffer-size> - Specify size in bytes of picture frame buffer.\n\
175 Defaults to 4,194,304 (4MB)\n\
176 -d <duration> - Number of frames to process, default all\n\
177 -f <start-frame> - Starting frame number, default 0\n\
178 -L - Write SMPTE UL values instead of MXF Interop\n\
179 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
180 Use one of [23|24|48], 24 is default\n\
181 -R - Repeat the first frame over the entire file (picture\n\
182 essence only, requires -c, -d)\n\
183 -S - Split Wave essence to stereo WAV files during extract.\n\
184 Default is multichannel WAV\n\
185 -1 - Split Wave essence to mono WAV files during extract.\n\
186 Default is multichannel WAV\n\
187 -W - Read input file only, do not write source file\n\
192 -H - Show MXF header metadata, used with option -i\n\
193 -n - Show index, used with option -i\n\
196 -s <num> - Number of bytes of frame buffer to be dumped as hex to\n\
197 stderr, used with option -v\n\
198 -v - Verbose, prints informative messages to stderr\n\
200 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
201 o All option arguments must be separated from the option by whitespace.\n\
202 o An argument of \"23\" to the -p option will be interpreted\n\
203 as 23000/1001 fps.\n\
229 bool error_flag; // true if the given options are in error or not complete
230 bool key_flag; // true if an encryption key was given
231 bool key_id_flag; // true if a key ID was given
232 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
233 bool write_hmac; // true if HMAC values are to be generated and written
234 bool read_hmac; // true if HMAC values are to be validated
235 bool split_wav; // true if PCM is to be extracted to stereo WAV files
236 bool mono_wav; // true if PCM is to be extracted to mono WAV files
237 bool verbose_flag; // true if the verbose option was selected
238 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
239 bool showindex_flag; // true if index is to be displayed
240 bool showheader_flag; // true if MXF file header is to be displayed
241 bool no_write_flag; // true if no output files are to be written
242 bool version_flag; // true if the version display option was selected
243 bool help_flag; // true if the help display option was selected
244 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
245 ui32_t start_frame; // frame number to begin processing
246 ui32_t duration; // number of frames to be processed
247 bool duration_flag; // true if duration argument given
248 bool do_repeat; // if true and -c -d, repeat first input frame
249 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
250 ui32_t picture_rate; // fps of picture when wrapping PCM
251 ui32_t fb_size; // size of picture frame buffer
252 ui32_t file_count; // number of elements in filenames[]
253 const char* file_root; // filename pre for files written by the extract mode
254 const char* out_file; // name of mxf file created by create mode
255 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
256 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
257 const char* filenames[MAX_IN_FILES]; // list of filenames to be processed
260 Rational PictureRate()
262 if ( picture_rate == 23 ) return EditRate_23_98;
263 if ( picture_rate == 48 ) return EditRate_48;
268 const char* szPictureRate()
270 if ( picture_rate == 23 ) return "23.976";
271 if ( picture_rate == 48 ) return "48";
276 CommandOptions(int argc, const char** argv) :
277 mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), encrypt_header_flag(true),
278 write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false),
279 verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false),
280 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false), start_frame(0),
281 duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false),
282 picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0)
284 memset(key_value, 0, KeyLen);
285 memset(key_id_value, 0, UUIDlen);
287 for ( int i = 1; i < argc; i++ )
290 if ( (strcmp( argv[i], "-help") == 0) )
296 if ( argv[i][0] == '-'
297 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
300 switch ( argv[i][1] )
302 case '1': mono_wav = true; break;
303 case '2': split_wav = true; break;
304 case '3': stereo_image_flag = true; break;
305 case 'i': mode = MMT_INFO; break;
306 case 'G': mode = MMT_GOP_START; break;
307 case 'W': no_write_flag = true; break;
308 case 'n': showindex_flag = true; break;
309 case 'H': showheader_flag = true; break;
310 case 'R': do_repeat = true; break;
311 case 'S': split_wav = true; break;
312 case 'V': version_flag = true; break;
313 case 'h': help_flag = true; break;
314 case 'v': verbose_flag = true; break;
315 case 'g': mode = MMT_GEN_KEY; 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 fprintf(stderr, "Hello, stereoscopic world!\n");
712 if ( Options.file_count != 2 )
714 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
718 // set up essence parser
719 Result_t result = ParserLeft.OpenRead(Options.filenames[0]);
721 if ( ASDCP_SUCCESS(result) )
722 result = ParserRight.OpenRead(Options.filenames[1]);
725 if ( ASDCP_SUCCESS(result) )
727 ParserLeft.FillPictureDescriptor(PDesc);
728 PDesc.EditRate = Options.PictureRate();
730 if ( Options.verbose_flag )
732 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
733 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
734 JP2K::PictureDescriptorDump(PDesc);
738 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
740 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
741 Kumu::GenRandomUUID(Info.AssetUUID);
743 if ( Options.use_smpte_labels )
745 Info.LabelSetType = LS_MXF_SMPTE;
746 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
749 // configure encryption
750 if( Options.key_flag )
752 Kumu::GenRandomUUID(Info.ContextID);
753 Info.EncryptedEssence = true;
755 if ( Options.key_id_flag )
756 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
758 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
760 Context = new AESEncContext;
761 result = Context->InitKey(Options.key_value);
763 if ( ASDCP_SUCCESS(result) )
764 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
766 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
768 Info.UsesHMAC = true;
769 HMAC = new HMACContext;
770 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
774 if ( ASDCP_SUCCESS(result) )
775 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
778 if ( ASDCP_SUCCESS(result) )
781 result = ParserLeft.Reset();
782 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
784 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
786 result = ParserLeft.ReadFrame(FrameBuffer);
788 if ( ASDCP_SUCCESS(result) )
790 if ( Options.verbose_flag )
791 FrameBuffer.Dump(stderr, Options.fb_dump_size);
793 if ( Options.encrypt_header_flag )
794 FrameBuffer.PlaintextOffset(0);
797 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
798 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
800 if ( ASDCP_SUCCESS(result) )
801 result = ParserRight.ReadFrame(FrameBuffer);
803 if ( ASDCP_SUCCESS(result) )
805 if ( Options.verbose_flag )
806 FrameBuffer.Dump(stderr, Options.fb_dump_size);
808 if ( Options.encrypt_header_flag )
809 FrameBuffer.PlaintextOffset(0);
812 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
813 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
816 if ( result == RESULT_ENDOFFILE )
820 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
821 result = Writer.Finalize();
826 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
827 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
830 write_JP2K_file(CommandOptions& Options)
832 AESEncContext* Context = 0;
833 HMACContext* HMAC = 0;
834 JP2K::MXFWriter Writer;
835 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
836 JP2K::PictureDescriptor PDesc;
837 JP2K::SequenceParser Parser;
838 byte_t IV_buf[CBC_BLOCK_SIZE];
839 Kumu::FortunaRNG RNG;
841 // set up essence parser
842 Result_t result = Parser.OpenRead(Options.filenames[0]);
845 if ( ASDCP_SUCCESS(result) )
847 Parser.FillPictureDescriptor(PDesc);
848 PDesc.EditRate = Options.PictureRate();
850 if ( Options.verbose_flag )
852 fprintf(stderr, "JPEG 2000 pictures\n");
853 fputs("PictureDescriptor:\n", stderr);
854 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
855 JP2K::PictureDescriptorDump(PDesc);
859 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
861 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
862 Kumu::GenRandomUUID(Info.AssetUUID);
864 if ( Options.use_smpte_labels )
866 Info.LabelSetType = LS_MXF_SMPTE;
867 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
870 // configure encryption
871 if( Options.key_flag )
873 Kumu::GenRandomUUID(Info.ContextID);
874 Info.EncryptedEssence = true;
876 if ( Options.key_id_flag )
877 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
879 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
881 Context = new AESEncContext;
882 result = Context->InitKey(Options.key_value);
884 if ( ASDCP_SUCCESS(result) )
885 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
887 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
889 Info.UsesHMAC = true;
890 HMAC = new HMACContext;
891 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
895 if ( ASDCP_SUCCESS(result) )
896 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
899 if ( ASDCP_SUCCESS(result) )
902 result = Parser.Reset();
904 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
906 if ( ! Options.do_repeat || duration == 1 )
908 result = Parser.ReadFrame(FrameBuffer);
910 if ( ASDCP_SUCCESS(result) )
912 if ( Options.verbose_flag )
913 FrameBuffer.Dump(stderr, Options.fb_dump_size);
915 if ( Options.encrypt_header_flag )
916 FrameBuffer.PlaintextOffset(0);
920 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
922 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
924 // The Writer class will forward the last block of ciphertext
925 // to the encryption context for use as the IV for the next
926 // frame. If you want to use non-sequitur IV values, un-comment
927 // the following line of code.
928 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
929 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
933 if ( result == RESULT_ENDOFFILE )
937 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
938 result = Writer.Finalize();
943 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
944 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
945 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
948 read_JP2K_file(CommandOptions& Options)
950 AESDecContext* Context = 0;
951 HMACContext* HMAC = 0;
952 JP2K::MXFReader Reader;
953 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
954 ui32_t frame_count = 0;
956 Result_t result = Reader.OpenRead(Options.filenames[0]);
958 if ( ASDCP_SUCCESS(result) )
960 JP2K::PictureDescriptor PDesc;
961 Reader.FillPictureDescriptor(PDesc);
963 frame_count = PDesc.ContainerDuration;
965 if ( Options.verbose_flag )
967 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
968 JP2K::PictureDescriptorDump(PDesc);
972 if ( ASDCP_SUCCESS(result) && Options.key_flag )
974 Context = new AESDecContext;
975 result = Context->InitKey(Options.key_value);
977 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
980 Reader.FillWriterInfo(Info);
984 HMAC = new HMACContext;
985 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
989 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
994 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
995 if ( last_frame > frame_count )
996 last_frame = frame_count;
998 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1000 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1002 if ( ASDCP_SUCCESS(result) )
1004 Kumu::FileWriter OutFile;
1007 snprintf(filename, 256, "%s%06u.j2c", Options.file_root, i);
1008 result = OutFile.OpenWrite(filename);
1010 if ( ASDCP_SUCCESS(result) )
1011 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
1013 if ( Options.verbose_flag )
1014 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1021 //------------------------------------------------------------------------------------------
1025 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
1026 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
1029 write_PCM_file(CommandOptions& Options)
1031 AESEncContext* Context = 0;
1032 HMACContext* HMAC = 0;
1033 PCMParserList Parser;
1034 PCM::MXFWriter Writer;
1035 PCM::FrameBuffer FrameBuffer;
1036 PCM::AudioDescriptor ADesc;
1037 Rational PictureRate = Options.PictureRate();
1038 byte_t IV_buf[CBC_BLOCK_SIZE];
1039 Kumu::FortunaRNG RNG;
1041 // set up essence parser
1042 Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate);
1044 // set up MXF writer
1045 if ( ASDCP_SUCCESS(result) )
1047 Parser.FillAudioDescriptor(ADesc);
1049 ADesc.SampleRate = PictureRate;
1050 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1052 if ( Options.verbose_flag )
1054 fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n",
1055 Options.szPictureRate(),
1056 PCM::CalcSamplesPerFrame(ADesc));
1057 fputs("AudioDescriptor:\n", stderr);
1058 PCM::AudioDescriptorDump(ADesc);
1062 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1064 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1065 Kumu::GenRandomUUID(Info.AssetUUID);
1067 if ( Options.use_smpte_labels )
1069 Info.LabelSetType = LS_MXF_SMPTE;
1070 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1073 // configure encryption
1074 if( Options.key_flag )
1076 Kumu::GenRandomUUID(Info.ContextID);
1077 Info.EncryptedEssence = true;
1079 if ( Options.key_id_flag )
1080 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1082 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1084 Context = new AESEncContext;
1085 result = Context->InitKey(Options.key_value);
1087 if ( ASDCP_SUCCESS(result) )
1088 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1090 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1092 Info.UsesHMAC = true;
1093 HMAC = new HMACContext;
1094 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1098 if ( ASDCP_SUCCESS(result) )
1099 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1102 if ( ASDCP_SUCCESS(result) )
1104 result = Parser.Reset();
1105 ui32_t duration = 0;
1107 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1109 result = Parser.ReadFrame(FrameBuffer);
1111 if ( ASDCP_SUCCESS(result) )
1113 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1115 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1116 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1117 result = RESULT_ENDOFFILE;
1121 if ( Options.verbose_flag )
1122 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1124 if ( ! Options.no_write_flag )
1126 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1128 // The Writer class will forward the last block of ciphertext
1129 // to the encryption context for use as the IV for the next
1130 // frame. If you want to use non-sequitur IV values, un-comment
1131 // the following line of code.
1132 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1133 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1138 if ( result == RESULT_ENDOFFILE )
1142 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1143 result = Writer.Finalize();
1148 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
1149 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
1150 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
1153 read_PCM_file(CommandOptions& Options)
1155 AESDecContext* Context = 0;
1156 HMACContext* HMAC = 0;
1157 PCM::MXFReader Reader;
1158 PCM::FrameBuffer FrameBuffer;
1159 WavFileWriter OutWave;
1160 PCM::AudioDescriptor ADesc;
1161 ui32_t last_frame = 0;
1163 Result_t result = Reader.OpenRead(Options.filenames[0]);
1165 if ( ASDCP_SUCCESS(result) )
1167 Reader.FillAudioDescriptor(ADesc);
1169 if ( ADesc.SampleRate != EditRate_23_98
1170 && ADesc.SampleRate != EditRate_24
1171 && ADesc.SampleRate != EditRate_48 )
1172 ADesc.SampleRate = Options.PictureRate();
1174 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1176 if ( Options.verbose_flag )
1177 PCM::AudioDescriptorDump(ADesc);
1180 if ( ASDCP_SUCCESS(result) )
1182 last_frame = ADesc.ContainerDuration;
1184 if ( Options.duration > 0 && Options.duration < last_frame )
1185 last_frame = Options.duration;
1187 if ( Options.start_frame > 0 )
1189 if ( Options.start_frame > ADesc.ContainerDuration )
1191 fprintf(stderr, "Start value greater than file duration.\n");
1195 last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
1198 ADesc.ContainerDuration = last_frame - Options.start_frame;
1199 OutWave.OpenWrite(ADesc, Options.file_root,
1200 ( Options.split_wav ? WavFileWriter::ST_STEREO :
1201 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
1204 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1206 Context = new AESDecContext;
1207 result = Context->InitKey(Options.key_value);
1209 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1212 Reader.FillWriterInfo(Info);
1214 if ( Info.UsesHMAC )
1216 HMAC = new HMACContext;
1217 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1221 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1226 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1228 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1230 if ( ASDCP_SUCCESS(result) )
1232 if ( Options.verbose_flag )
1233 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1235 result = OutWave.WriteFrame(FrameBuffer);
1243 #ifdef ASDCP_WITH_TIMED_TEXT
1245 //------------------------------------------------------------------------------------------
1246 // TimedText essence
1249 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1250 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1253 write_timed_text_file(CommandOptions& Options)
1255 AESEncContext* Context = 0;
1256 HMACContext* HMAC = 0;
1257 TimedText::DCSubtitleParser Parser;
1258 TimedText::MXFWriter Writer;
1259 TimedText::FrameBuffer FrameBuffer;
1260 TimedText::TimedTextDescriptor TDesc;
1261 byte_t IV_buf[CBC_BLOCK_SIZE];
1262 Kumu::FortunaRNG RNG;
1264 // set up essence parser
1265 Result_t result = Parser.OpenRead(Options.filenames[0]);
1267 // set up MXF writer
1268 if ( ASDCP_SUCCESS(result) )
1270 Parser.FillDescriptor(TDesc);
1271 FrameBuffer.Capacity(2*Kumu::Megabyte);
1273 if ( Options.verbose_flag )
1275 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1276 TimedText::DescriptorDump(TDesc);
1280 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1282 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1283 Kumu::GenRandomUUID(Info.AssetUUID);
1285 if ( Options.use_smpte_labels )
1287 Info.LabelSetType = LS_MXF_SMPTE;
1288 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1291 // configure encryption
1292 if( Options.key_flag )
1294 Kumu::GenRandomUUID(Info.ContextID);
1295 Info.EncryptedEssence = true;
1297 if ( Options.key_id_flag )
1298 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1300 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1302 Context = new AESEncContext;
1303 result = Context->InitKey(Options.key_value);
1305 if ( ASDCP_SUCCESS(result) )
1306 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1308 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1310 Info.UsesHMAC = true;
1311 HMAC = new HMACContext;
1312 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1316 if ( ASDCP_SUCCESS(result) )
1317 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1320 if ( ASDCP_FAILURE(result) )
1324 TimedText::ResourceList_t::const_iterator ri;
1326 result = Parser.ReadTimedTextResource(XMLDoc);
1328 if ( ASDCP_SUCCESS(result) )
1329 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1331 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1333 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1335 if ( ASDCP_SUCCESS(result) )
1337 if ( Options.verbose_flag )
1338 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1340 if ( ! Options.no_write_flag )
1342 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1344 // The Writer class will forward the last block of ciphertext
1345 // to the encryption context for use as the IV for the next
1346 // frame. If you want to use non-sequitur IV values, un-comment
1347 // the following line of code.
1348 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1349 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1353 if ( result == RESULT_ENDOFFILE )
1357 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1358 result = Writer.Finalize();
1364 // Read one or more timed text streams from a plaintext ASDCP file
1365 // Read one or more timed text streams from a ciphertext ASDCP file
1366 // Read one or more timed text streams from a ciphertext ASDCP file
1369 read_timed_text_file(CommandOptions& Options)
1371 AESDecContext* Context = 0;
1372 HMACContext* HMAC = 0;
1373 TimedText::MXFReader Reader;
1374 TimedText::FrameBuffer FrameBuffer;
1375 TimedText::TimedTextDescriptor TDesc;
1377 Result_t result = Reader.OpenRead(Options.filenames[0]);
1379 if ( ASDCP_SUCCESS(result) )
1381 Reader.FillDescriptor(TDesc);
1382 FrameBuffer.Capacity(2*Kumu::Megabyte);
1384 if ( Options.verbose_flag )
1385 TimedText::DescriptorDump(TDesc);
1388 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1390 Context = new AESDecContext;
1391 result = Context->InitKey(Options.key_value);
1393 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1396 Reader.FillWriterInfo(Info);
1398 if ( Info.UsesHMAC )
1400 HMAC = new HMACContext;
1401 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1405 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1410 if ( ASDCP_FAILURE(result) )
1414 TimedText::ResourceList_t::const_iterator ri;
1416 result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
1418 // do something with the XML here
1419 fprintf(stderr, "XMLDoc size: %lu\n", XMLDoc.size());
1421 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1423 result = Reader.ReadAncillaryResource((*ri).ResourceID, FrameBuffer, Context, HMAC);
1425 if ( ASDCP_SUCCESS(result) )
1427 // if ( Options.verbose_flag )
1428 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1430 // do something with the resource data here
1436 #endif // ASDCP_WITH_TIMED_TEXT
1438 //------------------------------------------------------------------------------------------
1442 // These classes wrap the irregular names in the asdcplib API
1443 // so that I can use a template to simplify the implementation
1444 // of show_file_info()
1446 class MyVideoDescriptor : public MPEG2::VideoDescriptor
1449 void FillDescriptor(MPEG2::MXFReader& Reader) {
1450 Reader.FillVideoDescriptor(*this);
1453 void Dump(FILE* stream) {
1454 MPEG2::VideoDescriptorDump(*this, stream);
1458 class MyPictureDescriptor : public JP2K::PictureDescriptor
1461 void FillDescriptor(JP2K::MXFReader& Reader) {
1462 Reader.FillPictureDescriptor(*this);
1465 void Dump(FILE* stream) {
1466 JP2K::PictureDescriptorDump(*this, stream);
1470 class MyAudioDescriptor : public PCM::AudioDescriptor
1473 void FillDescriptor(PCM::MXFReader& Reader) {
1474 Reader.FillAudioDescriptor(*this);
1477 void Dump(FILE* stream) {
1478 PCM::AudioDescriptorDump(*this, stream);
1482 #ifdef ASDCP_WITH_TIMED_TEXT
1483 class MyTextDescriptor : public TimedText::TimedTextDescriptor
1486 void FillDescriptor(TimedText::MXFReader& Reader) {
1487 Reader.FillDescriptor(*this);
1490 void Dump(FILE* stream) {
1491 TimedText::DescriptorDump(*this, stream);
1496 // MSVC didn't like the function template, so now it's a static class method
1497 template<class ReaderT, class DescriptorT>
1498 class FileInfoWrapper
1501 static void file_info(CommandOptions& Options, FILE* stream = 0)
1506 if ( Options.verbose_flag || Options.showheader_flag )
1509 Result_t result = Reader.OpenRead(Options.filenames[0]);
1511 if ( ASDCP_SUCCESS(result) )
1513 if ( Options.showheader_flag )
1514 Reader.DumpHeaderMetadata(stream);
1517 Reader.FillWriterInfo(WI);
1518 WriterInfoDump(WI, stream);
1521 Desc.FillDescriptor(Reader);
1524 if ( Options.showindex_flag )
1525 Reader.DumpIndex(stream);
1527 else if ( result == RESULT_FORMAT && Options.showheader_flag )
1529 Reader.DumpHeaderMetadata(stream);
1535 // Read header metadata from an ASDCP file
1538 show_file_info(CommandOptions& Options)
1540 EssenceType_t EssenceType;
1541 Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1543 if ( ASDCP_FAILURE(result) )
1546 if ( EssenceType == ESS_MPEG2_VES )
1548 fputs("File essence type is MPEG2 video.\n", stdout);
1549 FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options);
1551 else if ( EssenceType == ESS_PCM_24b_48k )
1553 fputs("File essence type is PCM audio.\n", stdout);
1554 FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options);
1556 else if ( EssenceType == ESS_JPEG_2000 )
1558 fputs("File essence type is JPEG 2000 pictures.\n", stdout);
1559 FileInfoWrapper<ASDCP::JP2K::MXFReader, MyPictureDescriptor>::file_info(Options);
1561 else if ( EssenceType == ESS_JPEG_2000_S )
1563 fputs("File essence type is JPEG 2000 stereoscopic pictures.\n", stdout);
1564 FileInfoWrapper<ASDCP::JP2K::MXFReader, MyPictureDescriptor>::file_info(Options);
1566 #ifdef ASDCP_WITH_TIMED_TEXT
1567 else if ( EssenceType == ESS_TIMED_TEXT )
1569 fputs("File essence type is Timed Text.\n", stdout);
1570 FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options);
1575 fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]);
1576 Kumu::FileReader Reader;
1577 MXF::OPAtomHeader TestHeader;
1579 result = Reader.OpenRead(Options.filenames[0]);
1581 if ( ASDCP_SUCCESS(result) )
1582 result = TestHeader.InitFromFile(Reader); // test UL and OP
1584 if ( ASDCP_SUCCESS(result) )
1586 TestHeader.Partition::Dump();
1588 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1591 fputs("File contains no Identification object.\n", stdout);
1593 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1596 fputs("File contains no SourcePackage object.\n", stdout);
1600 fputs("File is not MXF.\n", stdout);
1610 digest_file(const char* filename)
1612 using namespace Kumu;
1614 ASDCP_TEST_NULL_STR(filename);
1618 ByteString Buf(8192);
1620 Result_t result = Reader.OpenRead(filename);
1622 while ( ASDCP_SUCCESS(result) )
1624 ui32_t read_count = 0;
1625 result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count);
1627 if ( result == RESULT_ENDOFFILE )
1633 if ( ASDCP_SUCCESS(result) )
1634 SHA1_Update(&Ctx, Buf.Data(), read_count);
1637 if ( ASDCP_SUCCESS(result) )
1639 const ui32_t sha_len = 20;
1640 byte_t bin_buf[sha_len];
1642 SHA1_Final(bin_buf, &Ctx);
1644 fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename);
1652 main(int argc, const char** argv)
1654 Result_t result = RESULT_OK;
1655 CommandOptions Options(argc, argv);
1657 if ( Options.version_flag )
1660 if ( Options.help_flag )
1663 if ( Options.version_flag || Options.help_flag )
1666 if ( Options.error_flag )
1668 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PACKAGE);
1672 if ( Options.mode == MMT_INFO )
1674 result = show_file_info(Options);
1676 else if ( Options.mode == MMT_GOP_START )
1678 result = gop_start_test(Options);
1680 else if ( Options.mode == MMT_GEN_KEY )
1682 Kumu::FortunaRNG RNG;
1683 byte_t bin_buf[KeyLen];
1686 RNG.FillRandom(bin_buf, KeyLen);
1687 printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 40));
1689 else if ( Options.mode == MMT_GEN_ID )
1692 Kumu::GenRandomValue(TmpID);
1694 printf("%s\n", TmpID.EncodeHex(str_buf, 40));
1696 else if ( Options.mode == MMT_DIGEST )
1698 for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ )
1699 result = digest_file(Options.filenames[i]);
1701 else if ( Options.mode == MMT_EXTRACT )
1703 EssenceType_t EssenceType;
1704 result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1706 if ( ASDCP_SUCCESS(result) )
1708 switch ( EssenceType )
1711 result = read_MPEG2_file(Options);
1715 result = read_JP2K_file(Options);
1718 case ESS_PCM_24b_48k:
1719 result = read_PCM_file(Options);
1722 case ESS_TIMED_TEXT:
1723 #ifdef ASDCP_WITH_TIMED_TEXT
1724 result = read_timed_text_file(Options);
1727 fprintf(stderr, "asdcplib compiled without timed text support.\n");
1731 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]);
1736 else if ( Options.mode == MMT_CREATE )
1738 if ( Options.do_repeat && ! Options.duration_flag )
1740 fputs("Option -R requires -d <duration>\n", stderr);
1744 EssenceType_t EssenceType;
1745 result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType);
1747 if ( ASDCP_SUCCESS(result) )
1749 switch ( EssenceType )
1752 result = write_MPEG2_file(Options);
1756 if ( Options.stereo_image_flag )
1757 result = write_JP2K_S_file(Options);
1760 result = write_JP2K_file(Options);
1764 case ESS_PCM_24b_48k:
1765 result = write_PCM_file(Options);
1768 case ESS_TIMED_TEXT:
1769 #ifdef ASDCP_WITH_TIMED_TEXT
1770 result = write_timed_text_file(Options);
1773 fprintf(stderr, "asdcplib compiled without timed text support.\n");
1778 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1779 Options.filenames[0]);
1785 if ( ASDCP_FAILURE(result) )
1787 fputs("Program stopped on error.\n", stderr);
1789 if ( result != RESULT_FAIL )
1791 fputs(result, stderr);
1792 fputc('\n', stderr);
1803 // end asdcp-test.cpp