Release me
[asdcplib.git] / src / AS_DCP_internal.h
index 6fa4fa19b58901de33943cc18e6584849becac63..83272c09b431a8861ab0a0355abc0172d186d961 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2013, John Hurst
+Copyright (c) 2004-2018, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -47,6 +47,10 @@ using namespace ASDCP::MXF;
 #endif
 
 
+// uncomment to remove MXFGCGenericEssenceMultipleMappings from your AS-02 files
+// #define ASDCP_GCMULTI_PATCH
+
+
 #ifdef DEFAULT_MD_DECL
 ASDCP::MXF::OP1aHeader *g_OP1aHeader;
 ASDCP::MXF::OPAtomIndexFooter *g_OPAtomIndexFooter;
@@ -60,7 +64,6 @@ extern MXF::RIP *g_RIP;
 
 namespace ASDCP
 {
-  void default_md_object_init();
 
   //
   static std::vector<int>
@@ -74,14 +77,14 @@ namespace ASDCP
       {
        assert(r >= pstr);
        if ( r > pstr )
-         result.push_back(atoi(pstr));
+         result.push_back(strtol(pstr, 0, 10));
 
        pstr = r + 1;
        r = strchr(pstr, '.');
       }
 
     if( strlen(pstr) > 0 )
-      result.push_back(atoi(pstr));
+      result.push_back(strtol(pstr, 0, 10));
 
     assert(result.size() == 3);
     return result;
@@ -123,42 +126,55 @@ namespace ASDCP
   { 0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b,
     0x43, 0x48, 0x55, 0x4b, 0x43, 0x48, 0x55, 0x4b };
 
+  // Version of MXF spec to which an MXF file conforms
+  enum MXFVersion
+  {
+    MXFVersion_2004,
+    MXFVersion_2011,
+    MXFVersion_MAX
+  };
+
+  // version numbers from the MXF spec, to be written into files
+  
+  ui8_t const MXF_ObjectModelVersion = 1;
+  ui8_t const MXF_2004_MinorVersion = 2;
+  ui8_t const MXF_2011_MinorVersion = 3;
+           
+
   //------------------------------------------------------------------------------------------
   //
 
+  ui32_t derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate);
+
   Result_t MD_to_WriterInfo(MXF::Identification*, WriterInfo&);
   Result_t MD_to_CryptoInfo(MXF::CryptographicContext*, WriterInfo&, const Dictionary&);
 
   Result_t EncryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESEncContext*);
   Result_t DecryptFrameBuffer(const ASDCP::FrameBuffer&, ASDCP::FrameBuffer&, AESDecContext*);
 
-  Result_t MD_to_JP2K_PDesc(const ASDCP::MXF::RGBAEssenceDescriptor&  EssenceDescriptor,
-                           const ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor,
-                           const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate,
-                           ASDCP::JP2K::PictureDescriptor& PDesc);
-
-  Result_t JP2K_PDesc_to_MD(const JP2K::PictureDescriptor& PDesc,
-                           const ASDCP::Dictionary& dict,
-                           ASDCP::MXF::RGBAEssenceDescriptor *EssenceDescriptor,
-                           ASDCP::MXF::JPEG2000PictureSubDescriptor *EssenceSubDescriptor);
-
-  Result_t MD_to_JP2K_PDesc(const ASDCP::MXF::CDCIEssenceDescriptor&  EssenceDescriptor,
+  Result_t MD_to_JP2K_PDesc(const ASDCP::MXF::GenericPictureEssenceDescriptor&  EssenceDescriptor,
                            const ASDCP::MXF::JPEG2000PictureSubDescriptor& EssenceSubDescriptor,
                            const ASDCP::Rational& EditRate, const ASDCP::Rational& SampleRate,
                            ASDCP::JP2K::PictureDescriptor& PDesc);
 
   Result_t JP2K_PDesc_to_MD(const JP2K::PictureDescriptor& PDesc,
                            const ASDCP::Dictionary& dict,
-                           ASDCP::MXF::CDCIEssenceDescriptor *EssenceDescriptor,
-                           ASDCP::MXF::JPEG2000PictureSubDescriptor *EssenceSubDescriptor);
+                           ASDCP::MXF::GenericPictureEssenceDescriptor& EssenceDescriptor,
+                           ASDCP::MXF::JPEG2000PictureSubDescriptorEssenceSubDescriptor);
 
   Result_t PCM_ADesc_to_MD(PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
   Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, PCM::AudioDescriptor& ADesc);
 
