X-Git-Url: https://git.carlh.net/gitweb/?a=blobdiff_plain;f=src%2Fasdcp-test.cpp;h=ba610157d8db5eb3f696d1fa08aedd218c6f2062;hb=766b93f3654e6e80623a84774fce0307694340f9;hp=83fbb15649bb16a19d3dd5b45daa1f4c20867f4f;hpb=9bfe9676115531eb76649e1ebd5e14b904b53ae2;p=asdcplib.git diff --git a/src/asdcp-test.cpp b/src/asdcp-test.cpp index 83fbb15..ba61015 100755 --- a/src/asdcp-test.cpp +++ b/src/asdcp-test.cpp @@ -1,5 +1,5 @@ /* -Copyright (c) 2003-2005, John Hurst +Copyright (c) 2003-2008, John Hurst All rights reserved. Redistribution and use in source and binary forms, with or without @@ -47,20 +47,20 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. these features. */ -#include -#include - -#include +#include +#include #include #include -#include -#include #include #include +#include + +#include +#include using namespace ASDCP; -const ui32_t FRAME_BUFFER_SIZE = 4*1024*1024; +const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte; //------------------------------------------------------------------------------------------ // @@ -85,41 +85,28 @@ public: ProductName = "asdcp-test"; char s_buf[128]; - snprintf(s_buf, 128, "%lu.%lu.%lu", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR); + snprintf(s_buf, 128, "%u.%u.%u", VERSION_MAJOR, VERSION_APIMINOR, VERSION_IMPMINOR); ProductVersion = s_buf; } } s_MyInfo; -// Macros used to test command option data state. - -// True if a major mode has already been selected. -#define TEST_MAJOR_MODE() ( info_flag||create_flag||extract_flag||genkey_flag||genid_flag||gop_start_flag ) - -// Causes the caller to return if a major mode has already been selected, -// otherwise sets the given flag. -#define TEST_SET_MAJOR_MODE(f) if ( TEST_MAJOR_MODE() ) \ - { \ - fputs("Conflicting major mode, choose one of -(gcixG)).\n", stderr); \ - return; \ - } \ - (f) = true; // Increment the iterator, test for an additional non-option command line argument. // Causes the caller to return if there are no remaining arguments or if the next // argument begins with '-'. #define TEST_EXTRA_ARG(i,c) if ( ++i >= argc || argv[(i)][0] == '-' ) \ { \ - fprintf(stderr, "Argument not found for option %c.\n", (c)); \ + fprintf(stderr, "Argument not found for option -%c.\n", (c)); \ return; \ } // void -banner(FILE* stream = stderr) +banner(FILE* stream = stdout) { fprintf(stream, "\n\ %s (asdcplib %s)\n\n\ -Copyright (c) 2003-2005 John Hurst\n\n\ +Copyright (c) 2003-2008 John Hurst\n\n\ asdcplib may be copied only under the terms of the license found at\n\ the top of every file in the asdcplib distribution kit.\n\n\ Specify the -h (help) option for further information about %s\n\n", @@ -128,62 +115,108 @@ Specify the -h (help) option for further information about %s\n\n", // void -usage(FILE* stream = stderr) +usage(FILE* stream = stdout) { fprintf(stream, "\ -USAGE: %s [-i [-H, -n]|-c [-p , -e, -M, -R]|-x [-m][-S]|-g|-u|-G|-V|-h]\n\ - [-k ] [-j ] [-f ] [-d ]\n\ - [-b ] [-W] [-v [-s]] [, ...]\n\ -\n", PACKAGE); +USAGE: %s -c [-3] [-b ] [-d ] [-e|-E]\n\ + [-f ] [-j ] [-k ] [-L] [-M]\n\ + [-p ] [-R] [-s ] [-v] [-W]\n\ + [ ...]\n\ +\n\ + %s [-h|-help] [-V]\n\ +\n\ + %s -i [-H] [-n] [-v] \n\ +\n\ + %s -g | -u\n\ +\n\ + %s -G [-v] \n\ +\n\ + %s -t \n\ +\n\ + %s -x [-3] [-b ] [-d ]\n\ + [-f ] [-m] [-p ] [-R] [-s ] [-S|-1]\n\ + [-v] [-W] \n\ +\n", PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE, PACKAGE); fprintf(stream, "\ Major modes:\n\ - -i - show file info\n\ - -c - create AS-DCP file from input(s)\n\ - -x - extract essence from AS-DCP file to named file(s)\n\ - -g - generate a random 16 byte value to stdout\n\ - -u - generate a random UUID value to stdout\n\ - -G - Perform GOP start lookup test on MPEG file\n\ - -V - show version\n\ - -h - show help\n\ + -3 - With -c, create a stereoscopic image file. Expects two\n\ + directories of JP2K codestreams (directories must have\n\ + an equal number of frames; left eye is first).\n\ + - With -x, force stereoscopic interpretation of a JP2K\n\ + track file.\n\ + -c - Create an AS-DCP track file from input(s)\n\ + -g - Generate a random 16 byte value to stdout\n\ + -G - Perform GOP start lookup test on MXF+Interop MPEG file\n\ + -h | -help - Show help\n\ + -i - Show file info\n\ + -t - Calculate message digest of input file\n\ + -U - Dump UL catalog to stdout\n\ + -u - Generate a random UUID value to stdout\n\ + -V - Show version information\n\ + -x - Extract essence from AS-DCP file to named file(s)\n\ \n"); fprintf(stream, "\ Security Options:\n\ - -j - write key ID instead of creating a random value\n\ - -k - use key for ciphertext operations\n\ - -e - encrypt MPEG or JP2K headers (default)\n\ - -E - do not encrypt MPEG or JP2K headers\n\ - -M - do not create HMAC values when writing\n\ - -m - verify HMAC values when reading\n\ + -e - Encrypt MPEG or JP2K headers (default)\n\ + -E - Do not encrypt MPEG or JP2K headers\n\ + -j - Write key ID instead of creating a random value\n\ + -k - Use key for ciphertext operations\n\ + -m - verify HMAC values when reading\n\ + -M - Do not create HMAC values when writing\n\ \n"); fprintf(stream, "\ Read/Write Options:\n\ - -b - Size (in bytes) of the picture frame buffer, default: 2097152 (2MB)\n\ - -f - starting frame number, default 0\n\ - -d - number of frames to process, default all\n\ - -p - fps of picture when wrapping PCM or JP2K:, use one of [23|24|48], 24 is default\n\ - -R - Repeat the first frame over the entire file (picture essence only, requires -c, -d)\n\ - -S - Split Wave essence to stereo WAV files during extract (default = multichannel WAV)\n\ - -W - read input file only, do not write source file\n\ + -b - Specify size in bytes of picture frame buffer.\n\ + Defaults to 4,194,304 (4MB)\n\ + -d - Number of frames to process, default all\n\ + -f - Starting frame number, default 0\n\ + -L - Write SMPTE UL values instead of MXF Interop\n\ + -p - fps of picture when wrapping PCM or JP2K:\n\ + Use one of [23|24|48], 24 is default\n\ + -R - Repeat the first frame over the entire file (picture\n\ + essence only, requires -c, -d)\n\ + -S - Split Wave essence to stereo WAV files during extract.\n\ + Default is multichannel WAV\n\ + -1 - Split Wave essence to mono WAV files during extract.\n\ + Default is multichannel WAV\n\ + -W - Read input file only, do not write source file\n\ \n"); fprintf(stream, "\ Info Options:\n\ - -H - show MXF header metadata, used with option -i\n\ - -n - show index, used with option -i\n\ + -H - Show MXF header metadata, used with option -i\n\ + -n - Show index, used with option -i\n\ \n\ Other Options:\n\ - -s - number of bytes of frame buffer to be dumped as hex to stderr (use with -v)\n\ - -v - verbose, show extra detail during run\n\ + -s - Number of bytes of frame buffer to be dumped as hex to\n\ + stderr, used with option -v\n\ + -v - Verbose, prints informative messages to stderr\n\ \n\ NOTES: o There is no option grouping, all options must be distinct arguments.\n\ o All option arguments must be separated from the option by whitespace.\n\ - o An argument of \"23\" to the -p option will be interpreted as 23000/1001 fps.\n\ + o An argument of \"23\" to the -p option will be interpreted\n\ + as 23000/1001 fps.\n\ \n"); } +// +enum MajorMode_t +{ + MMT_NONE, + MMT_INFO, + MMT_CREATE, + MMT_EXTRACT, + MMT_GEN_ID, + MMT_GEN_KEY, + MMT_GOP_START, + MMT_DIGEST, + MMT_UL_LIST, +}; + + // // class CommandOptions @@ -191,19 +224,15 @@ class CommandOptions CommandOptions(); public: + MajorMode_t mode; bool error_flag; // true if the given options are in error or not complete - bool info_flag; // true if the file info mode was selected - bool create_flag; // true if the file create mode was selected - bool extract_flag; // true if the file extract mode was selected - bool genkey_flag; // true if we are to generate a new key value - bool genid_flag; // true if we are to generate a new UUID value - bool gop_start_flag; // true if we are to perform a GOP start lookup test bool key_flag; // true if an encryption key was given bool key_id_flag; // true if a key ID was given bool encrypt_header_flag; // true if mpeg headers are to be encrypted bool write_hmac; // true if HMAC values are to be generated and written bool read_hmac; // true if HMAC values are to be validated bool split_wav; // true if PCM is to be extracted to stereo WAV files + bool mono_wav; // true if PCM is to be extracted to mono WAV files bool verbose_flag; // true if the verbose option was selected ui32_t fb_dump_size; // number of bytes of frame buffer to dump bool showindex_flag; // true if index is to be displayed @@ -211,10 +240,12 @@ public: bool no_write_flag; // true if no output files are to be written bool version_flag; // true if the version display option was selected bool help_flag; // true if the help display option was selected + bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first) ui32_t start_frame; // frame number to begin processing ui32_t duration; // number of frames to be processed bool duration_flag; // true if duration argument given bool do_repeat; // if true and -c -d, repeat first input frame + bool use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values ui32_t picture_rate; // fps of picture when wrapping PCM ui32_t fb_size; // size of picture frame buffer ui32_t file_count; // number of elements in filenames[] @@ -242,26 +273,36 @@ public: // CommandOptions(int argc, const char** argv) : - error_flag(true), info_flag(false), create_flag(false), - extract_flag(false), genkey_flag(false), genid_flag(false), gop_start_flag(false), - key_flag(false), key_id_flag(false), encrypt_header_flag(true), - write_hmac(true), read_hmac(false), split_wav(false), + mode(MMT_NONE), error_flag(true), key_flag(false), key_id_flag(false), encrypt_header_flag(true), + write_hmac(true), read_hmac(false), split_wav(false), mono_wav(false), verbose_flag(false), fb_dump_size(0), showindex_flag(false), showheader_flag(false), - no_write_flag(false), version_flag(false), help_flag(false), start_frame(0), - duration(0xffffffff), duration_flag(false), do_repeat(false), picture_rate(24), - fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0) + no_write_flag(false), version_flag(false), help_flag(false), stereo_image_flag(false), start_frame(0), + duration(0xffffffff), duration_flag(false), do_repeat(false), use_smpte_labels(false), + picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_count(0), file_root(0), out_file(0) { memset(key_value, 0, KeyLen); memset(key_id_value, 0, UUIDlen); for ( int i = 1; i < argc; i++ ) { - if ( argv[i][0] == '-' && isalpha(argv[i][1]) && argv[i][2] == 0 ) + + if ( (strcmp( argv[i], "-help") == 0) ) + { + help_flag = true; + continue; + } + + if ( argv[i][0] == '-' + && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) + && argv[i][2] == 0 ) { switch ( argv[i][1] ) { - case 'i': TEST_SET_MAJOR_MODE(info_flag); break; - case 'G': TEST_SET_MAJOR_MODE(gop_start_flag); break; + case '1': mono_wav = true; break; + case '2': split_wav = true; break; + case '3': stereo_image_flag = true; break; + case 'i': mode = MMT_INFO; break; + case 'G': mode = MMT_GOP_START; break; case 'W': no_write_flag = true; break; case 'n': showindex_flag = true; break; case 'H': showheader_flag = true; break; @@ -270,22 +311,24 @@ public: case 'V': version_flag = true; break; case 'h': help_flag = true; break; case 'v': verbose_flag = true; break; - case 'g': genkey_flag = true; break; - case 'u': genid_flag = true; break; + case 'g': mode = MMT_GEN_KEY; break; + case 'U': mode = MMT_UL_LIST; break; + case 'u': mode = MMT_GEN_ID; break; case 'e': encrypt_header_flag = true; break; case 'E': encrypt_header_flag = false; break; case 'M': write_hmac = false; break; case 'm': read_hmac = true; break; + case 'L': use_smpte_labels = true; break; case 'c': - TEST_SET_MAJOR_MODE(create_flag); TEST_EXTRA_ARG(i, 'c'); + mode = MMT_CREATE; out_file = argv[i]; break; case 'x': - TEST_SET_MAJOR_MODE(extract_flag); TEST_EXTRA_ARG(i, 'x'); + mode = MMT_EXTRACT; file_root = argv[i]; break; @@ -293,11 +336,11 @@ public: TEST_EXTRA_ARG(i, 'k'); { ui32_t length; - hex2bin(argv[i], key_value, KeyLen, &length); + Kumu::hex2bin(argv[i], key_value, KeyLen, &length); if ( length != KeyLen ) { - fprintf(stderr, "Unexpected key length: %lu, expecting %lu characters.\n", KeyLen, length); + fprintf(stderr, "Unexpected key length: %u, expecting %u characters.\n", length, KeyLen); return; } } @@ -307,11 +350,11 @@ public: TEST_EXTRA_ARG(i, 'j'); { ui32_t length; - hex2bin(argv[i], key_id_value, UUIDlen, &length); + Kumu::hex2bin(argv[i], key_id_value, UUIDlen, &length); if ( length != UUIDlen ) { - fprintf(stderr, "Unexpected key ID length: %lu, expecting %lu characters.\n", UUIDlen, length); + fprintf(stderr, "Unexpected key ID length: %u, expecting %u characters.\n", length, UUIDlen); return; } } @@ -319,63 +362,78 @@ public: case 'f': TEST_EXTRA_ARG(i, 'f'); - start_frame = atoi(argv[i]); // TODO: test for negative value, should use strtol() + start_frame = abs(atoi(argv[i])); break; case 'd': TEST_EXTRA_ARG(i, 'd'); duration_flag = true; - duration = atoi(argv[i]); // TODO: test for negative value, should use strtol() + duration = abs(atoi(argv[i])); break; case 'p': TEST_EXTRA_ARG(i, 'p'); - picture_rate = atoi(argv[i]); + picture_rate = abs(atoi(argv[i])); break; case 's': TEST_EXTRA_ARG(i, 's'); - fb_dump_size = atoi(argv[i]); + fb_dump_size = abs(atoi(argv[i])); break; + case 't': mode = MMT_DIGEST; break; + case 'b': TEST_EXTRA_ARG(i, 'b'); - fb_size = atoi(argv[i]); + fb_size = abs(atoi(argv[i])); if ( verbose_flag ) - fprintf(stderr, "Frame Buffer size: %lu bytes.\n", fb_size); + fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size); break; default: - fprintf(stderr, "Unrecognized option: %c\n", argv[i][1]); + fprintf(stderr, "Unrecognized option: %s\n", argv[i]); return; } } else { - filenames[file_count++] = argv[i]; + + if ( argv[i][0] != '-' ) + { + filenames[file_count++] = argv[i]; + } + else + { + fprintf(stderr, "Unrecognized argument: %s\n", argv[i]); + return; + } if ( file_count >= MAX_IN_FILES ) { - fprintf(stderr, "Filename lists exceeds maximum list size: %lu\n", MAX_IN_FILES); + fprintf(stderr, "Filename lists exceeds maximum list size: %u\n", MAX_IN_FILES); return; } } } - if ( TEST_MAJOR_MODE() ) + if ( help_flag || version_flag ) + return; + + if ( ( mode == MMT_INFO + || mode == MMT_CREATE + || mode == MMT_EXTRACT + || mode == MMT_GOP_START + || mode == MMT_DIGEST ) && file_count == 0 ) { - if ( ! genkey_flag && ! genid_flag && file_count == 0 ) - { - fputs("Option requires at least one filename argument.\n", stderr); - return; - } + fputs("Option requires at least one filename argument.\n", stderr); + return; } - if ( ! TEST_MAJOR_MODE() && ! help_flag && ! version_flag ) + if ( mode == MMT_NONE && ! help_flag && ! version_flag ) { - fputs("No operation selected (use one of -(gcixG) or -h for help).\n", stderr); + fputs("No operation selected (use one of -[gGcitux] or -h for help).\n", stderr); return; } @@ -399,7 +457,7 @@ write_MPEG2_file(CommandOptions& Options) MPEG2::MXFWriter Writer; MPEG2::VideoDescriptor VDesc; byte_t IV_buf[CBC_BLOCK_SIZE]; - FortunaRNG RNG; + Kumu::FortunaRNG RNG; // set up essence parser Result_t result = Parser.OpenRead(Options.filenames[0]); @@ -413,7 +471,7 @@ write_MPEG2_file(CommandOptions& Options) { fputs("MPEG-2 Pictures\n", stderr); fputs("VideoDescriptor:\n", stderr); - fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); MPEG2::VideoDescriptorDump(VDesc); } } @@ -421,12 +479,18 @@ write_MPEG2_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) { WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here - GenRandomUUID(RNG, Info.AssetUUID); + Kumu::GenRandomUUID(Info.AssetUUID); + + if ( Options.use_smpte_labels ) + { + Info.LabelSetType = LS_MXF_SMPTE; + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); + } // configure encryption if( Options.key_flag ) { - GenRandomUUID(RNG, Info.ContextID); + Kumu::GenRandomUUID(Info.ContextID); Info.EncryptedEssence = true; if ( Options.key_id_flag ) @@ -444,7 +508,7 @@ write_MPEG2_file(CommandOptions& Options) { Info.UsesHMAC = true; HMAC = new HMACContext; - result = HMAC->InitKey(Options.key_value); + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); } } @@ -508,7 +572,7 @@ read_MPEG2_file(CommandOptions& Options) HMACContext* HMAC = 0; MPEG2::MXFReader Reader; MPEG2::FrameBuffer FrameBuffer(Options.fb_size); - FileWriter OutFile; + Kumu::FileWriter OutFile; ui32_t frame_count = 0; Result_t result = Reader.OpenRead(Options.filenames[0]); @@ -521,7 +585,7 @@ read_MPEG2_file(CommandOptions& Options) if ( Options.verbose_flag ) { - fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); MPEG2::VideoDescriptorDump(VDesc); } } @@ -546,7 +610,7 @@ read_MPEG2_file(CommandOptions& Options) if ( Info.UsesHMAC ) { HMAC = new HMACContext; - result = HMAC->InitKey(Options.key_value); + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); } else { @@ -597,7 +661,7 @@ gop_start_test(CommandOptions& Options) if ( Options.verbose_flag ) { - fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); MPEG2::VideoDescriptorDump(VDesc); } } @@ -618,7 +682,7 @@ gop_start_test(CommandOptions& Options) if ( FrameBuffer.FrameType() != FRAME_I ) fprintf(stderr, "Expecting an I frame, got %c\n", FrameTypeChar(FrameBuffer.FrameType())); - fprintf(stderr, "Requested frame %lu, got %lu\n", i, FrameBuffer.FrameNumber()); + fprintf(stderr, "Requested frame %u, got %u\n", i, FrameBuffer.FrameNumber()); } } @@ -628,6 +692,229 @@ gop_start_test(CommandOptions& Options) //------------------------------------------------------------------------------------------ // JPEG 2000 essence +// Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a plaintext ASDCP file +// Write one or more plaintext JPEG 2000 stereoscopic codestream pairs to a ciphertext ASDCP file +// +Result_t +write_JP2K_S_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + JP2K::MXFSWriter Writer; + JP2K::FrameBuffer FrameBuffer(Options.fb_size); + JP2K::PictureDescriptor PDesc; + JP2K::SequenceParser ParserLeft, ParserRight; + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; + + if ( Options.file_count != 2 ) + { + fprintf(stderr, "Two inputs are required for stereoscopic option.\n"); + return RESULT_FAIL; + } + + // set up essence parser + Result_t result = ParserLeft.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + result = ParserRight.OpenRead(Options.filenames[1]); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + ParserLeft.FillPictureDescriptor(PDesc); + PDesc.EditRate = Options.PictureRate(); + + if ( Options.verbose_flag ) + { + fputs("JPEG 2000 stereoscopic pictures\nPictureDescriptor:\n", stderr); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + JP2K::PictureDescriptorDump(PDesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here + Kumu::GenRandomUUID(Info.AssetUUID); + + if ( Options.use_smpte_labels ) + { + Info.LabelSetType = LS_MXF_SMPTE; + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); + } + + // configure encryption + if( Options.key_flag ) + { + Kumu::GenRandomUUID(Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, UUIDlen); + + Context = new AESEncContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) ) + result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + + if ( ASDCP_SUCCESS(result) && Options.write_hmac ) + { + Info.UsesHMAC = true; + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + } + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file, Info, PDesc); + } + + if ( ASDCP_SUCCESS(result) ) + { + ui32_t duration = 0; + result = ParserLeft.Reset(); + if ( ASDCP_SUCCESS(result) ) result = ParserRight.Reset(); + + while ( ASDCP_SUCCESS(result) && duration++ < Options.duration ) + { + result = ParserLeft.ReadFrame(FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( Options.encrypt_header_flag ) + FrameBuffer.PlaintextOffset(0); + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.WriteFrame(FrameBuffer, JP2K::SP_LEFT, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + result = ParserRight.ReadFrame(FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( Options.encrypt_header_flag ) + FrameBuffer.PlaintextOffset(0); + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.WriteFrame(FrameBuffer, JP2K::SP_RIGHT, Context, HMAC); + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} + +// Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a plaintext ASDCP file +// Read one or more plaintext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file +// Read one or more ciphertext JPEG 2000 stereoscopic codestream pairs from a ciphertext ASDCP file +Result_t +read_JP2K_S_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + JP2K::MXFSReader Reader; + JP2K::FrameBuffer FrameBuffer(Options.fb_size); + ui32_t frame_count = 0; + + Result_t result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + { + JP2K::PictureDescriptor PDesc; + Reader.FillPictureDescriptor(PDesc); + + frame_count = PDesc.ContainerDuration; + + if ( Options.verbose_flag ) + { + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); + JP2K::PictureDescriptorDump(PDesc); + } + } + + if ( ASDCP_SUCCESS(result) && Options.key_flag ) + { + Context = new AESDecContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) && Options.read_hmac ) + { + WriterInfo Info; + Reader.FillWriterInfo(Info); + + if ( Info.UsesHMAC ) + { + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + else + { + fputs("File does not contain HMAC values, ignoring -m option.\n", stderr); + } + } + } + + const int filename_max = 1024; + char filename[filename_max]; + ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count); + if ( last_frame > frame_count ) + last_frame = frame_count; + + for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ ) + { + result = Reader.ReadFrame(i, JP2K::SP_LEFT, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + Kumu::FileWriter OutFile; + ui32_t write_count; + snprintf(filename, filename_max, "%s%06uL.j2c", Options.file_root, i); + result = OutFile.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + } + + if ( ASDCP_SUCCESS(result) ) + result = Reader.ReadFrame(i, JP2K::SP_RIGHT, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + Kumu::FileWriter OutFile; + ui32_t write_count; + snprintf(filename, filename_max, "%s%06uR.j2c", Options.file_root, i); + result = OutFile.OpenWrite(filename); + + if ( ASDCP_SUCCESS(result) ) + result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count); + } + } + + return result; +} + + + // Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file // Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file // @@ -640,8 +927,8 @@ write_JP2K_file(CommandOptions& Options) JP2K::FrameBuffer FrameBuffer(Options.fb_size); JP2K::PictureDescriptor PDesc; JP2K::SequenceParser Parser; - byte_t IV_buf[CBC_BLOCK_SIZE]; - FortunaRNG RNG; + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; // set up essence parser Result_t result = Parser.OpenRead(Options.filenames[0]); @@ -656,7 +943,7 @@ write_JP2K_file(CommandOptions& Options) { fprintf(stderr, "JPEG 2000 pictures\n"); fputs("PictureDescriptor:\n", stderr); - fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); JP2K::PictureDescriptorDump(PDesc); } } @@ -664,12 +951,18 @@ write_JP2K_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) { WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here - GenRandomUUID(RNG, Info.AssetUUID); + Kumu::GenRandomUUID(Info.AssetUUID); + + if ( Options.use_smpte_labels ) + { + Info.LabelSetType = LS_MXF_SMPTE; + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); + } // configure encryption if( Options.key_flag ) { - GenRandomUUID(RNG, Info.ContextID); + Kumu::GenRandomUUID(Info.ContextID); Info.EncryptedEssence = true; if ( Options.key_id_flag ) @@ -687,7 +980,7 @@ write_JP2K_file(CommandOptions& Options) { Info.UsesHMAC = true; HMAC = new HMACContext; - result = HMAC->InitKey(Options.key_value); + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); } } @@ -763,7 +1056,7 @@ read_JP2K_file(CommandOptions& Options) if ( Options.verbose_flag ) { - fprintf(stderr, "Frame Buffer size: %lu\n", Options.fb_size); + fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size); JP2K::PictureDescriptorDump(PDesc); } } @@ -781,7 +1074,7 @@ read_JP2K_file(CommandOptions& Options) if ( Info.UsesHMAC ) { HMAC = new HMACContext; - result = HMAC->InitKey(Options.key_value); + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); } else { @@ -800,10 +1093,10 @@ read_JP2K_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) ) { - FileWriter OutFile; + Kumu::FileWriter OutFile; char filename[256]; ui32_t write_count; - snprintf(filename, 256, "%s%06lu.j2c", Options.file_root, i); + snprintf(filename, 256, "%s%06u.j2c", Options.file_root, i); result = OutFile.OpenWrite(filename); if ( ASDCP_SUCCESS(result) ) @@ -827,15 +1120,15 @@ read_JP2K_file(CommandOptions& Options) Result_t write_PCM_file(CommandOptions& Options) { - AESEncContext* Context = 0; - HMACContext* HMAC = 0; - PCMParserList Parser; - PCM::MXFWriter Writer; - PCM::FrameBuffer FrameBuffer; + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + PCMParserList Parser; + PCM::MXFWriter Writer; + PCM::FrameBuffer FrameBuffer; PCM::AudioDescriptor ADesc; - Rational PictureRate = Options.PictureRate(); - byte_t IV_buf[CBC_BLOCK_SIZE]; - FortunaRNG RNG; + Rational PictureRate = Options.PictureRate(); + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; // set up essence parser Result_t result = Parser.OpenRead(Options.file_count, Options.filenames, PictureRate); @@ -850,7 +1143,7 @@ write_PCM_file(CommandOptions& Options) if ( Options.verbose_flag ) { - fprintf(stderr, "48Khz PCM Audio, %s fps (%lu spf)\n", + fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n", Options.szPictureRate(), PCM::CalcSamplesPerFrame(ADesc)); fputs("AudioDescriptor:\n", stderr); @@ -861,12 +1154,18 @@ write_PCM_file(CommandOptions& Options) if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) { WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here - GenRandomUUID(RNG, Info.AssetUUID); + Kumu::GenRandomUUID(Info.AssetUUID); + + if ( Options.use_smpte_labels ) + { + Info.LabelSetType = LS_MXF_SMPTE; + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); + } // configure encryption if( Options.key_flag ) { - GenRandomUUID(RNG, Info.ContextID); + Kumu::GenRandomUUID(Info.ContextID); Info.EncryptedEssence = true; if ( Options.key_id_flag ) @@ -884,7 +1183,7 @@ write_PCM_file(CommandOptions& Options) { Info.UsesHMAC = true; HMAC = new HMACContext; - result = HMAC->InitKey(Options.key_value); + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); } } @@ -906,7 +1205,7 @@ write_PCM_file(CommandOptions& Options) if ( FrameBuffer.Size() != FrameBuffer.Capacity() ) { fprintf(stderr, "WARNING: Last frame read was short, PCM input is possibly not frame aligned.\n"); - fprintf(stderr, "Expecting %lu bytes, got %lu.\n", FrameBuffer.Capacity(), FrameBuffer.Size()); + fprintf(stderr, "Expecting %u bytes, got %u.\n", FrameBuffer.Capacity(), FrameBuffer.Size()); result = RESULT_ENDOFFILE; continue; } @@ -985,11 +1284,13 @@ read_PCM_file(CommandOptions& Options) return RESULT_FAIL; } - last_frame = xmin(Options.start_frame + last_frame, ADesc.ContainerDuration); + last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration); } ADesc.ContainerDuration = last_frame - Options.start_frame; - OutWave.OpenWrite(ADesc, Options.file_root, Options.split_wav); + OutWave.OpenWrite(ADesc, Options.file_root, + ( Options.split_wav ? WavFileWriter::ST_STEREO : + ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) )); } if ( ASDCP_SUCCESS(result) && Options.key_flag ) @@ -1005,7 +1306,7 @@ read_PCM_file(CommandOptions& Options) if ( Info.UsesHMAC ) { HMAC = new HMACContext; - result = HMAC->InitKey(Options.key_value); + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); } else { @@ -1031,6 +1332,198 @@ read_PCM_file(CommandOptions& Options) } +//------------------------------------------------------------------------------------------ +// TimedText essence + + +// Write one or more plaintext timed text streams to a plaintext ASDCP file +// Write one or more plaintext timed text streams to a ciphertext ASDCP file +// +Result_t +write_timed_text_file(CommandOptions& Options) +{ + AESEncContext* Context = 0; + HMACContext* HMAC = 0; + TimedText::DCSubtitleParser Parser; + TimedText::MXFWriter Writer; + TimedText::FrameBuffer FrameBuffer; + TimedText::TimedTextDescriptor TDesc; + byte_t IV_buf[CBC_BLOCK_SIZE]; + Kumu::FortunaRNG RNG; + + // set up essence parser + Result_t result = Parser.OpenRead(Options.filenames[0]); + + // set up MXF writer + if ( ASDCP_SUCCESS(result) ) + { + Parser.FillDescriptor(TDesc); + FrameBuffer.Capacity(2*Kumu::Megabyte); + + if ( Options.verbose_flag ) + { + fputs("D-Cinema Timed-Text Descriptor:\n", stderr); + TimedText::DescriptorDump(TDesc); + } + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + { + WriterInfo Info = s_MyInfo; // fill in your favorite identifiers here + Kumu::GenRandomUUID(Info.AssetUUID); + + if ( Options.use_smpte_labels ) + { + Info.LabelSetType = LS_MXF_SMPTE; + fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); + } + + // configure encryption + if( Options.key_flag ) + { + Kumu::GenRandomUUID(Info.ContextID); + Info.EncryptedEssence = true; + + if ( Options.key_id_flag ) + memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen); + else + RNG.FillRandom(Info.CryptographicKeyID, UUIDlen); + + Context = new AESEncContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) ) + result = Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + + if ( ASDCP_SUCCESS(result) && Options.write_hmac ) + { + Info.UsesHMAC = true; + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + } + + if ( ASDCP_SUCCESS(result) ) + result = Writer.OpenWrite(Options.out_file, Info, TDesc); + } + + if ( ASDCP_FAILURE(result) ) + return result; + + std::string XMLDoc; + TimedText::ResourceList_t::const_iterator ri; + + result = Parser.ReadTimedTextResource(XMLDoc); + + if ( ASDCP_SUCCESS(result) ) + result = Writer.WriteTimedTextResource(XMLDoc, Context, HMAC); + + for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ ) + { + result = Parser.ReadAncillaryResource((*ri).ResourceID, FrameBuffer); + + if ( ASDCP_SUCCESS(result) ) + { + if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + if ( ! Options.no_write_flag ) + { + result = Writer.WriteAncillaryResource(FrameBuffer, Context, HMAC); + + // The Writer class will forward the last block of ciphertext + // to the encryption context for use as the IV for the next + // frame. If you want to use non-sequitur IV values, un-comment + // the following line of code. + // if ( ASDCP_SUCCESS(result) && Options.key_flag ) + // Context->SetIVec(RNG.FillRandom(IV_buf, CBC_BLOCK_SIZE)); + } + } + + if ( result == RESULT_ENDOFFILE ) + result = RESULT_OK; + } + + if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag ) + result = Writer.Finalize(); + + return result; +} + + +// Read one or more timed text streams from a plaintext ASDCP file +// Read one or more timed text streams from a ciphertext ASDCP file +// Read one or more timed text streams from a ciphertext ASDCP file +// +Result_t +read_timed_text_file(CommandOptions& Options) +{ + AESDecContext* Context = 0; + HMACContext* HMAC = 0; + TimedText::MXFReader Reader; + TimedText::FrameBuffer FrameBuffer; + TimedText::TimedTextDescriptor TDesc; + + Result_t result = Reader.OpenRead(Options.filenames[0]); + + if ( ASDCP_SUCCESS(result) ) + { + Reader.FillDescriptor(TDesc); + FrameBuffer.Capacity(2*Kumu::Megabyte); + + if ( Options.verbose_flag ) + TimedText::DescriptorDump(TDesc); + } + + if ( ASDCP_SUCCESS(result) && Options.key_flag ) + { + Context = new AESDecContext; + result = Context->InitKey(Options.key_value); + + if ( ASDCP_SUCCESS(result) && Options.read_hmac ) + { + WriterInfo Info; + Reader.FillWriterInfo(Info); + + if ( Info.UsesHMAC ) + { + HMAC = new HMACContext; + result = HMAC->InitKey(Options.key_value, Info.LabelSetType); + } + else + { + fputs("File does not contain HMAC values, ignoring -m option.\n", stderr); + } + } + } + + if ( ASDCP_FAILURE(result) ) + return result; + + std::string XMLDoc; + TimedText::ResourceList_t::const_iterator ri; + + result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC); + + // do something with the XML here + fprintf(stderr, "XMLDoc size: %lu\n", XMLDoc.size()); + + for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ ) + { + result = Reader.ReadAncillaryResource((*ri).ResourceID, FrameBuffer, Context, HMAC); + + if ( ASDCP_SUCCESS(result) ) + { + // if ( Options.verbose_flag ) + FrameBuffer.Dump(stderr, Options.fb_dump_size); + + // do something with the resource data here + } + } + + return result; +} + //------------------------------------------------------------------------------------------ // @@ -1063,6 +1556,18 @@ class MyPictureDescriptor : public JP2K::PictureDescriptor } }; +class MyStereoPictureDescriptor : public JP2K::PictureDescriptor +{ + public: + void FillDescriptor(JP2K::MXFSReader& Reader) { + Reader.FillPictureDescriptor(*this); + } + + void Dump(FILE* stream) { + JP2K::PictureDescriptorDump(*this, stream); + } +}; + class MyAudioDescriptor : public PCM::AudioDescriptor { public: @@ -1075,24 +1580,41 @@ class MyAudioDescriptor : public PCM::AudioDescriptor } }; +class MyTextDescriptor : public TimedText::TimedTextDescriptor +{ + public: + void FillDescriptor(TimedText::MXFReader& Reader) { + Reader.FillDescriptor(*this); + } + + void Dump(FILE* stream) { + TimedText::DescriptorDump(*this, stream); + } +}; // MSVC didn't like the function template, so now it's a static class method template class FileInfoWrapper { public: - static void file_info(CommandOptions& Options, FILE* stream = 0) + static Result_t + file_info(CommandOptions& Options, const char* type_string, FILE* stream = 0) { + assert(type_string); if ( stream == 0 ) stream = stdout; + Result_t result = RESULT_OK; + if ( Options.verbose_flag || Options.showheader_flag ) { ReaderT Reader; - Result_t result = Reader.OpenRead(Options.filenames[0]); + result = Reader.OpenRead(Options.filenames[0]); if ( ASDCP_SUCCESS(result) ) { + fprintf(stdout, "File essence type is %s.\n", type_string); + if ( Options.showheader_flag ) Reader.DumpHeaderMetadata(stream); @@ -1112,6 +1634,8 @@ public: Reader.DumpHeaderMetadata(stream); } } + + return result; } }; @@ -1127,24 +1651,32 @@ show_file_info(CommandOptions& Options) return result; if ( EssenceType == ESS_MPEG2_VES ) - { - fputs("File essence type is MPEG2 video.\n", stdout); - FileInfoWrapper::file_info(Options); - } + result = FileInfoWrapper::file_info(Options, "MPEG2 video"); + else if ( EssenceType == ESS_PCM_24b_48k ) - { - fputs("File essence type is PCM audio.\n", stdout); - FileInfoWrapper::file_info(Options); - } + result = FileInfoWrapper::file_info(Options, "PCM audio"); + else if ( EssenceType == ESS_JPEG_2000 ) { - fputs("File essence type is JPEG 2000 pictures.\n", stdout); - FileInfoWrapper::file_info(Options); + if ( Options.stereo_image_flag ) + result = FileInfoWrapper::file_info(Options, "JPEG 2000 stereoscopic pictures"); + + else + result = FileInfoWrapper::file_info(Options, "JPEG 2000 pictures"); } + else if ( EssenceType == ESS_JPEG_2000_S ) + result = FileInfoWrapper::file_info(Options, "JPEG 2000 stereoscopic pictures"); + + else if ( EssenceType == ESS_TIMED_TEXT ) + result = FileInfoWrapper::file_info(Options, "Timed Text"); + else { fprintf(stderr, "File is not AS-DCP: %s\n", Options.filenames[0]); - FileReader Reader; + Kumu::FileReader Reader; MXF::OPAtomHeader TestHeader; result = Reader.OpenRead(Options.filenames[0]); @@ -1176,59 +1708,115 @@ show_file_info(CommandOptions& Options) } +// +Result_t +digest_file(const char* filename) +{ + using namespace Kumu; + + ASDCP_TEST_NULL_STR(filename); + FileReader Reader; + SHA_CTX Ctx; + SHA1_Init(&Ctx); + ByteString Buf(8192); + + Result_t result = Reader.OpenRead(filename); + + while ( ASDCP_SUCCESS(result) ) + { + ui32_t read_count = 0; + result = Reader.Read(Buf.Data(), Buf.Capacity(), &read_count); + + if ( result == RESULT_ENDOFFILE ) + { + result = RESULT_OK; + break; + } + + if ( ASDCP_SUCCESS(result) ) + SHA1_Update(&Ctx, Buf.Data(), read_count); + } + + if ( ASDCP_SUCCESS(result) ) + { + const ui32_t sha_len = 20; + byte_t bin_buf[sha_len]; + char sha_buf[64]; + SHA1_Final(bin_buf, &Ctx); + + fprintf(stdout, "%s %s\n", base64encode(bin_buf, sha_len, sha_buf, 64), filename); + } + + return result; +} + // int main(int argc, const char** argv) { Result_t result = RESULT_OK; + char str_buf[64]; CommandOptions Options(argc, argv); + if ( Options.version_flag ) + banner(); + if ( Options.help_flag ) - { - usage(); - return 0; - } + usage(); - if ( Options.error_flag ) - return 3; + if ( Options.version_flag || Options.help_flag ) + return 0; - if ( Options.version_flag ) - banner(); + if ( Options.error_flag ) + { + fprintf(stderr, "There was a problem. Type %s -h for help.\n", PACKAGE); + return 3; + } - if ( Options.info_flag ) + if ( Options.mode == MMT_INFO ) { result = show_file_info(Options); } - else if ( Options.gop_start_flag ) + else if ( Options.mode == MMT_GOP_START ) { result = gop_start_test(Options); } - else if ( Options.genkey_flag ) + else if ( Options.mode == MMT_GEN_KEY ) { - FortunaRNG RNG; + Kumu::FortunaRNG RNG; byte_t bin_buf[KeyLen]; - char str_buf[40]; RNG.FillRandom(bin_buf, KeyLen); - printf("%s\n", bin2hex(bin_buf, KeyLen, str_buf, 40)); + printf("%s\n", Kumu::bin2hex(bin_buf, KeyLen, str_buf, 64)); } - else if ( Options.genid_flag ) + else if ( Options.mode == MMT_GEN_ID ) { - FortunaRNG RNG; - byte_t bin_buf[KeyLen]; - char str_buf[40]; + UUID TmpID; + Kumu::GenRandomValue(TmpID); + printf("%s\n", TmpID.EncodeHex(str_buf, 64)); + } + else if ( Options.mode == MMT_DIGEST ) + { + for ( ui32_t i = 0; i < Options.file_count && ASDCP_SUCCESS(result); i++ ) + result = digest_file(Options.filenames[i]); + } + else if ( Options.mode == MMT_UL_LIST ) + { + MDD_t di = (MDD_t)0; - GenRandomUUID(RNG, bin_buf); - bin2hex(bin_buf, KeyLen, str_buf, 40); - printf("%s\n", hyphenate_UUID(str_buf, 40)); + while ( di < MDD_Max ) + { + MDDEntry TmpType = Dict::Type(di); + UL TmpUL(TmpType.ul); + fprintf(stdout, "%s: %s\n", TmpUL.EncodeString(str_buf, 64), TmpType.name); + di = (MDD_t)(di + 1); + } } - else if ( Options.extract_flag ) + else if ( Options.mode == MMT_EXTRACT ) { EssenceType_t EssenceType; result = ASDCP::EssenceType(Options.filenames[0], EssenceType); -#ifdef SMPTE_LABELS - fprintf(stderr, "ATTENTION! Expecting SMPTE Universal Labels\n"); -#endif + if ( ASDCP_SUCCESS(result) ) { switch ( EssenceType ) @@ -1238,20 +1826,31 @@ main(int argc, const char** argv) break; case ESS_JPEG_2000: - result = read_JP2K_file(Options); + if ( Options.stereo_image_flag ) + result = read_JP2K_S_file(Options); + else + result = read_JP2K_file(Options); + break; + + case ESS_JPEG_2000_S: + result = read_JP2K_S_file(Options); break; case ESS_PCM_24b_48k: result = read_PCM_file(Options); break; + case ESS_TIMED_TEXT: + result = read_timed_text_file(Options); + break; + default: fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.filenames[0]); return 5; } } } - else if ( Options.create_flag ) + else if ( Options.mode == MMT_CREATE ) { if ( Options.do_repeat && ! Options.duration_flag ) { @@ -1261,9 +1860,7 @@ main(int argc, const char** argv) EssenceType_t EssenceType; result = ASDCP::RawEssenceType(Options.filenames[0], EssenceType); -#ifdef SMPTE_LABELS - fprintf(stderr, "ATTENTION! Writing SMPTE Universal Labels\n"); -#endif + if ( ASDCP_SUCCESS(result) ) { switch ( EssenceType ) @@ -1273,13 +1870,22 @@ main(int argc, const char** argv) break; case ESS_JPEG_2000: - result = write_JP2K_file(Options); + if ( Options.stereo_image_flag ) + result = write_JP2K_S_file(Options); + + else + result = write_JP2K_file(Options); + break; case ESS_PCM_24b_48k: result = write_PCM_file(Options); break; + case ESS_TIMED_TEXT: + result = write_timed_text_file(Options); + break; + default: fprintf(stderr, "%s: Unknown file type, not ASDCP-compatible essence.\n", Options.filenames[0]); @@ -1287,14 +1893,23 @@ main(int argc, const char** argv) } } } + else + { + fprintf(stderr, "Unhandled mode: %d.\n", Options.mode); + return 6; + } - if ( result != RESULT_OK ) + if ( ASDCP_FAILURE(result) ) { fputs("Program stopped on error.\n", stderr); - if ( result != RESULT_FAIL ) + if ( result == RESULT_SFORMAT ) + { + fputs("Use option '-3' to force stereoscopic mode.\n", stderr); + } + else if ( result != RESULT_FAIL ) { - fputs(GetResultString(result), stderr); + fputs(result, stderr); fputc('\n', stderr); }