Added support for CAP, PRF and CPF markers
[asdcplib.git] / src / AS_DCP.h
index b7dc94ab5c75bc4f4d6f52a4e9d8f6d7aecd6b61..b28f1143f7588567025e3fb1b4c1004d7177d110 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2003-2005, John Hurst
+Copyright (c) 2003-2018, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -25,60 +25,74 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 /*! \file    AS_DCP.h
-    \version $Id$       
+    \version $Id$
     \brief   AS-DCP library, public interface
 
-The asdcplib library is a set of wrapper objects that offer simplified
-access to files conforming to the file formats proposed by the SMPTE
-D-Cinema packaging working group DC28.20.  The file format, labeled
-AS-DCP, is described in series of separate documents which include but
-may not be limited to:
-
- o AS-DCP Track File Specification
- o AS-DCP Track File Essence Encryption Specification
- o AS-DCP Operational Constraints Specification
- o SMPTE 330M - UMID
- o SMPTE 336M - KLV
- o SMPTE 377M - MXF
- o SMPTE 390M - OP-Atom
- o SMPTE 379M - Generic Container
- o SMPTE 381M - MPEG2 picture
- o SMPTE XXXM - JPEG 2000 picture
- o SMPTE 382M - WAV/PCM sound
+The asdcplib library is a set of file access objects that offer simplified
+access to files conforming to the standards published by the SMPTE
+D-Cinema Technology Committee 21DC. The file format, labeled AS-DCP,
+is described in a series of documents which includes but may not
+be be limited to:
+
+ o SMPTE ST 429-2:2011 DCP Operational Constraints
+ o SMPTE ST 429-3:2006 Sound and Picture Track File
+ o SMPTE ST 429-4:2006 MXF JPEG 2000 Application
+ o SMPTE ST 429-5:2009 Timed Text Track File
+ o SMPTE ST 429-6:2006 MXF Track File Essence Encryption
+ o SMPTE ST 429-10:2008 Stereoscopic Picture Track File
+ o SMPTE ST 429-14:2008 Aux Data Track File
+ o SMPTE ST 330:2004 - UMID
+ o SMPTE ST 336:2001 - KLV
+ o SMPTE ST 377:2004 - MXF (old version, required)
+ o SMPTE ST 377-1:2011 - MXF
+ o SMPTE ST 377-4:2012 - MXF Multichannel Audio Labeling Framework
+ o SMPTE ST 390:2011 - MXF OP-Atom
+ o SMPTE ST 379-1:2009 - MXF Generic Container (GC)
+ o SMPTE ST 381-1:2005 - MPEG2 picture in GC
+ o SMPTE ST 422:2006 - JPEG 2000 picture in GC
+ o SMPTE ST 382:2007 - WAV/PCM sound in GC
  o IETF RFC 2104 - HMAC/SHA1
- o NIST FIPS 197 - AES (Rijndael)
+ o NIST FIPS 197 - AES (Rijndael) (via OpenSSL)
+
+ o MXF Interop Track File Specification
+ o MXF Interop Track File Essence Encryption Specification
+ o MXF Interop Operational Constraints Specification
+ - Note: the MXF Interop documents are not formally published.
+   Contact the asdcplib support address to get copies.
 
 The following use cases are supported by the library:
 
- o Write a plaintext MPEG2 Video Elementary Stream to a plaintext ASDCP file
- o Write a plaintext MPEG2 Video Elementary Stream to a ciphertext ASDCP file
- o Read a plaintext MPEG2 Video Elementary Stream from a plaintext ASDCP file
- o Read a plaintext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
- o Read a ciphertext MPEG2 Video Elementary Stream from a ciphertext ASDCP file
- o Write one or more plaintext JPEG 2000 codestreams to a plaintext ASDCP file
- o Write one or more plaintext JPEG 2000 codestreams to a ciphertext ASDCP file
- o Read one or more plaintext JPEG 2000 codestreams from a plaintext ASDCP file
- o Read one or more plaintext JPEG 2000 codestreams from a ciphertext ASDCP file
- o Read one or more ciphertext JPEG 2000 codestreams from a ciphertext ASDCP file
- o Write one or more plaintext PCM audio streams to a plaintext ASDCP file
- o Write one or more plaintext PCM audio streams to a ciphertext ASDCP file
- o Read one or more plaintext PCM audio streams from a plaintext ASDCP file
- o Read one or more plaintext PCM audio streams from a ciphertext ASDCP file
- o Read one or more ciphertext PCM audio streams from a ciphertext ASDCP file
- o Read header metadata from an ASDCP file
-
-This project depends upon the following library:
- - OpenSSL http://www.openssl.org/
+ o Write essence to a plaintext or ciphertext AS-DCP file:
+ o Read essence from a plaintext or ciphertext AS-DCP file:
+     MPEG2 Video Elementary Stream
+     JPEG 2000 codestreams
+     JPEG 2000 stereoscopic codestream pairs
+     PCM audio streams
+     SMPTE 429-7 Timed Text XML with font and image resources
+     Aux Data (frame-wrapped synchronous blob)
+     Proposed Dolby (TM) Atmos track file
 
+ o Read header metadata from an AS-DCP file
+
+This project depends upon the following libraries:
+ - OpenSSL http://www.openssl.org/
+ - Expat http://expat.sourceforge.net/  or
+     Xerces-C http://xerces.apache.org/xerces-c/
+   An XML library is not needed if you don't need support for SMPTE ST 429-5:2009.
 */
 
-#ifndef _AS_DCP_H__
-#define _AS_DCP_H__
+#ifndef _AS_DCP_H_
+#define _AS_DCP_H_
 
+#include <KM_error.h>
+#include <KM_fileio.h>
 #include <stdio.h>
 #include <stdarg.h>
-#include <iostream>
 #include <math.h>
+#include <iosfwd>
+#include <string>
+#include <cstring>
+#include <list>
 
 //--------------------------------------------------------------------------------
 // common integer types
@@ -128,22 +142,12 @@ typedef unsigned int   ui32_t;
           T(const T&); \
           T& operator=(const T&)
 
