FIPS 186-2 fixes
[asdcplib.git] / src / h__Writer.cpp
index 411f63e9f35f66d9c55cf69ebf311c04ce51913e..69873b65fb59611eff9b62c702e4be0bc20b0044 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2005, John Hurst
+Copyright (c) 2004-2009, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -30,234 +30,472 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 #include "AS_DCP_internal.h"
-#include "MemIO.h"
-#include "Timecode.h"
-#include <assert.h>
+#include "KLV.h"
 
-using namespace mxflib;
 using namespace ASDCP;
+using namespace ASDCP::MXF;
 
+// a magic number identifying asdcplib
+#ifndef ASDCP_BUILD_NUMBER
+#define ASDCP_BUILD_NUMBER 0x6A68
+#endif
+
+
+static std::vector<int>
+version_split(const char* str)
+{
+  std::vector<int> result;
+
+  const char* pstr = str;
+  const char* r = strchr(pstr, '.');
+
+  while ( r != 0 )
+    {
+      assert(r >= pstr);
+      if ( r > pstr )
+       result.push_back(atoi(pstr));
+
+      pstr = r + 1;
+      r = strchr(pstr, '.');
+    }
 
-ASDCP::h__Writer::h__Writer() : m_FramesWritten(0), m_StreamOffset(0)
+  if( strlen(pstr) > 0 )
+    result.push_back(atoi(pstr));
+
+  assert(result.size() == 3);
+  return result;
+}
+
+
+//
+ASDCP::h__Writer::h__Writer(const Dictionary& d) :
+  m_HeaderPart(m_Dict), m_BodyPart(m_Dict), m_FooterPart(m_Dict), m_Dict(&d),
+  m_HeaderSize(0), m_EssenceStart(0),
+  m_EssenceDescriptor(0), m_FramesWritten(0), m_StreamOffset(0)
 {
-  init_mxf_types();
 }
 
 ASDCP::h__Writer::~h__Writer()
 {
 }
 
-// standard method of writing the header of a new MXF file
-Result_t
-ASDCP::h__Writer::WriteMXFHeader(EssenceType_t EssenceType, ASDCP::Rational& EditRate,
-                         ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
+//
+// add DMS CryptographicFramework entry to source package
+void
+AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
+           WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict)
 {
-  // write the stream metadata
-  m_Metadata = new Metadata();
-  assert(m_Metadata);
-  assert(m_Metadata->m_Object);
+  assert(Dict);
+  // Essence Track
+  StaticTrack* NewTrack = new StaticTrack(Dict);
+  HeaderPart.AddChildObject(NewTrack);
+  Package.Tracks.push_back(NewTrack->InstanceUID);
+  NewTrack->TrackName = "Descriptive Track";
+  NewTrack->TrackID = 3;
+
+  Sequence* Seq = new Sequence(Dict);
+  HeaderPart.AddChildObject(Seq);
+  NewTrack->Sequence = Seq->InstanceUID;
+  Seq->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
+
+  DMSegment* Segment = new DMSegment(Dict);
+  HeaderPart.AddChildObject(Segment);
+  Seq->StructuralComponents.push_back(Segment->InstanceUID);
+  Segment->EventComment = "AS-DCP KLV Encryption";
+  
+  CryptographicFramework* CFW = new CryptographicFramework(Dict);
+  HeaderPart.AddChildObject(CFW);
+  Segment->DMFramework = CFW->InstanceUID;
+
+  CryptographicContext* Context = new CryptographicContext(Dict);
+  HeaderPart.AddChildObject(Context);
+  CFW->ContextSR = Context->InstanceUID;
+
+  Context->ContextID.Set(Descr.ContextID);
+  Context->SourceEssenceContainer = WrappingUL; // ??????
+  Context->CipherAlgorithm.Set(Dict->ul(MDD_CipherAlgorithm_AES));
+  Context->MICAlgorithm.Set( Descr.UsesHMAC ? Dict->ul(MDD_MICAlgorithm_HMAC_SHA1) : Dict->ul(MDD_MICAlgorithm_NONE) );
+  Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
+}
 