-  void     AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
+  void     AddDmsCrypt(Partition& HeaderPart, SourcePackage& Package,
                       WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict);
 
-  Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
+  Result_t AddDmsTrackGenericPartUtf8Text(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, SourcePackage&,
+                                         ASDCP::MXF::RIP&, const Dictionary*&);
+  //
+  Result_t WriteGenericStreamPartition(Kumu::FileWriter&, ASDCP::MXF::OP1aHeader&, ASDCP::MXF::RIP&, const Dictionary*&,
+                                      const ASDCP::FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
+  
+  Result_t Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict,
                            const ASDCP::WriterInfo& Info, Kumu::fpos_t& LastPosition, ASDCP::FrameBuffer& CtFrameBuf,
                            ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
                            const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
@@ -166,9 +182,9 @@ namespace ASDCP
   Result_t Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
                             const ASDCP::WriterInfo& Info, ASDCP::FrameBuffer& CtFrameBuf, ui32_t& FramesWritten,
                             ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
+                            const ui32_t& MinEssenceElementBerLength,
                             AESEncContext* Ctx, HMACContext* HMAC);
 
-
   //
  class KLReader : public ASDCP::KLVPacket
     {
@@ -222,7 +238,7 @@ namespace ASDCP
        const MXF::RIP& GetRIP() const { return m_RIP; }
 
        //
-       Result_t OpenMXFRead(const char* filename)
+       Result_t OpenMXFRead(const std::string& filename)
        {
          m_LastPosition = 0;
          Result_t result = m_File.OpenRead(filename);
@@ -233,7 +249,7 @@ namespace ASDCP
          if ( ASDCP_SUCCESS(result) )
            {
              result = m_RIP.InitFromFile(m_File);
-             ui32_t test_s = m_RIP.PairArray.size();
+             ui32_t test_s = (ui32_t)m_RIP.PairArray.size();
 
              if ( ASDCP_FAILURE(result) )
                {
@@ -295,7 +311,8 @@ namespace ASDCP
        }
 
        // positions file before reading
-       Result_t ReadEKLVFrame(const ASDCP::MXF::Partition& CurrentPartition,
+       // allows external control of index offset
+       Result_t ReadEKLVFrame(const ui64_t& body_offset,
                               ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
                               const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
        {
@@ -308,8 +325,8 @@ namespace ASDCP
              return RESULT_RANGE;
            }
 
-         // get frame position and go read the frame's key and length
-         Kumu::fpos_t FilePosition = CurrentPartition.BodyOffset + TmpEntry.StreamOffset;
+         // get relative frame position, apply offset and go read the frame's key and length
+         Kumu::fpos_t FilePosition = body_offset + TmpEntry.StreamOffset;
          Result_t result = RESULT_OK;
 
          if ( FilePosition != m_LastPosition )
@@ -324,17 +341,46 @@ namespace ASDCP
          return result;
        }
 
+       // positions file before reading
+       // assumes "processed" index entries have absolute positions
+       Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
+                              const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
+       {
+         // look up frame index node
+         IndexTableSegment::IndexEntry TmpEntry;
+
+         if ( KM_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
+           {
+             DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
+             return RESULT_RANGE;
+           }
+
+         // get absolute frame position and go read the frame's key and length
+         Result_t result = RESULT_OK;
+
+         if ( TmpEntry.StreamOffset != m_LastPosition )
+           {
+             m_LastPosition = TmpEntry.StreamOffset;
+             result = m_File.Seek(TmpEntry.StreamOffset);
+           }
+
+         if ( KM_SUCCESS(result) )
+           result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
+
+         return result;
+       }
+
        // reads from current position
        Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
                                const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
        {
          assert(m_Dict);
-         return Read_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_LastPosition, m_CtFrameBuf,
+         return Read_EKLV_Packet(m_File, *m_Dict, m_Info, m_LastPosition, m_CtFrameBuf,
                                  FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
        }
 
        // Get the position of a frame from a track file
-       Result_t LocateFrame(const ASDCP::MXF::Partition& CurrentPartition,
+       Result_t LocateFrame(const ui64_t& body_offset,
                             ui32_t FrameNum, Kumu::fpos_t& streamOffset,
                             i8_t& temporalOffset, i8_t& keyFrameOffset)
        {
@@ -348,13 +394,77 @@ namespace ASDCP
            }
 
          // get frame position, temporal offset, and key frame ofset
-         streamOffset = CurrentPartition.BodyOffset + TmpEntry.StreamOffset;
+         streamOffset = body_offset + TmpEntry.StreamOffset;
          temporalOffset = TmpEntry.TemporalOffset;
          keyFrameOffset = TmpEntry.KeyFrameOffset;
          
          return RESULT_OK;
        }
 
+       // Reads a Generic Stream Partition payload. Returns RESULT_FORMAT if the SID is
+       // not present in the  RIP, or if the actual partition at ByteOffset does not have
+       // a matching BodySID value. Encryption is not currently supported.
+       Result_t ReadGenericStreamPartitionPayload(const ui32_t sid, ASDCP::FrameBuffer& frame_buf,
+                                                  AESDecContext* Ctx, HMACContext* HMAC)
+       {
+         Kumu::fpos_t start_offset = 0, end_offset = 0;
+         ui32_t sequence = 0;
+
+         // locate SID, record the offset
+         // Count the sequence length in because this is the sequence
+         // value needed to  complete the HMAC.
+         ASDCP::MXF::RIP::const_pair_iterator i;
+         for ( i = m_RIP.PairArray.begin(); i != m_RIP.PairArray.end(); ++i, ++sequence )
+           {
+             if ( sid == i->BodySID )
+               {
+                 start_offset = i->ByteOffset;
+               }
+             else if ( start_offset != 0 )
+               {
+                 end_offset = i->ByteOffset;
+                 break;
+               }
+           }
+
+         if ( start_offset == 0 || end_offset == 0 )
+           {
+             DefaultLogSink().Error("Body SID not found: %d.\n", sid);
+             return RESULT_NOT_FOUND;
+           }
+
+         // Read the Partition header and then read the payload.
+         Result_t result = m_File.Seek(start_offset);
+
+         if ( KM_SUCCESS(result) )
+           {
+             result = frame_buf.Capacity(end_offset-start_offset);
+           }
+    
+         if ( KM_SUCCESS(result) )
+           {
+             // read the partition header
+             ASDCP::MXF::Partition GSPart(m_Dict);
+             result = GSPart.InitFromFile(m_File);
+
+             if ( KM_SUCCESS(result) )
+               {
+                 // check the SID
+                 if ( GSPart.BodySID != sid )
+                   {
+                     DefaultLogSink().Error("Generic stream partition Body SID differs: %s\n", sid);
+                     result = RESULT_FORMAT;
+                   }
+                 else
+                   {
+                     result = ReadEKLVPacket(0, sequence, frame_buf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
+                   }
+               }
+           }
+  
+         return result;
+       }
+
        //
        void Close()
        {
@@ -380,13 +490,13 @@ namespace ASDCP
       template <class PackageT, class ClipT>
        TrackSet<ClipT>
        CreateTrackAndSequence(OP1aHeader& Header, PackageT& Package, const std::string TrackName,
-                              const MXF::Rational& EditRate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
+                              const MXF::Rational& clip_edit_rate, const UL& Definition, ui32_t TrackID, const Dictionary*& Dict)
        {
          TrackSet<ClipT> NewTrack;
 
          NewTrack.Track = new Track(Dict);
          Header.AddChildObject(NewTrack.Track);
-         NewTrack.Track->EditRate = EditRate;
+         NewTrack.Track->EditRate = clip_edit_rate;
          Package.Tracks.push_back(NewTrack.Track->InstanceUID);
          NewTrack.Track->TrackID = TrackID;
          NewTrack.Track->TrackName = TrackName.c_str();
@@ -403,17 +513,19 @@ namespace ASDCP
       template <class PackageT>
        TrackSet<TimecodeComponent>
        CreateTimecodeTrack(OP1aHeader& Header, PackageT& Package,
-                           const MXF::Rational& EditRate, ui32_t TCFrameRate, ui64_t TCStart, const Dictionary*& Dict)
+                           const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate, ui64_t TCStart, const Dictionary*& Dict)
        {
          assert(Dict);
          UL TCUL(Dict->ul(MDD_TimecodeDataDef));
 
-         TrackSet<TimecodeComponent> NewTrack = CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track", EditRate, TCUL, 1, Dict);
+         TrackSet<TimecodeComponent> NewTrack =
+           CreateTrackAndSequence<PackageT, TimecodeComponent>(Header, Package, "Timecode Track",
+                                                               tc_edit_rate, TCUL, 1, Dict);
 
          NewTrack.Clip = new TimecodeComponent(Dict);
          Header.AddChildObject(NewTrack.Clip);
          NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
-         NewTrack.Clip->RoundedTimecodeBase = TCFrameRate;
+         NewTrack.Clip->RoundedTimecodeBase = tc_frame_rate;
          NewTrack.Clip->StartTimecode = TCStart;
          NewTrack.Clip->DataDefinition = TCUL;
 
@@ -428,6 +540,7 @@ namespace ASDCP
        ST_READY,   // ready to write frames
        ST_RUNNING, // one or more frames written
        ST_FINAL,   // index written, file closed
+       ST_MAX
       };
 
       // implementation of h__WriterState class Goto_* methods
@@ -477,6 +590,7 @@ namespace ASDCP
 
        MaterialPackage*   m_MaterialPackage;
        SourcePackage*     m_FilePackage;
+       ContentStorage*    m_ContentStorage;
 
        FileDescriptor*    m_EssenceDescriptor;
        std::list<InterchangeObject*> m_EssenceSubDescriptorList;
@@ -491,9 +605,9 @@ namespace ASDCP
        DurationElementList_t m_DurationUpdateList;
 
       TrackFileWriter(const Dictionary& d) :
-       m_Dict(&d), m_HeaderPart(m_Dict), m_RIP(m_Dict),
-         m_HeaderSize(0), m_EssenceDescriptor(0),
-         m_FramesWritten(0), m_StreamOffset(0)
+       m_Dict(&d), m_HeaderSize(0), m_HeaderPart(m_Dict), m_RIP(m_Dict),
+         m_MaterialPackage(0), m_FilePackage(0), m_ContentStorage(0),
+         m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
          {
            default_md_object_init();
          }
@@ -504,11 +618,11 @@ namespace ASDCP
 
        const MXF::RIP& GetRIP() const { return m_RIP; }
 
-       void InitHeader()
+       void InitHeader(const MXFVersion& mxf_ver)
        {
          assert(m_Dict);
          assert(m_EssenceDescriptor);
-
          m_HeaderPart.m_Primer.ClearTagList();
          m_HeaderPart.m_Preface = new Preface(m_Dict);
          m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
@@ -518,6 +632,20 @@ namespace ASDCP
          m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a));
          m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
 
+         if ( mxf_ver == MXFVersion_2004 )
+           {
+             m_HeaderPart.MinorVersion = MXF_2004_MinorVersion;
+             m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2004_MinorVersion);
+             m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
+           }
+         else
+           {
+             assert(mxf_ver == MXFVersion_2011);
+             m_HeaderPart.MinorVersion = MXF_2011_MinorVersion;
+             m_HeaderPart.m_Preface->Version = ((MXF_ObjectModelVersion << 8) | MXF_2011_MinorVersion);
+             m_HeaderPart.m_Preface->ObjectModelVersion = MXF_ObjectModelVersion;
+           }
+
          // Identification
          Identification* Ident = new Identification(m_Dict);
          m_HeaderPart.AddChildObject(Ident);
@@ -540,18 +668,21 @@ namespace ASDCP
        }
 
        //
-       void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
+       void AddSourceClip(const MXF::Rational& clip_edit_rate,
+                          const MXF::Rational& tc_edit_rate, ui32_t TCFrameRate,
                           const std::string& TrackName, const UL& EssenceUL,
                           const UL& DataDefinition, const std::string& PackageLabel)
        {
-         //
-         ContentStorage* Storage = new ContentStorage(m_Dict);
-         m_HeaderPart.AddChildObject(Storage);
-         m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
+         if ( m_ContentStorage == 0 )
+           {
+             m_ContentStorage = new ContentStorage(m_Dict);
+             m_HeaderPart.AddChildObject(m_ContentStorage);
+             m_HeaderPart.m_Preface->ContentStorage = m_ContentStorage->InstanceUID;
+           }
 
          EssenceContainerData* ECD = new EssenceContainerData(m_Dict);
          m_HeaderPart.AddChildObject(ECD);
-         Storage->EssenceContainerData.push_back(ECD->InstanceUID);
+         m_ContentStorage->EssenceContainerData.push_back(ECD->InstanceUID);
          ECD->IndexSID = 129;
          ECD->BodySID = 1;
 
@@ -564,22 +695,26 @@ namespace ASDCP
          // Material Package
          //
          m_MaterialPackage = new MaterialPackage(m_Dict);
-         m_MaterialPackage->Name = "AS-DCP Material Package";
+         m_MaterialPackage->Name = "Material Package";
          m_MaterialPackage->PackageUID = MaterialPackageUMID;
          m_HeaderPart.AddChildObject(m_MaterialPackage);
-         Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
+         m_ContentStorage->Packages.push_back(m_MaterialPackage->InstanceUID);
 
          TrackSet<TimecodeComponent> MPTCTrack =
            CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
-                                                EditRate, TCFrameRate, 0, m_Dict);
-         m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration));
-         m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration));
+                                                tc_edit_rate, TCFrameRate, 0, m_Dict);
+
+         MPTCTrack.Sequence->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration.get()));
+         MPTCTrack.Clip->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration.get()));
 
          TrackSet<SourceClip> MPTrack =
            CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
