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.SampleRate = 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.SampleRate != EditRate_23_98
1349 && ADesc.SampleRate != EditRate_24
1350 && ADesc.SampleRate != EditRate_48 )
1351 ADesc.SampleRate = Options.PictureRate();
1353 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
1355 if ( Options.verbose_flag )
1356 PCM::AudioDescriptorDump(ADesc);
1359 if ( ASDCP_SUCCESS(result) )
1361 last_frame = ADesc.ContainerDuration;
1363 if ( Options.duration > 0 && Options.duration < last_frame )
1364 last_frame = Options.duration;
1366 if ( Options.start_frame > 0 )
1368 if ( Options.start_frame > ADesc.ContainerDuration )
1370 fprintf(stderr, "Start value greater than file duration.\n");
1374 last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
1377 ADesc.ContainerDuration = last_frame - Options.start_frame;
1378 OutWave.OpenWrite(ADesc, Options.file_root,
1379 ( Options.split_wav ? WavFileWriter::ST_STEREO :
1380 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
1383 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1385 Context = new AESDecContext;
1386 result = Context->InitKey(Options.key_value);
1388 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1391 Reader.FillWriterInfo(Info);
1393 if ( Info.UsesHMAC )
1395 HMAC = new HMACContext;
1396 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1400 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1405 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
1407 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
1409 if ( ASDCP_SUCCESS(result) )
1411 if ( Options.verbose_flag )
1412 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1414 result = OutWave.WriteFrame(FrameBuffer);
1422 //------------------------------------------------------------------------------------------
1423 // TimedText essence
1426 // Write one or more plaintext timed text streams to a plaintext ASDCP file
1427 // Write one or more plaintext timed text streams to a ciphertext ASDCP file
1430 write_timed_text_file(CommandOptions& Options)
1432 AESEncContext* Context = 0;
1433 HMACContext* HMAC = 0;
1434 TimedText::DCSubtitleParser Parser;
1435 TimedText::MXFWriter Writer;
1436 TimedText::FrameBuffer FrameBuffer;
1437 TimedText::TimedTextDescriptor TDesc;
1438 byte_t IV_buf[CBC_BLOCK_SIZE];
1439 Kumu::FortunaRNG RNG;
1441 // set up essence parser
1442 Result_t result = Parser.OpenRead(Options.filenames[0]);
1444 // set up MXF writer
1445 if ( ASDCP_SUCCESS(result) )
1447 Parser.FillTimedTextDescriptor(TDesc);
1448 FrameBuffer.Capacity(2*Kumu::Megabyte);
1450 if ( Options.verbose_flag )
1452 fputs("D-Cinema Timed-Text Descriptor:\n", stderr);
1453 TimedText::DescriptorDump(TDesc);
1457 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1459 WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here
1460 if ( Options.asset_id_flag )
1461 memcpy(Info.AssetUUID, Options.asset_id_value, UUIDlen);
1463 Kumu::GenRandomUUID(Info.AssetUUID);
1465 if ( Options.use_smpte_labels )
1467 Info.LabelSetType = LS_MXF_SMPTE;
1468 fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n");
1471 // configure encryption
1472 if( Options.key_flag )
1474 Kumu::GenRandomUUID(Info.ContextID);
1475 Info.EncryptedEssence = true;
1477 if ( Options.key_id_flag )
1478 memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
1480 RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
1482 Context = new AESEncContext;
1483 result = Context->InitKey(Options.key_value);
1485 if ( ASDCP_SUCCESS(result) )
1486 result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1488 if ( ASDCP_SUCCESS(result) && Options.write_hmac )
1490 Info.UsesHMAC = true;
1491 HMAC = new HMACContext;
1492 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1496 if ( ASDCP_SUCCESS(result) )
1497 result = Writer.OpenWrite(Options.out_file, Info, TDesc);
1500 if ( ASDCP_FAILURE(result) )
1504 TimedText::ResourceList_t::const_iterator ri;
1506 result = Parser.ReadTimedTextResource(XMLDoc);
1508 if ( ASDCP_SUCCESS(result) )
1509 result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC);
1511 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1513 result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer);
1515 if ( ASDCP_SUCCESS(result) )
1517 if ( Options.verbose_flag )
1518 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1520 if ( ! Options.no_write_flag )
1522 result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC);
1524 // The Writer class will forward the last block of ciphertext
1525 // to the encryption context for use as the IV for the next
1526 // frame. If you want to use non-sequitur IV values, un-comment
1527 // the following line of code.
1528 // if ( ASDCP_SUCCESS(result) && Options.key_flag )
1529 // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE));
1533 if ( result == RESULT_ENDOFFILE )
1537 if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
1538 result = Writer.Finalize();
1544 // Read one or more timed text streams from a plaintext ASDCP file
1545 // Read one or more timed text streams from a ciphertext ASDCP file
1546 // Read one or more timed text streams from a ciphertext ASDCP file
1549 read_timed_text_file(CommandOptions& Options)
1551 AESDecContext* Context = 0;
1552 HMACContext* HMAC = 0;
1553 TimedText::MXFReader Reader;
1554 TimedText::FrameBuffer FrameBuffer;
1555 TimedText::TimedTextDescriptor TDesc;
1557 Result_t result = Reader.OpenRead(Options.filenames[0]);
1559 if ( ASDCP_SUCCESS(result) )
1561 Reader.FillTimedTextDescriptor(TDesc);
1562 FrameBuffer.Capacity(2*Kumu::Megabyte);
1564 if ( Options.verbose_flag )
1565 TimedText::DescriptorDump(TDesc);
1568 if ( ASDCP_SUCCESS(result) && Options.key_flag )
1570 Context = new AESDecContext;
1571 result = Context->InitKey(Options.key_value);
1573 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
1576 Reader.FillWriterInfo(Info);
1578 if ( Info.UsesHMAC )
1580 HMAC = new HMACContext;
1581 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
1585 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
1590 if ( ASDCP_FAILURE(result) )
1594 TimedText::ResourceList_t::const_iterator ri;
1596 result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
1598 // do something with the XML here
1599 fprintf(stderr, "XMLDoc size: %lu\n", XMLDoc.size());
1601 for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
1603 result = Reader.ReadAncillaryResource((*ri).ResourceID, FrameBuffer, Context, HMAC);
1605 if ( ASDCP_SUCCESS(result) )
1607 // if ( Options.verbose_flag )
1608 FrameBuffer.Dump(stderr, Options.fb_dump_size);
1610 // do something with the resource data here
1617 //------------------------------------------------------------------------------------------
1621 // These classes wrap the irregular names in the asdcplib API
1622 // so that I can use a template to simplify the implementation
1623 // of show_file_info()
1625 class MyVideoDescriptor : public MPEG2::VideoDescriptor
1628 void FillDescriptor(MPEG2::MXFReader& Reader) {
1629 Reader.FillVideoDescriptor(*this);
1632 void Dump(FILE* stream) {
1633 MPEG2::VideoDescriptorDump(*this, stream);
1637 class MyPictureDescriptor : public JP2K::PictureDescriptor
1640 void FillDescriptor(JP2K::MXFReader& Reader) {
1641 Reader.FillPictureDescriptor(*this);
1644 void Dump(FILE* stream) {
1645 JP2K::PictureDescriptorDump(*this, stream);
1649 class MyStereoPictureDescriptor : public JP2K::PictureDescriptor
1652 void FillDescriptor(JP2K::MXFSReader& Reader) {
1653 Reader.FillPictureDescriptor(*this);
1656 void Dump(FILE* stream) {
1657 JP2K::PictureDescriptorDump(*this, stream);
1661 class MyAudioDescriptor : public PCM::AudioDescriptor
1664 void FillDescriptor(PCM::MXFReader& Reader) {
1665 Reader.FillAudioDescriptor(*this);
1668 void Dump(FILE* stream) {
1669 PCM::AudioDescriptorDump(*this, stream);
1673 class MyTextDescriptor : public TimedText::TimedTextDescriptor
1676 void FillDescriptor(TimedText::MXFReader& Reader) {
1677 Reader.FillTimedTextDescriptor(*this);
1680 void Dump(FILE* stream) {
1681 TimedText::DescriptorDump(*this, stream);
1685 // MSVC didn't like the function template, so now it's a static class method
1686 template<class ReaderT, class DescriptorT>
1687 class FileInfoWrapper
1691 file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0)
1693 assert(type_string);
1697 Result_t result = RESULT_OK;
1699 if ( Options.verbose_flag || Options.showheader_flag )
1702 result = Reader.OpenRead(Options.filenames[0]);
1704 if ( ASDCP_SUCCESS(result) )
1706 fprintf(stdout, "File essence type is %s.\n", type_string);
1708 if ( Options.showheader_flag )
1709 Reader.DumpHeaderMetadata(stream);
1712 Reader.FillWriterInfo(WI);
1713 WriterInfoDump(WI, stream);
1716 Desc.FillDescriptor(Reader);
1719 if ( Options.showindex_flag )
1720 Reader.DumpIndex(stream);
1722 else if ( result == RESULT_FORMAT && Options.showheader_flag )
1724 Reader.DumpHeaderMetadata(stream);
1732 // Read header metadata from an ASDCP file
1735 show_file_info(CommandOptions& Options)
1737 EssenceType_t EssenceType;
1738 Result_t result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1740 if ( ASDCP_FAILURE(result) )
1743 if ( EssenceType == ESS_MPEG2_VES )
1744 result = FileInfoWrapper<ASDCP::MPEG2::MXFReader, MyVideoDescriptor>::file_info(Options, "MPEG2 video");
1746 else if ( EssenceType == ESS_PCM_24b_48k || EssenceType == ESS_PCM_24b_96k )
1747 result = FileInfoWrapper<ASDCP::PCM::MXFReader, MyAudioDescriptor>::file_info(Options, "PCM audio");
1749 else if ( EssenceType == ESS_JPEG_2000 )
1751 if ( Options.stereo_image_flag )
1752 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1753 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1756 result = FileInfoWrapper<ASDCP::JP2K::MXFReader,
1757 MyPictureDescriptor>::file_info(Options, "JPEG 2000 pictures");
1759 else if ( EssenceType == ESS_JPEG_2000_S )
1760 result = FileInfoWrapper<ASDCP::JP2K::MXFSReader,
1761 MyStereoPictureDescriptor>::file_info(Options, "JPEG 2000 stereoscopic pictures");
1763 else if ( EssenceType == ESS_TIMED_TEXT )
1764 result = FileInfoWrapper<ASDCP::TimedText::MXFReader, MyTextDescriptor>::file_info(Options, "Timed Text");
1768 fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]);
1769 Kumu::FileReader Reader;
1770 const Dictionary* Dict = &DefaultCompositeDict();
1771 MXF::OPAtomHeader TestHeader(Dict);
1773 result = Reader.OpenRead(Options.filenames[0]);
1775 if ( ASDCP_SUCCESS(result) )
1776 result = TestHeader.InitFromFile(Reader); // test UL and OP
1778 if ( ASDCP_SUCCESS(result) )
1780 TestHeader.Partition::Dump(stdout);
1782 if ( MXF::Identification* ID = TestHeader.GetIdentification() )
1785 fputs("File contains no Identification object.\n", stdout);
1787 if ( MXF::SourcePackage* SP = TestHeader.GetSourcePackage() )
1790 fputs("File contains no SourcePackage object.\n", stdout);
1794 fputs("File is not MXF.\n", stdout);
1804 digest_file(const char* filename)
1806 using namespace Kumu;
1808 ASDCP_TEST_NULL_STR(filename);
1812 ByteString Buf(8192);
1814 Result_t result = Reader.OpenRead(filename);
1816 while ( ASDCP_SUCCESS(result) )
1818 ui32_t read_count = 0;
1819 result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count);
1821 if ( result == RESULT_ENDOFFILE )
1827 if ( ASDCP_SUCCESS(result) )
1828 SHA1_Update(&Ctx, Buf.Data(), read_count);
1831 if ( ASDCP_SUCCESS(result) )
1833 const ui32_t sha_len = 20;
1834 byte_t bin_buf[sha_len];
1836 SHA1_Final(bin_buf, &Ctx);
1838 fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename);
1846 main(int argc, const char** argv)
1848 Result_t result = RESULT_OK;
1850 CommandOptions Options(argc, argv);
1852 if ( Options.version_flag )
1855 if ( Options.help_flag )
1858 if ( Options.version_flag || Options.help_flag )
1861 if ( Options.error_flag )
1863 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
1867 if ( Options.mode == MMT_INFO )
1869 result = show_file_info(Options);
1871 else if ( Options.mode == MMT_GOP_START )
1873 result = gop_start_test(Options);
1875 else if ( Options.mode == MMT_GEN_KEY )
1877 Kumu::FortunaRNG RNG;
1878 byte_t bin_buf[KeyLen];
1880 RNG.FillRandom(bin_buf, KeyLen);
1881 printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 64));
1883 else if ( Options.mode == MMT_GEN_ID )
1886 Kumu::GenRandomValue(TmpID);
1887 printf("%s\n", TmpID.EncodeHex(str_buf, 64));
1889 else if ( Options.mode == MMT_DIGEST )
1891 for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ )
1892 result = digest_file(Options.filenames[i]);
1894 else if ( Options.mode == MMT_UL_LIST )
1896 if ( Options.use_smpte_labels )
1897 DefaultSMPTEDict().Dump(stdout);
1899 DefaultInteropDict().Dump(stdout);
1901 else if ( Options.mode == MMT_EXTRACT )
1903 EssenceType_t EssenceType;
1904 result = ASDCP::EssenceType(Options.filenames[0], EssenceType);
1906 if ( ASDCP_SUCCESS(result) )
1908 switch ( EssenceType )
1911 result = read_MPEG2_file(Options);
1915 if ( Options.stereo_image_flag )
1916 result = read_JP2K_S_file(Options);
1918 result = read_JP2K_file(Options);
1921 case ESS_JPEG_2000_S:
1922 result = read_JP2K_S_file(Options);
1925 case ESS_PCM_24b_48k:
1926 case ESS_PCM_24b_96k:
1927 result = read_PCM_file(Options);
1930 case ESS_TIMED_TEXT:
1931 result = read_timed_text_file(Options);
1935 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]);
1940 else if ( Options.mode == MMT_CREATE )
1942 if ( Options.do_repeat && ! Options.duration_flag )
1944 fputs("Option -R requires -d <duration>\n", stderr);
1948 EssenceType_t EssenceType;
1949 result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType);
1951 if ( ASDCP_SUCCESS(result) )
1953 switch ( EssenceType )
1956 result = write_MPEG2_file(Options);
1960 if ( Options.stereo_image_flag )
1961 result = write_JP2K_S_file(Options);
1964 result = write_JP2K_file(Options);
1968 case ESS_PCM_24b_48k:
1969 case ESS_PCM_24b_96k:
1970 result = write_PCM_file(Options);
1973 case ESS_TIMED_TEXT:
1974 result = write_timed_text_file(Options);
1978 fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n",
1979 Options.filenames[0]);
1986 fprintf(stderr, "Unhandled mode: %d.\n", Options.mode);
1990 if ( ASDCP_FAILURE(result) )
1992 fputs("Program stopped on error.\n", stderr);
1994 if ( result == RESULT_SFORMAT )
1996 fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
1998 else if ( result != RESULT_FAIL )
2000 fputs(result, stderr);
2001 fputc('\n', stderr);
2012 // end asdcp-test.cpp