-  if ( m_Info.EncryptedEssence )
-    {
-      UL DMSUL(CryptoFrameworkUL_Data);
-      m_Metadata->AddDMScheme(DMSUL);
-    }
+//
+void
+ASDCP::h__Writer::InitHeader()
+{
+  assert(m_Dict);
+  assert(m_EssenceDescriptor);
 
-  // Set the OP label
-  // If we are writing OP-Atom we write the header as OP1a initially as another process
-  // may try to read the file before it is complete and then it will NOT be a valid OP-Atom file
-  m_Metadata->SetOP(OP1aUL);
-
-  // Build the Material Package
-  // DRAGONS: We should really try and determine the UMID type rather than cop-out!
-  UMID PackageUMID;
-  PackageUMID.MakeUMID(0x0d); // mixed type
-
-#if ASDCP_USE_MXFLIB
-  mxflib::Rational EditRate_;
-  EditRate_.Numerator = EditRate.Numerator;
-  EditRate_.Denominator = EditRate.Denominator;
-#else
-#define EditRate_ EditRate
-#endif
+  m_HeaderPart.m_Primer.ClearTagList();
+  m_HeaderPart.m_Preface = new Preface(m_Dict);
+  m_HeaderPart.AddChildObject(m_HeaderPart.m_Preface);
 
-  m_MaterialPackage = m_Metadata->AddMaterialPackage("AS-DCP Material Package", PackageUMID);
-  m_Metadata->SetPrimaryPackage(m_MaterialPackage);    // This will be overwritten for OP-Atom
-  
-  TrackPtr MPTimecodeTrack = m_MaterialPackage->AddTimecodeTrack(EditRate_);
-  m_MPTimecode = MPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0, 0);
+  // Set the Operational Pattern label -- we're just starting and have no RIP or index,
+  // so we tell the world by using OP1a
+  m_HeaderPart.m_Preface->OperationalPattern = UL(m_Dict->ul(MDD_OP1a));
+  m_HeaderPart.OperationalPattern = m_HeaderPart.m_Preface->OperationalPattern;
 
-  TrackPtr FPTimecodeTrack = 0;
-  mxflib::UUID assetUUID(m_Info.AssetUUID);
+  // First RIP Entry
+  if ( m_Info.LabelSetType == LS_MXF_SMPTE )
+    m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // 3-part, no essence in header
+  else
+    m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
 