-                                                               TrackName, EditRate, DataDefinition,
+                                                               TrackName, clip_edit_rate, DataDefinition,
                                                                2, m_Dict);
-         m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
+         MPTrack.Sequence->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration.get()));
 
          MPTrack.Clip = new SourceClip(m_Dict);
          m_HeaderPart.AddChildObject(MPTrack.Clip);
@@ -587,7 +722,9 @@ namespace ASDCP
          MPTrack.Clip->DataDefinition = DataDefinition;
          MPTrack.Clip->SourcePackageID = SourcePackageUMID;
          MPTrack.Clip->SourceTrackID = 2;
-         m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration));
+
+         MPTrack.Clip->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration.get()));
 
   
          //
@@ -599,19 +736,24 @@ namespace ASDCP
          ECD->LinkedPackageUID = SourcePackageUMID;
 
          m_HeaderPart.AddChildObject(m_FilePackage);
-         Storage->Packages.push_back(m_FilePackage->InstanceUID);
+         m_ContentStorage->Packages.push_back(m_FilePackage->InstanceUID);
 
          TrackSet<TimecodeComponent> FPTCTrack =
            CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
-                                              EditRate, TCFrameRate,
-                                              ui64_C(3600) * TCFrameRate, m_Dict);
-         m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration));
-         m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration));
+                                              tc_edit_rate, TCFrameRate, 0, m_Dict);
+
+         FPTCTrack.Sequence->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration.get()));
+         FPTCTrack.Clip->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration.get()));
+
          TrackSet<SourceClip> FPTrack =
            CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
