2 Copyright (c) 2003-2010, John Hurst
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
8 1. Redistributions of source code must retain the above copyright
9 notice, this list of conditions and the following disclaimer.
10 2. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13 3. The name of the author may not be used to endorse or promote products
14 derived from this software without specific prior written permission.
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 /*! \file asdcp-test.cpp
29 \brief AS-DCP file manipulation utility
31 This program provides command line access to the major features of the asdcplib
32 library, and serves as a library unit test which provides the functionality of
33 the supported use cases.
35 For more information about asdcplib, please refer to the header file AS_DCP.h
37 WARNING: While the asdcplib library attempts to provide a complete and secure
38 implementation of the cryptographic features of the AS-DCP file formats, this
39 unit test program is NOT secure and is therefore NOT SUITABLE FOR USE in a
40 production environment without some modification.
42 In particular, this program uses weak IV generation and externally generated
43 plaintext keys. These shortcomings exist because cryptographic-quality
44 random number generation and key management are outside the scope of the
45 asdcplib library. Developers using asdcplib for commercial implementations
46 claiming SMPTE conformance are expected to provide proper implementations of
50 #include <KM_fileio.h>
52 #include <PCMParserList.h>
53 #include <WavFileWriter.h>
56 #include <openssl/sha.h>
61 using namespace ASDCP;
63 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
65 //------------------------------------------------------------------------------------------
67 // command line option parser class
69 static const char* PROGRAM_NAME = "asdcp-test"; // program name for messages
70 const ui32_t MAX_IN_FILES = 16; // maximum number of input files handled by
71 // the command option parser
73 // local program identification info written to file headers
74 class MyInfo : public WriterInfo
79 static byte_t default_ProductUUID_Data[UUIDlen] =
80 { 0x7d, 0x83, 0x6e, 0x16, 0x37, 0xc7, 0x4c, 0x22,
81 0xb2, 0xe0, 0x46, 0xa7, 0x17, 0xe8, 0x4f, 0x42 };
83 memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
84 CompanyName = "WidgetCo";
85 ProductName = "asdcp-test";
86 ProductVersion = ASDCP::Version();
92 // Increment the iterator, test for an additional non-option command line argument.
93 // Causes the caller to return if there are no remaining arguments or if the next
94 // argument begins with '-'.
95 #define TEST_EXTRA_ARG(i,c) if ( ++i >= argc || argv[(i)][0] == '-' ) \
97 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
102 banner(FILE* stream = stdout)
105 %s (asdcplib %s)\n\n\
106 Copyright (c) 2003-2010 John Hurst\n\n\
107 asdcplib may be copied only under the terms of the license found at\n\
108 the top of every file in the asdcplib distribution kit.\n\n\
109 Specify the -h (help) option for further information about %s\n\n",
110 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
115 usage(FILE* stream = stdout)
118 USAGE: %s -c <output-file> [-3] [-a <uuid>] [-b <buffer-size>]\n\
119 [-d <duration>] [-e|-E] [-f <start-frame>] [-j <key-id-string>]\n\
120 [-k <key-string>] [-l <label>] [-L] [-M] [-p <frame-rate>] [-R]\n\
121 [-s <num>] [-v] [-W] [-z|-Z] <input-file> [<input-file-2> ...]\n\
123 %s [-h|-help] [-V]\n\
125 %s -i [-H] [-n] [-v] <input-file>\n\
129 %s -G [-v] <input-file>\n\
131 %s -t <input-file>\n\
133 %s -x <file-prefix> [-3] [-b <buffer-size>] [-d <duration>]\n\
134 [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <num>] [-S|-1]\n\
135 [-v] [-W] [-w] <input-file>\n\
136 \n", PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
140 -3 - With -c, create a stereoscopic image file. Expects two\n\
141 directories of JP2K codestreams (directories must have\n\
142 an equal number of frames; left eye is first).\n\
143 - With -x, force stereoscopic interpretation of a JP2K\n\
145 -c <output-file> - Create an AS-DCP track file from input(s)\n\
146 -g - Generate a random 16 byte value to stdout\n\
147 -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\
148 -h | -help - Show help\n\
149 -i - Show file info\n\
150 -t - Calculate message digest of input file\n\
151 -U - Dump UL catalog to stdout\n\
152 -u - Generate a random UUID value to stdout\n\
153 -V - Show version information\n\
154 -x <root-name> - Extract essence from AS-DCP file to named file(s)\n\
159 -e - Encrypt MPEG or JP2K headers (default)\n\
160 -E - Do not encrypt MPEG or JP2K headers\n\
161 -j <key-id-str> - Write key ID instead of creating a random value\n\
162 -k <key-string> - Use key for ciphertext operations\n\
163 -m - verify HMAC values when reading\n\
164 -M - Do not create HMAC values when writing\n\
168 Read/Write Options:\n\
169 -a <UUID> - Specify the Asset ID of a file (with -c)\n\
170 -b <buffer-size> - Specify size in bytes of picture frame buffer.\n\
171 Defaults to 4,194,304 (4MB)\n\
172 -d <duration> - Number of frames to process, default all\n\
173 -f <start-frame> - Starting frame number, default 0\n\
174 -l <label> - Use given channel format label when writing MXF sound\n\
175 files. SMPTE 429-2 labels: '5.1', '6.1', '7.1'. Default\n\
176 is no label (valid for Interop only).\n\
177 -L - Write SMPTE UL values instead of MXF Interop\n\
178 -p <rate> - fps of picture when wrapping PCM or JP2K:\n\
179 Use one of [23|24|48], 24 is default\n\
180 -R - Repeat the first frame over the entire file (picture\n\
181 essence only, requires -c, -d)\n\
182 -S - Split Wave essence to stereo WAV files during extract.\n\
183 Default is multichannel WAV\n\
184 -1 - Split Wave essence to mono WAV files during extract.\n\
185 Default is multichannel WAV\n\
186 -W - Read input file only, do not write source file\n\
187 -w <width> - Width of numeric element in a series of frame file names\n\
188 (use with -x, default 6).\n\
189 -z - Fail if j2c inputs have unequal parameters (default)\n\
190 -Z - Ignore unequal parameters in j2c inputs\n\
195 -H - Show MXF header metadata, used with option -i\n\
196 -n - Show index, used with option -i\n\
199 -s <num> - Number of bytes of frame buffer to be dumped as hex to\n\
200 stderr, used with option -v\n\
201 -v - Verbose, prints informative messages to stderr\n\
203 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
204 o All option arguments must be separated from the option by whitespace.\n\
205 o An argument of \"23\" to the -p option will be interpreted\n\
206 as 23000/1001 fps.\n\
226 decode_channel_fmt(const std::string& label_name)
228 if ( label_name == "5.1" )
229 return PCM::CF_CFG_1;
231 else if ( label_name == "6.1" )
232 return PCM::CF_CFG_2;
234 else if ( label_name == "7.1" )
235 return PCM::CF_CFG_3;
237 fprintf(stderr, "Error decoding channel format string: %s\n", label_name.c_str());
238 fprintf(stderr, "Expecting '5.1', '6.1', or '7.1'\n");
250 bool error_flag; // true if the given options are in error or not complete
251 bool key_flag; // true if an encryption key was given
252 bool key_id_flag; // true if a key ID was given
253 bool asset_id_flag; // true if an asset ID was given
254 bool encrypt_header_flag; // true if mpeg headers are to be encrypted
255 bool write_hmac; // true if HMAC values are to be generated and written
256 bool read_hmac; // true if HMAC values are to be validated
257 bool split_wav; // true if PCM is to be extracted to stereo WAV files
258 bool mono_wav; // true if PCM is to be extracted to mono WAV files
259 bool verbose_flag; // true if the verbose option was selected
260 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
261 bool showindex_flag; // true if index is to be displayed
262 bool showheader_flag; // true if MXF file header is to be displayed
263 bool no_write_flag; // true if no output files are to be written
264 bool version_flag; // true if the version display option was selected
265 bool help_flag; // true if the help display option was selected
266 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
267 ui32_t number_width; // number of digits in a serialized filename (for JPEG extract)
268 ui32_t start_frame; // frame number to begin processing
269 ui32_t duration; // number of frames to be processed
270 bool duration_flag; // true if duration argument given
271 bool do_repeat; // if true and -c -d, repeat first input frame
272 bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
273 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
274 ui32_t picture_rate; // fps of picture when wrapping PCM
275 ui32_t fb_size; // size of picture frame buffer
276 ui32_t file_count; // number of elements in filenames[]
277 const char* file_root; // filename pre for files written by the extract mode
278 const char* out_file; // name of mxf file created by create mode
279 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
280 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
281 byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
282 const char* filenames[MAX_IN_FILES]; // list of filenames to be processed
283 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
286 Rational PictureRate()
288 if ( picture_rate == 23 ) return EditRate_23_98;
289 if ( picture_rate == 48 ) return EditRate_48;
294 const char* szPictureRate()
296 if ( picture_rate == 23 ) return "23.976";
297 if ( picture_rate == 48 ) return "48";
302 CommandOptions(int argc, const char** argv) :
303 mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
304 encrypt_header_flag(true), write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false),
305 verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false),
306 no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false),
307 number_width(6), start_frame(0),
308 duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false), j2c_pedantic(true),
309 picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0),
310 channel_fmt(PCM::CF_NONE)
312 memset(key_value, 0, KeyLen);
313 memset(key_id_value, 0, UUIDlen);
315 for ( int i = 1; i < argc; i++ )
318 if ( (strcmp( argv[i], "-help") == 0) )
324 if ( argv[i][0] == '-'
325 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
328 switch ( argv[i][1] )
330 case '1': mono_wav = true; break;
331 case '2': split_wav = true; break;
332 case '3': stereo_image_flag = true; break;
335 asset_id_flag = true;
336 TEST_EXTRA_ARG(i, 'a');
339 Kumu::hex2bin(argv[i], asset_id_value, UUIDlen, &length);
341 if ( length != UUIDlen )
343 fprintf(stderr, "Unexpected asset ID length: %u, expecting %u characters.\n", length, UUIDlen);
350 TEST_EXTRA_ARG(i, 'b');
351 fb_size = abs(atoi(argv[i]));
354 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
359 TEST_EXTRA_ARG(i, 'c');
365 TEST_EXTRA_ARG(i, 'd');
366 duration_flag = true;
367 duration = abs(atoi(argv[i]));
370 case 'E': encrypt_header_flag = false; break;
371 case 'e': encrypt_header_flag = true; break;
374 TEST_EXTRA_ARG(i, 'f');
375 start_frame = abs(atoi(argv[i]));
378 case 'G': mode = MMT_GOP_START; break;
379 case 'g': mode = MMT_GEN_KEY; break;
380 case 'H': showheader_flag = true; break;
381 case 'h': help_flag = true; break;
382 case 'i': mode = MMT_INFO; break;
384 case 'j': key_id_flag = true;
385 TEST_EXTRA_ARG(i, 'j');
388 Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length);
390 if ( length != UUIDlen )
392 fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen);
398 case 'k': key_flag = true;
399 TEST_EXTRA_ARG(i, 'k');
402 Kumu::hex2bin(argv[i], key_value, KeyLen, &length);
404 if ( length != KeyLen )
406 fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen);
413 TEST_EXTRA_ARG(i, 'l');
414 channel_fmt = decode_channel_fmt(argv[i]);
417 case 'L': use_smpte_labels = true; break;
418 case 'M': write_hmac = false; break;
419 case 'm': read_hmac = true; break;
420 case 'n': showindex_flag = true; break;
423 TEST_EXTRA_ARG(i, 'p');
424 picture_rate = abs(atoi(argv[i]));
427 case 'R': do_repeat = true; break;
428 case 'S': split_wav = true; break;
431 TEST_EXTRA_ARG(i, 's');
432 fb_dump_size = abs(atoi(argv[i]));
435 case 't': mode = MMT_DIGEST; break;
436 case 'U': mode = MMT_UL_LIST; break;
437 case 'u': mode = MMT_GEN_ID; break;
438 case 'V': version_flag = true; break;
439 case 'v': verbose_flag = true; break;
440 case 'W': no_write_flag = true; break;
443 TEST_EXTRA_ARG(i, 'w');
444 number_width = abs(atoi(argv[i]));
448 TEST_EXTRA_ARG(i, 'x');
453 case 'Z': j2c_pedantic = false; break;
454 case 'z': j2c_pedantic = true; break;
457 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
464 if ( argv[i][0] != '-' )
466 filenames[file_count++] = argv[i];
470 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
474 if ( file_count >= MAX_IN_FILES )
476 fprintf(stderr, "Filename lists exceeds maximum list size: %u\n", MAX_IN_FILES);
482 if ( help_flag || version_flag )
485 if ( ( mode == MMT_INFO
486 || mode == MMT_CREATE
487 || mode == MMT_EXTRACT
488 || mode == MMT_GOP_START
489 || mode == MMT_DIGEST ) && file_count == 0 )
491 fputs("Option requires at least one filename argument.\n", stderr);
495 if ( mode == MMT_NONE && ! help_flag && ! version_flag )
497 fputs("No operation selected (use one of -[gGcitux] or -h for help).\n", stderr);
505 //------------------------------------------------------------------------------------------
508 // Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
509 // Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
512 write_MPEG2_file(CommandOptions& Options)
514 AESEncContext* Context = 0;
515 HMACContext* HMAC = 0;
516 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
517 MPEG2::Parser Parser;
518 MPEG2::MXFWriter Writer;
519 MPEG2::VideoDescriptor VDesc;
520 byte_t IV_buf[CBC_BLOCK_SIZE];
521 Kumu::FortunaRNG RNG;
523 // set up essence parser
524 Result_t result = Parser.OpenRead(Options.filenames[0]);
527 if ( ASDCP_SUCCESS(result) )
529 Parser.FillVideoDescriptor(VDesc);
531 if ( Options.verbose_flag )
533 fputs("MPEG-2 Pictures\n", stderr);
534 fputs("VideoDescriptor:\n", stderr);
535 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
536 MPEG2::VideoDescriptorDump(VDesc);
540 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
542 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
543 if ( Options.asset_id_flag )
544 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
546 Kumu::GenRandomUUID(Info.AssetUUID);
548 if ( Options.use_smpte_labels )
550 Info.LabelSetType = LS_MXF_SMPTE;
551 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
554 // configure encryption
555 if( Options.key_flag )
557 Kumu::GenRandomUUID(Info.ContextID);
558 Info.EncryptedEssence = true;
560 if ( Options.key_id_flag )
561 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
563 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
565 Context = new AESEncContext;
566 result = Context->InitKey(Options.key_value);
568 if ( ASDCP_SUCCESS(result) )
569 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
571 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
573 Info.UsesHMAC = true;
574 HMAC = new HMACContext;
575 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
579 if ( ASDCP_SUCCESS(result) )
580 result = Writer.OpenWrite(Options.out_file, Info, VDesc);
583 if ( ASDCP_SUCCESS(result) )
584 // loop through the frames
586 result = Parser.Reset();
589 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
591 if ( ! Options.do_repeat || duration == 1 )
593 result = Parser.ReadFrame(FrameBuffer);
595 if ( ASDCP_SUCCESS(result) )
597 if ( Options.verbose_flag )
598 FrameBuffer.Dump(stderr, Options.fb_dump_size);
600 if ( Options.encrypt_header_flag )
601 FrameBuffer.PlaintextOffset(0);
605 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
607 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
609 // The Writer class will forward the last block of ciphertext
610 // to the encryption context for use as the IV for the next
611 // frame. If you want to use non-sequitur IV values, un-comment
612 // the following line of code.
613 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
614 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
618 if ( result == RESULT_ENDOFFILE )
622 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
623 result = Writer.Finalize();
628 // Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file
629 // Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
630 // Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
633 read_MPEG2_file(CommandOptions& Options)
635 AESDecContext* Context = 0;
636 HMACContext* HMAC = 0;
637 MPEG2::MXFReader Reader;
638 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
639 Kumu::FileWriter OutFile;
640 ui32_t frame_count = 0;
642 Result_t result = Reader.OpenRead(Options.filenames[0]);
644 if ( ASDCP_SUCCESS(result) )
646 MPEG2::VideoDescriptor VDesc;
647 Reader.FillVideoDescriptor(VDesc);
648 frame_count = VDesc.ContainerDuration;
650 if ( Options.verbose_flag )
652 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
653 MPEG2::VideoDescriptorDump(VDesc);
657 if ( ASDCP_SUCCESS(result) )
660 snprintf(filename, 256, "%s.ves", Options.file_root);
661 result = OutFile.OpenWrite(filename);
664 if ( ASDCP_SUCCESS(result) && Options.key_flag )
666 Context = new AESDecContext;
667 result = Context->InitKey(Options.key_value);
669 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
672 Reader.FillWriterInfo(Info);
676 HMAC = new HMACContext;
677 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
681 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
686 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
687 if ( last_frame > frame_count )
688 last_frame = frame_count;
690 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
692 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
694 if ( ASDCP_SUCCESS(result) )
696 if ( Options.verbose_flag )
697 FrameBuffer.Dump(stderr, Options.fb_dump_size);
699 ui32_t write_count = 0;
700 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
710 gop_start_test(CommandOptions& Options)
712 using namespace ASDCP::MPEG2;
715 MPEG2::FrameBuffer FrameBuffer(Options.fb_size);
716 ui32_t frame_count = 0;
718 Result_t result = Reader.OpenRead(Options.filenames[0]);
720 if ( ASDCP_SUCCESS(result) )
722 MPEG2::VideoDescriptor VDesc;
723 Reader.FillVideoDescriptor(VDesc);
724 frame_count = VDesc.ContainerDuration;
726 if ( Options.verbose_flag )
728 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
729 MPEG2::VideoDescriptorDump(VDesc);
733 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
734 if ( last_frame > frame_count )
735 last_frame = frame_count;
737 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
739 result = Reader.ReadFrameGOPStart(i, FrameBuffer);
741 if ( ASDCP_SUCCESS(result) )
743 if ( Options.verbose_flag )
744 FrameBuffer.Dump(stderr, Options.fb_dump_size);
746 if ( FrameBuffer.FrameType() != FRAME_I )
747 fprintf(stderr, "Expecting an I frame, got %c\n", FrameTypeChar(FrameBuffer.FrameType()));
749 fprintf(stderr, "Requested frame %u, got %u\n", i, FrameBuffer.FrameNumber());
756 //------------------------------------------------------------------------------------------
759 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file
760 // Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file
763 write_JP2K_S_file(CommandOptions& Options)
765 AESEncContext* Context = 0;
766 HMACContext* HMAC = 0;
767 JP2K::MXFSWriter Writer;
768 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
769 JP2K::PictureDescriptor PDesc;
770 JP2K::SequenceParser ParserLeft, ParserRight;
771 byte_t IV_buf[CBC_BLOCK_SIZE];
772 Kumu::FortunaRNG RNG;
774 if ( Options.file_count != 2 )
776 fprintf(stderr, "Two inputs are required for stereoscopic option.\n");
780 // set up essence parser
781 Result_t result = ParserLeft.OpenRead(Options.filenames[0], Options.j2c_pedantic);
783 if ( ASDCP_SUCCESS(result) )
784 result = ParserRight.OpenRead(Options.filenames[1], Options.j2c_pedantic);
787 if ( ASDCP_SUCCESS(result) )
789 ParserLeft.FillPictureDescriptor(PDesc);
790 PDesc.EditRate = Options.PictureRate();
792 if ( Options.verbose_flag )
794 fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr);
795 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
796 JP2K::PictureDescriptorDump(PDesc);
800 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
802 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
803 if ( Options.asset_id_flag )
804 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
806 Kumu::GenRandomUUID(Info.AssetUUID);
808 if ( Options.use_smpte_labels )
810 Info.LabelSetType = LS_MXF_SMPTE;
811 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
814 // configure encryption
815 if( Options.key_flag )
817 Kumu::GenRandomUUID(Info.ContextID);
818 Info.EncryptedEssence = true;
820 if ( Options.key_id_flag )
821 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
823 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
825 Context = new AESEncContext;
826 result = Context->InitKey(Options.key_value);
828 if ( ASDCP_SUCCESS(result) )
829 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
831 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
833 Info.UsesHMAC = true;
834 HMAC = new HMACContext;
835 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
839 if ( ASDCP_SUCCESS(result) )
840 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
843 if ( ASDCP_SUCCESS(result) )
846 result = ParserLeft.Reset();
847 if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset();
849 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
851 result = ParserLeft.ReadFrame(FrameBuffer);
853 if ( ASDCP_SUCCESS(result) )
855 if ( Options.verbose_flag )
856 FrameBuffer.Dump(stderr, Options.fb_dump_size);
858 if ( Options.encrypt_header_flag )
859 FrameBuffer.PlaintextOffset(0);
862 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
863 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC);
865 if ( ASDCP_SUCCESS(result) )
866 result = ParserRight.ReadFrame(FrameBuffer);
868 if ( ASDCP_SUCCESS(result) )
870 if ( Options.verbose_flag )
871 FrameBuffer.Dump(stderr, Options.fb_dump_size);
873 if ( Options.encrypt_header_flag )
874 FrameBuffer.PlaintextOffset(0);
877 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
878 result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC);
881 if ( result == RESULT_ENDOFFILE )
885 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
886 result = Writer.Finalize();
891 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a plaintext ASDCP file
892 // Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
893 // Read one or more ciphertext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file
895 read_JP2K_S_file(CommandOptions& Options)
897 AESDecContext* Context = 0;
898 HMACContext* HMAC = 0;
899 JP2K::MXFSReader Reader;
900 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
901 ui32_t frame_count = 0;
903 Result_t result = Reader.OpenRead(Options.filenames[0]);
905 if ( ASDCP_SUCCESS(result) )
907 JP2K::PictureDescriptor PDesc;
908 Reader.FillPictureDescriptor(PDesc);
910 frame_count = PDesc.ContainerDuration;
912 if ( Options.verbose_flag )
914 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
915 JP2K::PictureDescriptorDump(PDesc);
919 if ( ASDCP_SUCCESS(result) && Options.key_flag )
921 Context = new AESDecContext;
922 result = Context->InitKey(Options.key_value);
924 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
927 Reader.FillWriterInfo(Info);
931 HMAC = new HMACContext;
932 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
936 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
941 const int filename_max = 1024;
942 char filename[filename_max];
943 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
944 if ( last_frame > frame_count )
945 last_frame = frame_count;
947 char left_format[64]; char right_format[64];
948 snprintf(left_format, 64, "%%s%%0%duL.j2c", Options.number_width);
949 snprintf(right_format, 64, "%%s%%0%duR.j2c", Options.number_width);
951 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
953 result = Reader.ReadFrame(i, JP2K::SP_LEFT, FrameBuffer, Context, HMAC);
955 if ( ASDCP_SUCCESS(result) )
957 Kumu::FileWriter OutFile;
959 snprintf(filename, filename_max, left_format, Options.file_root, i);
960 result = OutFile.OpenWrite(filename);
962 if ( ASDCP_SUCCESS(result) )
963 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
965 if ( Options.verbose_flag )
966 FrameBuffer.Dump(stderr, Options.fb_dump_size);
969 if ( ASDCP_SUCCESS(result) )
970 result = Reader.ReadFrame(i, JP2K::SP_RIGHT, FrameBuffer, Context, HMAC);
972 if ( ASDCP_SUCCESS(result) )
974 Kumu::FileWriter OutFile;
976 snprintf(filename, filename_max, right_format, Options.file_root, i);
977 result = OutFile.OpenWrite(filename);
979 if ( ASDCP_SUCCESS(result) )
980 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
989 // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
990 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
993 write_JP2K_file(CommandOptions& Options)
995 AESEncContext* Context = 0;
996 HMACContext* HMAC = 0;
997 JP2K::MXFWriter Writer;
998 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
999 JP2K::PictureDescriptor PDesc;
1000 JP2K::SequenceParser Parser;
1001 byte_t IV_buf[CBC_BLOCK_SIZE];
1002 Kumu::FortunaRNG RNG;
1004 // set up essence parser
1005 Result_t result = Parser.OpenRead(Options.filenames[0], Options.j2c_pedantic);
1007 // set up MXF writer
1008 if ( ASDCP_SUCCESS(result) )
1010 Parser.FillPictureDescriptor(PDesc);
1011 PDesc.EditRate = Options.PictureRate();
1013 if ( Options.verbose_flag )
1015 fprintf(stderr, "JPEG 2000 pictures\n");
1016 fputs("PictureDescriptor:\n", stderr);
1017 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1018 JP2K::PictureDescriptorDump(PDesc);
1022 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1024 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1025 if ( Options.asset_id_flag )
1026 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1028 Kumu::GenRandomUUID(Info.AssetUUID);
1030 if ( Options.use_smpte_labels )
1032 Info.LabelSetType = LS_MXF_SMPTE;
1033 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1036 // configure encryption
1037 if( Options.key_flag )
1039 Kumu::GenRandomUUID(Info.ContextID);
1040 Info.EncryptedEssence = true;
1042 if ( Options.key_id_flag )
1043 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1045 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1047 Context = new AESEncContext;
1048 result = Context->InitKey(Options.key_value);
1050 if ( ASDCP_SUCCESS(result) )
1051 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1053 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1055 Info.UsesHMAC = true;
1056 HMAC = new HMACContext;
1057 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1061 if ( ASDCP_SUCCESS(result) )
1062 result = Writer.OpenWrite(Options.out_file, Info, PDesc);
1065 if ( ASDCP_SUCCESS(result) )
1067 ui32_t duration = 0;
1068 result = Parser.Reset();
1070 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1072 if ( ! Options.do_repeat || duration == 1 )
1074 result = Parser.ReadFrame(FrameBuffer);
1076 if ( ASDCP_SUCCESS(result) )
1078 if ( Options.verbose_flag )
1079 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1081 if ( Options.encrypt_header_flag )
1082 FrameBuffer.PlaintextOffset(0);
1086 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1088 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1090 // The Writer class will forward the last block of ciphertext
1091 // to the encryption context for use as the IV for the next
1092 // frame. If you want to use non-sequitur IV values, un-comment
1093 // the following line of code.
1094 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1095 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1099 if ( result == RESULT_ENDOFFILE )
1103 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1104 result = Writer.Finalize();
1109 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
1110 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
1111 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
1114 read_JP2K_file(CommandOptions& Options)
1116 AESDecContext* Context = 0;
1117 HMACContext* HMAC = 0;
1118 JP2K::MXFReader Reader;
1119 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
1120 ui32_t frame_count = 0;
1122 Result_t result = Reader.OpenRead(Options.filenames[0]);
1124 if ( ASDCP_SUCCESS(result) )
1126 JP2K::PictureDescriptor PDesc;
1127 Reader.FillPictureDescriptor(PDesc);
1129 frame_count = PDesc.ContainerDuration;
1131 if ( Options.verbose_flag )
1133 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
1134 JP2K::PictureDescriptorDump(PDesc);
1138 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1140 Context = new AESDecContext;
1141 result = Context->InitKey(Options.key_value);
1143 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1146 Reader.FillWriterInfo(Info);
1148 if ( Info.UsesHMAC )
1150 HMAC = new HMACContext;
1151 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1155 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1160 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
1161 if ( last_frame > frame_count )
1162 last_frame = frame_count;
1164 char name_format[64];
1165 snprintf(name_format, 64, "%%s%%0%du.j2c", Options.number_width);
1167 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1169 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1171 if ( ASDCP_SUCCESS(result) )
1173 Kumu::FileWriter OutFile;
1176 snprintf(filename, 256, name_format, Options.file_root, i);
1177 result = OutFile.OpenWrite(filename);
1179 if ( ASDCP_SUCCESS(result) )
1180 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
1182 if ( Options.verbose_flag )
1183 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1190 //------------------------------------------------------------------------------------------
1194 // Write one or more plaintext PCM audio streams to a plaintext ASDCP file
1195 // Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
1198 write_PCM_file(CommandOptions& Options)
1200 AESEncContext* Context = 0;
1201 HMACContext* HMAC = 0;
1202 PCMParserList Parser;
1203 PCM::MXFWriter Writer;
1204 PCM::FrameBuffer FrameBuffer;
1205 PCM::AudioDescriptor ADesc;
1206 Rational PictureRate = Options.PictureRate();
1207 byte_t IV_buf[CBC_BLOCK_SIZE];
1208 Kumu::FortunaRNG RNG;
1210 // set up essence parser
1211 Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate);
1213 // set up MXF writer
1214 if ( ASDCP_SUCCESS(result) )
1216 Parser.FillAudioDescriptor(ADesc);
1218 ADesc.EditRate = PictureRate;
1219 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1220 ADesc.ChannelFormat = Options.channel_fmt;
1222 if ( Options.use_smpte_labels && ADesc.ChannelFormat == PCM::CF_NONE)
1224 fprintf(stderr, "ATTENTION! Writing SMPTE audio without ChannelAssignment property (see option -l)\n");
1227 if ( Options.verbose_flag )
1229 fprintf(stderr, "%.1fkHz PCM Audio, %s fps (%u spf)\n",
1230 ADesc.AudioSamplingRate.Quotient() / 1000.0,
1231 Options.szPictureRate(),
1232 PCM::CalcSamplesPerFrame(ADesc));
1233 fputs("AudioDescriptor:\n", stderr);
1234 PCM::AudioDescriptorDump(ADesc);
1238 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1240 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1241 if ( Options.asset_id_flag )
1242 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1244 Kumu::GenRandomUUID(Info.AssetUUID);
1246 if ( Options.use_smpte_labels )
1248 Info.LabelSetType = LS_MXF_SMPTE;
1249 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1252 // configure encryption
1253 if( Options.key_flag )
1255 Kumu::GenRandomUUID(Info.ContextID);
1256 Info.EncryptedEssence = true;
1258 if ( Options.key_id_flag )
1259 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1261 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1263 Context = new AESEncContext;
1264 result = Context->InitKey(Options.key_value);
1266 if ( ASDCP_SUCCESS(result) )
1267 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1269 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1271 Info.UsesHMAC = true;
1272 HMAC = new HMACContext;
1273 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1277 if ( ASDCP_SUCCESS(result) )
1278 result = Writer.OpenWrite(Options.out_file, Info, ADesc);
1281 if ( ASDCP_SUCCESS(result) )
1283 result = Parser.Reset();
1284 ui32_t duration = 0;
1286 while ( ASDCP_SUCCESS(result) && duration++ < Options.duration )
1288 result = Parser.ReadFrame(FrameBuffer);
1290 if ( ASDCP_SUCCESS(result) )
1292 if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
1294 fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n");
1295 fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size());
1296 result = RESULT_ENDOFFILE;
1300 if ( Options.verbose_flag )
1301 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1303 if ( ! Options.no_write_flag )
1305 result = Writer.WriteFrame(FrameBuffer, Context, HMAC);
1307 // The Writer class will forward the last block of ciphertext
1308 // to the encryption context for use as the IV for the next
1309 // frame. If you want to use non-sequitur IV values, un-comment
1310 // the following line of code.
1311 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1312 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1317 if ( result == RESULT_ENDOFFILE )
1321 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1322 result = Writer.Finalize();
1327 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
1328 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
1329 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
1332 read_PCM_file(CommandOptions& Options)
1334 AESDecContext* Context = 0;
1335 HMACContext* HMAC = 0;
1336 PCM::MXFReader Reader;
1337 PCM::FrameBuffer FrameBuffer;
1338 WavFileWriter OutWave;
1339 PCM::AudioDescriptor ADesc;
1340 ui32_t last_frame = 0;
1342 Result_t result = Reader.OpenRead(Options.filenames[0]);
1344 if ( ASDCP_SUCCESS(result) )
1346 Reader.FillAudioDescriptor(ADesc);
1348 if ( ADesc.EditRate != EditRate_23_98
1349 && ADesc.EditRate != EditRate_24
1350 && ADesc.EditRate != EditRate_25
1351 && ADesc.EditRate != EditRate_48
1352 && ADesc.EditRate != EditRate_50
1353 && ADesc.EditRate != EditRate_60 )
1354 ADesc.EditRate = Options.PictureRate();
1356 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1358 if ( Options.verbose_flag )
1359 PCM::AudioDescriptorDump(ADesc);
1362 if ( ASDCP_SUCCESS(result) )
1364 last_frame = ADesc.ContainerDuration;
1366 if ( Options.duration > 0 && Options.duration < last_frame )
1367 last_frame = Options.duration;
1369 if ( Options.start_frame > 0 )
1371 if ( Options.start_frame > ADesc.ContainerDuration )
1373 fprintf(stderr, "Start value greater than file duration.\n");
1377 last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
1380 ADesc.ContainerDuration = last_frame - Options.start_frame;
1381 OutWave.OpenWrite(ADesc, Options.file_root,
1382 ( Options.split_wav ? WavFileWriter::ST_STEREO :
1383 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
1386 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1388 Context = new AESDecContext;
1389 result = Context->InitKey(Options.key_value);
1391 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1394 Reader.FillWriterInfo(Info);
1396 if ( Info.UsesHMAC )
1398 HMAC = new HMACContext;
1399 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1403 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1408 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1410 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1412 if ( ASDCP_SUCCESS(result) )
1414 if ( Options.verbose_flag )
1415 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1417 result = OutWave.WriteFrame(FrameBuffer);
1425 //------------------------------------------------------------------------------------------
1426 // TimedText essence
1429 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1430 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1433 write_timed_text_file(CommandOptions& Options)
1435 AESEncContext* Context = 0;
1436 HMACContext* HMAC = 0;
1437 TimedText::DCSubtitleParser Parser;
1438 TimedText::MXFWriter Writer;
1439 TimedText::FrameBuffer FrameBuffer;
1440 TimedText::TimedTextDescriptor TDesc;
1441 byte_t IV_buf[CBC_BLOCK_SIZE];
1442 Kumu::FortunaRNG RNG;
1444 // set up essence parser
1445 Result_t result = Parser.OpenRead(Options.filenames[0]);
1447 // set up MXF writer
1448 if ( ASDCP_SUCCESS(result) )
1450 Parser.FillTimedTextDescriptor(TDesc);
1451 FrameBuffer.Capacity(2*Kumu::Megabyte);
1453 if ( Options.verbose_flag )
1455 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1456 TimedText::DescriptorDump(TDesc);
1460 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1462 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1463 if ( Options.asset_id_flag )
1464 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1466 Kumu::GenRandomUUID(Info.AssetUUID);
1468 if ( Options.use_smpte_labels )
1470 Info.LabelSetType = LS_MXF_SMPTE;
1471 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1474 // configure encryption
1475 if( Options.key_flag )
1477 Kumu::GenRandomUUID(Info.ContextID);
1478 Info.EncryptedEssence = true;
1480 if ( Options.key_id_flag )
1481 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1483 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1485 Context = new AESEncContext;
1486 result = Context->InitKey(Options.key_value);
1488 if ( ASDCP_SUCCESS(result) )
1489 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1491 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1493 Info.UsesHMAC = true;
1494 HMAC = new HMACContext;
1495 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1499 if ( ASDCP_SUCCESS(result) )
1500 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1503 if ( ASDCP_FAILURE(result) )
1507 TimedText::ResourceList_t::const_iterator ri;
1509 result = Parser.ReadTimedTextResource(XMLDoc);
1511 if ( ASDCP_SUCCESS(result) )
1512 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1514 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1516 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1518 if ( ASDCP_SUCCESS(result) )
1520 if ( Options.verbose_flag )
1521 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1523 if ( ! Options.no_write_flag )
1525 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1527 // The Writer class will forward the last block of ciphertext
1528 // to the encryption context for use as the IV for the next
1529 // frame. If you want to use non-sequitur IV values, un-comment
1530 // the following line of code.
1531 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1532 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1536 if ( result == RESULT_ENDOFFILE )
1540 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1541 result = Writer.Finalize();
1547 // Read one or more timed text streams from a plaintext ASDCP file
1548 // Read one or more timed text streams from a ciphertext ASDCP file
1549 // Read one or more timed text streams from a ciphertext ASDCP file
1552 read_timed_text_file(CommandOptions& Options)
1554 AESDecContext* Context = 0;
1555 HMACContext* HMAC = 0;
1556 TimedText::MXFReader Reader;
1557 TimedText::FrameBuffer FrameBuffer;
1558 TimedText::TimedTextDescriptor TDesc;
1560 Result_t result = Reader.OpenRead(Options.filenames[0]);
1562 if ( ASDCP_SUCCESS(result) )
1564 Reader.FillTimedTextDescriptor(TDesc);
1565 FrameBuffer.Capacity(2*Kumu::Megabyte);
1567 if ( Options.verbose_flag )
1568 TimedText::DescriptorDump(TDesc);
1571 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1573 Context = new AESDecContext;
1574 result = Context->InitKey(Options.key_value);
1576 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1579 Reader.FillWriterInfo(Info);
1581 if ( Info.UsesHMAC )
1583 HMAC = new HMACContext;
1584 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1588 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1593 if ( ASDCP_FAILURE(result) )
1597 TimedText::ResourceList_t::const_iterator ri;
1599 result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
1601 // do something with the XML here
1602 fprintf(stderr, "XMLDoc size: %lu\n", XMLDoc.size());
1604 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1606 result = Reader.ReadAncillaryResource((*ri).ResourceID, FrameBuffer, Context, HMAC);
1608 if ( ASDCP_SUCCESS(result) )
1610 // if ( Options.verbose_flag )
1611 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1613 // do something with the resource data here
1620 //------------------------------------------------------------------------------------------
1624 // These classes wrap the irregular names in the asdcplib API
1625 // so that I can use a template to simplify the implementation
1626 // of show_file_info()
1628 class MyVideoDescriptor : public MPEG2::VideoDescriptor
1631 void FillDescriptor(MPEG2::MXFReader& Reader) {
1632 Reader.FillVideoDescriptor(*this);
1635 void Dump(FILE* stream) {
1636 MPEG2::VideoDescriptorDump(*this, stream);
1640 class MyPictureDescriptor : public JP2K::PictureDescriptor
1643 void FillDescriptor(JP2K::MXFReader& Reader) {
1644 Reader.FillPictureDescriptor(*this);
1647 void Dump(FILE* stream) {
1648 JP2K::PictureDescriptorDump(*this, stream);
1652 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
1655 void FillDescriptor(JP2K::MXFSReader& Reader) {
1656 Reader.FillPictureDescriptor(*this);
1659 void Dump(FILE* stream) {
1660 JP2K::PictureDescriptorDump(*this, stream);
1664 class MyAudioDescriptor : public PCM::AudioDescriptor
1667 void FillDescriptor(PCM::MXFReader& Reader) {
1668 Reader.FillAudioDescriptor(*this);
1671 void Dump(FILE* stream) {
1672 PCM::AudioDescriptorDump(*this, stream);
1676 class MyTextDescriptor : public TimedText::TimedTextDescriptor
1679 void FillDescriptor(TimedText::MXFReader& Reader) {
1680 Reader.FillTimedTextDescriptor(*this);
1683 void Dump(FILE* stream) {
1684 TimedText::DescriptorDump(*this, stream);
1688 // MSVC didn't like the function template, so now it's a static class method
1689 template<class ReaderT, class DescriptorT>
1690 class FileInfoWrapper
1694 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
1696 assert(type_string);
1700 Result_t result = RESULT_OK;
1702 if ( Options.verbose_flag || Options.showheader_flag )
1705 result = Reader.OpenRead(Options.filenames[0]);
1707 if ( ASDCP_SUCCESS(result) )
1709 fprintf(stdout, "File essence type is %s.\n", type_string);
1711 if ( Options.showheader_flag )
1712 Reader.DumpHeaderMetadata(stream);
1715 Reader.FillWriterInfo(WI);
1716 WriterInfoDump(WI, stream);
1719 Desc.FillDescriptor(Reader);
1722 if ( Options.showindex_flag )
1723 Reader.DumpIndex(stream);
1725 else if ( result == RESULT_FORMAT && Options.showheader_flag )
1727 Reader.DumpHeaderMetadata(stream);
1735 // Read header metadata from an ASDCP file
1738 show_file_info(CommandOptions& Options)
1740 EssenceType_t EssenceType;
1741 Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1743 if ( ASDCP_FAILURE(result) )
1746 if ( EssenceType == ESS_MPEG2_VES )
1747 result = FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options, "MPEG2 video");
1749 else if ( EssenceType == ESS_PCM_24b_48k || EssenceType == ESS_PCM_24b_96k )
1750 result = FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options, "PCM audio");
1752 else if ( EssenceType == ESS_JPEG_2000 )
1754 if ( Options.stereo_image_flag )
1755 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1756 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1759 result = FileInfoWrapper<ASDCP::JP2K::MXFReader,
1760 MyPictureDescriptor>::file_info(Options, "JPEG 2000 pictures");
1762 else if ( EssenceType == ESS_JPEG_2000_S )
1763 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1764 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1766 else if ( EssenceType == ESS_TIMED_TEXT )
1767 result = FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options, "Timed Text");
1771 fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]);
1772 Kumu::FileReader Reader;
1773 const Dictionary* Dict = &DefaultCompositeDict();
1774 MXF::OPAtomHeader TestHeader(Dict);
1776 result = Reader.OpenRead(Options.filenames[0]);
1778 if ( ASDCP_SUCCESS(result) )
1779 result = TestHeader.InitFromFile(Reader); // test UL and OP
1781 if ( ASDCP_SUCCESS(result) )
1783 TestHeader.Partition::Dump(stdout);
1785 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1788 fputs("File contains no Identification object.\n", stdout);
1790 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1793 fputs("File contains no SourcePackage object.\n", stdout);
1797 fputs("File is not MXF.\n", stdout);
1807 digest_file(const char* filename)
1809 using namespace Kumu;
1811 ASDCP_TEST_NULL_STR(filename);
1815 ByteString Buf(8192);
1817 Result_t result = Reader.OpenRead(filename);
1819 while ( ASDCP_SUCCESS(result) )
1821 ui32_t read_count = 0;
1822 result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count);
1824 if ( result == RESULT_ENDOFFILE )
1830 if ( ASDCP_SUCCESS(result) )
1831 SHA1_Update(&Ctx, Buf.Data(), read_count);
1834 if ( ASDCP_SUCCESS(result) )
1836 const ui32_t sha_len = 20;
1837 byte_t bin_buf[sha_len];
1839 SHA1_Final(bin_buf, &Ctx);
1841 fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename);
1849 main(int argc, const char** argv)
1851 Result_t result = RESULT_OK;
1853 CommandOptions Options(argc, argv);
1855 if ( Options.version_flag )
1858 if ( Options.help_flag )
1861 if ( Options.version_flag || Options.help_flag )
1864 if ( Options.error_flag )
1866 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1870 if ( Options.mode == MMT_INFO )
1872 result = show_file_info(Options);
1874 else if ( Options.mode == MMT_GOP_START )
1876 result = gop_start_test(Options);
1878 else if ( Options.mode == MMT_GEN_KEY )
1880 Kumu::FortunaRNG RNG;
1881 byte_t bin_buf[KeyLen];
1883 RNG.FillRandom(bin_buf, KeyLen);
1884 printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 64));
1886 else if ( Options.mode == MMT_GEN_ID )
1889 Kumu::GenRandomValue(TmpID);
1890 printf("%s\n", TmpID.EncodeHex(str_buf, 64));
1892 else if ( Options.mode == MMT_DIGEST )
1894 for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ )
1895 result = digest_file(Options.filenames[i]);
1897 else if ( Options.mode == MMT_UL_LIST )
1899 if ( Options.use_smpte_labels )
1900 DefaultSMPTEDict().Dump(stdout);
1902 DefaultInteropDict().Dump(stdout);
1904 else if ( Options.mode == MMT_EXTRACT )
1906 EssenceType_t EssenceType;
1907 result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1909 if ( ASDCP_SUCCESS(result) )
1911 switch ( EssenceType )
1914 result = read_MPEG2_file(Options);
1918 if ( Options.stereo_image_flag )
1919 result = read_JP2K_S_file(Options);
1921 result = read_JP2K_file(Options);
1924 case ESS_JPEG_2000_S:
1925 result = read_JP2K_S_file(Options);
1928 case ESS_PCM_24b_48k:
1929 case ESS_PCM_24b_96k:
1930 result = read_PCM_file(Options);
1933 case ESS_TIMED_TEXT:
1934 result = read_timed_text_file(Options);
1938 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]);
1943 else if ( Options.mode == MMT_CREATE )
1945 if ( Options.do_repeat && ! Options.duration_flag )
1947 fputs("Option -R requires -d <duration>\n", stderr);
1951 EssenceType_t EssenceType;
1952 result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType);
1954 if ( ASDCP_SUCCESS(result) )
1956 switch ( EssenceType )
1959 result = write_MPEG2_file(Options);
1963 if ( Options.stereo_image_flag )
1964 result = write_JP2K_S_file(Options);
1967 result = write_JP2K_file(Options);
1971 case ESS_PCM_24b_48k:
1972 case ESS_PCM_24b_96k:
1973 result = write_PCM_file(Options);
1976 case ESS_TIMED_TEXT:
1977 result = write_timed_text_file(Options);
1981 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1982 Options.filenames[0]);
1989 fprintf(stderr, "Unhandled mode: %d.\n", Options.mode);
1993 if ( ASDCP_FAILURE(result) )
1995 fputs("Program stopped on error.\n", stderr);
1997 if ( result == RESULT_SFORMAT )
1999 fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
2001 else if ( result != RESULT_FAIL )
2003 fputs(result, stderr);
2004 fputc('\n', stderr);
2015 // end asdcp-test.cpp