-  UMID EssenceUMID;
-  
-  switch ( EssenceType )
-    {
-    case ESS_MPEG2_VES:
-      PackageUMID.MakeUMID(0x0f, assetUUID);
-      m_FilePackage = m_Metadata->AddFilePackage(1, MPEG_PACKAGE_LABEL, PackageUMID);
-      m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate_);
-      m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate_);
-      break;
-         
-    case ESS_JPEG_2000:
-      PackageUMID.MakeUMID(0x0f, assetUUID);
-      m_FilePackage = m_Metadata->AddFilePackage(1, JP2K_PACKAGE_LABEL, PackageUMID);
-      m_MPTrack = m_MaterialPackage->AddPictureTrack(EditRate_);
-      m_FPTrack = m_FilePackage->AddPictureTrack(0, EditRate_);
-      break;
-         
-    case ESS_PCM_24b_48k:
-      PackageUMID.MakeUMID(0x0f, assetUUID);
-      m_FilePackage = m_Metadata->AddFilePackage(1, PCM_PACKAGE_LABEL, PackageUMID);
-      m_MPTrack = m_MaterialPackage->AddSoundTrack(EditRate_);
-      m_FPTrack = m_FilePackage->AddSoundTrack(0, EditRate_);
-      break;
-
-    default: return RESULT_RAW_ESS;
-    }
+  //
+  // Identification
+  //
+  Identification* Ident = new Identification(m_Dict);
+  m_HeaderPart.AddChildObject(Ident);
+  m_HeaderPart.m_Preface->Identifications.push_back(Ident->InstanceUID);
+
+  Kumu::GenRandomValue(Ident->ThisGenerationUID);
+  Ident->CompanyName = m_Info.CompanyName.c_str();
+  Ident->ProductName = m_Info.ProductName.c_str();
+  Ident->VersionString = m_Info.ProductVersion.c_str();
+  Ident->ProductUID.Set(m_Info.ProductUUID);
+  Ident->Platform = ASDCP_PLATFORM;
+
+  std::vector<int> version = version_split(Version());
+
+  Ident->ToolkitVersion.Major = version[0];
+  Ident->ToolkitVersion.Minor = version[1];
+  Ident->ToolkitVersion.Patch = version[2];
+  Ident->ToolkitVersion.Build = ASDCP_BUILD_NUMBER;
+  Ident->ToolkitVersion.Release = VersionType::RL_RELEASE;
+}
+
+//
+template <class ClipT>
+struct TrackSet
+{
+  MXF::Track*    Track;
+  MXF::Sequence* Sequence;
+  ClipT*         Clip;
+
+  TrackSet() : Track(0), Sequence(0), Clip(0) {}
+};
+
+//
+template <class PackageT, class ClipT>
+TrackSet<ClipT>
+CreateTrackAndSequence(OPAtomHeader& Header, PackageT& Package, const std::string TrackName,
+                      const MXF::Rational& EditRate, 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;
+  Package.Tracks.push_back(NewTrack.Track->InstanceUID);
+  NewTrack.Track->TrackID = TrackID;
+  NewTrack.Track->TrackName = TrackName.c_str();
+
+  NewTrack.Sequence = new Sequence(Dict);
+  Header.AddChildObject(NewTrack.Sequence);
+  NewTrack.Track->Sequence = NewTrack.Sequence->InstanceUID;
+  NewTrack.Sequence->DataDefinition = Definition;
+
+  return NewTrack;
+}
+
+//
+template <class PackageT>
+TrackSet<TimecodeComponent>
+CreateTimecodeTrack(OPAtomHeader& Header, PackageT& Package,
+                   const MXF::Rational& EditRate, ui32_t TCFrameRate, 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);
+
+  NewTrack.Clip = new TimecodeComponent(Dict);
+  Header.AddChildObject(NewTrack.Clip);
+  NewTrack.Sequence->StructuralComponents.push_back(NewTrack.Clip->InstanceUID);
+  NewTrack.Clip->RoundedTimecodeBase = TCFrameRate;
+  NewTrack.Clip->StartTimecode = TCStart;
+  NewTrack.Clip->DataDefinition = TCUL;
+
+  return NewTrack;
+}
+
+
+//
+void
+ASDCP::h__Writer::AddSourceClip(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<SourceClip> MPTrack = CreateTrackAndSequence<MaterialPackage, SourceClip>(m_HeaderPart, *m_MaterialPackage,
+                                                                                    TrackName, EditRate, DataDefinition,
+                                                                                    2, m_Dict);
+  m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
+
+  MPTrack.Clip = new SourceClip(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));
 
-  // Add an essence element
-  FPTimecodeTrack = m_FilePackage->AddTimecodeTrack(EditRate_);
-  m_FPTimecode = FPTimecodeTrack->AddTimecodeComponent(TCFrameRate, 0/* NDF */,
-                                                      tc_to_frames(TCFrameRate, 1, 0, 0, 0) );
-  
-  // Add a single Component to this Track of the Material Package
-  m_MPClip = m_MPTrack->AddSourceClip();
   
-  // Add a single Component to this Track of the File Package
-  m_FPClip = m_FPTrack->AddSourceClip();
-  const byte_t* SourceEssenceContainerLabel = 0;
+  //
+  // 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<SourceClip> FPTrack = CreateTrackAndSequence<SourcePackage, SourceClip>(m_HeaderPart, *m_FilePackage,
+                                                                                  TrackName, EditRate, DataDefinition,
+                                                                                  2, m_Dict);
+  m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
+
+  FPTrack.Clip = new SourceClip(m_Dict);
+  m_HeaderPart.AddChildObject(FPTrack.Clip);
+  FPTrack.Sequence->StructuralComponents.push_back(FPTrack.Clip->InstanceUID);
+  FPTrack.Clip->DataDefinition = DataDefinition;
+
+  // 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;
+}
 
-  // Frame wrapping
-  if ( m_Info.EncryptedEssence )
-    {
-      switch ( EssenceType )
-       {
-       case ESS_MPEG2_VES:
-         SourceEssenceContainerLabel = WrappingUL_Data_MPEG2_VES;
-          break;
-         
-       case ESS_JPEG_2000:
-         SourceEssenceContainerLabel = WrappingUL_Data_JPEG_2000;
-          break;
-           
-       case ESS_PCM_24b_48k:
-         SourceEssenceContainerLabel = WrappingUL_Data_PCM_24b_48k;
-          break;
-         
-       default:
-         return RESULT_RAW_ESS;
-       }
-    }
+//
+void
+ASDCP::h__Writer::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));
 
