Modified to enforce execution order of the predicates
[asdcplib.git] / src / h__Writer.cpp
index e05cadb3aeca4b7b15c48591244cc0ae3b5c2e60..71e9ce63bc268897cbd1fe1b712b2424738b77a5 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
@@ -39,13 +39,13 @@ using namespace ASDCP::MXF;
 ui32_t
 ASDCP::derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate)
 {
-  return floor(0.5 + edit_rate.Quotient());
+  return (ui32_t)floor(0.5 + edit_rate.Quotient());
 }
 
 //
 // add DMS CryptographicFramework entry to source package
 void
-ASDCP::AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
+ASDCP::AddDmsCrypt(Partition& HeaderPart, SourcePackage& Package,
                   WriterInfo& Descr, const UL& WrappingUL, const Dictionary*& Dict)
 {
   assert(Dict);
@@ -65,7 +65,8 @@ ASDCP::AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
   HeaderPart.AddChildObject(Segment);
   Seq->StructuralComponents.push_back(Segment->InstanceUID);
   Segment->EventComment = "AS-DCP KLV Encryption";
-  
+  Segment->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
+
   CryptographicFramework* CFW = new CryptographicFramework(Dict);
   HeaderPart.AddChildObject(CFW);
   Segment->DMFramework = CFW->InstanceUID;
@@ -81,7 +82,134 @@ ASDCP::AddDMScrypt(Partition& HeaderPart, SourcePackage& Package,
   Context->CryptographicKeyID.Set(Descr.CryptographicKeyID);
 }
 
+static std::string const rp2057_static_track_label = "SMPTE RP 2057 Generic Stream Text-Based Set";
+
+//
+static bool
+id_batch_contains(const Array<Kumu::UUID>& batch, const Kumu::UUID& value)
+{
+  Array<Kumu::UUID>::const_iterator i;
+  for ( i = batch.begin(); i != batch.end(); ++i )
+    {
+      if ( *i == value )
+       {
+         return true;
+       }
+    }
+  return false;
+}
+
+//
+Result_t
+ASDCP::AddDmsTrackGenericPartUtf8Text(Kumu::FileWriter& file_writer, MXF::OP1aHeader& header_part,
+                                     SourcePackage& source_package, MXF::RIP& rip, const Dictionary*& Dict)
+{
+  Sequence* Sequence_obj = 0;
+  InterchangeObject* tmp_iobj = 0;
+  std::list<InterchangeObject*> object_list;
+
+  // get the SourcePackage else die
+  header_part.GetMDObjectByType(Dict->ul(MDD_SourcePackage), &tmp_iobj);
+  SourcePackage *SourcePackage_obj = dynamic_cast<SourcePackage*>(tmp_iobj);
+  if ( SourcePackage_obj == 0 )
+    {
+      DefaultLogSink().Error("MXF Metadata contains no SourcePackage Set.\n");
+      return RESULT_FORMAT;
+    }
+
+  // find the first StaticTrack object, having the right label, that is ref'd by the source package
+  StaticTrack *StaticTrack_obj = 0;
+  header_part.GetMDObjectsByType(Dict->ul(MDD_StaticTrack), object_list);
+  std::list<InterchangeObject*>::iterator j;
+  for ( j = object_list.begin(); j != object_list.end(); ++j )
+    {
+      StaticTrack_obj = dynamic_cast<StaticTrack*>(*j);
+      assert(StaticTrack_obj);
+      if ( id_batch_contains(SourcePackage_obj->Tracks, StaticTrack_obj->InstanceUID)
+          && StaticTrack_obj->TrackName.get() == rp2057_static_track_label )
+       {
+         break;
+       }
+      StaticTrack_obj = 0;
+    }
+
+  // find the Sequence associated with this Track
+  if ( StaticTrack_obj )
+    {
+      object_list.clear();
+      header_part.GetMDObjectsByType(Dict->ul(MDD_Sequence), object_list);
+      for ( j = object_list.begin(); j != object_list.end(); ++j )
+       {
+         Sequence_obj = dynamic_cast<Sequence*>(*j);
+         assert(Sequence_obj);
+         if ( Sequence_obj->InstanceUID == StaticTrack_obj->Sequence )
+           {
+             break;
+           }
+         Sequence_obj = 0;
+       }
+    }
+
+  if ( Sequence_obj == 0 )
+    {
+      // this is the first insertion, create the static track
+      assert(Dict);
+      StaticTrack* static_track = new StaticTrack(Dict);
+      header_part.AddChildObject(static_track);
+      source_package.Tracks.push_back(static_track->InstanceUID);
+      static_track->TrackName = "Descriptive Track";
+      static_track->TrackID = 4;
+
+      Sequence_obj = new Sequence(Dict);
+      header_part.AddChildObject(Sequence_obj);
+      static_track->Sequence = Sequence_obj->InstanceUID;
+      Sequence_obj->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
+      header_part.m_Preface->DMSchemes.push_back(UL(Dict->ul(MDD_MXFTextBasedFramework)));
+    }
+
+  assert(Sequence_obj);
+  // Create the DM segment and framework packs
+  DMSegment* Segment = new DMSegment(Dict);
+  header_part.AddChildObject(Segment);
+  Sequence_obj->StructuralComponents.push_back(Segment->InstanceUID);
+  Segment->EventComment = rp2057_static_track_label;
+  Segment->DataDefinition = UL(Dict->ul(MDD_DescriptiveMetaDataDef));
+
+  //
+  TextBasedDMFramework *dmf_obj = new TextBasedDMFramework(Dict);
+  assert(dmf_obj);
+  header_part.AddChildObject(dmf_obj);
+  Segment->DMFramework = dmf_obj->InstanceUID;
+  GenRandomValue(dmf_obj->ObjectRef);
+
+  // Create a new SID on the RIP, located at the current file position
+  ui32_t max_sid = 0;
+  ASDCP::MXF::RIP::pair_iterator i;
+  for ( i = rip.PairArray.begin(); i != rip.PairArray.end(); ++i )
+    {
+      if ( max_sid < i->BodySID )
+       {
+         max_sid = i->BodySID;
+       }
+    }
 
+  if ( max_sid == 0 )
+    {
+      DefaultLogSink().Error("Unable to add a GS Partition before the essence container has been established.\n");
+      return RESULT_FORMAT;
+    }
+
+  rip.PairArray.push_back(RIP::PartitionPair(max_sid + 1, file_writer.Tell()));
+
+  // Add new GSTBS linked to DMF
+  GenericStreamTextBasedSet *gst_obj = new GenericStreamTextBasedSet(Dict);
+  header_part.AddChildObject(gst_obj);
+  gst_obj->InstanceUID = dmf_obj->ObjectRef;
+  gst_obj->GenericStreamSID = max_sid + 1;
+  gst_obj->PayloadSchemeID = UL(Dict->ul(MDD_MXFTextBasedFramework));
+  
+  return RESULT_OK;
+}
 
 //
 ASDCP::h__ASDCPWriter::h__ASDCPWriter(const Dictionary& d) :
@@ -106,7 +234,7 @@ ASDCP::h__ASDCPWriter::CreateBodyPart(const MXF::Rational& EditRate, ui32_t Byte
       m_BodyPart.BodySID = 1;
       UL OPAtomUL(m_Dict->ul(MDD_OPAtom));
       m_BodyPart.OperationalPattern = OPAtomUL;
-      m_RIP.PairArray.push_back(RIP::Pair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
+      m_RIP.PairArray.push_back(RIP::PartitionPair(1, m_BodyPart.ThisPartition)); // Second RIP Entry
       
       UL BodyUL(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
       result = m_BodyPart.WriteToFile(m_File, BodyUL);
@@ -141,22 +269,27 @@ ASDCP::h__ASDCPWriter::WriteASDCPHeader(const std::string& PackageLabel, const U
                                        const std::string& TrackName, const UL& EssenceUL, const UL& DataDefinition,
                                        const MXF::Rational& EditRate, ui32_t TCFrameRate, ui32_t BytesPerEditUnit)
 {
-  InitHeader();
+  InitHeader(MXFVersion_2004);
 
   // First RIP Entry
   if ( m_Info.LabelSetType == LS_MXF_SMPTE )  // ERK
     {
-      m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // 3-part, no essence in header
+      m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // 3-part, no essence in header
     }
   else
     {
-      m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
+      m_RIP.PairArray.push_back(RIP::PartitionPair(1, 0)); // 2-part, essence in header
     }
 
   // timecode rate and essence rate are the same
   AddSourceClip(EditRate, EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
   AddEssenceDescriptor(WrappingUL);
 
+#ifdef ASDCP_GCMULTI_PATCH
+  UL GenericContainerUL(m_Dict->ul(MDD_GCMulti));
+  m_HeaderPart.EssenceContainers.push_back(GenericContainerUL);
+#endif
+
   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
 
   if ( KM_SUCCESS(result) )
@@ -168,10 +301,12 @@ ASDCP::h__ASDCPWriter::WriteASDCPHeader(const std::string& PackageLabel, const U
 //
 Result_t
 ASDCP::h__ASDCPWriter::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
+                                      const ui32_t& MinEssenceElementBerLength,               
                                       AESEncContext* Ctx, HMACContext* HMAC)
 {
   return Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
-                          m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
+                          m_StreamOffset, FrameBuf, EssenceUL, MinEssenceElementBerLength,
+                          Ctx, HMAC);
 }
 
 // standard method of writing the header and footer of a completed MXF file
@@ -191,7 +326,7 @@ ASDCP::h__ASDCPWriter::WriteASDCPFooter()
   m_FooterPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
 
   Kumu::fpos_t here = m_File.Tell();
-  m_RIP.PairArray.push_back(RIP::Pair(0, here)); // Last RIP Entry
+  m_RIP.PairArray.push_back(RIP::PartitionPair(0, here)); // Last RIP Entry
   m_HeaderPart.FooterPartition = here;
 
   assert(m_Dict);
@@ -230,6 +365,7 @@ Result_t
 ASDCP::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)
 {
   Result_t result = RESULT_OK;
@@ -268,7 +404,7 @@ ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict,
 
          // construct encrypted triplet header
          ui32_t ETLength = klv_cryptinfo_size + CtFrameBuf.Size();
-         ui32_t BER_length = MXF_BER_LENGTH;
+         ui32_t essence_element_BER_length = MinEssenceElementBerLength;
 
          if ( Info.UsesHMAC )
            ETLength += klv_intpack_size;
@@ -277,18 +413,18 @@ ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict,
 
          if ( ETLength > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
            {
-             BER_length = Kumu::get_BER_length_for_value(ETLength);
+             essence_element_BER_length = Kumu::get_BER_length_for_value(ETLength);
 
              // the packet is longer by the difference in expected vs. actual BER length
-             ETLength += BER_length - MXF_BER_LENGTH;
+             ETLength += essence_element_BER_length - MXF_BER_LENGTH;
 
-             if ( BER_length == 0 )
+             if ( essence_element_BER_length == 0 )
                result = RESULT_KLV_CODING;
            }
 
          if ( ASDCP_SUCCESS(result) )
            {
-             if ( ! ( Overhead.WriteBER(ETLength, BER_length)                      // write encrypted triplet length
+             if ( ! ( Overhead.WriteBER(ETLength, essence_element_BER_length)                      // write encrypted triplet length
                       && Overhead.WriteBER(UUIDlen, MXF_BER_LENGTH)                // write ContextID length
                       && Overhead.WriteRaw(Info.ContextID, UUIDlen)              // write ContextID
                       && Overhead.WriteBER(sizeof(ui64_t), MXF_BER_LENGTH)         // write PlaintextOffset length
@@ -297,7 +433,7 @@ ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict,
                       && 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(CtFrameBuf.Size(), BER_length) ) )    // write ESV length
+                      && Overhead.WriteBER(CtFrameBuf.Size(), essence_element_BER_length) ) )    // write ESV length
                {
                  result = RESULT_KLV_CODING;
                }
@@ -339,18 +475,18 @@ ASDCP::Write_EKLV_Packet(Kumu::FileWriter& File, const ASDCP::Dictionary& Dict,
     }
   else
     {
-      ui32_t BER_length = MXF_BER_LENGTH;
+      ui32_t essence_element_BER_length = MinEssenceElementBerLength;
 
       if ( FrameBuf.Size() > 0x00ffffff ) // Need BER integer longer than MXF_BER_LENGTH bytes
        {
-         BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
+         essence_element_BER_length = Kumu::get_BER_length_for_value(FrameBuf.Size());
 
-         if ( BER_length == 0 )
+         if ( essence_element_BER_length == 0 )
            result = RESULT_KLV_CODING;
        }
 
       Overhead.WriteRaw((byte_t*)EssenceUL, SMPTE_UL_LENGTH);
-      Overhead.WriteBER(FrameBuf.Size(), BER_length);
+      Overhead.WriteBER(FrameBuf.Size(), essence_element_BER_length);
 
       if ( ASDCP_SUCCESS(result) )
        result = File.Writev(Overhead.Data(), Overhead.Length());