-                                                             TrackName, EditRate, DataDefinition,
+                                                             TrackName, clip_edit_rate, DataDefinition,
                                                              2, m_Dict);
-         m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
+
+         FPTrack.Sequence->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration.get()));
 
          // Consult ST 379:2004 Sec. 6.3, "Element to track relationship" to see where "12" comes from.
          FPTrack.Track->TrackNumber = KM_i32_BE(Kumu::cp2i<ui32_t>((EssenceUL.Value() + 12)));
@@ -624,93 +766,10 @@ namespace ASDCP
          // for now we do not allow setting this value, so all files will be 'original'
          FPTrack.Clip->SourceTrackID = 0;
          FPTrack.Clip->SourcePackageID = NilUMID;
-         m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration));
-
-         m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
-       }
-
-       //
-       void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
-                         const std::string& TrackName, const UL& DataDefinition,
-                         const std::string& PackageLabel)
-       {
-         //
-         ContentStorage* Storage = new ContentStorage(m_Dict);
-         m_HeaderPart.AddChildObject(Storage);
-         m_HeaderPart.m_Preface->ContentStorage = Storage->InstanceUID;
-
-         EssenceContainerData* ECD = new EssenceContainerData(m_Dict);
-         m_HeaderPart.AddChildObject(ECD);
-         Storage->EssenceContainerData.push_back(ECD->InstanceUID);
-         ECD->IndexSID = 129;
-         ECD->BodySID = 1;
-
-         UUID assetUUID(m_Info.AssetUUID);
-         UMID SourcePackageUMID, MaterialPackageUMID;
-         SourcePackageUMID.MakeUMID(0x0f, assetUUID);
-         MaterialPackageUMID.MakeUMID(0x0f); // unidentified essence
-
-         //
-         // Material Package
-         //
-         m_MaterialPackage = new MaterialPackage(m_Dict);
-         m_MaterialPackage->Name = "AS-DCP Material Package";
-         m_MaterialPackage->PackageUID = MaterialPackageUMID;
-         m_HeaderPart.AddChildObject(m_MaterialPackage);
-         Storage->Packages.push_back(m_MaterialPackage->InstanceUID);
-
-         TrackSet<TimecodeComponent> MPTCTrack =
-           CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
-                                                EditRate, TCFrameRate, 0, m_Dict);
-         m_DurationUpdateList.push_back(&(MPTCTrack.Sequence->Duration));
-         m_DurationUpdateList.push_back(&(MPTCTrack.Clip->Duration));
-
-         TrackSet<DMSegment> MPTrack =
-           CreateTrackAndSequence<MaterialPackage, DMSegment>(m_HeaderPart, *m_MaterialPackage,
-                                                              TrackName, EditRate, DataDefinition,
-                                                              2, m_Dict);
-         m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
 