-  mem_ptr<UL> WrappingUL;
-  switch ( EssenceType )
-    {
-    case ESS_MPEG2_VES:
-      WrappingUL = new UL(WrappingUL_Data_MPEG2_VES); // memchk TESTED
-      break;
-        
-    case ESS_JPEG_2000:
-      WrappingUL = new UL(WrappingUL_Data_JPEG_2000); // memchk TESTED
-      break;
-        
-    case ESS_PCM_24b_48k:
-      WrappingUL = new UL(WrappingUL_Data_PCM_24b_48k); // memchk TESTED
-      break;
-      
-    default:
-      return RESULT_RAW_ESS;
-    }
-  assert(!WrappingUL.empty());
-  m_EssenceDescriptor->SetValue("EssenceContainer", DataChunk(klv_key_size, WrappingUL->GetValue()));
   
-  // Write a File Descriptor only on the internally ref'ed Track 
-  m_EssenceDescriptor->SetUint("LinkedTrackID", m_FPTrack->GetUint("TrackID"));
-  m_FilePackage->AddChild("Descriptor")->MakeLink(*m_EssenceDescriptor);
+  //
+  // 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";
+
+  m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration));
+  m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
+}
 
-  UL CryptEssenceUL(WrappingUL_Data_Crypt);
+//
+void
+ASDCP::h__Writer::AddEssenceDescriptor(const UL& WrappingUL)
+{
+  //
+  // Essence Descriptor
+  //
+  m_EssenceDescriptor->EssenceContainer = WrappingUL;
+  m_HeaderPart.m_Preface->PrimaryPackage = m_FilePackage->InstanceUID;
+
+  //
+  // Essence Descriptors
+  //
+  assert(m_Dict);
+  UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
+  m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
 
   if ( m_Info.EncryptedEssence )
     {
-      m_Metadata->AddEssenceType(CryptEssenceUL);
+      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);
     }
   else
     {
-      UL GCUL(GCMulti_Data);
-      m_Metadata->AddEssenceType(GCUL);
-      m_Metadata->AddEssenceType(*WrappingUL);
+      m_HeaderPart.EssenceContainers.push_back(WrappingUL);
     }
 
-  // Link the MP to the FP
-  m_MPClip->MakeLink(m_FPTrack, 0);
+  m_HeaderPart.m_Preface->EssenceContainers = m_HeaderPart.EssenceContainers;
+  m_HeaderPart.AddChildObject(m_EssenceDescriptor);
 
-  //
-  // ** Write out the header **
-  //
+  std::list<InterchangeObject*>::iterator sdli = m_EssenceSubDescriptorList.begin();
+  for ( ; sdli != m_EssenceSubDescriptorList.end(); sdli++ )
+      m_HeaderPart.AddChildObject(*sdli);
 
-  m_HeaderPart = new Partition("OpenHeader");
-  assert(m_HeaderPart);
-  m_HeaderPart->SetKAG(1);                     // Everything else can stay at default
-  m_HeaderPart->SetUint("BodySID", 1);
-  
-  m_HeaderPart->AddMetadata(m_Metadata);
-
-  // Build an Ident set describing us and link into the metadata
-  MDObject* Ident = new MDObject("Identification");
-  assert(Ident);
-  Ident->SetString("CompanyName", m_Info.CompanyName);
-  Ident->SetString("ProductName", m_Info.ProductName);
-  Ident->SetString("VersionString", m_Info.ProductVersion);
-  UUID ProductUID(m_Info.ProductUUID);
-  Ident->SetValue("ProductUID", DataChunk(UUIDlen, ProductUID.GetValue()));
-
-  // TODO: get Oliver to show me how this works
-  //      Ident->SetString("ToolkitVersion", ?);
-
-  m_Metadata->UpdateGenerations(*Ident);
+  m_FilePackage->Descriptor = m_EssenceDescriptor->InstanceUID;
+}
 
