2 Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
10 1. Redistributions of source code must retain the above copyright
11 notice, this list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright
13 notice, this list of conditions and the following disclaimer in the
14 documentation and/or other materials provided with the distribution.
15 3. The name of the author may not be used to endorse or promote products
16 derived from this software without specific prior written permission.
18 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /*! \file as-02-unwrap.cpp
31 \brief AS-02 file manipulation utility
33 This program extracts picture and sound from AS-02 files.
35 For more information about AS-02, please refer to the header file AS_02.h
36 For more information about asdcplib, please refer to the header file AS_DCP.h
39 #include <KM_fileio.h>
41 #include <WavFileWriter.h>
44 Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, ASDCP::PCM::AudioDescriptor& ADesc);
47 using namespace ASDCP;
49 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
51 //------------------------------------------------------------------------------------------
53 // command line option parser class
55 static const char* PROGRAM_NAME = "as-02-unwrap"; // program name for messages
57 // Increment the iterator, test for an additional non-option command line argument.
58 // Causes the caller to return if there are no remaining arguments or if the next
59 // argument begins with '-'.
60 #define TEST_EXTRA_ARG(i,c) \
61 if ( ++i >= argc || argv[(i)][0] == '-' ) { \
62 fprintf(stderr, "Argument not found for option -%c.\n", (c)); \
68 banner(FILE* stream = stdout)
72 Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
73 asdcplib may be copied only under the terms of the license found at\n\
74 the top of every file in the asdcplib distribution kit.\n\n\
75 Specify the -h (help) option for further information about %s\n\n",
76 PROGRAM_NAME, ASDCP::Version(), PROGRAM_NAME);
81 usage(FILE* stream = stdout)
84 USAGE: %s [-h|-help] [-V]\n\
86 %s [-1|-2] [-b <buffer-size>] [-d <duration>]\n\
87 [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <size>] [-v] [-W]\n\
88 [-w] <input-file> [<file-prefix>]\n\n",
89 PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
93 -1 - Split Wave essence to mono WAV files during extract.\n\
94 Default is multichannel WAV\n\
95 -2 - Split Wave essence to stereo WAV files during extract.\n\
96 Default is multichannel WAV\n\
97 -b <buffer-size> - Specify size in bytes of picture frame buffer\n\
98 Defaults to 4,194,304 (4MB)\n\
99 -d <duration> - Number of frames to process, default all\n\
100 -f <start-frame> - Starting frame number, default 0\n\
101 -h | -help - Show help\n\
102 -k <key-string> - Use key for ciphertext operations\n\
103 -m - verify HMAC values when reading\n\
104 -s <size> - Number of bytes to dump to output when -v is given\n\
105 -V - Show version information\n\
106 -v - Verbose, prints informative messages to stderr\n\
107 -W - Read input file only, do not write destination file\n\
108 -w <width> - Width of numeric element in a series of frame file names\n\
110 -z - Fail if j2c inputs have unequal parameters (default)\n\
111 -Z - Ignore unequal parameters in j2c inputs\n\
113 NOTES: o There is no option grouping, all options must be distinct arguments.\n\
114 o All option arguments must be separated from the option by whitespace.\n\n");
123 bool error_flag; // true if the given options are in error or not complete
124 bool key_flag; // true if an encryption key was given
125 bool read_hmac; // true if HMAC values are to be validated
126 bool split_wav; // true if PCM is to be extracted to stereo WAV files
127 bool mono_wav; // true if PCM is to be extracted to mono WAV files
128 bool verbose_flag; // true if the verbose option was selected
129 ui32_t fb_dump_size; // number of bytes of frame buffer to dump
130 bool no_write_flag; // true if no output files are to be written
131 bool version_flag; // true if the version display option was selected
132 bool help_flag; // true if the help display option was selected
133 bool stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
134 ui32_t number_width; // number of digits in a serialized filename (for JPEG extract)
135 ui32_t start_frame; // frame number to begin processing
136 ui32_t duration; // number of frames to be processed
137 bool duration_flag; // true if duration argument given
138 bool j2c_pedantic; // passed to JP2K::SequenceParser::OpenRead
139 ui32_t picture_rate; // fps of picture when wrapping PCM
140 ui32_t fb_size; // size of picture frame buffer
141 Rational edit_rate; // frame buffer size for reading clip-wrapped PCM
142 const char* file_prefix; // filename pre for files written by the extract mode
143 byte_t key_value[KeyLen]; // value of given encryption key (when key_flag is true)
144 byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
145 PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
146 const char* input_filename;
147 std::string prefix_buffer;
150 CommandOptions(int argc, const char** argv) :
151 error_flag(true), key_flag(false), read_hmac(false), split_wav(false),
152 mono_wav(false), verbose_flag(false), fb_dump_size(0), no_write_flag(false),
153 version_flag(false), help_flag(false), number_width(6),
154 start_frame(0), duration(0xffffffff), duration_flag(false), j2c_pedantic(true),
155 picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_prefix(0),
158 memset(key_value, 0, KeyLen);
159 memset(key_id_value, 0, UUIDlen);
161 for ( int i = 1; i < argc; ++i )
164 if ( (strcmp( argv[i], "-help") == 0) )
170 if ( argv[i][0] == '-'
171 && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
174 switch ( argv[i][1] )
176 case '1': mono_wav = true; break;
177 case '2': split_wav = true; break;
180 TEST_EXTRA_ARG(i, 'b');
181 fb_size = abs(atoi(argv[i]));
184 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
189 TEST_EXTRA_ARG(i, 'd');
190 duration_flag = true;
191 duration = abs(atoi(argv[i]));
195 TEST_EXTRA_ARG(i, 'f');
196 start_frame = abs(atoi(argv[i]));
199 case 'h': help_flag = true; break;
200 case 'm': read_hmac = true; break;
203 TEST_EXTRA_ARG(i, 'p');
204 picture_rate = abs(atoi(argv[i]));
208 TEST_EXTRA_ARG(i, 's');
209 fb_dump_size = abs(atoi(argv[i]));
212 case 'V': version_flag = true; break;
213 case 'v': verbose_flag = true; break;
214 case 'W': no_write_flag = true; break;
217 TEST_EXTRA_ARG(i, 'w');
218 number_width = abs(atoi(argv[i]));
221 case 'Z': j2c_pedantic = false; break;
222 case 'z': j2c_pedantic = true; break;
225 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
231 if ( argv[i][0] != '-' )
233 if ( input_filename == 0 )
235 input_filename = argv[i];
237 else if ( file_prefix == 0 )
239 file_prefix = argv[i];
244 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
250 if ( help_flag || version_flag )
253 if ( input_filename == 0 )
255 fputs("At least one filename argument is required.\n", stderr);
259 if ( file_prefix == 0 )
261 prefix_buffer = Kumu::PathSetExtension(input_filename, "") + "_";
262 file_prefix = prefix_buffer.c_str();
270 //------------------------------------------------------------------------------------------
274 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
275 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
276 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
279 read_JP2K_file(CommandOptions& Options)
281 AESDecContext* Context = 0;
282 HMACContext* HMAC = 0;
283 AS_02::JP2K::MXFReader Reader;
284 JP2K::FrameBuffer FrameBuffer(Options.fb_size);
285 ui32_t frame_count = 0;
287 Result_t result = Reader.OpenRead(Options.input_filename);
289 if ( ASDCP_SUCCESS(result) )
291 if ( Options.verbose_flag )
293 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
296 ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
297 ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
299 result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
300 reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
302 if ( KM_SUCCESS(result) )
304 assert(rgba_descriptor);
305 frame_count = rgba_descriptor->ContainerDuration;
307 if ( Options.verbose_flag )
309 rgba_descriptor->Dump();
314 result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
315 reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
317 if ( KM_SUCCESS(result) )
319 assert(cdci_descriptor);
320 frame_count = cdci_descriptor->ContainerDuration;
322 if ( Options.verbose_flag )
324 cdci_descriptor->Dump();
329 fprintf(stderr, "File does not contain an essence descriptor.\n");
330 frame_count = Reader.AS02IndexReader().GetDuration();
334 if ( frame_count == 0 )
336 frame_count = Reader.AS02IndexReader().GetDuration();
339 if ( frame_count == 0 )
341 fprintf(stderr, "Unable to determine file duration.\n");
346 if ( ASDCP_SUCCESS(result) && Options.key_flag )
348 Context = new AESDecContext;
349 result = Context->InitKey(Options.key_value);
351 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
354 Reader.FillWriterInfo(Info);
358 HMAC = new HMACContext;
359 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
363 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
368 ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
369 if ( last_frame > frame_count )
370 last_frame = frame_count;
372 char name_format[64];
373 snprintf(name_format, 64, "%%s%%0%du.j2c", Options.number_width);
375 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
377 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
379 if ( ASDCP_SUCCESS(result) )
381 Kumu::FileWriter OutFile;
384 snprintf(filename, 256, name_format, Options.file_prefix, i);
385 result = OutFile.OpenWrite(filename);
387 if ( ASDCP_SUCCESS(result) )
388 result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
390 if ( Options.verbose_flag )
391 FrameBuffer.Dump(stderr, Options.fb_dump_size);
398 //------------------------------------------------------------------------------------------
401 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
402 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
403 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
406 read_PCM_file(CommandOptions& Options)
408 AESDecContext* Context = 0;
409 HMACContext* HMAC = 0;
410 AS_02::PCM::MXFReader Reader;
411 PCM::FrameBuffer FrameBuffer;
412 WavFileWriter OutWave;
413 ui32_t last_frame = 0;
414 ASDCP::MXF::WaveAudioDescriptor *wave_descriptor = 0;
416 if ( Options.edit_rate == Rational(0,0) ) // todo, make this available to the CLI
418 Options.edit_rate = EditRate_24;
421 Result_t result = Reader.OpenRead(Options.input_filename, Options.edit_rate);
423 if ( ASDCP_SUCCESS(result) )
425 if ( Options.verbose_flag )
427 fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
430 result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor),
431 reinterpret_cast<MXF::InterchangeObject**>(&wave_descriptor));
433 if ( KM_SUCCESS(result) )
435 assert(wave_descriptor);
436 last_frame = wave_descriptor->ContainerDuration;
438 if ( Options.verbose_flag )
440 wave_descriptor->Dump();
445 fprintf(stderr, "File does not contain an essence descriptor.\n");
446 last_frame = Reader.AS02IndexReader().GetDuration();
449 if ( last_frame == 0 )
451 fprintf(stderr, "Unable to determine file duration.\n");
455 FrameBuffer.Capacity(AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, Options.edit_rate));
457 if ( Options.verbose_flag )
459 wave_descriptor->Dump();
463 if ( ASDCP_SUCCESS(result) )
465 if ( Options.duration > 0 && Options.duration < last_frame )
466 last_frame = Options.duration;
468 if ( Options.start_frame > 0 )
470 if ( Options.start_frame > last_frame )
472 fprintf(stderr, "Start value greater than file duration.\n");
476 last_frame = Kumu::xmin(Options.start_frame + last_frame, last_frame);
479 last_frame = last_frame - Options.start_frame;
481 PCM::AudioDescriptor ADesc;
483 result = MD_to_PCM_ADesc(wave_descriptor, ADesc);
485 if ( ASDCP_SUCCESS(result) )
487 ADesc.ContainerDuration = last_frame;
488 ADesc.EditRate = Options.edit_rate;
490 result = OutWave.OpenWrite(ADesc, Options.file_prefix,
491 ( Options.split_wav ? WavFileWriter::ST_STEREO :
492 ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
496 if ( ASDCP_SUCCESS(result) && Options.key_flag )
498 Context = new AESDecContext;
499 result = Context->InitKey(Options.key_value);
501 if ( ASDCP_SUCCESS(result) && Options.read_hmac )
504 Reader.FillWriterInfo(Info);
508 HMAC = new HMACContext;
509 result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
513 fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
518 for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
520 result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
522 if ( ASDCP_SUCCESS(result) )
524 if ( Options.verbose_flag )
525 FrameBuffer.Dump(stderr, Options.fb_dump_size);
527 result = OutWave.WriteFrame(FrameBuffer);
537 main(int argc, const char** argv)
540 CommandOptions Options(argc, argv);
542 if ( Options.version_flag )
545 if ( Options.help_flag )
548 if ( Options.version_flag || Options.help_flag )
551 if ( Options.error_flag )
553 fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
557 EssenceType_t EssenceType;
558 Result_t result = ASDCP::EssenceType(Options.input_filename, EssenceType);
560 if ( ASDCP_SUCCESS(result) )
562 switch ( EssenceType )
565 result = read_JP2K_file(Options);
568 case ESS_PCM_24b_48k:
569 case ESS_PCM_24b_96k:
570 result = read_PCM_file(Options);
574 fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.input_filename);
579 if ( ASDCP_FAILURE(result) )
581 fputs("Program stopped on error.\n", stderr);
583 if ( result == RESULT_SFORMAT )
585 fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
587 else if ( result != RESULT_FAIL )
589 fputs(result, stderr);
601 // end as-02-unwrap.cpp