tweezes
[asdcplib.git] / src / h__02_Writer.cpp
index 598431b8c833499f92e99d8527c9f8248367a52c..a121142c9dbc9c03bad7ec3bf63723af1109a344 100644 (file)
@@ -1,28 +1,30 @@
 /*
-  Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
-  All rights reserved.
-
-  Redistribution and use in source and binary forms, with or without
-  modification, are permitted provided that the following conditions
-  are met:
-  1. Redistributions of source code must retain the above copyright
-  notice, this list of conditions and the following disclaimer.
-  2. Redistributions in binary form must reproduce the above copyright
-  notice, this list of conditions and the following disclaimer in the
-  documentation and/or other materials provided with the distribution.
-  3. The name of the author may not be used to endorse or promote products
-  derived from this software without specific prior written permission.
-
-  THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
-  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
-  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS,
+John Hurst
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions
+are met:
+1. Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+3. The name of the author may not be used to endorse or promote products
+   derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */ 
 /*! \file    h__02_Writer.cpp
   \version $Id$
@@ -40,96 +42,79 @@ static const ui32_t CBRIndexEntriesPerSegment = 5000;
 //------------------------------------------------------------------------------------------
 //
 
-AS_02::AS02IndexWriter::AS02IndexWriter(const ASDCP::Dictionary*& d) :
-  Partition(d), m_CurrentSegment(0), m_BytesPerEditUnit(0), m_Dict(d), m_ECOffset(0), m_Lookup(0)
+AS_02::MXF::AS02IndexWriter::AS02IndexWriter(const ASDCP::Dictionary*& d) :
+  Partition(d), m_CurrentSegment(0), m_BytesPerEditUnit(0), m_Dict(d), m_Lookup(0)
 {
   BodySID = 0;
   IndexSID = 129;
 }
 
-AS_02::AS02IndexWriter::~AS02IndexWriter() {}
+AS_02::MXF::AS02IndexWriter::~AS02IndexWriter() {}
 
 //
 Result_t
-AS_02::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
+AS_02::MXF::AS02IndexWriter::WriteToFile(Kumu::FileWriter& Writer)
 {
-  //      UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
-
   assert(m_Dict);
-  ASDCP::FrameBuffer FooterBuffer;
-  ui32_t   footer_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
-  Result_t result = FooterBuffer.Capacity(footer_size); 
-  ui32_t   iseg_count = 0;
+  ASDCP::FrameBuffer index_body_buffer;
+  ui32_t   index_body_size = m_PacketList->m_List.size() * MaxIndexSegmentSize; // segment-count * max-segment-size
+  Result_t result = index_body_buffer.Capacity(index_body_size); 
+  ui64_t start_position = 0;
 
   if ( m_CurrentSegment != 0 )
     {
       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
+      start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
       m_CurrentSegment = 0;
     }
 
   std::list<InterchangeObject*>::iterator pl_i = m_PacketList->m_List.begin();
-  for ( ; pl_i != m_PacketList->m_List.end() && ASDCP_SUCCESS(result); pl_i++ )
+  for ( ; pl_i != m_PacketList->m_List.end() && KM_SUCCESS(result); pl_i++ )
     {
-      if ( (*pl_i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
-       {
-         iseg_count++;
-         IndexTableSegment* Segment = (IndexTableSegment*)(*pl_i);
-
-         if ( m_BytesPerEditUnit != 0 )
-           {
-             if ( iseg_count != 1 )
-               return RESULT_STATE;
-
-             ///             Segment->IndexDuration = duration;
-           }
-       }
-
       InterchangeObject* object = *pl_i;
       object->m_Lookup = m_Lookup;
 
       ASDCP::FrameBuffer WriteWrapper;
-      WriteWrapper.SetData(FooterBuffer.Data() + FooterBuffer.Size(),
-                          FooterBuffer.Capacity() - FooterBuffer.Size());
+      WriteWrapper.SetData(index_body_buffer.Data() + index_body_buffer.Size(),
+                          index_body_buffer.Capacity() - index_body_buffer.Size());
       result = object->WriteToBuffer(WriteWrapper);
-      FooterBuffer.Size(FooterBuffer.Size() + WriteWrapper.Size());
+      index_body_buffer.Size(index_body_buffer.Size() + WriteWrapper.Size());
+      delete *pl_i;
+      *pl_i = 0;
     }
 
-  if ( ASDCP_SUCCESS(result) )
+  m_PacketList->m_List.clear();
+
+  if ( KM_SUCCESS(result) )
     {
-      IndexByteCount = FooterBuffer.Size();
+      IndexByteCount = index_body_buffer.Size();
       UL body_ul(m_Dict->ul(MDD_ClosedCompleteBodyPartition));
       result = Partition::WriteToFile(Writer, body_ul);
     }
 
-  if ( ASDCP_SUCCESS(result) )
+  if ( KM_SUCCESS(result) )
     {
       ui32_t write_count = 0;
-      result = Writer.Write(FooterBuffer.RoData(), FooterBuffer.Size(), &write_count);
-      assert(write_count == FooterBuffer.Size());
+      result = Writer.Write(index_body_buffer.RoData(), index_body_buffer.Size(), &write_count);
+      assert(write_count == index_body_buffer.Size());
     }
 
-  return result;
-}
-
-//
-void
-AS_02::AS02IndexWriter::ResetCBR(Kumu::fpos_t offset)
-{
-  m_ECOffset = offset;
-
-  std::list<InterchangeObject*>::iterator i;
-
-  for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
+  if ( KM_SUCCESS(result) )
     {
-      delete *i;
+      m_CurrentSegment = new IndexTableSegment(m_Dict);
+      assert(m_CurrentSegment);
+      AddChildObject(m_CurrentSegment);
+      m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
+      m_CurrentSegment->IndexEditRate = m_EditRate;
+      m_CurrentSegment->IndexStartPosition = start_position;
     }
 
-  m_PacketList->m_List.clear();
+  return result;
 }
 
 //
 void
-AS_02::AS02IndexWriter::Dump(FILE* stream)
+AS_02::MXF::AS02IndexWriter::Dump(FILE* stream)
 {
   if ( stream == 0 )
     stream = stderr;
@@ -143,7 +128,7 @@ AS_02::AS02IndexWriter::Dump(FILE* stream)
 
 //
 ui32_t
-AS_02::AS02IndexWriter::GetDuration() const
+AS_02::MXF::AS02IndexWriter::GetDuration() const
 {
   ui32_t duration;
   std::list<InterchangeObject*>::const_iterator i;
@@ -162,7 +147,7 @@ AS_02::AS02IndexWriter::GetDuration() const
 
 //
 void
-AS_02::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
+AS_02::MXF::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate)
 {
   assert(lookup);
   m_Lookup = lookup;
@@ -177,18 +162,17 @@ AS_02::AS02IndexWriter::SetIndexParamsCBR(IPrimerLookup* lookup, ui32_t size, co
 
 //
 void
-AS_02::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
+AS_02::MXF::AS02IndexWriter::SetIndexParamsVBR(IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset)
 {
   assert(lookup);
   m_Lookup = lookup;
   m_BytesPerEditUnit = 0;
   m_EditRate = Rate;
-  m_ECOffset = offset;
 }
 
 //
 void
-AS_02::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
+AS_02::MXF::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entry)
 {
   if ( m_BytesPerEditUnit != 0 )  // are we CBR? that's bad 
     {
@@ -208,15 +192,16 @@ AS_02::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entr
     }
   else if ( m_CurrentSegment->IndexEntryArray.size() >= CBRIndexEntriesPerSegment )
     { // no, this one is full, start another
+      DefaultLogSink().Warn("%s, line %d: This has never been tested.\n", __FILE__, __LINE__);
       m_CurrentSegment->IndexDuration = m_CurrentSegment->IndexEntryArray.size();
-      ui64_t StartPosition = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
+      ui64_t start_position = m_CurrentSegment->IndexStartPosition + m_CurrentSegment->IndexDuration;
 
       m_CurrentSegment = new IndexTableSegment(m_Dict);
       assert(m_CurrentSegment);
       AddChildObject(m_CurrentSegment);
       m_CurrentSegment->DeltaEntryArray.push_back(IndexTableSegment::DeltaEntry());
       m_CurrentSegment->IndexEditRate = m_EditRate;
-      m_CurrentSegment->IndexStartPosition = StartPosition;
+      m_CurrentSegment->IndexStartPosition = start_position;
     }
 
   m_CurrentSegment->IndexEntryArray.push_back(Entry);
@@ -228,7 +213,7 @@ AS_02::AS02IndexWriter::PushIndexEntry(const IndexTableSegment::IndexEntry& Entr
 
 //
 AS_02::h__AS02Writer::h__AS02Writer(const ASDCP::Dictionary& d) : ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>(d),
-                                                                 m_IndexWriter(m_Dict), m_PartitionSpace(0),
+                                                                 m_ECStart(0), m_ClipStart(0), m_IndexWriter(m_Dict), m_PartitionSpace(0),
                                                                  m_IndexStrategy(AS_02::IS_FOLLOW) {}
 
 AS_02::h__AS02Writer::~h__AS02Writer() {}
@@ -247,7 +232,7 @@ AS_02::h__AS02Writer::WriteAS02Header(const std::string& PackageLabel, const ASD
 
   InitHeader();
 
-  AddSourceClip(EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
+  AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
   AddEssenceDescriptor(WrappingUL);
   m_RIP.PairArray.push_back(RIP::Pair(0, 0)); // Header partition RIP entry
   m_IndexWriter.OperationalPattern = m_HeaderPart.OperationalPattern;
@@ -258,12 +243,12 @@ AS_02::h__AS02Writer::WriteAS02Header(const std::string& PackageLabel, const ASD
   if ( ASDCP_SUCCESS(result) )
     {
       m_PartitionSpace *= ceil(EditRate.Quotient());  // convert seconds to edit units
-      Kumu::fpos_t ECoffset = m_File.Tell();
+      m_ECStart = m_File.Tell();
       m_IndexWriter.IndexSID = 129;
 
       if ( BytesPerEditUnit == 0 )
        {
-         m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, ECoffset);
+         m_IndexWriter.SetIndexParamsVBR(&m_HeaderPart.m_Primer, EditRate, m_ECStart);
        }
       else
        {
@@ -275,7 +260,7 @@ AS_02::h__AS02Writer::WriteAS02Header(const std::string& PackageLabel, const ASD
       body_part.BodySID = 1;
       body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
       body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
-      body_part.ThisPartition = m_File.Tell();
+      body_part.ThisPartition = m_ECStart;
       result = body_part.WriteToFile(m_File, body_ul);
       m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition)); // Second RIP Entry
     }
@@ -287,9 +272,18 @@ AS_02::h__AS02Writer::WriteAS02Header(const std::string& PackageLabel, const ASD
 Result_t
 AS_02::h__AS02Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
 {
+  ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
+
   Result_t result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
                                      m_StreamOffset, FrameBuf, EssenceUL, Ctx, HMAC);
 
+  if ( KM_SUCCESS(result) )
+    {  
+      IndexTableSegment::IndexEntry Entry;
+      Entry.StreamOffset = this_stream_offset;
+      m_IndexWriter.PushIndexEntry(Entry);
+    }
+
   if ( m_FramesWritten > 0 && ( m_FramesWritten % m_PartitionSpace ) == 0 )
     {
       m_IndexWriter.ThisPartition = m_File.Tell();
@@ -302,15 +296,86 @@ AS_02::h__AS02Writer::WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const b
       body_part.OperationalPattern = m_HeaderPart.OperationalPattern;
       body_part.EssenceContainers = m_HeaderPart.EssenceContainers;
       body_part.ThisPartition = m_File.Tell();
+
+      body_part.BodyOffset = m_StreamOffset;
       result = body_part.WriteToFile(m_File, body_ul);
       m_RIP.PairArray.push_back(RIP::Pair(1, body_part.ThisPartition));
-      m_IndexWriter.ResetCBR(m_File.Tell());
     }
 
   return result;
 }
 
-// standard method of writing the header and footer of a completed MXF file
+//
+bool
+AS_02::h__AS02Writer::HasOpenClip() const
+{
+  return m_ClipStart != 0;
+}
+
+//
+Result_t
+AS_02::h__AS02Writer::StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC)
+{
+  if ( Ctx != 0 )
+    {
+      DefaultLogSink().Error("Encryption not yet supported for PCM clip-wrap.\n");
+      return RESULT_STATE;
+    }
+
+  if ( m_ClipStart != 0 )
+    {
+      DefaultLogSink().Error("Cannot open clip, clip already open.\n");
+      return RESULT_STATE;
+    }
+
+  m_ClipStart = m_File.Tell();
+  byte_t clip_buffer[24] = {0};
+  memcpy(clip_buffer, EssenceUL, 16);
+  bool check = Kumu::write_BER(clip_buffer+16, 0, 8);
+  assert(check);
+  return m_File.Write(clip_buffer, 24);
+}
+
+//
+Result_t
+AS_02::h__AS02Writer::WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf)
+{
+  if ( m_ClipStart == 0 )
+    {
+      DefaultLogSink().Error("Cannot write clip block, no clip open.\n");
+      return RESULT_STATE;
+    }
+
+  return m_File.Write(FrameBuf.RoData(), FrameBuf.Size());
+}
+
+//
+Result_t
+AS_02::h__AS02Writer::FinalizeClip(ui32_t bytes_per_frame)
+{
+  if ( m_ClipStart == 0 )
+    {
+      DefaultLogSink().Error("Cannot close clip, clip not open.\n");
+      return RESULT_STATE;
+    }
+
+  ui64_t current_position = m_File.Tell();
+  Result_t result = m_File.Seek(m_ClipStart+16);
+
+  if ( ASDCP_SUCCESS(result) )
+    {
+      byte_t clip_buffer[8] = {0};
+      bool check = Kumu::write_BER(clip_buffer, m_FramesWritten * bytes_per_frame, 8);
+      assert(check);
+      result = m_File.Write(clip_buffer, 8);
+    }
+
+  m_File.Seek(current_position);
+  m_ClipStart = 0;
+  return result;
+}
+
+// standard method of writing the header and footer of a completed AS-02 file
 //
 Result_t
 AS_02::h__AS02Writer::WriteAS02Footer()