-  if ( m_Info.EncryptedEssence )
-    AddDMScrypt(m_FilePackage, m_Info, SourceEssenceContainerLabel);
+//
+Result_t
+ASDCP::h__Writer::CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit)
+{
+  assert(m_Dict);
+  Result_t result = RESULT_OK;
 
-  // Write the header partition
-  m_File->WritePartition(*m_HeaderPart, HeaderPadding);
+  // create a body partition if we're writing proper 429-3/OP-Atom
+  if ( m_Info.LabelSetType == LS_MXF_SMPTE )
+    {
+      // Body Partition
+      m_BodyPart.EssenceContainers = m_HeaderPart.EssenceContainers;
+      m_BodyPart.ThisPartition = m_File.Tell();
+      m_BodyPart.BodySID = 1;
+      UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
+      m_BodyPart.OperationalPattern = OPAtomUL;
+      m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
       
-  // set up the index
-  switch ( EssenceType )
+      UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
+      result = m_BodyPart.WriteToFile(m_File, BodyUL);
+    }
+  else
+    {
+      m_HeaderPart.BodySID = 1;
+    }
+
+  if ( ASDCP_SUCCESS(result) )
     {
-    case ESS_MPEG2_VES:
-    case ESS_JPEG_2000:
-      m_IndexMan = new IndexManager(0, 0);
-      m_IndexMan->SetPosTableIndex(0, -1);
-      break;
-
-    case ESS_PCM_24b_48k:
-      m_IndexMan = new IndexManager(0, BytesPerEditUnit);
-      break;
-
-    case ESS_UNKNOWN:
-      return RESULT_INIT;
+      // Index setup
+      Kumu::fpos_t ECoffset = m_File.Tell();
+      m_FooterPart.IndexSID = 129;
+
+      if ( BytesPerEditUnit == 0 )
+       m_FooterPart.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
+      else
+       m_FooterPart.SetIndexParamsCBR(&m_HeaderPart.m_Primer, BytesPerEditUnit, EditRate);
     }
 
-  m_IndexMan->SetBodySID(1);
-  m_IndexMan->SetIndexSID(129);
-  m_IndexMan->SetEditRate(EditRate_);
+  return result;
+}
+
+//
+Result_t
+ASDCP::h__Writer::WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
+                                const std::string& TrackName, const UL& DataDefinition,
+                                const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
+{
+  InitHeader();
+  AddSourceClip(EditRate, TCFrameRate, TrackName, DataDefinition, PackageLabel);
+  AddEssenceDescriptor(WrappingUL);
+
+  Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
+
+  if ( KM_SUCCESS(result) )
+    result = CreateBodyPart(EditRate, BytesPerEditUnit);
 
-  return RESULT_OK;
+  return result;
 }
 
+
 // standard method of writing a plaintext or encrypted frame
 Result_t
 ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
                                  AESEncContext* Ctx, HMACContext* HMAC)
 {
-  Result_t result;
+  Result_t result = RESULT_OK;
   IntegrityPack IntPack;
 
   byte_t overhead[128];
-  MemIOWriter Overhead(overhead, 128);
+  Kumu::MemIOWriter Overhead(overhead, 128);
+  assert(m_Dict);
 
   if ( FrameBuf.Size() == 0 )
     {
@@ -285,7 +523,7 @@ ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte
 
       if ( ASDCP_SUCCESS(result) )
        { // write UL
-         Overhead.WriteRaw((byte_t*)CryptEssenceUL_Data, klv_key_size);
+         Overhead.WriteRaw(m_Dict->ul(MDD_CryptEssence), SMPTE_UL_LENGTH);
 
          // construct encrypted triplet header
          ui32_t ETLength = klv_cryptinfo_size + m_CtFrameBuf.Size();
@@ -293,27 +531,27 @@ ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte
          if ( m_Info.UsesHMAC )
            ETLength += klv_intpack_size;
          else
-           ETLength += (klv_length_size * 3); // for empty intpack
+           ETLength += (MXF_BER_LENGTH * 3); // for empty intpack
 
-         Overhead.WriteBER(ETLength, klv_length_size);                  // write encrypted triplet length
-         Overhead.WriteBER(UUIDlen, klv_length_size);                   // write ContextID length
+         Overhead.WriteBER(ETLength, MXF_BER_LENGTH);                  // write encrypted triplet length
+         Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH);                   // write ContextID length
          Overhead.WriteRaw(m_Info.ContextID, UUIDlen);                  // write ContextID
-         Overhead.WriteBER(sizeof(ui64_t), klv_length_size);            // write PlaintextOffset length
+         Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write PlaintextOffset length
          Overhead.WriteUi64BE(FrameBuf.PlaintextOffset());              // write PlaintextOffset
-         Overhead.WriteBER(klv_key_size, klv_length_size);              // write essence UL length
-         Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size);           // write the essence UL
-         Overhead.WriteBER(sizeof(ui64_t), klv_length_size);            // write SourceLength length
+         Overhead.WriteBER(SMPTE_UL_LENGTH, MXF_BER_LENGTH);              // write essence UL length
+         Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);           // write the essence UL
+         Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH);            // write SourceLength length
          Overhead.WriteUi64BE(FrameBuf.Size());                         // write SourceLength
