Added library names
[asdcplib.git] / src / as-02-unwrap.cpp
index 6e6850773a4627a85939200e58e7d0a0eaac9922..f0b3ba355dd6c0198db2a145f8fbbb80b7cc1e54 100755 (executable)
@@ -1,5 +1,7 @@
 /*
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -36,8 +38,13 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #include <KM_fileio.h>
 #include <AS_02.h>
+#include "AS_02_ACES.h"
 #include <WavFileWriter.h>
 
+namespace ASDCP {
+  Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, ASDCP::PCM::AudioDescriptor& ADesc);
+}
+
 using namespace ASDCP;
 
 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
@@ -63,7 +70,7 @@ banner(FILE* stream = stdout)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2018, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, 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",
@@ -80,7 +87,7 @@ USAGE: %s [-h|-help] [-V]\n\
        %s [-1|-2] [-b <buffer-size>] [-d <duration>]\n\
        [-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <size>] [-v] [-W]\n\
        [-w] <input-file> [<file-prefix>]\n\n",
-         PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
+         PROGRAM_NAME, PROGRAM_NAME);
 
   fprintf(stream, "\
 Options:\n\
@@ -92,6 +99,7 @@ Options:\n\
                       Defaults to 4,194,304 (4MB)\n\
   -d <duration>     - Number of frames to process, default all\n\
   -f <start-frame>  - Starting frame number, default 0\n\
+  -g <SID>          - Extract the Generic Stream Partition payload\n\
   -h | -help        - Show help\n\
   -k <key-string>   - Use key for ciphertext operations\n\
   -m                - verify HMAC values when reading\n\
@@ -132,11 +140,13 @@ public:
   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
   ui32_t picture_rate;   // fps of picture when wrapping PCM
   ui32_t fb_size;        // size of picture frame buffer
+  Rational edit_rate;    // frame buffer size for reading clip-wrapped PCM
   const char* file_prefix; // filename pre for files written by the extract mode
   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
-  PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
   const char* input_filename;
+  const char* extension;
+  i32_t g_stream_sid;     // Stream ID of a generic stream partition payload to be extracted
   std::string prefix_buffer;
 
   //
@@ -146,7 +156,7 @@ public:
     version_flag(false), help_flag(false), number_width(6),
     start_frame(0), duration(0xffffffff), duration_flag(false), j2c_pedantic(true),
     picture_rate(24), fb_size(FRAME_BUFFER_SIZE), file_prefix(0),
-    input_filename(0)
+    input_filename(0), extension(0), g_stream_sid(0)
   {
     memset(key_value, 0, KeyLen);
     memset(key_id_value, 0, UUIDlen);
@@ -171,7 +181,7 @@ public:
 
              case 'b':
                TEST_EXTRA_ARG(i, 'b');
-               fb_size = abs(atoi(argv[i]));
+               fb_size = Kumu::xabs(strtol(argv[i], 0, 10));
 
                if ( verbose_flag )
                  fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
@@ -181,25 +191,35 @@ public:
              case 'd':
                TEST_EXTRA_ARG(i, 'd');
                duration_flag = true;
-               duration = abs(atoi(argv[i]));
+               duration = Kumu::xabs(strtol(argv[i], 0, 10));
+               break;
+
+             case 'e':
+               TEST_EXTRA_ARG(i, 'e');
+               extension = argv[i];
                break;
 
              case 'f':
                TEST_EXTRA_ARG(i, 'f');
-               start_frame = abs(atoi(argv[i]));
+               start_frame = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
+             case 'g':
+               TEST_EXTRA_ARG(i, 'g');
+               g_stream_sid = strtol(argv[i], 0, 10);
+               break;
+                 
              case 'h': help_flag = true; break;
              case 'm': read_hmac = true; break;
 
              case 'p':
                TEST_EXTRA_ARG(i, 'p');
-               picture_rate = abs(atoi(argv[i]));
+               picture_rate = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 's':
                TEST_EXTRA_ARG(i, 's');
-               fb_dump_size = abs(atoi(argv[i]));
+               fb_dump_size = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 'V': version_flag = true; break;
@@ -208,7 +228,7 @@ public:
 
              case 'w':
                TEST_EXTRA_ARG(i, 'w');
-               number_width = abs(atoi(argv[i]));
+               number_width = Kumu::xabs(strtol(argv[i], 0, 10));
                break;
 
              case 'Z': j2c_pedantic = false; break;
@@ -281,15 +301,58 @@ read_JP2K_file(CommandOptions& Options)
 
   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);
+       }
+
+      ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
+      ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
+
+      result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
+                                                    reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
+
+      if ( KM_SUCCESS(result) )
+       {
+         assert(rgba_descriptor);
+         frame_count = (ui32_t)rgba_descriptor->ContainerDuration;
+
+         if ( Options.verbose_flag )
+           {
+             rgba_descriptor->Dump();
+           }
+       }
+      else
+       {
+         result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
+                                                        reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
+
+         if ( KM_SUCCESS(result) )
+           {
+             assert(cdci_descriptor);
+             frame_count = (ui32_t)cdci_descriptor->ContainerDuration;
+
+             if ( Options.verbose_flag )
+               {
+                 cdci_descriptor->Dump();
+               }
+           }
+         else
+           {
+             fprintf(stderr, "File does not contain an essence descriptor.\n");
+             frame_count = Reader.AS02IndexReader().GetDuration();
+           }
+       }
+
+      if ( frame_count == 0 )
+       {
+         frame_count = Reader.AS02IndexReader().GetDuration();
+       }
+
+      if ( frame_count == 0 )
+       {
+         fprintf(stderr, "Unable to determine file duration.\n");
+         return RESULT_FAIL;
        }
     }
 
@@ -326,25 +389,224 @@ read_JP2K_file(CommandOptions& Options)
     {
       result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
 
-      if ( ASDCP_SUCCESS(result) )
+      char filename[1024];
+      snprintf(filename, 1024, name_format, Options.file_prefix, i);
+
+      if ( ASDCP_SUCCESS(result) && Options.verbose_flag )
+       {
+         printf("Frame %d, %d bytes", i, FrameBuffer.Size());
+
+         if ( ! Options.no_write_flag )
+           {
+             printf(" -> %s", filename);
+           }
+
+         printf("\n");
+       }
+
+      if ( ASDCP_SUCCESS(result)  && ( ! Options.no_write_flag ) )
        {
          Kumu::FileWriter OutFile;
-         char filename[256];
          ui32_t write_count;
-         snprintf(filename, 256, name_format, Options.file_prefix, 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) && Options.verbose_flag )
+           {
+             FrameBuffer.Dump(stderr, Options.fb_dump_size);
+           }
        }
     }
 
   return result;
 }
 
+
+//------------------------------------------------------------------------------------------
+// ACES essence
+
+//
+Result_t
+read_ACES_file(CommandOptions& Options)
+{
+  AESDecContext*     Context = 0;
+  HMACContext*       HMAC = 0;
+  AS_02::ACES::MXFReader Reader;
+  AS_02::ACES::FrameBuffer FrameBuffer(Options.fb_size);
+  ui64_t             frame_count = 0;
+  AS_02::ACES::ResourceList_t resource_list_t;
+
+  Result_t result = Reader.OpenRead(Options.input_filename);
+
+  if (ASDCP_SUCCESS(result))
+  {
+    if (Options.verbose_flag)
+    {
+      fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+    }
+    ASDCP::MXF::RGBAEssenceDescriptor *aces_descriptor = 0;
+
+    result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
+      reinterpret_cast<MXF::InterchangeObject**>(&aces_descriptor));
+
+    if (KM_SUCCESS(result))
+    {
+      assert(aces_descriptor);
+      frame_count = aces_descriptor->ContainerDuration;
+
+      if (Options.verbose_flag)
+      {
+        aces_descriptor->Dump();
+      }
+    }
+    else
+    {
+      fprintf(stderr, "File does not contain an essence descriptor.\n");
+      frame_count = Reader.AS02IndexReader().GetDuration();
+    }
+
+    if (frame_count == 0)
+    {
+      frame_count = Reader.AS02IndexReader().GetDuration();
+    }
+
+    if (frame_count == 0)
+    {
+      fprintf(stderr, "Unable to determine file duration.\n");
+      return RESULT_FAIL;
+    }
+  }
+
+  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);
+      }
+    }
+  }
+
+  ui32_t last_frame = Options.start_frame + (Options.duration ? Options.duration : frame_count);
+  if (last_frame > frame_count)
+    last_frame = frame_count;
+
+  char name_format[64];
+  snprintf(name_format, 64, "%%s%%0%du.exr", Options.number_width);
+
+  for (ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++)
+  {
+    result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
+
+    char filename[1024];
+    snprintf(filename, 1024, name_format, Options.file_prefix, i);
+
+    if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+    {
+      printf("Frame %d, %d bytes", i, FrameBuffer.Size());
+
+      if (!Options.no_write_flag)
+      {
+        printf(" -> %s", filename);
+      }
+
+      printf("\n");
+    }
+
+    if (ASDCP_SUCCESS(result) && (!Options.no_write_flag))
+    {
+      Kumu::FileWriter OutFile;
+      ui32_t write_count;
+      result = OutFile.OpenWrite(filename);
+
+      if (ASDCP_SUCCESS(result))
+        result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+
+      if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+      {
+        FrameBuffer.Dump(stderr, Options.fb_dump_size);
+      }
+    }
+  }
+
+  snprintf(name_format, 64, "TargetFrame_%%s.%%s");
+  result = Reader.FillAncillaryResourceList(resource_list_t);
+  if (ASDCP_SUCCESS(result))
+  {
+    AS_02::ACES::ResourceList_t::iterator it;
+    for (it = resource_list_t.begin(); it != resource_list_t.end(); it++)
+    {
+      UUID resource_id;
+      resource_id.Set(it->ResourceID);
+      result = Reader.ReadAncillaryResource(resource_id, FrameBuffer);
+
+      char filename[1024];
+      char buf[64];
+      resource_id.EncodeString(buf, 64);
+      std::string extension;
+      switch (it->Type)
+      {
+      case AS_02::ACES::MT_PNG:
+        extension = "png";
+        break;
+      case AS_02::ACES::MT_TIFF:
+        extension = "tif";
+        break;
+      default:
+        break;
+      }
+      snprintf(filename, 1024, name_format, buf, extension.c_str());
+
+      if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+      {
+        printf("Read Anc resource, size: %d\n", FrameBuffer.Size() );
+
+        if (!Options.no_write_flag)
+        {
+          printf(" -> %s", filename);
+        }
+
+        printf("\n");
+      }
+
+      if (ASDCP_SUCCESS(result) && (!Options.no_write_flag))
+      {
+        Kumu::FileWriter OutFile;
+        ui32_t write_count;
+        result = OutFile.OpenWrite(filename);
+
+        if (ASDCP_SUCCESS(result))
+          result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+
+        if (ASDCP_SUCCESS(result) && Options.verbose_flag)
+        {
+          FrameBuffer.Dump(stderr, Options.fb_dump_size);
+        }
+      }
+    }
+
+  }
+
+
+  return result;
+}
+
+
+
 //------------------------------------------------------------------------------------------
 // PCM essence
 
@@ -360,43 +622,109 @@ read_PCM_file(CommandOptions& Options)
   AS_02::PCM::MXFReader     Reader;
   PCM::FrameBuffer   FrameBuffer;
   WavFileWriter      OutWave;
-  PCM::AudioDescriptor ADesc;
   ui32_t last_frame = 0;
+  ASDCP::MXF::WaveAudioDescriptor *wave_descriptor = 0;
 
-  Result_t result = Reader.OpenRead(Options.input_filename);
-
-  if ( ASDCP_SUCCESS(result) )
+  if ( Options.edit_rate == Rational(0,0) ) // todo, make this available to the CLI
     {
-      Reader.FillAudioDescriptor(ADesc);
-      ADesc.EditRate = Rational(1, 1);
-      FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
+      Options.edit_rate = EditRate_24;
+    }
+
+  Result_t result = Reader.OpenRead(Options.input_filename, Options.edit_rate);
 
+  if ( KM_SUCCESS(result) )
+    {
       if ( Options.verbose_flag )
-       PCM::AudioDescriptorDump(ADesc);
+       {
+         fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+       }
+      
+      ASDCP::MXF::InterchangeObject* tmp_obj = 0;
+
+      result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor), &tmp_obj);
+
+      if ( KM_SUCCESS(result) )
+       {
+         wave_descriptor = dynamic_cast<ASDCP::MXF::WaveAudioDescriptor*>(tmp_obj);
+
+         if ( wave_descriptor == 0 )
+           {
+             fprintf(stderr, "File does not contain an essence descriptor.\n");
+             return RESULT_FAIL;
+           }
+      
+         if ( Options.verbose_flag )
+           {
+             wave_descriptor->Dump();
+           }
+
+         if ( wave_descriptor->ContainerDuration.get() == 0 )
+           {
+             fprintf(stderr, "ContainerDuration not set in file descriptor, attempting to use index duration.\n");
+             last_frame = Reader.AS02IndexReader().GetDuration();
+           }
+         else
+           {
+             last_frame = (ui32_t)wave_descriptor->ContainerDuration;
+           }
+
+         if ( last_frame == 0 )
+           {
+             fprintf(stderr, "ContainerDuration not set in index, attempting to use Duration from SourceClip.\n");
+             result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_SourceClip), &tmp_obj);
+             if ( KM_SUCCESS(result))
+               {
+                 ASDCP::MXF::SourceClip *sourceClip = dynamic_cast<ASDCP::MXF::SourceClip*>(tmp_obj);
+                 if ( ! sourceClip->Duration.empty() )
+                   {
+                     last_frame = (ui32_t)sourceClip->Duration;
+                   }
+               }
+           }
+
+         if ( last_frame == 0 )
+           {
+             fprintf(stderr, "Unable to determine file duration.\n");
+             return RESULT_FAIL;
+           }
+
+         assert(wave_descriptor);
+         FrameBuffer.Capacity(AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, Options.edit_rate));
+         last_frame = AS_02::MXF::CalcFramesFromDurationInSamples(last_frame, *wave_descriptor, Options.edit_rate);
+       }
     }
 
   if ( ASDCP_SUCCESS(result) )
     {
-      last_frame = ADesc.ContainerDuration;
-
       if ( Options.duration > 0 && Options.duration < last_frame )
        last_frame = Options.duration;
 
       if ( Options.start_frame > 0 )
        {
-         if ( Options.start_frame > ADesc.ContainerDuration )
+         if ( Options.start_frame > last_frame )
            {
              fprintf(stderr, "Start value greater than file duration.\n");
              return RESULT_FAIL;
            }
 
-         last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
+         last_frame = Kumu::xmin(Options.start_frame + last_frame, last_frame);
        }
 
-      ADesc.ContainerDuration = last_frame - Options.start_frame;
-      OutWave.OpenWrite(ADesc, Options.file_prefix,
-                       ( Options.split_wav ? WavFileWriter::ST_STEREO : 
-                         ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
+      last_frame = last_frame - Options.start_frame;
+
+      PCM::AudioDescriptor ADesc;
+
+      result = MD_to_PCM_ADesc(wave_descriptor, ADesc);
+
+      if ( ASDCP_SUCCESS(result) )
+       {
+         ADesc.ContainerDuration = last_frame;
+         ADesc.EditRate = Options.edit_rate;
+
+         result = OutWave.OpenWrite(ADesc, Options.file_prefix,
+                                    ( Options.split_wav ? WavFileWriter::ST_STEREO : 
+                                      ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
+       }
     }
 
   if ( ASDCP_SUCCESS(result) && Options.key_flag )
@@ -428,7 +756,17 @@ read_PCM_file(CommandOptions& Options)
       if ( ASDCP_SUCCESS(result) )
        {
          if ( Options.verbose_flag )
-           FrameBuffer.Dump(stderr, Options.fb_dump_size);
+           {
+             FrameBuffer.FrameNumber(i);
+             FrameBuffer.Dump(stderr, Options.fb_dump_size);
+           }
+
+         if ( FrameBuffer.Size() != FrameBuffer.Capacity() )
+           {
+             fprintf(stderr, "Last frame is incomplete, padding with zeros.\n");
+             // actually, it has already been zeroed for us, we just need to recognize the appropriate size
+             FrameBuffer.Size(FrameBuffer.Capacity());
+           }
 
          result = OutWave.WriteFrame(FrameBuffer);
        }
@@ -438,11 +776,204 @@ read_PCM_file(CommandOptions& Options)
 }
 
 
+//------------------------------------------------------------------------------------------
+// TimedText essence
+
+// Read one or more timed text streams from a plaintext AS-02 file
+//
+Result_t
+read_timed_text_file(CommandOptions& Options)
+{
+  AESDecContext*     Context = 0;
+  HMACContext*       HMAC = 0;
+  AS_02::TimedText::MXFReader     Reader;
+  TimedText::FrameBuffer   FrameBuffer(Options.fb_size);
+  //ASDCP::TimedText::FrameBuffer   FrameBuffer(Options.fb_size);
+  AS_02::TimedText::TimedTextDescriptor TDesc;
+  ASDCP::MXF::TimedTextDescriptor *tt_descriptor = 0;
+
+  Result_t result = Reader.OpenRead(Options.input_filename);
+
+  if ( ASDCP_SUCCESS(result) )
+    {
+      result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_TimedTextDescriptor),
+                                                    reinterpret_cast<MXF::InterchangeObject**>(&tt_descriptor));
+    if ( Options.verbose_flag ) {
+       tt_descriptor->Dump();
+    }
+
+
+  if ( ASDCP_FAILURE(result) )
+    return result;
+
+  std::string XMLDoc;
+  std::string out_path = Kumu::PathDirname(Options.file_prefix);
+  ui32_t write_count;
+  char buf[64];
+  TimedText::ResourceList_t::const_iterator ri;
+
+  result = Reader.ReadTimedTextResource(XMLDoc);
+
+  if ( ASDCP_SUCCESS(result) )
+    {
+      Reader.FillTimedTextDescriptor(TDesc);
+      FrameBuffer.Capacity(Options.fb_size);
+
+      if ( Options.verbose_flag )
+       TimedText::DescriptorDump(TDesc);
+    }
+
+  if ( ASDCP_SUCCESS(result) && ( ! Options.no_write_flag ) )
+    {
+      Kumu::FileWriter Writer;
+      result = Writer.OpenWrite(Options.file_prefix);
+
+      if ( ASDCP_SUCCESS(result) )
+       result = Writer.Write(reinterpret_cast<const byte_t*>(XMLDoc.c_str()), XMLDoc.size(), &write_count);
+    }
+
+  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) && ( ! Options.no_write_flag ) )
+       {
+         Kumu::FileWriter Writer;
+         if (out_path != "") {
+                 result = Writer.OpenWrite(Kumu::PathJoin(out_path, Kumu::UUID(ri->ResourceID).EncodeHex(buf, 64)).c_str());
+         } else {
+                 // Workaround for a bug in Kumu::PathJoin
+                 result = Writer.OpenWrite(Kumu::UUID(ri->ResourceID).EncodeHex(buf, 64));
+         }
+
+         if ( ASDCP_SUCCESS(result) )
+           result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count);
+
+             if ( Options.verbose_flag )
+               FrameBuffer.Dump(stderr, Options.fb_dump_size);
+       }
+    }
+    }
+  return result;
+}
+
+//
+Result_t
+read_isxd_file(CommandOptions& Options)
+{
+  AESDecContext*     Context = 0;
+  HMACContext*       HMAC = 0;
+  AS_02::ISXD::MXFReader    Reader;
+  ASDCP::FrameBuffer  FrameBuffer;
+  ui32_t             frame_count = 0;
+
+  Result_t result = Reader.OpenRead(Options.input_filename);
+
+  if ( ASDCP_SUCCESS(result) )
+    {
+      result = FrameBuffer.Capacity(Options.fb_size);
+      frame_count = Reader.AS02IndexReader().GetDuration();
+    }
+
+  if ( ASDCP_SUCCESS(result) )
+    {
+      std::list<MXF::InterchangeObject*> object_list;
+      Reader.OP1aHeader().GetMDObjectsByType(DefaultSMPTEDict().ul(MDD_GenericStreamTextBasedSet), object_list);
+
+      std::list<MXF::InterchangeObject*>::iterator i;
+      for ( i = object_list.begin(); i != object_list.end(); ++i )
+       {
+         MXF::GenericStreamTextBasedSet *text_object = dynamic_cast<MXF::GenericStreamTextBasedSet*>(*i);
+         assert(text_object);
+         text_object->Dump(stderr);
+       }
+    }
+
+  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);
+           }
+       }
+    }
+
+  ui32_t last_frame = Options.start_frame + ( Options.duration ? Options.duration : frame_count);
+  if ( last_frame > frame_count )
+    last_frame = frame_count;
+
+  char name_format[64];
+  snprintf(name_format,  64, "%%s%%0%du.%s", Options.number_width, Options.extension);
+
+  for ( ui32_t i = Options.start_frame; ASDCP_SUCCESS(result) && i < last_frame; i++ )
+    {
+      result = Reader.ReadFrame(i, FrameBuffer, Context, HMAC);
+
+      if ( ASDCP_SUCCESS(result) )
+       {
+         if ( ! Options.no_write_flag )
+           {
+             Kumu::FileWriter OutFile;
+             char filename[256];
+             ui32_t write_count;
+             snprintf(filename, 256, name_format, Options.file_prefix, i);
+             result = OutFile.OpenWrite(filename);
+
+             if ( ASDCP_SUCCESS(result) )
+               result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+           }
+       }
+    }
+
+  return result;
+}
+
+Result_t
+extract_generic_stream_partition_payload(const std::string& in_filename, const ui32_t sid, const std::string& out_filename)
+{
+  ASDCP::FrameBuffer payload;
+  AS_02::ISXD::MXFReader reader;
+
+  Result_t result = reader.OpenRead(in_filename);
+          
+  if ( KM_SUCCESS(result) )
+    {
+      result = reader.ReadGenericStreamPartitionPayload(sid, payload);
+    }
+  
+  if ( KM_SUCCESS(result) )
+    {
+      Kumu::FileWriter writer;
+      ui32_t write_count = 0;
+      result = writer.OpenWrite(out_filename);
+      
+      if ( KM_SUCCESS(result) )
+       {
+         result = writer.Write(payload.RoData(), payload.Size(), &write_count);
+       }
+    }
+
+  return result;
+}
+
+
 //
 int
 main(int argc, const char** argv)
 {
-  char     str_buf[64];
   CommandOptions Options(argc, argv);
 
   if ( Options.version_flag )
@@ -467,17 +998,38 @@ main(int argc, const char** argv)
     {
       switch ( EssenceType )
        {
-       case ESS_JPEG_2000:
+       case ESS_AS02_JPEG_2000:
          result = read_JP2K_file(Options);
          break;
-
-       case ESS_PCM_24b_48k:
-       case ESS_PCM_24b_96k:
+       //PB
+       case ESS_AS02_ACES:
+         result = read_ACES_file(Options);
+         break;
+       //--
+       case ESS_AS02_PCM_24b_48k:
+       case ESS_AS02_PCM_24b_96k:
          result = read_PCM_file(Options);
          break;
 
+       case ESS_AS02_TIMED_TEXT:
+         result = read_timed_text_file(Options);
+         break;
+
+       case ESS_AS02_ISXD:
+         if ( Options.g_stream_sid == 0 )
+           {
+             result = read_isxd_file(Options);
+           }
+         else
+           {
+             result = extract_generic_stream_partition_payload(Options.input_filename,
+                                                               Options.g_stream_sid,
+                                                               Options.file_prefix);
+           }
+         break;
+
        default:
-         fprintf(stderr, "%s: Unknown file type, not ASDCP essence.\n", Options.input_filename);
+         fprintf(stderr, "%s: Unknown file type (%d), not AS-02 essence.\n", Options.input_filename, EssenceType);
          return 5;
        }
     }
@@ -486,11 +1038,7 @@ main(int argc, const char** argv)
     {
       fputs("Program stopped on error.\n", stderr);
 
-      if ( result == RESULT_SFORMAT )
-       {
-         fputs("Use option '-3' to force stereoscopic mode.\n", stderr);
-       }
-      else if ( result != RESULT_FAIL )
+      if ( result != RESULT_FAIL )
        {
          fputs(result, stderr);
          fputc('\n', stderr);