-
 //--------------------------------------------------------------------------------
 // All library components are defined in the namespace ASDCP
 //
 namespace ASDCP {
-  // The version number consists of three segments: major, API minor, and
-  // implementation minor. Whenever a change is made to AS_DCP.h, the API minor
-  // version will increment. Changes made to the internal implementation will
-  // result in the incrementing of the implementation minor version.
-
-  // For example, if asdcplib version 1.0.0 were modified to accomodate changes
-  // in file format, and if no changes were made to AS_DCP.h, the new version would be
-  // 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1.
-  const ui32_t VERSION_MAJOR = 1;
-  const ui32_t VERSION_APIMINOR = 0;
-  const ui32_t VERSION_IMPMINOR = 4;
+  //
+  // The version number declaration and explanation have moved to ../configure.ac
   const char* Version();
 
   // UUIDs are passed around as strings of UUIDlen bytes
@@ -152,81 +156,45 @@ namespace ASDCP {
   // Encryption keys are passed around as strings of KeyLen bytes
   const ui32_t KeyLen = 16;
 
-  // Key IDs are really UUIDs, so it makes no sense to have this value
-  //  // Encryption key IDs are passed around as strings of KeyIDlen bytes
-  //  const ui32_t KeyIDlen = 16;
-
-
-  //---------------------------------------------------------------------------------
-  // message logging
-
-  // Error and debug messages will be delivered to an object having this interface.
-  // The default implementation sends only LOG_ERROR and LOG_WARN messages to stderr.
-  // To receive LOG_INFO or LOG_DEBUG messages, or to send messages somewhere other
-  // than stderr, implement this interface and register an instance of your new class
-  // by calling SetDefaultLogSink().
-  class ILogSink
-    {
-    public:
-      enum LogType_t { LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG };
-      
-      virtual ~ILogSink() {}
-      virtual void Error(const char*, ...) = 0; // receives error messges
-      virtual void Warn(const char*, ...) = 0;  // receives warning messges
-      virtual void Info(const char*, ...) = 0;  // receives info messages
-      virtual void Debug(const char*, ...) = 0; // receives debug messages
-      virtual void Logf(LogType_t, const char*, ...) = 0; // log a formatted string with positional parameters
-      virtual void vLogf(LogType_t, const char*, va_list*) = 0; // log a formatted string with a va_list struct
-    };
-
-  // Sets the internal default sink to the given receiver. If the given value
-  // is zero, sets the default sink to the internally allocated stderr sink.
-  void SetDefaultLogSink(ILogSink* = 0);
-
-  // Returns the internal default sink.
-  ILogSink& DefaultLogSink();
-
   //---------------------------------------------------------------------------------
   // return values
 
-  // Each method or subroutine in this library that is not void or does not directly
-  // return a value will instead return a result code from this enumeration.
-  enum Result_t {
-    RESULT_FALSE      =  1,   // successful but negative
-    RESULT_OK         =  0,   // No errors detected
-    RESULT_FAIL       = -1,   // An undefined error was detected
-    RESULT_PTR        = -2,   // An unexpected NULL pointer was given
-    RESULT_NULL_STR   = -3,   // An unexpected empty string was given
-    RESULT_SMALLBUF   = -4,   // The given frame buffer is too small
-    RESULT_INIT       = -5,   // The object is not yet initialized
-    RESULT_NOT_FOUND  = -6,   // The requested file does not exist on the system
-    RESULT_NO_PERM    = -7,   // Insufficient privilege exists to perform the operation
-    RESULT_FILEOPEN   = -8,   // Failure opening file
-    RESULT_FORMAT     = -9,   // The file format is not proper OP-Atom/AS-DCP
-    RESULT_BADSEEK    = -10,  // An invalid file location was requested
-    RESULT_READFAIL   = -11,  // File read error
-    RESULT_WRITEFAIL  = -12,  // File write error
-    RESULT_RAW_ESS    = -13,  // Unknown raw essence file type
-    RESULT_RAW_FORMAT = -14,  // Raw essence format invalid
-    RESULT_STATE      = -15,  // Object state error
-    RESULT_ENDOFFILE  = -16,  // Attempt to read past end of file
-    RESULT_CONFIG     = -17,  // Invalid configuration option detected
-    RESULT_RANGE      = -18,  // Frame number out of range
-    RESULT_CRYPT_CTX  = -19,  // AESEncContext required when writing to encrypted file
-    RESULT_LARGE_PTO  = -20,  // Plaintext offset exceeds frame buffer size
-    RESULT_ALLOC      = -21,  // Error allocating memory
-    RESULT_CAPEXTMEM  = -22,  // Cannot resize externally allocated memory
-    RESULT_CHECKFAIL  = -23,  // The check value did not decrypt correctly
-    RESULT_HMACFAIL   = -24,  // HMAC authentication failure
-    RESULT_HMAC_CTX   = -25,  // HMAC context required
-    RESULT_CRYPT_INIT = -26,  // Error initializing block cipher context
-    RESULT_EMPTY_FB   = -27,  // Attempted to write an empty frame buffer
-  };
-
-  // Returns a pointer to an English language string describing the given result code.
-  // If the result code is not a valid member of the Result_t enum, the string
-  // "**UNKNOWN**" will be returned.
-  const char* GetResultString(Result_t);
+  using Kumu::Result_t;
+
+  using Kumu::RESULT_FALSE;
+  using Kumu::RESULT_OK;
+  using Kumu::RESULT_FAIL;
+  using Kumu::RESULT_PTR;
+  using Kumu::RESULT_NULL_STR;
+  using Kumu::RESULT_ALLOC;
+  using Kumu::RESULT_PARAM;
+  using Kumu::RESULT_SMALLBUF;
+  using Kumu::RESULT_INIT;
+  using Kumu::RESULT_NOT_FOUND;
+  using Kumu::RESULT_NO_PERM;
+  using Kumu::RESULT_FILEOPEN;
+  using Kumu::RESULT_BADSEEK;
+  using Kumu::RESULT_READFAIL;
+  using Kumu::RESULT_WRITEFAIL;
+  using Kumu::RESULT_STATE;
+  using Kumu::RESULT_ENDOFFILE;
+  using Kumu::RESULT_CONFIG;
+
+  KM_DECLARE_RESULT(FORMAT,     -101, "The file format is not proper OP-Atom/AS-DCP.");
+  KM_DECLARE_RESULT(RAW_ESS,    -102, "Unknown raw essence file type.");
+  KM_DECLARE_RESULT(RAW_FORMAT, -103, "Raw essence format invalid.");
+  KM_DECLARE_RESULT(RANGE,      -104, "Frame number out of range.");
+  KM_DECLARE_RESULT(CRYPT_CTX,  -105, "AESEncContext required when writing to encrypted file.");
+  KM_DECLARE_RESULT(LARGE_PTO,  -106, "Plaintext offset exceeds frame buffer size.");
+  KM_DECLARE_RESULT(CAPEXTMEM,  -107, "Cannot resize externally allocated memory.");
+  KM_DECLARE_RESULT(CHECKFAIL,  -108, "The check value did not decrypt correctly.");
+  KM_DECLARE_RESULT(HMACFAIL,   -109, "HMAC authentication failure.");
+  KM_DECLARE_RESULT(HMAC_CTX,   -110, "HMAC context required.");
+  KM_DECLARE_RESULT(CRYPT_INIT, -111, "Error initializing block cipher context.");
+  KM_DECLARE_RESULT(EMPTY_FB,   -112, "Empty frame buffer.");
+  KM_DECLARE_RESULT(KLV_CODING, -113, "KLV coding error.");
+  KM_DECLARE_RESULT(SPHASE,     -114, "Stereoscopic phase mismatch.");
+  KM_DECLARE_RESULT(SFORMAT,    -115, "Rate mismatch, file may contain stereoscopic essence.");
 
   //---------------------------------------------------------------------------------
   // file identification
@@ -234,27 +202,43 @@ namespace ASDCP {
   // The file accessors in this library implement a bounded set of essence types.
   // This list will be expanded when support for new types is added to the library.
   enum EssenceType_t {
-    ESS_UNKNOWN,     // the file is not a supported AS-DCP essence container
-    ESS_MPEG2_VES,   // the file contains an MPEG video elementary stream
-    ESS_JPEG_2000,   // the file contains one or more JPEG 2000 codestreams
-    ESS_PCM_24b_48k  // the file contains one or more PCM audio pairs
+    ESS_UNKNOWN,              // the file is not a supported AS-DCP of AS-02 essence container
+
+    // 
+    ESS_MPEG2_VES,            // the file contains an MPEG-2 video elementary stream
+
+    // d-cinema essence types
+    ESS_JPEG_2000,            // the file contains one or more JPEG 2000 codestreams
+    ESS_PCM_24b_48k,          // the file contains one or more PCM audio pairs
+    ESS_PCM_24b_96k,          // the file contains one or more PCM audio pairs
+    ESS_TIMED_TEXT,           // the file contains an XML timed text document and one or more resources
+    ESS_JPEG_2000_S,          // the file contains one or more JPEG 2000 codestream pairs (stereoscopic)
+    ESS_DCDATA_UNKNOWN,       // the file contains one or more D-Cinema Data bytestreams
+    ESS_DCDATA_DOLBY_ATMOS,   // the file contains one or more DolbyATMOS bytestreams
+
+    // IMF essence types
+    ESS_AS02_JPEG_2000,       // the file contains one or more JPEG 2000 codestreams
+    ESS_AS02_PCM_24b_48k,     // the file contains one or more PCM audio pairs, clip wrapped
+    ESS_AS02_PCM_24b_96k,     // the file contains one or more PCM audio pairs, clip wrapped
+    ESS_AS02_TIMED_TEXT,      // the file contains a TTML document and zero or more resources
+    ESS_AS02_ISXD,            // the file contains an ISXD document stream (per SMPTE RDD 47)
+    ESS_AS02_ACES,            // the file contains two or more ACES codestreams (per SMPTE ST 2067-50)
+    ESS_MAX
   };
 
-
   // Determine the type of essence contained in the given MXF file. RESULT_OK
   // is returned if the file is successfully opened and contains a valid MXF
   // stream. If there is an error, the result code will indicate the reason.
-  Result_t EssenceType(const char* filename, EssenceType_t& type);
+  Result_t EssenceType(const std::string& filename, EssenceType_t& type);
 
   // Determine the type of essence contained in the given raw file. RESULT_OK
   // is returned if the file is successfully opened and contains a known
   // stream type. If there is an error, the result code will indicate the reason.
-  Result_t RawEssenceType(const char* filename, EssenceType_t& type);
+  Result_t RawEssenceType(const std::string& filename, EssenceType_t& type);
 
-  // Locate the named object in the file header and dump it to the given stream.
-  // The default dump stream is stderr.
-  Result_t FindObject(const char* filename, const char* objname, FILE* = 0);
 
+  //---------------------------------------------------------------------------------
+  // base types
 
   // A simple container for rational numbers.
   class Rational
@@ -277,13 +261,59 @@ namespace ASDCP {
     inline bool operator!=(const Rational& rhs) const {
       return ( rhs.Numerator != Numerator || rhs.Denominator != Denominator );
     }
+
+    inline bool operator<(const Rational& rhs) {
+      if ( Numerator < rhs.Numerator )     return true;
+      if ( Numerator == rhs.Numerator && Denominator < rhs.Denominator )    return true;
+      return false;
+    }
+
+    inline bool operator>(const Rational& rhs) {
+      if ( Numerator > rhs.Numerator )     return true;
+      if ( Numerator == rhs.Numerator && Denominator > rhs.Denominator )     return true;
+      return false;
+    }
   };
 
+  // Encodes a rational number as a string having a single delimiter character between
+  // numerator and denominator.  Retuns the buffer pointer to allow convenient in-line use.
+  const char* EncodeRational(const Rational&, char* str_buf, ui32_t buf_len, char delimiter = ' ');
+
+  // Decodes a rational number havng a single non-digit delimiter character between
+  // the numerator and denominator.  Returns false if the string does not contain
+  // the expected syntax.
+  bool DecodeRational(const char* str_rat, Rational&);
+
+
   // common edit rates, use these instead of hard coded constants
-  const Rational EditRate_24(24,1);
-  const Rational EditRate_23_98(24000,1001);
-  const Rational EditRate_48(48,1);
-  const Rational SampleRate_48k(48000,1);
+  const Rational EditRate_24 = Rational(24,1);
+  const Rational EditRate_23_98 = Rational(24000,1001); // Not a DCI-compliant value!
+  const Rational EditRate_48 = Rational(48,1);
+  const Rational SampleRate_48k = Rational(48000,1);
+  const Rational SampleRate_96k = Rational(96000,1);
+
+  // Additional frame rates, see ST 428-11, ST 429-13
+  // These rates are new and not supported by all systems. Do not assume that
+  // a package made using one of these rates will work just anywhere!
+  const Rational EditRate_25 = Rational(25,1);
+  const Rational EditRate_30 = Rational(30,1);
+  const Rational EditRate_50 = Rational(50,1);
+  const Rational EditRate_60 = Rational(60,1);
+  const Rational EditRate_96 = Rational(96,1);
+  const Rational EditRate_100 = Rational(100,1);
+  const Rational EditRate_120 = Rational(120,1);
+  const Rational EditRate_192 = Rational(192,1);
+  const Rational EditRate_200 = Rational(200,1);
+  const Rational EditRate_240 = Rational(240,1);
+
+  // Archival frame rates, see ST 428-21
+  // These rates are new and not supported by all systems. Do not assume that
+  // a package made using one of these rates will work just anywhere!
+  const Rational EditRate_16 = Rational(16,1);
+  const Rational EditRate_18 = Rational(200,11); // 18.182
+  const Rational EditRate_20 = Rational(20,1);
+  const Rational EditRate_22 = Rational(240,11); // 21.818
+
 
   // Non-reference counting container for internal member objects.
   // Please do not use this class for any other purpose.
@@ -308,6 +338,79 @@ namespace ASDCP {
       inline bool empty()      const { return m_p == 0; }
     };
 
+
+  //---------------------------------------------------------------------------------
+  // WriterInfo class - encapsulates writer identification details used for
+  // OpenWrite() calls.  Replace these values at runtime to identify your product.
+  //
+  // MXF files use SMPTE Universal Labels to identify data items. The set of Labels
+  // in a file is determined by the MXF Operational Pattern and any constraining
+  // documentation. There are currently two flavors of AS-DCP file in use: MXF Interop
+  // (AKA JPEG Interop) and SMPTE. The two differ in the values of three labels:
+  //
+  //   OPAtom
+  //      Interop : 06 0e 2b 34 04 01 01 01  0d 01 02 01 10 00 00 00
+  //      SMPTE   : 06 0e 2b 34 04 01 01 02  0d 01 02 01 10 00 00 00
+  //
+  //   EKLV Packet:
+  //      Interop : 06 0e 2b 34 02 04 01 07  0d 01 03 01 02 7e 01 00
+  //      SMPTE   : 06 0e 2b 34 02 04 01 01  0d 01 03 01 02 7e 01 00
+  //
+  //   GenericDescriptor/SubDescriptors:
+  //      Interop : 06 0e 2b 34 01 01 01 02  06 01 01 04 06 10 00 00
+  //      SMPTE   : 06 0e 2b 34 01 01 01 09  06 01 01 04 06 10 00 00
+  //
+  // asdcplib will read any (otherwise valid) file which has any combination of the
+  // above values. When writing files, MXF Interop labels are used by default. To
+  // write a file containing SMPTE labels, replace the default label set value in
+  // the WriterInfo before calling OpenWrite()
+  //
+  //
+  enum LabelSet_t
+  {
+    LS_MXF_UNKNOWN,
+    LS_MXF_INTEROP,
+    LS_MXF_SMPTE,
+    LS_MAX
+  };
+
+  //
+  struct WriterInfo
+  {
+    byte_t      ProductUUID[UUIDlen];
+    byte_t      AssetUUID[UUIDlen];
+    byte_t      ContextID[UUIDlen];
+    byte_t      CryptographicKeyID[UUIDlen];
+    bool        EncryptedEssence; // true if essence data is (or is going to be) encrypted
+    bool        UsesHMAC;         // true if HMAC exists or is to be calculated
+    std::string ProductVersion;
+    std::string CompanyName;
+    std::string ProductName;
+    LabelSet_t  LabelSetType;
+
+    WriterInfo() : EncryptedEssence(false), UsesHMAC(false), LabelSetType(LS_MXF_INTEROP)
+    {
+      static byte_t default_ProductUUID_Data[UUIDlen] = {
+       0x43, 0x05, 0x9a, 0x1d, 0x04, 0x32, 0x41, 0x01,
+       0xb8, 0x3f, 0x73, 0x68, 0x15, 0xac, 0xf3, 0x1d };
+
+      memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
+      memset(AssetUUID, 0, UUIDlen);
+      memset(ContextID, 0, UUIDlen);
+      memset(CryptographicKeyID, 0, UUIDlen);
+
+      ProductVersion = "Unreleased ";
+      ProductVersion += Version();
+      CompanyName = "DCI";
+      ProductName = "asdcplib";
+    }
+  };
+
+  // Print WriterInfo to std::ostream
+  std::ostream& operator << (std::ostream& strm, const WriterInfo& winfo);
+  // Print WriterInfo to stream, stderr by default.
+  void WriterInfoDump(const WriterInfo&, FILE* = 0);
+
   //---------------------------------------------------------------------------------
   // cryptographic support
 
@@ -332,7 +435,7 @@ namespace ASDCP {
       // Initializes Rijndael CBC encryption context.
       // Returns error if the key argument is NULL.
       Result_t InitKey(const byte_t* key);
-      
+
       // Initializes 16 byte CBC Initialization Vector. This operation may be performed
       // any number of times for a given key.
       // Returns error if the i_vec argument is NULL.
@@ -383,7 +486,7 @@ namespace ASDCP {
       // Initializes HMAC context. The key argument must point to a binary
       // key that is CBC_KEY_SIZE bytes in length. Returns error if the key
       // argument is NULL.
-      Result_t InitKey(const byte_t* key);
+      Result_t InitKey(const byte_t* key, LabelSet_t);
 
       // Reset internal state, allows repeated cycles of Update -> Finalize
       void Reset();
@@ -406,41 +509,6 @@ namespace ASDCP {
       Result_t TestHMACValue(const byte_t* buf) const;
     };
 
-  //---------------------------------------------------------------------------------
-  // WriterInfo class - encapsulates writer identification details used for
-  // OpenWrite() calls.  Replace these values at runtime to identify your product.
-  //
-  struct WriterInfo
-  {
-    byte_t      ProductUUID[UUIDlen];
-    byte_t      AssetUUID[UUIDlen];
-    byte_t      ContextID[UUIDlen];
-    byte_t      CryptographicKeyID[UUIDlen];
-    bool        EncryptedEssence; // true if essence data is (or is to be) encrypted
-    bool        UsesHMAC;         // true if HMAC exists or is to be calculated
-    std::string ProductVersion;
-    std::string CompanyName;
-    std::string ProductName;
-    
-    WriterInfo() : EncryptedEssence(false), UsesHMAC(false) {
-      static byte_t default_ProductUUID_Data[UUIDlen] = { 0x43, 0x05, 0x9a, 0x1d, 0x04, 0x32, 0x41, 0x01,
-                                                         0xb8, 0x3f, 0x73, 0x68, 0x15, 0xac, 0xf3, 0x1d };
-      
-      memcpy(ProductUUID, default_ProductUUID_Data, UUIDlen);
-      memset(AssetUUID, 0, UUIDlen);
-      memset(ContextID, 0, UUIDlen);
-      memset(CryptographicKeyID, 0, UUIDlen);
-
-      ProductVersion = "Unreleased ";
-      ProductVersion += Version();
-      CompanyName = "DCI";
-      ProductName = "asdcplib";
-    }
-  };
-
-  // Print WriterInfo to stream, stderr by default.
-  void WriterInfoDump(const WriterInfo&, FILE* = 0);
-
   //---------------------------------------------------------------------------------
   // frame buffer base class
   //
@@ -521,6 +589,16 @@ namespace ASDCP {
       inline ui32_t  PlaintextOffset() const { return m_PlaintextOffset; }
     };
 
+  //---------------------------------------------------------------------------------
+  // Accessors in the MXFReader and MXFWriter classes below return these types to
+  // provide direct access to MXF metadata structures declared in MXF.h and Metadata.h
+
+  namespace MXF {
+    // #include<Metadata.h> to use these
+    class OP1aHeader;
+    class OPAtomIndexFooter;
+    class RIP;
+  };
 
   //---------------------------------------------------------------------------------
   // MPEG2 video elementary stream support
@@ -552,24 +630,26 @@ namespace ASDCP {
       // MPEG2VideoDescriptor object.
       struct VideoDescriptor
        {
-         Rational EditRate;                // 
-         ui32_t   FrameRate;               // 
-         Rational SampleRate;              // 
-         ui8_t    FrameLayout;             // 
-         ui32_t   StoredWidth;             // 
-         ui32_t   StoredHeight;            // 
-         Rational AspectRatio;             // 
-         ui32_t   ComponentDepth;          // 
-         ui32_t   HorizontalSubsampling;   // 
-         ui32_t   VerticalSubsampling;     // 
-         ui8_t    ColorSiting;             // 
-         ui8_t    CodedContentType;        // 
-         bool     LowDelay;                // 
-         ui32_t   BitRate;                 // 
-         ui8_t    ProfileAndLevel;         // 
-         ui32_t   ContainerDuration;       // 
+         Rational EditRate;                //
+         ui32_t   FrameRate;               //
+         Rational SampleRate;              //
+         ui8_t    FrameLayout;             //
+         ui32_t   StoredWidth;             //
+         ui32_t   StoredHeight;            //
+         Rational AspectRatio;             //
+         ui32_t   ComponentDepth;          //
+         ui32_t   HorizontalSubsampling;   //
+         ui32_t   VerticalSubsampling;     //
+         ui8_t    ColorSiting;             //
+         ui8_t    CodedContentType;        //
+         bool     LowDelay;                //
+         ui32_t   BitRate;                 //
+         ui8_t    ProfileAndLevel;         //
+         ui32_t   ContainerDuration;       //
       };
 
+      // Print VideoDescriptor to std::ostream
+      std::ostream& operator << (std::ostream& strm, const VideoDescriptor& vdesc);
       // Print VideoDescriptor to stream, stderr by default.
       void VideoDescriptorDump(const VideoDescriptor&, FILE* = 0);
 
@@ -595,7 +675,7 @@ namespace ASDCP {
            {
              Capacity(size);
            }
-           
+
          virtual ~FrameBuffer() {}
 
          // Sets the MPEG frame type of the picture data in the frame buffer.
@@ -646,7 +726,7 @@ namespace ASDCP {
 
          // Opens the stream for reading, parses enough data to provide a complete
          // set of stream metadata for the MXFWriter below.
-         Result_t OpenRead(const char* filename) const;
+         Result_t OpenRead(const std::string& filename) const;
 
          // Fill a VideoDescriptor struct with the values from the file's header.
          // Returns RESULT_INIT if the file is not open.
@@ -675,10 +755,16 @@ namespace ASDCP {
          MXFWriter();
          virtual ~MXFWriter();
 
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
          // Open the file for writing. The file must not exist. Returns error if
          // the operation cannot be completed or if nonsensical data is discovered
          // in the essence descriptor.
-         Result_t OpenWrite(const char* filename, const WriterInfo&,
+         Result_t OpenWrite(const std::string& filename, const WriterInfo&,
                             const VideoDescriptor&, ui32_t HeaderSize = 16384);
 
          // Writes a frame of essence to the MXF file. If the optional AESEncContext
@@ -702,9 +788,15 @@ namespace ASDCP {
          MXFReader();
          virtual ~MXFReader();
 
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
          // Open the file for reading. The file must exist. Returns error if the
          // operation cannot be completed.
-         Result_t OpenRead(const char* filename) const;
+         Result_t OpenRead(const std::string& filename) const;
 
          // Returns RESULT_INIT if the file is not open.
          Result_t Close() const;
@@ -726,6 +818,12 @@ namespace ASDCP {
          // out of range, or if optional decrypt or HAMC operations fail.
          Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
 
+         // Using the index table read from the footer partition, lookup the frame number
+         // and return the offset into the file at which to read that frame of essence.
+         // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is
+         // out of range.
+         Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const;
+
          // Calculates the first frame in transport order of the GOP in which the requested
          // frame is located.  Calls ReadFrame() to fetch the frame at the calculated position.
          // Returns RESULT_INIT if the file is not open.
@@ -746,22 +844,49 @@ namespace ASDCP {
        };
     } // namespace MPEG2
 
+  //---------------------------------------------------------------------------------
   //
+
+
+
   namespace PCM
     {
+      // The default value of the ChannelFormat element of the AudioDescriptor struct
+      // is CF_NONE. If the value is set to one of the other ChannelFormat_t enum
+      // values, the file's Wave Audio Descriptor will contain a Channel Assignment
+      // element.
+      //
+      // The channel format should be one of (CF_CFG_1, CF_CFG_2, or CF_CFG_3) for
+      // SMPTE 429-2 files. It should normally be CF_NONE for JPEG Interop files, but
+      // the 429-2 may also be used.
+      //
+      enum ChannelFormat_t {
+       CF_NONE,
+       CF_CFG_1, // 5.1 with optional HI/VI
+       CF_CFG_2, // 6.1 (5.1 + center surround) with optional HI/VI
+       CF_CFG_3, // 7.1 (SDDS) with optional HI/VI
+       CF_CFG_4, // Wild Track Format
+       CF_CFG_5, // 7.1 DS with optional HI/VI
+       CF_CFG_6, // ST 377-4 (MCA) labels (see also ASDCP::MXF::decode_mca_string)
+       CF_MAXIMUM
+      };
+
       struct AudioDescriptor
        {
-         Rational SampleRate;         // rate of frame wrapping
+         Rational EditRate;         // rate of frame wrapping
          Rational AudioSamplingRate;  // rate of audio sample
-         ui32_t   Locked;             // 
+         ui32_t   Locked;             //
          ui32_t   ChannelCount;       // number of channels
          ui32_t   QuantizationBits;   // number of bits per single-channel sample
          ui32_t   BlockAlign;         // number of bytes ber sample, all channels
-         ui32_t   AvgBps;             // 
-         ui32_t   LinkedTrackID;      // 
+         ui32_t   AvgBps;             //
+         ui32_t   LinkedTrackID;      //
          ui32_t   ContainerDuration;  // number of frames
+         ChannelFormat_t ChannelFormat; // audio channel arrangement
       };
 
+      // Print AudioDescriptor to std::ostream
+      std::ostream& operator << (std::ostream& strm, const AudioDescriptor& adesc);
       // Print debugging information to stream (stderr default)
       void   AudioDescriptorDump(const AudioDescriptor&, FILE* = 0);
 
@@ -774,7 +899,7 @@ namespace ASDCP {
       // Returns number of samples per frame of data described by ADesc
       inline ui32_t CalcSamplesPerFrame(const AudioDescriptor& ADesc)
        {
-         double tmpd = ADesc.AudioSamplingRate.Quotient() / ADesc.SampleRate.Quotient();
+         double tmpd = ADesc.AudioSamplingRate.Quotient() / ADesc.EditRate.Quotient();
          return (ui32_t)ceil(tmpd);
        }
 
@@ -791,7 +916,7 @@ namespace ASDCP {
          FrameBuffer() {}
          FrameBuffer(ui32_t size) { Capacity(size); }
          virtual ~FrameBuffer() {}
-       
+
          // Print debugging information to stream (stderr default)
          void Dump(FILE* = 0, ui32_t dump_bytes = 0) const;
        };
@@ -813,7 +938,7 @@ namespace ASDCP {
          // Opens the stream for reading, parses enough data to provide a complete
          // set of stream metadata for the MXFWriter below. PictureRate controls
          // ther frame rate for the MXF frame wrapping option.
-         Result_t OpenRead(const char* filename, const Rational& PictureRate) const;
+         Result_t OpenRead(const std::string& filename, const Rational& PictureRate) const;
 
          // Fill an AudioDescriptor struct with the values from the file's header.
          // Returns RESULT_INIT if the file is not open.
@@ -825,6 +950,8 @@ namespace ASDCP {
          // Reads the next sequential frame in the input file and places it in the
          // frame buffer. Fails if the buffer is too small or the stream is empty.
          Result_t ReadFrame(FrameBuffer&) const;
+
+         Result_t Seek(ui32_t frame_number) const;
        };
 
 
@@ -839,10 +966,16 @@ namespace ASDCP {
          MXFWriter();
          virtual ~MXFWriter();
 
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
          // Open the file for writing. The file must not exist. Returns error if
          // the operation cannot be completed or if nonsensical data is discovered
          // in the essence descriptor.
-         Result_t OpenWrite(const char* filename, const WriterInfo&,
+         Result_t OpenWrite(const std::string& filename, const WriterInfo&,
                             const AudioDescriptor&, ui32_t HeaderSize = 16384);
 
          // Writes a frame of essence to the MXF file. If the optional AESEncContext
@@ -866,9 +999,15 @@ namespace ASDCP {
          MXFReader();
          virtual ~MXFReader();
 
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
          // Open the file for reading. The file must exist. Returns error if the
          // operation cannot be completed.
-         Result_t OpenRead(const char* filename) const;
+         Result_t OpenRead(const std::string& filename) const;
 
          // Returns RESULT_INIT if the file is not open.
          Result_t Close() const;
@@ -890,25 +1029,86 @@ namespace ASDCP {
          // out of range, or if optional decrypt or HAMC operations fail.
          Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
 
+         // Using the index table read from the footer partition, lookup the frame number
+         // and return the offset into the file at which to read that frame of essence.
+         // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is
+         // out of range.
+         Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const;
+
          // Print debugging information to stream
          void     DumpHeaderMetadata(FILE* = 0) const;
          void     DumpIndex(FILE* = 0) const;
        };
     } // namespace PCM
 
+  //---------------------------------------------------------------------------------
   //
   namespace JP2K
     {
       const ui32_t MaxComponents = 3;
-      const ui32_t DefaultCodingDataLength = 64;
+      const ui32_t MaxPrecincts = 32; // ISO 15444-1 Annex A.6.1
+      const ui32_t MaxDefaults = 256; // made up
+      const ui8_t  MaxCapabilities = 32;
+                       const ui16_t MaxPRFN = 4;
+                       const ui16_t MaxCPFN = 4;
+
+#pragma pack(1)
+      struct ImageComponent_t  // ISO 15444-1 Annex A.5.1
+      {
+       ui8_t Ssize;
+       ui8_t XRsize;
+       ui8_t YRsize;
+      };
+
+      struct CodingStyleDefault_t // ISO 15444-1 Annex A.6.1
+      {
+       ui8_t   Scod;
+
+       struct
+       {
+         ui8_t  ProgressionOrder;
+         ui8_t  NumberOfLayers[sizeof(ui16_t)];
+         ui8_t  MultiCompTransform;
+       } SGcod;
+
+       struct
+       {
+         ui8_t  DecompositionLevels;
+         ui8_t  CodeblockWidth;
+         ui8_t  CodeblockHeight;
+         ui8_t  CodeblockStyle;
+         ui8_t  Transformation;
+         ui8_t  PrecinctSize[MaxPrecincts];
+       } SPcod;
+      };
+
+      struct QuantizationDefault_t // ISO 15444-1 Annex A.6.4
+      {
+       ui8_t  Sqcd;
+       ui8_t  SPqcd[MaxDefaults];
+       ui8_t  SPqcdLength;
+      };
+
+      struct ExtendedCapabilities_t // ISO 15444-1 Annex A.5.2
+      {
+       ui32_t  Pcap; // Pcap = 0 means that no extended capabilities are required
+       ui16_t  Ccap[MaxCapabilities]; // Ccap^i in ISO/IEC 15444-1 corresponds to Ccap[i -1]
+      };
 
-      struct ImageComponent
+                       struct Profile_t // ISO 15444-1
       {
-       byte_t Ssize;
-       byte_t XRsize;
-       byte_t YRsize;
+       ui16_t  N; // N = 0 means that the profile is signaled through Rsiz exclusively
+       ui16_t  Pprf[MaxPRFN]; // Pprf^i in ISO/IEC 15444-1 corresponds to Pprf[i -1]
       };
 
+                       struct CorrespondingProfile_t // ISO 15444-1
+      {
+       ui16_t  N; // N = 0 means that no corresponding profile is signaled
+       ui16_t  Pcpf[MaxCPFN]; // Pcpf^i in ISO/IEC 15444-1 corresponds to Pcpf[i -1]
+      };
+
+#pragma pack()
+
       struct PictureDescriptor
       {
        Rational       EditRate;
@@ -927,13 +1127,16 @@ namespace ASDCP {
        ui32_t         XTOsize;
        ui32_t         YTOsize;
        ui16_t         Csize;
-       ImageComponent ImageComponents[MaxComponents];
-       byte_t         CodingStyle[DefaultCodingDataLength];
-       ui32_t         CodingStyleLength;
-       byte_t         QuantDefault[DefaultCodingDataLength];
-       ui32_t         QuantDefaultLength;
+       ImageComponent_t      ImageComponents[MaxComponents];
+       CodingStyleDefault_t  CodingStyleDefault;
+       QuantizationDefault_t QuantizationDefault;
+  ExtendedCapabilities_t ExtendedCapabilities;
+  Profile_t   Profile;
+       CorrespondingProfile_t   CorrespondingProfile;    
       };
 
+      // Print debugging information to std::ostream
+      std::ostream& operator << (std::ostream& strm, const PictureDescriptor& pdesc);
       // Print debugging information to stream (stderr default)
       void   PictureDescriptorDump(const PictureDescriptor&, FILE* = 0);
 
@@ -944,7 +1147,7 @@ namespace ASDCP {
          FrameBuffer() {}
          FrameBuffer(ui32_t size) { Capacity(size); }
          virtual ~FrameBuffer() {}
-       
+
          // Print debugging information to stream (stderr default)
          void Dump(FILE* = 0, ui32_t dump_bytes = 0) const;
        };
@@ -968,13 +1171,17 @@ namespace ASDCP {
          // The frame buffer's PlaintextOffset parameter will be set to the first
          // byte of the data segment. Set this value to zero if you want
          // encrypted headers.
-         Result_t OpenReadFrame(const char* filename, FrameBuffer&) const;
+         Result_t OpenReadFrame(const std::string& filename, FrameBuffer&) const;
 
          // Fill a PictureDescriptor struct with the values from the file's codestream.
          // Returns RESULT_INIT if the file is not open.
          Result_t FillPictureDescriptor(PictureDescriptor&) const;
        };
 
+      // Parses the data in the frame buffer to fill in the picture descriptor. Copies
+      // the offset of the image data into start_of_data. Returns error if the parser fails.
+      Result_t ParseMetadataIntoDesc(const FrameBuffer&, PictureDescriptor&, byte_t* start_of_data = 0);
+
       // An object which reads a sequence of files containing JPEG 2000 pictures.
       class SequenceParser
        {
@@ -992,9 +1199,18 @@ namespace ASDCP {
          // alphabetically by filename. The parser will automatically parse enough data
          // from the first file to provide a complete set of stream metadata for the
          // MXFWriter below.  If the "pedantic" parameter is given and is true, the
-         // parser will check the metadata for each codestream and fail if a 
+         // parser will check the metadata for each codestream and fail if a
+         // mismatch is detected.
+         Result_t OpenRead(const std::string& filename, bool pedantic = false) const;
+
+         // Opens a file sequence for reading.  The sequence is expected to contain one or
+         // more filenames, each naming a file containing the codestream for exactly one
+         // picture. The parser will automatically parse enough data
+         // from the first file to provide a complete set of stream metadata for the
+         // MXFWriter below.  If the "pedantic" parameter is given and is true, the
+         // parser will check the metadata for each codestream and fail if a
          // mismatch is detected.
-         Result_t OpenRead(const char* filename, bool pedantic = false) const;
+         Result_t OpenRead(const std::list<std::string>& file_list, bool pedantic = false) const;
 
          // Fill a PictureDescriptor struct with the values from the first file's codestream.
          // Returns RESULT_INIT if the directory is not open.
@@ -1024,10 +1240,16 @@ namespace ASDCP {
          MXFWriter();
          virtual ~MXFWriter();
 
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
          // Open the file for writing. The file must not exist. Returns error if
          // the operation cannot be completed or if nonsensical data is discovered
          // in the essence descriptor.
-         Result_t OpenWrite(const char* filename, const WriterInfo&,
+         Result_t OpenWrite(const std::string& filename, const WriterInfo&,
                             const PictureDescriptor&, ui32_t HeaderSize = 16384);
 
          // Writes a frame of essence to the MXF file. If the optional AESEncContext
@@ -1051,9 +1273,15 @@ namespace ASDCP {
          MXFReader();
          virtual ~MXFReader();
 
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
          // Open the file for reading. The file must exist. Returns error if the
          // operation cannot be completed.
-         Result_t OpenRead(const char* filename) const;
+         Result_t OpenRead(const std::string& filename) const;
 
          // Returns RESULT_INIT if the file is not open.
          Result_t Close() const;
@@ -1075,15 +1303,659 @@ namespace ASDCP {
          // out of range, or if optional decrypt or HAMC operations fail.
          Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
 
+         // Using the index table read from the footer partition, lookup the frame number
+         // and return the offset into the file at which to read that frame of essence.
+         // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is
+         // out of range.
+         Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const;
+
+         // Print debugging information to stream
+         void     DumpHeaderMetadata(FILE* = 0) const;
+         void     DumpIndex(FILE* = 0) const;
+       };
+
+
+      // Stereoscopic Image support
+      //
+
+      enum StereoscopicPhase_t
+      {
+       SP_LEFT,
+       SP_RIGHT
+      };
+
+      struct SFrameBuffer
+      {
+       JP2K::FrameBuffer Left;
+       JP2K::FrameBuffer Right;
+
+       SFrameBuffer(ui32_t size) {
+         Left.Capacity(size);
+         Right.Capacity(size);
+       }
+      };
+
+      class MXFSWriter
+      {
+         class h__SWriter;
+         mem_ptr<h__SWriter> m_Writer;
+         ASDCP_NO_COPY_CONSTRUCT(MXFSWriter);
+
+       public:
+         MXFSWriter();
+         virtual ~MXFSWriter();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for writing. The file must not exist. Returns error if
+         // the operation cannot be completed or if nonsensical data is discovered
+         // in the essence descriptor.
+         Result_t OpenWrite(const std::string& filename, const WriterInfo&,
+                            const PictureDescriptor&, ui32_t HeaderSize = 16384);
+
+         // Writes a pair of frames of essence to the MXF file. If the optional AESEncContext
+         // argument is present, the essence is encrypted prior to writing.
+         // Fails if the file is not open, is finalized, or an operating system
+         // error occurs.
+         Result_t WriteFrame(const SFrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
+
+         // Writes a frame of essence to the MXF file. If the optional AESEncContext
+         // argument is present, the essence is encrypted prior to writing.
+         // Fails if the file is not open, is finalized, or an operating system
+         // error occurs. Frames must be written in the proper phase (L-R-L-R),
+         // RESULT_SPHASE will be returned if phase is reversed. The first frame
+         // written must be left eye.
+         Result_t WriteFrame(const FrameBuffer&, StereoscopicPhase_t phase,
+                             AESEncContext* = 0, HMACContext* = 0);
+
+         // Closes the MXF file, writing the index and revised header.  Returns
+         // RESULT_SPHASE if WriteFrame was called an odd number of times.
+         Result_t Finalize();
+       };
+
+      //
+      class MXFSReader
+       {
+         class h__SReader;
+         mem_ptr<h__SReader> m_Reader;
+         ASDCP_NO_COPY_CONSTRUCT(MXFSReader);
+
+       public:
+         MXFSReader();
+         virtual ~MXFSReader();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for reading. The file must exist. Returns error if the
+         // operation cannot be completed.
+         Result_t OpenRead(const std::string& filename) const;
+
+         // Returns RESULT_INIT if the file is not open.
+         Result_t Close() const;
+
+         // Fill an AudioDescriptor struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillPictureDescriptor(PictureDescriptor&) const;
+
+         // Fill a WriterInfo struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillWriterInfo(WriterInfo&) const;
+
+         // Reads a pair of frames of essence from the MXF file. If the optional AESEncContext
+         // argument is present, the essence is decrypted after reading. If the MXF
+         // file is encrypted and the AESDecContext argument is NULL, the frame buffer
+         // will contain the ciphertext frame data. If the HMACContext argument is
+         // not NULL, the HMAC will be calculated (if the file supports it).
+         // Returns RESULT_INIT if the file is not open, failure if the frame number is
+         // out of range, or if optional decrypt or HAMC operations fail.
+         Result_t ReadFrame(ui32_t frame_number, SFrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
+
+         // Reads a frame of essence from the MXF file. If the optional AESEncContext
+         // argument is present, the essence is decrypted after reading. If the MXF
+         // file is encrypted and the AESDecContext argument is NULL, the frame buffer
+         // will contain the ciphertext frame data. If the HMACContext argument is
+         // not NULL, the HMAC will be calculated (if the file supports it).
+         // Returns RESULT_INIT if the file is not open, failure if the frame number is
+         // out of range, or if optional decrypt or HAMC operations fail.
+         Result_t ReadFrame(ui32_t frame_number, StereoscopicPhase_t phase,
+                            FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
+
+         // Using the index table read from the footer partition, lookup the frame number
+         // and return the offset into the file at which to read that frame of essence.
+         // Returns RESULT_INIT if the file is not open, and RESULT_FRAME if the frame number is
+         // out of range.
+         Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const;
+
          // Print debugging information to stream
          void     DumpHeaderMetadata(FILE* = 0) const;
          void     DumpIndex(FILE* = 0) const;
        };
     } // namespace JP2K
+
+  //---------------------------------------------------------------------------------
+  //
+  namespace TimedText
+    {
+      enum MIMEType_t { MT_BIN, MT_PNG, MT_OPENTYPE };
+
+      struct TimedTextResourceDescriptor
+      {
+       byte_t      ResourceID[UUIDlen];
+          MIMEType_t  Type;
+
+        TimedTextResourceDescriptor() : Type(MT_BIN) {}
+      };
+
+      typedef std::list<TimedTextResourceDescriptor> ResourceList_t;
+
+      struct TimedTextDescriptor
+      {
+       Rational       EditRate;                //
+       ui32_t         ContainerDuration;
+       byte_t         AssetID[UUIDlen];
+       std::string    NamespaceName;
+       std::string    EncodingName;
+       ResourceList_t ResourceList;
+
+      TimedTextDescriptor() : ContainerDuration(0), EncodingName("UTF-8") {} // D-Cinema format is always UTF-8
+      };
+
+      // Print debugging information to std::ostream
+      std::ostream& operator << (std::ostream& strm, const TimedTextDescriptor& tinfo);
+      // Print debugging information to stream (stderr default)
+      void   DescriptorDump(const TimedTextDescriptor&, FILE* = 0);
+
+      //
+      class FrameBuffer : public ASDCP::FrameBuffer
+      {
+       ASDCP_NO_COPY_CONSTRUCT(FrameBuffer); // TODO: should have copy construct
+
+      protected:
+       byte_t      m_AssetID[UUIDlen];
+       std::string m_MIMEType;
+
+      public:
+       FrameBuffer() { memset(m_AssetID, 0, UUIDlen); }
+       FrameBuffer(ui32_t size) { Capacity(size); memset(m_AssetID, 0, UUIDlen); }
+       virtual ~FrameBuffer() {}
+
+       inline const byte_t* AssetID() const { return m_AssetID; }
+       inline void          AssetID(const byte_t* buf) { memcpy(m_AssetID, buf, UUIDlen); }
+       inline const char*   MIMEType() const { return m_MIMEType.c_str(); }
+       inline void          MIMEType(const std::string& s) { m_MIMEType = s; }
+
+       // Print debugging information to stream (stderr default)
+       void Dump(FILE* = 0, ui32_t dump_bytes = 0) const;
+      };
+
+      // An abstract base for a lookup service that returns the resource data
+      // identified by the given ancillary resource id.
+      //
+      class IResourceResolver
+      {
+      public:
+       virtual ~IResourceResolver() {}
+       virtual Result_t ResolveRID(const byte_t* uuid, FrameBuffer&) const = 0; // return data for RID
+      };
+
+      // Resolves resource references by testing the named directory for file names containing
+      // the respective UUID.
+      //
+      class LocalFilenameResolver : public ASDCP::TimedText::IResourceResolver
+       {
+         std::string m_Dirname;
+         ASDCP_NO_COPY_CONSTRUCT(LocalFilenameResolver);
+
+       public:
+         LocalFilenameResolver();
+         virtual ~LocalFilenameResolver();
+         Result_t OpenRead(const std::string& dirname);
+         Result_t ResolveRID(const byte_t* uuid, FrameBuffer& FrameBuf) const;
+       };
+
+      //
+      class DCSubtitleParser
+       {
+         class h__SubtitleParser;
+         mem_ptr<h__SubtitleParser> m_Parser;
+         ASDCP_NO_COPY_CONSTRUCT(DCSubtitleParser);
+
+       public:
+         DCSubtitleParser();
+         virtual ~DCSubtitleParser();
+
+         // Opens an XML file for reading, parses data to provide a complete
+         // set of stream metadata for the MXFWriter below.
+         Result_t OpenRead(const std::string& filename) const;
+
+         // Parses an XML document to provide a complete set of stream metadata
+         // for the MXFWriter below. The optional filename argument is used to
+         // initialize the default resource resolver (see ReadAncillaryResource).
+         Result_t OpenRead(const std::string& xml_doc, const std::string& filename) const;
+
+         // Fill a TimedTextDescriptor struct with the values from the file's contents.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillTimedTextDescriptor(TimedTextDescriptor&) const;
+
+         // Reads the complete Timed Text Resource into the given string.
+         Result_t ReadTimedTextResource(std::string&) const;
+
+         // Reads the Ancillary Resource having the given ID. Fails if the buffer
+         // is too small or the resource does not exist. The optional Resolver
+         // argument can be provided which will be used to retrieve the resource
+         // having a particulat UUID. If a Resolver is not supplied, the default
+         // internal resolver will return the contents of the file having the UUID
+         // as the filename. The filename must exist in the same directory as the
+         // XML file opened with OpenRead().
+         Result_t ReadAncillaryResource(const byte_t* uuid, FrameBuffer&,
+                                        const IResourceResolver* Resolver = 0) const;
+       };
+
+      //
+      class MXFWriter
+       {
+         class h__Writer;
+         mem_ptr<h__Writer> m_Writer;
+         ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
+
+       public:
+         MXFWriter();
+         virtual ~MXFWriter();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for writing. The file must not exist. Returns error if
+         // the operation cannot be completed or if nonsensical data is discovered
+         // in the essence descriptor.
+         Result_t OpenWrite(const std::string& filename, const WriterInfo&,
+                            const TimedTextDescriptor&, ui32_t HeaderSize = 16384);
+
+         // Writes the Timed-Text Resource to the MXF file. The file must be UTF-8
+         // encoded. If the optional AESEncContext argument is present, the essence
+         // is encrypted prior to writing. Fails if the file is not open, is finalized,
+         // or an operating system error occurs.
+         // This method may only be called once, and it must be called before any
+         // call to WriteAncillaryResource(). RESULT_STATE will be returned if these
+         // conditions are not met.
+         Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
+
+         // Writes an Ancillary Resource to the MXF file. If the optional AESEncContext
+         // argument is present, the essence is encrypted prior to writing.
+         // Fails if the file is not open, is finalized, or an operating system
+         // error occurs. RESULT_STATE will be returned if the method is called before
+         // WriteTimedTextResource()
+         Result_t WriteAncillaryResource(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
+
+         // Closes the MXF file, writing the index and revised header.
+         Result_t Finalize();
+       };
+
+      //
+      class MXFReader
+       {
+         class h__Reader;
+         mem_ptr<h__Reader> m_Reader;
+         ASDCP_NO_COPY_CONSTRUCT(MXFReader);
+
+       public:
+         MXFReader();
+         virtual ~MXFReader();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for reading. The file must exist. Returns error if the
+         // operation cannot be completed.
+         Result_t OpenRead(const std::string& filename) const;
+
+         // Returns RESULT_INIT if the file is not open.
+         Result_t Close() const;
+
+         // Fill a TimedTextDescriptor struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillTimedTextDescriptor(TimedTextDescriptor&) const;
+
+         // Fill a WriterInfo struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillWriterInfo(WriterInfo&) const;
+
+         // Reads the complete Timed Text Resource into the given string. Fails if the resource
+         // is encrypted and AESDecContext is NULL (use the following method to retrieve the
+         // raw ciphertet block).
+         Result_t ReadTimedTextResource(std::string&, AESDecContext* = 0, HMACContext* = 0) const;
+
+         // Reads the complete Timed Text Resource from the MXF file. If the optional AESEncContext
+         // argument is present, the resource is decrypted after reading. If the MXF
+         // file is encrypted and the AESDecContext argument is NULL, the frame buffer
+         // will contain the ciphertext frame data. If the HMACContext argument is
+         // not NULL, the HMAC will be calculated (if the file supports it).
+         // Returns RESULT_INIT if the file is not open, failure if the frame number is
+         // out of range, or if optional decrypt or HAMC operations fail.
+         Result_t ReadTimedTextResource(FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
+
+         // Reads the timed-text resource having the given UUID from the MXF file. If the
+         // optional AESEncContext argument is present, the resource is decrypted after
+         // reading. If the MXF file is encrypted and the AESDecContext argument is NULL,
+         // the frame buffer will contain the ciphertext frame data. If the HMACContext
+         // argument is not NULL, the HMAC will be calculated (if the file supports it).
+         // Returns RESULT_INIT if the file is not open, failure if the frame number is
+         // out of range, or if optional decrypt or HAMC operations fail.
+         Result_t ReadAncillaryResource(const byte_t* uuid, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
+
+         // Print debugging information to stream
+         void     DumpHeaderMetadata(FILE* = 0) const;
+         void     DumpIndex(FILE* = 0) const;
+       };
+    } // namespace TimedText
+
+  //---------------------------------------------------------------------------------
+  //
+  namespace DCData
+  {
+    struct DCDataDescriptor
+    {
+      Rational EditRate;                 // Sample rate
+      ui32_t   ContainerDuration;          // number of frames
+      byte_t   AssetID[UUIDlen];           // The UUID for the DCData track
+      byte_t   DataEssenceCoding[UUIDlen]; // The coding for the data carried
+    };
+
+    // Print DCDataDescriptor to std::ostream
+    std::ostream& operator << (std::ostream& strm, const DCDataDescriptor& ddesc);
+    // Print debugging information to stream (stderr default)
+    void DCDataDescriptorDump(const DCDataDescriptor&, FILE* = 0);
+
+    //
+    class FrameBuffer : public ASDCP::FrameBuffer
+       {
+     public:
+         FrameBuffer() {}
+         FrameBuffer(ui32_t size) { Capacity(size); }
+         virtual ~FrameBuffer() {}
+
+         // Print debugging information to stream (stderr default)
+         void Dump(FILE* = 0, ui32_t dump_bytes = 0) const;
+       };
+
+    // An object which opens and reads a DC Data file.  The file is expected
+    // to contain exactly one complete frame of DC data essence as an unwrapped (raw)
+    // byte stream.
+    class BytestreamParser
+       {
+         class h__BytestreamParser;
+         mem_ptr<h__BytestreamParser> m_Parser;
+         ASDCP_NO_COPY_CONSTRUCT(BytestreamParser);
+
+     public:
+         BytestreamParser();
+         virtual ~BytestreamParser();
+
+         // Opens a file for reading, parses enough data to provide a complete
+      // set of stream metadata for the MXFWriter below.
+         // The frame buffer's PlaintextOffset parameter will be set to the first
+         // byte of the data segment. Set this value to zero if you want
+         // encrypted headers.
+         Result_t OpenReadFrame(const std::string& filename, FrameBuffer&) const;
+
+         // Fill a DCDataDescriptor struct with the values from the file's bytestream.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillDCDataDescriptor(DCDataDescriptor&) const;
+       };
+
+    // An object which reads a sequence of files containing DC Data.
+    class SequenceParser
+       {
+         class h__SequenceParser;
+         mem_ptr<h__SequenceParser> m_Parser;
+         ASDCP_NO_COPY_CONSTRUCT(SequenceParser);
+
+     public:
+         SequenceParser();
+         virtual ~SequenceParser();
+
+         // Opens a directory for reading.  The directory is expected to contain one or
+         // more files, each containing the bytestream for exactly one frame. The files
+      // must be named such that the frames are in temporal order when sorted
+         // alphabetically by filename.
+         Result_t OpenRead(const std::string& filename) const;
+
+         // Opens a file sequence for reading.  The sequence is expected to contain one or
+         // more filenames, each naming a file containing the bytestream for exactly one
+         // frame.
+         Result_t OpenRead(const std::list<std::string>& file_list) const;
+
+         // Fill a DCDataDescriptor struct with default values.
+         // Returns RESULT_INIT if the directory is not open.
+         Result_t FillDCDataDescriptor(DCDataDescriptor&) const;
+
+         // Rewind the directory to the beginning.
+         Result_t Reset() const;
+
+         // Reads the next sequential frame in the directory and places it in the
+         // frame buffer. Fails if the buffer is too small or the direcdtory
+         // contains no more files.
+         // The frame buffer's PlaintextOffset parameter will be set to the first
+         // byte of the data segment. Set this value to zero if you want
+         // encrypted headers.
+         Result_t ReadFrame(FrameBuffer&) const;
+       };
+
+    //
+    class MXFWriter
+       {
+         class h__Writer;
+         mem_ptr<h__Writer> m_Writer;
+         ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
+
+     public:
+         MXFWriter();
+         virtual ~MXFWriter();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for writing. The file must not exist. Returns error if
+         // the operation cannot be completed or if nonsensical data is discovered
+         // in the essence descriptor.
+         Result_t OpenWrite(const std::string& filename, const WriterInfo&,
+                            const DCDataDescriptor&, ui32_t HeaderSize = 16384);
+
+         // Writes a frame of essence to the MXF file. If the optional AESEncContext
+         // argument is present, the essence is encrypted prior to writing.
+         // Fails if the file is not open, is finalized, or an operating system
+         // error occurs.
+         Result_t WriteFrame(const FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
+
+         // Closes the MXF file, writing the index and revised header.
+         Result_t Finalize();
+       };
+
+    //
+    class MXFReader
+       {
+         class h__Reader;
+         mem_ptr<h__Reader> m_Reader;
+         ASDCP_NO_COPY_CONSTRUCT(MXFReader);
+
+     public:
+         MXFReader();
+         virtual ~MXFReader();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for reading. The file must exist. Returns error if the
+         // operation cannot be completed.
+         Result_t OpenRead(const std::string& filename) const;
+
+         // Returns RESULT_INIT if the file is not open.
+         Result_t Close() const;
+
+         // Fill a DCDataDescriptor struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillDCDataDescriptor(DCDataDescriptor&) const;
+
+         // Fill a WriterInfo struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillWriterInfo(WriterInfo&) const;
+
+         // Reads a frame of essence from the MXF file. If the optional AESEncContext
+         // argument is present, the essence is decrypted after reading. If the MXF
+         // file is encrypted and the AESDecContext argument is NULL, the frame buffer
+         // will contain the ciphertext frame data. If the HMACContext argument is
+         // not NULL, the HMAC will be calculated (if the file supports it).
+         // Returns RESULT_INIT if the file is not open, failure if the frame number is
+         // out of range, or if optional decrypt or HAMC operations fail.
+         Result_t ReadFrame(ui32_t frame_number, FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
+
+         // Using the index table read from the footer partition, lookup the frame number
+         // and return the offset into the file at which to read that frame of essence.
+         // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is
+         // out of range.
+         Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const;
+
+         // Print debugging information to stream
+         void     DumpHeaderMetadata(FILE* = 0) const;
+         void     DumpIndex(FILE* = 0) const;
+       };
+
+  } // namespace DCData
+
+  //---------------------------------------------------------------------------------
+  //
+  namespace ATMOS
+  {
+    struct AtmosDescriptor : public DCData::DCDataDescriptor
+    {
+      ui32_t FirstFrame;       // Frame number of the frame to align with the FFOA of the picture track
+      ui16_t MaxChannelCount;  // Max number of channels in bitstream
+      ui16_t MaxObjectCount;   // Max number of objects in bitstream
+      byte_t AtmosID[UUIDlen]; // UUID of Atmos Project
+      ui8_t  AtmosVersion;     // ATMOS Coder Version used to create bitstream
+    };
+
+    // Print AtmosDescriptor to std::ostream
+    std::ostream& operator << (std::ostream& strm, const AtmosDescriptor& adesc);
+    // Print debugging information to stream (stderr default)
+    void AtmosDescriptorDump(const AtmosDescriptor&, FILE* = 0);
+    // Determine if a file is a raw atmos file
+    bool IsDolbyAtmos(const std::string& filename);
+
+    //
+    class MXFWriter
+       {
+
+      class h__Writer;
+         mem_ptr<h__Writer> m_Writer;
+         ASDCP_NO_COPY_CONSTRUCT(MXFWriter);
+
+     public:
+         MXFWriter();
+         virtual ~MXFWriter();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for writing. The file must not exist. Returns error if
+         // the operation cannot be completed or if nonsensical data is discovered
+         // in the essence descriptor.
+         Result_t OpenWrite(const std::string& filename, const WriterInfo&,
+                            const AtmosDescriptor&, ui32_t HeaderSize = 16384);
+
+         // Writes a frame of essence to the MXF file. If the optional AESEncContext
+         // argument is present, the essence is encrypted prior to writing.
+         // Fails if the file is not open, is finalized, or an operating system
+         // error occurs.
+      Result_t WriteFrame(const DCData::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
+
+         // Closes the MXF file, writing the index and revised header.
+         Result_t Finalize();
+       };
+
+    //
+    class MXFReader
+       {
+      class h__Reader;
+         mem_ptr<h__Reader> m_Reader;
+         ASDCP_NO_COPY_CONSTRUCT(MXFReader);
+
+     public:
+         MXFReader();
+         virtual ~MXFReader();
+
+         // Warning: direct manipulation of MXF structures can interfere
+         // with the normal operation of the wrapper.  Caveat emptor!
+         virtual MXF::OP1aHeader& OP1aHeader();
+         virtual MXF::OPAtomIndexFooter& OPAtomIndexFooter();
+         virtual MXF::RIP& RIP();
+
+         // Open the file for reading. The file must exist. Returns error if the
+         // operation cannot be completed.
+         Result_t OpenRead(const std::string& filename) const;
+
+         // Returns RESULT_INIT if the file is not open.
+         Result_t Close() const;
+
+         // Fill an AtmosDescriptor struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillAtmosDescriptor(AtmosDescriptor&) const;
+
+         // Fill a WriterInfo struct with the values from the file's header.
+         // Returns RESULT_INIT if the file is not open.
+         Result_t FillWriterInfo(WriterInfo&) const;
+
+         // Reads a frame of essence from the MXF file. If the optional AESEncContext
+         // argument is present, the essence is decrypted after reading. If the MXF
+         // file is encrypted and the AESDecContext argument is NULL, the frame buffer
+         // will contain the ciphertext frame data. If the HMACContext argument is
+         // not NULL, the HMAC will be calculated (if the file supports it).
+         // Returns RESULT_INIT if the file is not open, failure if the frame number is
+         // out of range, or if optional decrypt or HAMC operations fail.
+         Result_t ReadFrame(ui32_t frame_number, DCData::FrameBuffer&, AESDecContext* = 0, HMACContext* = 0) const;
+
+         // Using the index table read from the footer partition, lookup the frame number
+         // and return the offset into the file at which to read that frame of essence.
+         // Returns RESULT_INIT if the file is not open, and RESULT_RANGE if the frame number is
+         // out of range.
+         Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset) const;
+
+         // Print debugging information to stream
+         void     DumpHeaderMetadata(FILE* = 0) const;
+         void     DumpIndex(FILE* = 0) const;
+       };
+
+  } // namespace ATMOS
+
+
+
 } // namespace ASDCP
 
 
-#endif // _AS_DCP_H__
+#endif // _AS_DCP_H_
 
 //
 // end AS_DCP.h