-         Overhead.WriteBER(m_CtFrameBuf.Size(), klv_length_size);       // write ESV length
+         Overhead.WriteBER(m_CtFrameBuf.Size(), MXF_BER_LENGTH);       // write ESV length
 
-         result = m_File->Writev(Overhead.Data(), Overhead.Size());
+         result = m_File.Writev(Overhead.Data(), Overhead.Length());
        }
 
       if ( ASDCP_SUCCESS(result) )
        {
-         m_StreamOffset += Overhead.Size();
+         m_StreamOffset += Overhead.Length();
          // write encrypted source value
-         result = m_File->Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
+         result = m_File.Writev((byte_t*)m_CtFrameBuf.RoData(), m_CtFrameBuf.Size());
        }
 
       if ( ASDCP_SUCCESS(result) )
@@ -321,7 +559,7 @@ ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte
          m_StreamOffset += m_CtFrameBuf.Size();
 
          byte_t hmoverhead[512];
-         MemIOWriter HMACOverhead(hmoverhead, 512);
+         Kumu::MemIOWriter HMACOverhead(hmoverhead, 512);
 
          // write the HMAC
          if ( m_Info.UsesHMAC )
@@ -331,29 +569,29 @@ ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte
          else
            { // we still need the var-pack length values if the intpack is empty
              for ( ui32_t i = 0; i < 3 ; i++ )
-               HMACOverhead.WriteBER(0, klv_length_size);
+               HMACOverhead.WriteBER(0, MXF_BER_LENGTH);
            }
 
          // write HMAC
-         result = m_File->Writev(HMACOverhead.Data(), HMACOverhead.Size());
-         m_StreamOffset += HMACOverhead.Size();
+         result = m_File.Writev(HMACOverhead.Data(), HMACOverhead.Length());
+         m_StreamOffset += HMACOverhead.Length();
        }
     }
   else
     {
-      Overhead.WriteRaw((byte_t*)EssenceUL, klv_key_size);
-      Overhead.WriteBER(FrameBuf.Size(), klv_length_size);
-      result = m_File->Writev(Overhead.Data(), Overhead.Size());
+      Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
+      Overhead.WriteBER(FrameBuf.Size(), MXF_BER_LENGTH);
+      result = m_File.Writev(Overhead.Data(), Overhead.Length());
  
       if ( ASDCP_SUCCESS(result) )
-       result = m_File->Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
+       result = m_File.Writev((byte_t*)FrameBuf.RoData(), FrameBuf.Size());
 
       if ( ASDCP_SUCCESS(result) )
-       m_StreamOffset += Overhead.Size() + FrameBuf.Size();
+       m_StreamOffset += Overhead.Length() + FrameBuf.Size();
     }
 
   if ( ASDCP_SUCCESS(result) )
-    result = m_File->Writev();
+    result = m_File.Writev();
 
   return result;
 }
@@ -362,95 +600,49 @@ ASDCP::h__Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf, const byte
 // standard method of writing the header and footer of a completed MXF file
 //
 Result_t
