2 Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, 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 as-02-unwrap.cpp
29 \brief AS-02 file manipulation utility
31 This program extracts picture and sound from AS-02 files.
33 For more information about AS-02, please refer to the header file AS_02.h
34 For more information about asdcplib, please refer to the header file AS_DCP.h
37 #include <KM_fileio.h>
39 #include <WavFileWriter.h>
41 using namespace ASDCP;
43 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
45 //------------------------------------------------------------------------------------------
47 // command line option parser class
49 static const char* PROGRAM_NAME = "as-02-unwrap"; // program name for messages
51 // Increment the iterator, test for an additional non-option command line argument.
52 // Causes the caller to return if there are no remaining arguments or if the next
53 // argument begins with '-'.
54 #define TEST_EXTRA_ARG(i,c) \
55 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
56 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
62 banner(FILE* stream = stdout)
66 Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
67 asdcplib may be copied only under the terms of the license found at\n\
68 the top of every file in the asdcplib distribution kit.\n\n\
69 Specify the -h (help) option for further information about %s\n\n",
70 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
75 usage(FILE* stream = stdout)
78 USAGE: %s [-h|-help] [-V]\n\
80 %s [-1|-2] [-b <buffer-size>] [-d <duration>]\n\
81 [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <size>] [-v] [-W]\n\
82 [-w] <input-file> [<file-prefix>]\n\n",
83 PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
87 -1 - Split Wave essence to mono WAV files during extract.\n\
88 Default is multichannel WAV\n\
89 -2 - Split Wave essence to stereo WAV files during extract.\n\
90 Default is multichannel WAV\n\
91 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
92 Defaults to 4,194,304 (4MB)\n\
93 -d <duration> - Number of frames to process, default all\n\
94 -f <start-frame> - Starting frame number, default 0\n\
95 -h | -help - Show help\n\
96 -k <key-string> - Use key for ciphertext operations\n\
97 -m - verify HMAC values when reading\n\
98 -s <size> - Number of bytes to dump to output when -v is given\n\
99 -V - Show version information\n\
100 -v - Verbose, prints informative messages to stderr\n\
101 -W - Read input file only, do not write destination file\n\
102 -w <width> - Width of numeric element in a series of frame file names\n\
104 -z - Fail if j2c inputs have unequal parameters (default)\n\
105 -Z - Ignore unequal parameters in j2c inputs\n\
107 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
108 o All option arguments must be separated from the option by whitespace.\n\n");
117 bool error_flag; // true if the given options are in error or not complete
118 bool key_flag; // true if an encryption key was given
119 bool read_hmac; // true if HMAC values are to be validated
120 bool split_wav; // true if PCM is to be extracted to stereo WAV files
121 bool mono_wav; // true if PCM is to be extracted to mono WAV files
122 bool verbose_flag; // true if the verbose option was selected
123 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
124 bool no_write_flag; // true if no output files are to be written
125 bool version_flag; // true if the version display option was selected
126 bool help_flag; // true if the help display option was selected
127 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
128 ui32_t number_width; // number of digits in a serialized filename (for JPEG extract)
129 ui32_t start_frame; // frame number to begin processing
130 ui32_t duration; // number of frames to be processed
131 bool duration_flag; // true if duration argument given
132 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
133 ui32_t picture_rate; // fps of picture when wrapping PCM
134 ui32_t fb_size; // size of picture frame buffer
135 const char* file_prefix; // filename pre for files written by the extract mode
136 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
137 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
138 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
139 const char* input_filename;
140 std::string prefix_buffer;
143 CommandOptions(int argc, const char** argv) :
144 error_flag(true), key_flag(false), read_hmac(false), split_wav(false),
145 mono_wav(false), verbose_flag(false), fb_dump_size(0), no_write_flag(false),
146 version_flag(false), help_flag(false), number_width(6),
147 start_frame(0), duration(0xffffffff), duration_flag(false), j2c_pedantic(true),
148 picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_prefix(0),
151 memset(key_value, 0, KeyLen);
152 memset(key_id_value, 0, UUIDlen);
154 for ( int i = 1; i < argc; ++i )
157 if ( (strcmp( argv[i], "-help") == 0) )
163 if ( argv[i][0] == '-'
164 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
167 switch ( argv[i][1] )
169 case '1': mono_wav = true; break;
170 case '2': split_wav = true; break;
173 TEST_EXTRA_ARG(i, 'b');
174 fb_size = abs(atoi(argv[i]));
177 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
182 TEST_EXTRA_ARG(i, 'd');
183 duration_flag = true;
184 duration = abs(atoi(argv[i]));
188 TEST_EXTRA_ARG(i, 'f');
189 start_frame = abs(atoi(argv[i]));
192 case 'h': help_flag = true; break;
193 case 'm': read_hmac = true; break;
196 TEST_EXTRA_ARG(i, 'p');
197 picture_rate = abs(atoi(argv[i]));
201 TEST_EXTRA_ARG(i, 's');
202 fb_dump_size = abs(atoi(argv[i]));
205 case 'V': version_flag = true; break;
206 case 'v': verbose_flag = true; break;
207 case 'W': no_write_flag = true; break;
210 TEST_EXTRA_ARG(i, 'w');
211 number_width = abs(atoi(argv[i]));
214 case 'Z': j2c_pedantic = false; break;
215 case 'z': j2c_pedantic = true; break;
218 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
224 if ( argv[i][0] != '-' )
226 if ( input_filename == 0 )
228 input_filename = argv[i];
230 else if ( file_prefix == 0 )
232 file_prefix = argv[i];
237 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
243 if ( help_flag || version_flag )
246 if ( input_filename == 0 )
248 fputs("At least one filename argument is required.\n", stderr);
252 if ( file_prefix == 0 )
254 prefix_buffer = Kumu::PathSetExtension(input_filename, "") + "_";
255 file_prefix = prefix_buffer.c_str();
263 //------------------------------------------------------------------------------------------
267 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
268 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
269 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
272 read_JP2K_file(CommandOptions& Options)
274 AESDecContext* Context = 0;
275 HMACContext* HMAC = 0;
276 JP2K::MXFReader Reader;
277 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
278 ui32_t frame_count = 0;
280 Result_t result = Reader.OpenRead(Options.input_filename);
282 if ( ASDCP_SUCCESS(result) )
284 JP2K::PictureDescriptor PDesc;
285 Reader.FillPictureDescriptor(PDesc);
287 frame_count = PDesc.ContainerDuration;
289 if ( Options.verbose_flag )
291 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
292 JP2K::PictureDescriptorDump(PDesc);
296 if ( ASDCP_SUCCESS(result) && Options.key_flag )
298 Context = new AESDecContext;
299 result = Context->InitKey(Options.key_value);
301 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
304 Reader.FillWriterInfo(Info);
308 HMAC = new HMACContext;
309 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
313 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
318 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
319 if ( last_frame > frame_count )
320 last_frame = frame_count;
322 char name_format[64];
323 snprintf(name_format, 64, "%%s%%0%du.j2c", Options.number_width);
325 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
327 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
329 if ( ASDCP_SUCCESS(result) )
331 Kumu::FileWriter OutFile;
334 snprintf(filename, 256, name_format, Options.file_prefix, i);
335 result = OutFile.OpenWrite(filename);
337 if ( ASDCP_SUCCESS(result) )
338 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
340 if ( Options.verbose_flag )
341 FrameBuffer.Dump(stderr, Options.fb_dump_size);
348 //------------------------------------------------------------------------------------------
351 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
352 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
353 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
356 read_PCM_file(CommandOptions& Options)
358 AESDecContext* Context = 0;
359 HMACContext* HMAC = 0;
360 PCM::MXFReader Reader;
361 PCM::FrameBuffer FrameBuffer;
362 WavFileWriter OutWave;
363 PCM::AudioDescriptor ADesc;
364 ui32_t last_frame = 0;
366 Result_t result = Reader.OpenRead(Options.input_filename);
368 if ( ASDCP_SUCCESS(result) )
370 Reader.FillAudioDescriptor(ADesc);
371 ADesc.EditRate = Rational(1, 1);
372 FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
374 if ( Options.verbose_flag )
375 PCM::AudioDescriptorDump(ADesc);
378 if ( ASDCP_SUCCESS(result) )
380 last_frame = ADesc.ContainerDuration;
382 if ( Options.duration > 0 && Options.duration < last_frame )
383 last_frame = Options.duration;
385 if ( Options.start_frame > 0 )
387 if ( Options.start_frame > ADesc.ContainerDuration )
389 fprintf(stderr, "Start value greater than file duration.\n");
393 last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
396 ADesc.ContainerDuration = last_frame - Options.start_frame;
397 OutWave.OpenWrite(ADesc, Options.file_prefix,
398 ( Options.split_wav ? WavFileWriter::ST_STEREO :
399 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
402 if ( ASDCP_SUCCESS(result) && Options.key_flag )
404 Context = new AESDecContext;
405 result = Context->InitKey(Options.key_value);
407 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
410 Reader.FillWriterInfo(Info);
414 HMAC = new HMACContext;
415 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
419 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
424 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
426 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
428 if ( ASDCP_SUCCESS(result) )
430 if ( Options.verbose_flag )
431 FrameBuffer.Dump(stderr, Options.fb_dump_size);
433 result = OutWave.WriteFrame(FrameBuffer);
443 main(int argc, const char** argv)
446 CommandOptions Options(argc, argv);
448 if ( Options.version_flag )
451 if ( Options.help_flag )
454 if ( Options.version_flag || Options.help_flag )
457 if ( Options.error_flag )
459 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
463 EssenceType_t EssenceType;
464 Result_t result = ASDCP::EssenceType(Options.input_filename, EssenceType);
466 if ( ASDCP_SUCCESS(result) )
468 switch ( EssenceType )
471 result = read_JP2K_file(Options);
474 case ESS_PCM_24b_48k:
475 case ESS_PCM_24b_96k:
476 result = read_PCM_file(Options);
480 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.input_filename);
485 if ( ASDCP_FAILURE(result) )
487 fputs("Program stopped on error.\n", stderr);
489 if ( result == RESULT_SFORMAT )
491 fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
493 else if ( result != RESULT_FAIL )
495 fputs(result, stderr);
507 // end as-02-unwrap.cpp