o Added optional Generic Partition to IMF Aux Data prototype, used to carry global...
[asdcplib.git] / src / as-02-unwrap.cpp
1 /*
2 Copyright (c) 2011-2016, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
3 John Hurst
4
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions
9 are met:
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.
17
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.
28 */
29 /*! \file    as-02-unwrap.cpp
30     \version $Id$       
31     \brief   AS-02 file manipulation utility
32
33   This program extracts picture and sound from AS-02 files.
34
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
37 */
38
39 #include <KM_fileio.h>
40 #include <AS_02.h>
41 #include <WavFileWriter.h>
42
43 namespace ASDCP {
44   Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, ASDCP::PCM::AudioDescriptor& ADesc);
45 }
46
47 using namespace ASDCP;
48
49 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
50
51 //------------------------------------------------------------------------------------------
52 //
53 // command line option parser class
54
55 static const char* PROGRAM_NAME = "as-02-unwrap";  // program name for messages
56
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));       \
63     return;                                                             \
64   }
65
66 //
67 void
68 banner(FILE* stream = stdout)
69 {
70   fprintf(stream, "\n\
71 %s (asdcplib %s)\n\n\
72 Copyright (c) 2011-2015, 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);
77 }
78
79 //
80 void
81 usage(FILE* stream = stdout)
82 {
83   fprintf(stream, "\
84 USAGE: %s [-h|-help] [-V]\n\
85 \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);
90
91   fprintf(stream, "\
92 Options:\n\
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   -e <extension>    - Extension to use for aux data files. default \"bin\"\n\
101   -f <start-frame>  - Starting frame number, default 0\n\
102   -g <filename>     - Extract global metadata to the named file.\n\
103   -h | -help        - Show help\n\
104   -k <key-string>   - Use key for ciphertext operations\n\
105   -m                - verify HMAC values when reading\n\
106   -s <size>         - Number of bytes to dump to output when -v is given\n\
107   -V                - Show version information\n\
108   -v                - Verbose, prints informative messages to stderr\n\
109   -W                - Read input file only, do not write destination file\n\
110   -w <width>        - Width of numeric element in a series of frame file names\n\
111                       (default 6)\n\
112   -z                - Fail if j2c inputs have unequal parameters (default)\n\
113   -Z                - Ignore unequal parameters in j2c inputs\n\
114 \n\
115   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
116          o All option arguments must be separated from the option by whitespace.\n\n");
117 }
118
119 //
120 class CommandOptions
121 {
122   CommandOptions();
123
124 public:
125   bool   error_flag;     // true if the given options are in error or not complete
126   bool   key_flag;       // true if an encryption key was given
127   bool   read_hmac;      // true if HMAC values are to be validated
128   bool   split_wav;      // true if PCM is to be extracted to stereo WAV files
129   bool   mono_wav;       // true if PCM is to be extracted to mono WAV files
130   bool   verbose_flag;   // true if the verbose option was selected
131   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
132   bool   no_write_flag;  // true if no output files are to be written
133   bool   version_flag;   // true if the version display option was selected
134   bool   help_flag;      // true if the help display option was selected
135   bool   stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
136   ui32_t number_width;   // number of digits in a serialized filename (for JPEG extract)
137   ui32_t start_frame;    // frame number to begin processing
138   ui32_t duration;       // number of frames to be processed
139   bool   duration_flag;  // true if duration argument given
140   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
141   ui32_t picture_rate;   // fps of picture when wrapping PCM
142   ui32_t fb_size;        // size of picture frame buffer
143   Rational edit_rate;    // frame buffer size for reading clip-wrapped PCM
144   const char* file_prefix; // filename pre for files written by the extract mode
145   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
146   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
147   PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
148   const char* input_filename;
149   const char* extension;
150   std::string global_metadata_filename, prefix_buffer;
151
152   //
153   CommandOptions(int argc, const char** argv) :
154     error_flag(true), key_flag(false), read_hmac(false), split_wav(false),
155     mono_wav(false), verbose_flag(false), fb_dump_size(0), no_write_flag(false),
156     version_flag(false), help_flag(false), number_width(6),
157     start_frame(0), duration(0xffffffff), duration_flag(false), j2c_pedantic(true),
158     picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_prefix(0),
159     input_filename(0), extension("bin")
160   {
161     memset(key_value, 0, KeyLen);
162     memset(key_id_value, 0, UUIDlen);
163
164     for ( int i = 1; i < argc; ++i )
165       {
166
167         if ( (strcmp( argv[i], "-help") == 0) )
168           {
169             help_flag = true;
170             continue;
171           }
172          
173         if ( argv[i][0] == '-'
174              && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) )
175              && argv[i][2] == 0 )
176           {
177             switch ( argv[i][1] )
178               {
179               case '1': mono_wav = true; break;
180               case '2': split_wav = true; break;
181
182               case 'b':
183                 TEST_EXTRA_ARG(i, 'b');
184                 fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
185
186                 if ( verbose_flag )
187                   fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
188
189                 break;
190
191               case 'd':
192                 TEST_EXTRA_ARG(i, 'd');
193                 duration_flag = true;
194                 duration = Kumu::xabs(strtol(argv[i], 0, 10));
195                 break;
196
197               case 'e':
198                 TEST_EXTRA_ARG(i, 'e');
199                 extension = argv[i];
200                 break;
201
202               case 'f':
203                 TEST_EXTRA_ARG(i, 'f');
204                 start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
205                 break;
206
207               case 'g':
208                 TEST_EXTRA_ARG(i, 'g');
209                 global_metadata_filename = argv[i];
210                 break;
211
212               case 'h': help_flag = true; break;
213               case 'm': read_hmac = true; break;
214
215               case 'p':
216                 TEST_EXTRA_ARG(i, 'p');
217                 picture_rate = Kumu::xabs(strtol(argv[i], 0, 10));
218                 break;
219
220               case 's':
221                 TEST_EXTRA_ARG(i, 's');
222                 fb_dump_size = Kumu::xabs(strtol(argv[i], 0, 10));
223                 break;
224
225               case 'V': version_flag = true; break;
226               case 'v': verbose_flag = true; break;
227               case 'W': no_write_flag = true; break;
228
229               case 'w':
230                 TEST_EXTRA_ARG(i, 'w');
231                 number_width = Kumu::xabs(strtol(argv[i], 0, 10));
232                 break;
233
234               case 'Z': j2c_pedantic = false; break;
235               case 'z': j2c_pedantic = true; break;
236
237               default:
238                 fprintf(stderr, "Unrecognized option: %s\n", argv[i]);
239                 return;
240               }
241           }
242         else
243           {
244             if ( argv[i][0] != '-' )
245               {
246                 if ( input_filename == 0 )
247                   {
248                     input_filename = argv[i];
249                   }
250                 else if ( file_prefix == 0 )
251                   {
252                     file_prefix = argv[i];
253                   }
254               }
255             else
256               {
257                 fprintf(stderr, "Unrecognized argument: %s\n", argv[i]);
258                 return;
259               }
260           }
261       }
262
263     if ( help_flag || version_flag )
264       return;
265     
266     if ( input_filename == 0 )
267       {
268         fputs("At least one filename argument is required.\n", stderr);
269         return;
270       }
271
272     if ( file_prefix == 0 )
273       {
274         prefix_buffer = Kumu::PathSetExtension(input_filename, "") + "_";
275         file_prefix = prefix_buffer.c_str();
276       }
277
278     error_flag = false;
279   }
280 };
281
282
283 //------------------------------------------------------------------------------------------
284 // JPEG 2000 essence
285
286
287 // Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
288 // Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
289 // Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
290 //
291 Result_t
292 read_JP2K_file(CommandOptions& Options)
293 {
294   AESDecContext*     Context = 0;
295   HMACContext*       HMAC = 0;
296   AS_02::JP2K::MXFReader    Reader;
297   JP2K::FrameBuffer  FrameBuffer(Options.fb_size);
298   ui32_t             frame_count = 0;
299
300   Result_t result = Reader.OpenRead(Options.input_filename);
301
302   if ( ASDCP_SUCCESS(result) )
303     {
304       if ( Options.verbose_flag )
305         {
306           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
307         }
308
309       ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
310       ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
311
312       result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
313                                                      reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
314
315       if ( KM_SUCCESS(result) )
316         {
317           assert(rgba_descriptor);
318           frame_count = rgba_descriptor->ContainerDuration;
319
320           if ( Options.verbose_flag )
321             {
322               rgba_descriptor->Dump();
323             }
324         }
325       else
326         {
327           result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
328                                                          reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
329
330           if ( KM_SUCCESS(result) )
331             {
332               assert(cdci_descriptor);
333               frame_count = cdci_descriptor->ContainerDuration;
334
335               if ( Options.verbose_flag )
336                 {
337                   cdci_descriptor->Dump();
338                 }
339             }
340           else
341             {
342               fprintf(stderr, "File does not contain an essence descriptor.\n");
343               frame_count = Reader.AS02IndexReader().GetDuration();
344             }
345         }
346
347       if ( frame_count == 0 )
348         {
349           frame_count = Reader.AS02IndexReader().GetDuration();
350         }
351
352       if ( frame_count == 0 )
353         {
354           fprintf(stderr, "Unable to determine file duration.\n");
355           return RESULT_FAIL;
356         }
357     }
358
359   if ( ASDCP_SUCCESS(result) && Options.key_flag )
360     {
361       Context = new AESDecContext;
362       result = Context->InitKey(Options.key_value);
363
364       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
365         {
366           WriterInfo Info;
367           Reader.FillWriterInfo(Info);
368
369           if ( Info.UsesHMAC )
370             {
371               HMAC = new HMACContext;
372               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
373             }
374           else
375             {
376               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
377             }
378         }
379     }
380
381   ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
382   if ( last_frame > frame_count )
383     last_frame = frame_count;
384
385   char name_format[64];
386   snprintf(name_format,  64, "%%s%%0%du.j2c", Options.number_width);
387
388   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
389     {
390       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
391
392       char filename[1024];
393       snprintf(filename, 1024, name_format, Options.file_prefix, i);
394
395       if ( ASDCP_SUCCESS(result) && Options.verbose_flag )
396         {
397           printf("Frame %d, %d bytes", i, FrameBuffer.Size());
398
399           if ( ! Options.no_write_flag )
400             {
401               printf(" -> %s", filename);
402             }
403
404           printf("\n");
405         }
406
407       if ( ASDCP_SUCCESS(result)  && ( ! Options.no_write_flag ) )
408         {
409           Kumu::FileWriter OutFile;
410           ui32_t write_count;
411           result = OutFile.OpenWrite(filename);
412
413           if ( ASDCP_SUCCESS(result) )
414             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
415
416           if ( ASDCP_SUCCESS(result) && Options.verbose_flag )
417             {
418               FrameBuffer.Dump(stderr, Options.fb_dump_size);
419             }
420         }
421     }
422
423   return result;
424 }
425
426 //------------------------------------------------------------------------------------------
427 // PCM essence
428
429 // Read one or more plaintext PCM audio streams from a plaintext ASDCP file
430 // Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
431 // Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
432 //
433 Result_t
434 read_PCM_file(CommandOptions& Options)
435 {
436   AESDecContext*     Context = 0;
437   HMACContext*       HMAC = 0;
438   AS_02::PCM::MXFReader     Reader;
439   PCM::FrameBuffer   FrameBuffer;
440   WavFileWriter      OutWave;
441   ui32_t last_frame = 0;
442   ASDCP::MXF::WaveAudioDescriptor *wave_descriptor = 0;
443
444   if ( Options.edit_rate == Rational(0,0) ) // todo, make this available to the CLI
445     {
446       Options.edit_rate = EditRate_24;
447     }
448
449   Result_t result = Reader.OpenRead(Options.input_filename, Options.edit_rate);
450
451   if ( KM_SUCCESS(result) )
452     {
453       if ( Options.verbose_flag )
454         {
455           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
456         }
457       
458       ASDCP::MXF::InterchangeObject* tmp_obj = 0;
459
460       result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor), &tmp_obj);
461
462       if ( KM_SUCCESS(result) )
463         {
464           wave_descriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(tmp_obj);
465
466           if ( wave_descriptor == 0 )
467             {
468               fprintf(stderr, "File does not contain an essence descriptor.\n");
469               return RESULT_FAIL;
470             }
471       
472           if ( Options.verbose_flag )
473             {
474               wave_descriptor->Dump();
475             }
476
477           if ( wave_descriptor->ContainerDuration.get() == 0 )
478             {
479               fprintf(stderr, "ContainerDuration not set in file descriptor, attempting to use index duration.\n");
480               last_frame = Reader.AS02IndexReader().GetDuration();
481             }
482           else
483             {
484               last_frame = wave_descriptor->ContainerDuration;
485             }
486
487           if ( last_frame == 0 )
488             {
489               fprintf(stderr, "ContainerDuration not set in index, attempting to use Duration from SourceClip.\n");
490               result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_SourceClip), &tmp_obj);
491               if ( KM_SUCCESS(result))
492                 {
493                   ASDCP::MXF::SourceClip *sourceClip = dynamic_cast<ASDCP::MXF::SourceClip*>(tmp_obj);
494                   if ( ! sourceClip->Duration.empty() )
495                     {
496                       last_frame = sourceClip->Duration;
497                     }
498                 }
499             }
500
501           if ( last_frame == 0 )
502             {
503               fprintf(stderr, "Unable to determine file duration.\n");
504               return RESULT_FAIL;
505             }
506
507           assert(wave_descriptor);
508           FrameBuffer.Capacity(AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, Options.edit_rate));
509           last_frame = AS_02::MXF::CalcFramesFromDurationInSamples(last_frame, *wave_descriptor, Options.edit_rate);
510         }
511     }
512
513   if ( ASDCP_SUCCESS(result) )
514     {
515       if ( Options.duration > 0 && Options.duration < last_frame )
516         last_frame = Options.duration;
517
518       if ( Options.start_frame > 0 )
519         {
520           if ( Options.start_frame > last_frame )
521             {
522               fprintf(stderr, "Start value greater than file duration.\n");
523               return RESULT_FAIL;
524             }
525
526           last_frame = Kumu::xmin(Options.start_frame + last_frame, last_frame);
527         }
528
529       last_frame = last_frame - Options.start_frame;
530
531       PCM::AudioDescriptor ADesc;
532
533       result = MD_to_PCM_ADesc(wave_descriptor, ADesc);
534
535       if ( ASDCP_SUCCESS(result) )
536         {
537           ADesc.ContainerDuration = last_frame;
538           ADesc.EditRate = Options.edit_rate;
539
540           result = OutWave.OpenWrite(ADesc, Options.file_prefix,
541                                      ( Options.split_wav ? WavFileWriter::ST_STEREO : 
542                                        ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
543         }
544     }
545
546   if ( ASDCP_SUCCESS(result) && Options.key_flag )
547     {
548       Context = new AESDecContext;
549       result = Context->InitKey(Options.key_value);
550
551       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
552         {
553           WriterInfo Info;
554           Reader.FillWriterInfo(Info);
555
556           if ( Info.UsesHMAC )
557             {
558               HMAC = new HMACContext;
559               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
560             }
561           else
562             {
563               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
564             }
565         }
566     }
567
568   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
569     {
570       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
571
572       if ( ASDCP_SUCCESS(result) )
573         {
574           if ( Options.verbose_flag )
575             {
576               FrameBuffer.FrameNumber(i);
577               FrameBuffer.Dump(stderr, Options.fb_dump_size);
578             }
579
580           if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
581             {
582               fprintf(stderr, "Last frame is incomplete, padding with zeros.\n");
583               // actually, it has already been zeroed for us, we just need to recognize the appropriate size
584               FrameBuffer.Size(FrameBuffer.Capacity());
585             }
586
587           result = OutWave.WriteFrame(FrameBuffer);
588         }
589     }
590
591   return result;
592 }
593
594
595 //------------------------------------------------------------------------------------------
596 // TimedText essence
597
598 // Read one or more timed text streams from a plaintext AS-02 file
599 //
600 Result_t
601 read_timed_text_file(CommandOptions& Options)
602 {
603   AESDecContext*     Context = 0;
604   HMACContext*       HMAC = 0;
605   AS_02::TimedText::MXFReader     Reader;
606   TimedText::FrameBuffer   FrameBuffer(Options.fb_size);
607   //ASDCP::TimedText::FrameBuffer   FrameBuffer(Options.fb_size);
608   AS_02::TimedText::TimedTextDescriptor TDesc;
609   ASDCP::MXF::TimedTextDescriptor *tt_descriptor = 0;
610
611   Result_t result = Reader.OpenRead(Options.input_filename);
612
613   if ( ASDCP_SUCCESS(result) )
614     {
615       result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_TimedTextDescriptor),
616                                                      reinterpret_cast<MXF::InterchangeObject**>(&tt_descriptor));
617     if ( Options.verbose_flag ) {
618         tt_descriptor->Dump();
619     }
620
621
622   if ( ASDCP_FAILURE(result) )
623     return result;
624
625   std::string XMLDoc;
626   std::string out_path = Kumu::PathDirname(Options.file_prefix);
627   ui32_t write_count;
628   char buf[64];
629   TimedText::ResourceList_t::const_iterator ri;
630
631   result = Reader.ReadTimedTextResource(XMLDoc);
632
633   if ( ASDCP_SUCCESS(result) )
634     {
635       Reader.FillTimedTextDescriptor(TDesc);
636       FrameBuffer.Capacity(Options.fb_size);
637
638       if ( Options.verbose_flag )
639         TimedText::DescriptorDump(TDesc);
640     }
641
642   if ( ASDCP_SUCCESS(result) && ( ! Options.no_write_flag ) )
643     {
644       Kumu::FileWriter Writer;
645       result = Writer.OpenWrite(Options.file_prefix);
646
647       if ( ASDCP_SUCCESS(result) )
648         result = Writer.Write(reinterpret_cast<const byte_t*>(XMLDoc.c_str()), XMLDoc.size(), &write_count);
649     }
650
651   for ( ri = TDesc.ResourceList.begin() ; ri != TDesc.ResourceList.end() && ASDCP_SUCCESS(result); ri++ )
652     {
653       result = Reader.ReadAncillaryResource(ri->ResourceID, FrameBuffer, Context, HMAC);
654
655       if ( ASDCP_SUCCESS(result) && ( ! Options.no_write_flag ) )
656         {
657           Kumu::FileWriter Writer;
658           if (out_path != "") {
659                   result = Writer.OpenWrite(Kumu::PathJoin(out_path, Kumu::UUID(ri->ResourceID).EncodeHex(buf, 64)).c_str());
660           } else {
661                   // Workaround for a bug in Kumu::PathJoin
662                   result = Writer.OpenWrite(Kumu::UUID(ri->ResourceID).EncodeHex(buf, 64));
663           }
664
665           if ( ASDCP_SUCCESS(result) )
666             result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count);
667
668               if ( Options.verbose_flag )
669                 FrameBuffer.Dump(stderr, Options.fb_dump_size);
670         }
671     }
672     }
673   return result;
674 }
675
676 // Read one or more plaintext DCData bytestreams from a plaintext ASDCP file
677 // Read one or more plaintext DCData bytestreams from a ciphertext ASDCP file
678 // Read one or more ciphertext DCData byestreams from a ciphertext ASDCP file
679 //
680 Result_t
681 read_aux_data_file(CommandOptions& Options)
682 {
683   AESDecContext*     Context = 0;
684   HMACContext*       HMAC = 0;
685   AS_02::AuxData::MXFReader Reader;
686   DCData::FrameBuffer FrameBuffer(Options.fb_size);
687   ui32_t             frame_count = 0;
688
689   ASDCP::FrameBuffer global_metadata;
690   Result_t result = Reader.OpenRead(Options.input_filename, global_metadata);
691
692   if ( ASDCP_SUCCESS(result)
693        && global_metadata.Size()
694        && ! Options.global_metadata_filename.empty() )
695     {
696       ui32_t write_count = 0;
697       Kumu::FileWriter Writer;
698
699       result = Writer.OpenWrite(Options.global_metadata_filename);
700
701       if ( ASDCP_SUCCESS(result) )
702         {
703           result = Writer.Write(global_metadata.RoData(), global_metadata.Size(), &write_count);
704         }
705
706       if ( ASDCP_SUCCESS(result) && global_metadata.Size() != write_count) 
707         {
708           return RESULT_WRITEFAIL;
709         }
710     }
711
712   if ( ASDCP_SUCCESS(result) )
713     {
714       frame_count = Reader.AS02IndexReader().GetDuration();
715
716       if ( Options.verbose_flag )
717         {
718           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
719         }
720     }
721
722   if ( ASDCP_SUCCESS(result) && Options.key_flag )
723     {
724       Context = new AESDecContext;
725       result = Context->InitKey(Options.key_value);
726
727       if ( ASDCP_SUCCESS(result) && Options.read_hmac )
728         {
729           WriterInfo Info;
730           Reader.FillWriterInfo(Info);
731
732           if ( Info.UsesHMAC )
733             {
734               HMAC = new HMACContext;
735               result = HMAC->InitKey(Options.key_value, Info.LabelSetType);
736             }
737           else
738             {
739               fputs("File does not contain HMAC values, ignoring -m option.\n", stderr);
740             }
741         }
742     }
743
744   ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
745   if ( last_frame > frame_count )
746     last_frame = frame_count;
747
748   char name_format[64];
749   snprintf(name_format,  64, "%%s%%0%du.%s", Options.number_width, Options.extension);
750
751   for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
752     {
753       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
754
755       if ( ASDCP_SUCCESS(result) )
756         {
757           if ( ! Options.no_write_flag )
758             {
759           Kumu::FileWriter OutFile;
760           char filename[256];
761           ui32_t write_count;
762           snprintf(filename, 256, name_format, Options.file_prefix, i);
763           result = OutFile.OpenWrite(filename);
764
765           if ( ASDCP_SUCCESS(result) )
766             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
767         }
768
769           if ( Options.verbose_flag )
770             FrameBuffer.Dump(stderr, Options.fb_dump_size);
771         }
772     }
773
774   return result;
775 }
776
777
778 //
779 int
780 main(int argc, const char** argv)
781 {
782   char     str_buf[64];
783   CommandOptions Options(argc, argv);
784
785   if ( Options.version_flag )
786     banner();
787
788   if ( Options.help_flag )
789     usage();
790
791   if ( Options.version_flag || Options.help_flag )
792     return 0;
793
794   if ( Options.error_flag )
795     {
796       fprintf(stderr, "There was a problem. Type %s -h for help.\n", PROGRAM_NAME);
797       return 3;
798     }
799
800   EssenceType_t EssenceType;
801   Result_t result = ASDCP::EssenceType(Options.input_filename, EssenceType);
802
803   if ( ASDCP_SUCCESS(result) )
804     {
805       switch ( EssenceType )
806         {
807         case ESS_AS02_JPEG_2000:
808           result = read_JP2K_file(Options);
809           break;
810
811         case ESS_AS02_PCM_24b_48k:
812         case ESS_AS02_PCM_24b_96k:
813           result = read_PCM_file(Options);
814           break;
815
816         case ESS_AS02_TIMED_TEXT:
817           result = read_timed_text_file(Options);
818           break;
819
820         case ESS_DCDATA_UNKNOWN:
821           result = read_aux_data_file(Options);
822           break;
823
824         default:
825           fprintf(stderr, "%s: Unknown file type, not AS-02 essence.\n", Options.input_filename);
826           return 5;
827         }
828     }
829
830   if ( ASDCP_FAILURE(result) )
831     {
832       fputs("Program stopped on error.\n", stderr);
833
834       if ( result != RESULT_FAIL )
835         {
836           fputs(result, stderr);
837           fputc('\n', stderr);
838         }
839
840       return 1;
841     }
842
843   return 0;
844 }
845
846
847 //
848 // end as-02-unwrap.cpp
849 //