-ASDCP::h__Writer::WriteMXFFooter(EssenceType_t EssenceType)
+ASDCP::h__Writer::WriteMXFFooter()
 {
-  // write the index
-  DataChunk IndexChunk;
-  ui32_t IndexSID = 0;
-
-  // Find all essence container data sets so we can update "IndexSID"
-  MDObjectListPtr ECDataSets = 0;
-  MDObject* Ptr = (*m_Metadata)["ContentStorage"];
-  if ( Ptr )
-    Ptr = Ptr->GetLink();
-  if ( Ptr )
-    Ptr = (*Ptr)["EssenceContainerData"];
-  if ( Ptr )
-    ECDataSets = Ptr->ChildList("EssenceContainer");
-
-  // ** Handle full index tables next **
-  // ***********************************
-      
-  // Make an index table containing all available entries
-  IndexTablePtr Index = m_IndexMan->MakeIndex();
-  m_IndexMan->AddEntriesToIndex(Index);
-      
-  // Write the index table
-  Index->WriteIndex(IndexChunk);
-                               
-  // Record the IndexSID for when the index is written
-  IndexSID = Index->IndexSID;
-
-  // Update IndexSID in essence container data set
-  MDObjectList::iterator ECD_it = ECDataSets->begin();
-  while(ECD_it != ECDataSets->end())
-    {
-      if((*ECD_it)->GetLink())
-       {
-         if((*ECD_it)->GetLink()->GetUint("BodySID") == m_IndexMan->GetBodySID())
-           {
-             (*ECD_it)->GetLink()->SetUint("IndexSID", m_IndexMan->GetIndexSID());
-             break;
-           }
-       }
+  // Set top-level file package correctly for OP-Atom
 
-      ECD_it++;
-    }
+  //  m_MPTCSequence->Duration = m_MPTimecode->Duration = m_MPClSequence->Duration = m_MPClip->Duration = 
+  //    m_FPTCSequence->Duration = m_FPTimecode->Duration = m_FPClSequence->Duration = m_FPClip->Duration = 
 
-  // If we are writing OP-Atom this is the first place we can claim it
-  m_Metadata->SetOP(OPAtomUL);
-  
-  // Set top-level file package correctly for OP-Atom
-  m_Metadata->SetPrimaryPackage(m_FilePackage);
-  
-  m_Metadata->SetTime();
-  m_MPTimecode->SetDuration(m_FramesWritten);
+  DurationElementList_t::iterator dli = m_DurationUpdateList.begin();
 
-  m_MPClip->SetDuration(m_FramesWritten);
-  m_FPTimecode->SetDuration(m_FramesWritten);
-  m_FPClip->SetDuration(m_FramesWritten);
-  m_EssenceDescriptor->SetInt64("ContainerDuration", m_FramesWritten);
+  for (; dli != m_DurationUpdateList.end(); dli++ )
+    **dli = m_FramesWritten;
 
-  // Turn the header or body partition into a footer
-  m_HeaderPart->ChangeType("CompleteFooter");
-  m_HeaderPart->SetUint("IndexSID",  IndexSID);
+  m_EssenceDescriptor->ContainerDuration = m_FramesWritten;
+  m_FooterPart.PreviousPartition = m_HeaderPart.m_RIP.PairArray.back().ByteOffset;
 
-  // Make sure any new sets are linked in
-  m_HeaderPart->UpdateMetadata(m_Metadata);
+  Kumu::fpos_t here = m_File.Tell();
+  m_HeaderPart.m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
+  m_HeaderPart.FooterPartition = here;
 
-  // Actually write the footer
-  m_File->WritePartitionWithIndex(*m_HeaderPart, &IndexChunk, false);
+  assert(m_Dict);
+  // re-label the partition
+  UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
+  m_HeaderPart.OperationalPattern = OPAtomUL;
+  m_HeaderPart.m_Preface->OperationalPattern = m_HeaderPart.OperationalPattern;
 
-  // Add a RIP
-  m_File->WriteRIP();
+  m_FooterPart.OperationalPattern = m_HeaderPart.OperationalPattern;
+  m_FooterPart.EssenceContainers = m_HeaderPart.EssenceContainers;
+  m_FooterPart.FooterPartition = here;
+  m_FooterPart.ThisPartition = here;
 
-  //
-  // ** Update the header ** 
-  //
-  // For generalized OPs update the value of "FooterPartition" in the header pack
-  // For OP-Atom re-write the entire header
-  //
-  ASDCP::fpos_t FooterPos = m_HeaderPart->GetUint64("FooterPartition");
-  m_File->Seek(0);
+  Result_t result = m_FooterPart.WriteToFile(m_File, m_FramesWritten);
 
-  m_HeaderPart->ChangeType("ClosedCompleteHeader");
-  m_HeaderPart->SetUint64("FooterPartition", FooterPos);
-  m_HeaderPart->SetUint64("BodySID", 1);
+  if ( ASDCP_SUCCESS(result) )
+    result = m_HeaderPart.m_RIP.WriteToFile(m_File);
 
-  m_File->ReWritePartition(*m_HeaderPart);
-  m_File->Close();
-  
-  return RESULT_OK;
+  if ( ASDCP_SUCCESS(result) )
+    result = m_File.Seek(0);
+
+  if ( ASDCP_SUCCESS(result) )
+    result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
+
+  m_File.Close();
+  return result;
 }
 
 //