-         MPTrack.Clip = new DMSegment(m_Dict);
-         m_HeaderPart.AddChildObject(MPTrack.Clip);
-         MPTrack.Sequence->StructuralComponents.push_back(MPTrack.Clip->InstanceUID);
-         MPTrack.Clip->DataDefinition = DataDefinition;
-         //  MPTrack.Clip->SourcePackageID = SourcePackageUMID;
-         //  MPTrack.Clip->SourceTrackID = 2;
-         m_DurationUpdateList.push_back(&(MPTrack.Clip->Duration));
-
-  
-         //
-         // File (Source) Package
-         //
-         m_FilePackage = new SourcePackage(m_Dict);
-         m_FilePackage->Name = PackageLabel.c_str();
-         m_FilePackage->PackageUID = SourcePackageUMID;
-         ECD->LinkedPackageUID = SourcePackageUMID;
-
-         m_HeaderPart.AddChildObject(m_FilePackage);
-         Storage->Packages.push_back(m_FilePackage->InstanceUID);
-
-         TrackSet<TimecodeComponent> FPTCTrack =
-           CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
-                                              EditRate, TCFrameRate,
-                                              ui64_C(3600) * TCFrameRate, m_Dict);
-         m_DurationUpdateList.push_back(&(FPTCTrack.Sequence->Duration));
-         m_DurationUpdateList.push_back(&(FPTCTrack.Clip->Duration));
-
-         TrackSet<DMSegment> FPTrack =
-           CreateTrackAndSequence<SourcePackage, DMSegment>(m_HeaderPart, *m_FilePackage,
-                                                            TrackName, EditRate, DataDefinition,
-                                                            2, m_Dict);
-         m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
-
-         FPTrack.Clip = new DMSegment(m_Dict);
-         m_HeaderPart.AddChildObject(FPTrack.Clip);
-         FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
-         FPTrack.Clip->DataDefinition = DataDefinition;
-         FPTrack.Clip->EventComment = "D-Cinema Timed Text";
+         FPTrack.Clip->Duration.set_has_value();
+         m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration.get()));
 
-         m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration));
          m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
        }
 
@@ -727,15 +786,18 @@ namespace ASDCP
          // Essence Descriptors
          //
          assert(m_Dict);
+#ifndef ASDCP_GCMULTI_PATCH
          UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
          m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
+#endif
 
          if ( m_Info.EncryptedEssence )
            {
              UL CryptEssenceUL(m_Dict->ul(MDD_EncryptedContainerLabel));
              m_HeaderPart.EssenceContainers.push_back(CryptEssenceUL);
              m_HeaderPart.m_Preface->DMSchemes.push_back(UL(m_Dict->ul(MDD_CryptographicFrameworkLabel)));
-             AddDMScrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict);
+             AddDmsCrypt(m_HeaderPart, *m_FilePackage, m_Info, WrappingUL, m_Dict);
+             //// TODO: fix DMSegment Duration value
            }
          else
            {
@@ -752,6 +814,48 @@ namespace ASDCP
          m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
        }
 
+       Result_t AddDmsGenericPartUtf8Text(const ASDCP::FrameBuffer& frame_buffer,
+                                          ASDCP::AESEncContext* enc = 0, ASDCP::HMACContext* hmac = 0)
+       {
+         Kumu::fpos_t previous_partition_offset = m_RIP.PairArray.back().ByteOffset;
+         Result_t result = AddDmsTrackGenericPartUtf8Text(m_File, m_HeaderPart, *m_FilePackage, m_RIP, m_Dict);
+
+         if ( KM_SUCCESS(result) )
+           {
+             // m_RIP now contains an entry (at the back) for the new generic stream
+             // (this entry was created during the call to AddDmsTrackGenericPartUtf8Text())
+             if ( m_File.Tell() != m_RIP.PairArray.back().ByteOffset )
+               {
+                 DefaultLogSink().Error("File offset has moved since RIP modification. Unrecoverable error.\n");
+                 return RESULT_FAIL;
+               }
+         
+             // create generic stream partition header
+             static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
+             ASDCP::MXF::Partition GSPart(m_Dict);
+
+             GSPart.MajorVersion = m_HeaderPart.MajorVersion;
+             GSPart.MinorVersion = m_HeaderPart.MinorVersion;
+             GSPart.ThisPartition = m_RIP.PairArray.back().ByteOffset;
+             GSPart.PreviousPartition = previous_partition_offset;
+             GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
+             GSPart.BodySID = m_RIP.PairArray.back().BodySID;
+             GSPart.EssenceContainers = m_HeaderPart.EssenceContainers;
+
+             static UL gs_part_ul(m_Dict->ul(MDD_GenericStreamPartition));
+             Result_t result = GSPart.WriteToFile(m_File, gs_part_ul);
+         
+             if ( KM_SUCCESS(result) )
+               {
+                 result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
+                                            m_StreamOffset, frame_buffer, GenericStream_DataElement.Value(),
+                                            MXF_BER_LENGTH, enc, hmac);
+               }
+           }
+
+         return result;
+       }
+
        //
        void Close()
        {
@@ -777,7 +881,7 @@ namespace ASDCP
       h__ASDCPReader(const Dictionary&);
       virtual ~h__ASDCPReader();
 
-      Result_t OpenMXFRead(const char* filename);
+      Result_t OpenMXFRead(const std::string& filename);
       Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
                             const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
       Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
@@ -805,6 +909,7 @@ namespace ASDCP
 
       Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
       Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
+                              const ui32_t& MinEssenceElementBerLength,
                               AESEncContext* Ctx, HMACContext* HMAC);
       Result_t WriteASDCPFooter();
     };