ReadFileIntoString() modified to return OK when the file is empty
[asdcplib.git] / src / AS_02_internal.h
index 15f4486ed92fe8af05a7335c89922847e8ed579b..4be764d8935a30907a0248ca18d6dcbfb90ca579 100644 (file)
@@ -1,28 +1,30 @@
 /*
-  Copyright (c) 2011-2012, 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-2018, 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    AS_02_internal.h
   \version $Id: AS_02_internal.h ***       
 #ifndef _AS_02_INTERNAL_H_
 #define _AS_02_INTERNAL_H_
 
-#include <KM_platform.h>
-#include <KM_util.h>
-#include <KM_log.h>
-#include "Metadata.h"
+#include "KM_log.h"
 #include "AS_DCP_internal.h"
 #include "AS_02.h"
 
 using Kumu::DefaultLogSink;
 
-using namespace ASDCP;
-using namespace ASDCP::MXF;
+#ifdef DEFAULT_02_MD_DECL
+AS_02::MXF::AS02IndexReader *g_AS02IndexReader;
+#else
+extern AS_02::MXF::AS02IndexReader *g_AS02IndexReader;
+#endif
+
 
 namespace AS_02
 {
-  static void CalculateIndexPartitionSize(ui32_t& size,ui32_t numberOfIndexEntries)
+
+  void default_md_object_init();
+
+
+  //
+  class h__AS02Reader : public ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>
+    {
+      ASDCP_NO_COPY_CONSTRUCT(h__AS02Reader);
+      h__AS02Reader();
+
+    public:
+      h__AS02Reader(const ASDCP::Dictionary&);
+      virtual ~h__AS02Reader();
+
+      Result_t OpenMXFRead(const std::string& filename);
+
+      // USE FRAME WRAPPING...
+      Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
+                            const byte_t* EssenceUL, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC);
+
+     // OR CLIP WRAPPING...
+      // clip wrapping is handled directly by the essence-specific classes
+      //      Result_t ReadyClip(const ui32_t& FrameNum, const byte_t* EssenceUL, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC, ui64_t& position);
+      ///      Result_t ReadClipBlock(ASDCP::FrameBuffer& FrameBuf, const ui32_t& read_size);
+
+      // NOT BOTH!
+    };
+
+
+  namespace MXF
   {
-    if(numberOfIndexEntries){
-      //Partition::ArchiveSize(); HeaderSize = 124 bytes
-      //KLV-Item = 20 bytes
-      //ItemSize IndexEntry = 11 bytes
-      //number of IndexEntries - parameter
-      //IndexEntryArray = 12 bytes
-      //size for other Elements(PosTableCount etc.) = 108 bytes
-      //see Index.cpp ASDCP::MXF::IndexTableSegment::WriteToTLVSet(TLVWriter& TLVSet) how this is computed 
-      size = 124 + 20 + 11 * numberOfIndexEntries + 12 +108;
-      size += 20;//because maybe we must fill the last partition, minimum required space for KLV-Item
-    }
-    else{
-      //Partition HeaderSize = 124 bytes
-      //KLV-Item = 20 bytes
-      //244 for 2 IndexTableSegments
-      size = 124 + 20 + 244;
-    }
+    //
+    class AS02IndexWriterVBR : public ASDCP::MXF::Partition
+      {
+       ASDCP::MXF::IndexTableSegment*  m_CurrentSegment;
+       ASDCP::MXF::Rational m_EditRate;
+
+       KM_NO_COPY_CONSTRUCT(AS02IndexWriterVBR);
+       AS02IndexWriterVBR();
+
+      public:
+       const ASDCP::Dictionary*&  m_Dict;
+       ASDCP::IPrimerLookup*      m_Lookup;
+      
+       AS02IndexWriterVBR(const ASDCP::Dictionary*&);
+       virtual ~AS02IndexWriterVBR();
+
+       //
+       void SetPrimerLookup(ASDCP::IPrimerLookup* lookup) {
+         assert(lookup);
+         m_Lookup = lookup;
+       }
+
+       Result_t WriteToFile(Kumu::FileWriter& Writer);
+       void     Dump(FILE* = 0);
+
+       ui32_t GetDuration() const;
+       void PushIndexEntry(const ASDCP::MXF::IndexTableSegment::IndexEntry&);
+       void SetEditRate(const ASDCP::Rational& edit_rate);
+      };
+
+
+   //
+    class AS02IndexWriterCBR : public ASDCP::MXF::Partition
+      {
+       ASDCP::MXF::IndexTableSegment*  m_CurrentSegment;
+       ASDCP::MXF::Rational m_EditRate;
+
+       KM_NO_COPY_CONSTRUCT(AS02IndexWriterCBR);
+       AS02IndexWriterCBR();
+
+      public:
+       const ASDCP::Dictionary*&  m_Dict;
+       ASDCP::IPrimerLookup* m_Lookup;
+       ui32_t m_Duration;
+       ui32_t m_SampleSize;
+      
+       AS02IndexWriterCBR(const ASDCP::Dictionary*&);
+       virtual ~AS02IndexWriterCBR();
+
+       //
+       void SetPrimerLookup(ASDCP::IPrimerLookup* lookup) {
+         assert(lookup);
+         m_Lookup = lookup;
+       }
+
+       Result_t WriteToFile(Kumu::FileWriter& Writer);
+       ui32_t GetDuration() const;
+       void SetEditRate(const ASDCP::Rational& edit_rate, const ui32_t& sample_size);
+      };
   }
 
   //
-  class h__Reader
-  {
-    ASDCP_NO_COPY_CONSTRUCT(h__Reader);
-    h__Reader();
-
-  public:
-    const Dictionary*  m_Dict;
-    Kumu::FileReader   m_File;
-    OPAtomHeader       m_HeaderPart;
-    //more than one Body-Partition use a list 
-    std::vector<Partition*> m_BodyPartList;
-    OPAtomIndexFooter  m_FooterPart;
-    ui64_t             m_EssenceStart;
-    WriterInfo         m_Info;
-    ASDCP::FrameBuffer m_CtFrameBuf;
-    Kumu::fpos_t       m_LastPosition;
-
-    IndexStrategy_t    m_IndexStrategy; //Shim parameter index_strategy_frame/clip
-    ui32_t             m_PartitionSpace;
-
-    ////new elements for AS-02
-    Partition*         m_pCurrentBodyPartition;
-    AS_02::MXF::OP1aIndexBodyPartion* m_pCurrentIndexPartition;
-    ui32_t            m_start_pos;
-
-    h__Reader(const Dictionary&);
-    virtual ~h__Reader();
-
-    Result_t InitInfo();
-    virtual Result_t OpenMXFRead(const char* filename) = 0;
-    Result_t InitMXFIndex();
-
-    // positions file before reading
-    virtual Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
-                                  const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC) = 0;
-
-    // reads from current position
-    virtual Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
-                                   const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC) = 0;
-    void     Close();
-  };
+  template <class IndexWriterType>
+  class h__AS02Writer : public ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>
+    {
+      ASDCP_NO_COPY_CONSTRUCT(h__AS02Writer);
+      h__AS02Writer();
+
+    public:
+      ui32_t  m_PartitionSpace;  // edit units per partition
+      IndexWriterType m_IndexWriter;
+      ui64_t  m_ECStart; // offset of the first essence element
+
+      //
+      h__AS02Writer(const ASDCP::Dictionary& d) :
+          ASDCP::MXF::TrackFileWriter<ASDCP::MXF::OP1aHeader>(d), m_IndexWriter(m_Dict), m_ECStart(0) {}
+
+      ~h__AS02Writer() {}
+
+
+      
+      // all the above for a single source clip
+      Result_t WriteAS02Header(const std::string& PackageLabel, const ASDCP::UL& WrappingUL,
+                              const std::string& TrackName, const ASDCP::UL& EssenceUL,
+                              const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate,
+                              const ui32_t& TCFrameRate)
+      {
+       if ( EditRate.Numerator == 0 || EditRate.Denominator == 0 )
+         {
+           DefaultLogSink().Error("Non-zero edit-rate reqired.\n");
+           return RESULT_PARAM;
+         }
+
+       InitHeader(MXFVersion_2011);
+
+       AddSourceClip(EditRate, EditRate/*TODO: for a moment*/, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
+       AddEssenceDescriptor(WrappingUL);
+
+       this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
+       this->m_RIP.PairArray.push_back(RIP::PartitionPair(0, 0)); // Header partition RIP entry
+       this->m_IndexWriter.MajorVersion = m_HeaderPart.MajorVersion;
+       this->m_IndexWriter.MinorVersion = m_HeaderPart.MinorVersion;
+       this->m_IndexWriter.OperationalPattern = this->m_HeaderPart.OperationalPattern;
+       this->m_IndexWriter.EssenceContainers = this->m_HeaderPart.EssenceContainers;
+
+       Result_t result = this->m_HeaderPart.WriteToFile(this->m_File, this->m_HeaderSize);
+
+       if ( KM_SUCCESS(result) )
+         {
+           this->m_PartitionSpace *= (ui32_t)floor( EditRate.Quotient() + 0.5 );  // convert seconds to edit units
+           this->m_ECStart = this->m_File.Tell();
+           this->m_IndexWriter.IndexSID = 129;
+
+           UL body_ul(this->m_Dict->ul(MDD_ClosedCompleteBodyPartition));
+           Partition body_part(this->m_Dict);
+           body_part.BodySID = 1;
+           body_part.MajorVersion = this->m_HeaderPart.MajorVersion;
+           body_part.MinorVersion = this->m_HeaderPart.MinorVersion;
+           body_part.OperationalPattern = this->m_HeaderPart.OperationalPattern;
+           body_part.EssenceContainers = this->m_HeaderPart.EssenceContainers;
+           body_part.ThisPartition = this->m_ECStart;
+           result = body_part.WriteToFile(this->m_File, body_ul);
+           this->m_RIP.PairArray.push_back(RIP::PartitionPair(1, body_part.ThisPartition)); // Second RIP Entry
+         }
+
+       return result;
+      }
+
+      void FlushIndexPartition()
+      {
+       if ( this->m_IndexWriter.GetDuration() > 0 )
+         {
+           this->m_IndexWriter.ThisPartition = this->m_File.Tell();
+           this->m_IndexWriter.WriteToFile(this->m_File);
+           this->m_RIP.PairArray.push_back(RIP::PartitionPair(0, this->m_IndexWriter.ThisPartition));
+         }
+      }
+      
+      // standard method of writing the header and footer of a completed AS-02 file
+      //
+      Result_t WriteAS02Footer()
+      {
+       this->FlushIndexPartition();
+         
+       // update all Duration properties
+       ASDCP::MXF::Partition footer_part(this->m_Dict);
+       DurationElementList_t::iterator dli = this->m_DurationUpdateList.begin();
+
+       for (; dli != this->m_DurationUpdateList.end(); ++dli )
+         {
+           **dli = this->m_FramesWritten;
+         }
+
+       this->m_EssenceDescriptor->ContainerDuration = this->m_FramesWritten;
+       footer_part.PreviousPartition = this->m_RIP.PairArray.back().ByteOffset;
+
+       Kumu::fpos_t here = this->m_File.Tell();
+       this->m_RIP.PairArray.push_back(RIP::PartitionPair(0, here)); // Last RIP Entry
+       this->m_HeaderPart.FooterPartition = here;
+
+       assert(this->m_Dict);
+       footer_part.MajorVersion = this->m_HeaderPart.MajorVersion;
+       footer_part.MinorVersion = this->m_HeaderPart.MinorVersion;
+       footer_part.OperationalPattern = this->m_HeaderPart.OperationalPattern;
+       footer_part.EssenceContainers = this->m_HeaderPart.EssenceContainers;
+       footer_part.FooterPartition = here;
+       footer_part.ThisPartition = here;
+
+       UL footer_ul(this->m_Dict->ul(MDD_CompleteFooter));
+       Result_t result = footer_part.WriteToFile(this->m_File, footer_ul);
+
+       if ( KM_SUCCESS(result) )
+         result = this->m_RIP.WriteToFile(this->m_File);
+
+       if ( KM_SUCCESS(result) )
+         result = this->m_File.Seek(0);
+       
+       if ( KM_SUCCESS(result) )
+         result = m_HeaderPart.WriteToFile(this->m_File, this->m_HeaderSize);
+  
+       if ( KM_SUCCESS(result) )
+         {
+           ASDCP::MXF::RIP::const_pair_iterator i = this->m_RIP.PairArray.begin();
+           ui64_t header_byte_count = this->m_HeaderPart.HeaderByteCount;
+           ui64_t previous_partition = 0;
+
+           for ( i = this->m_RIP.PairArray.begin(); KM_SUCCESS(result) && i != this->m_RIP.PairArray.end(); ++i )
+             {
+               ASDCP::MXF::Partition plain_part(this->m_Dict);
+               result = this->m_File.Seek(i->ByteOffset);
+
+               if ( KM_SUCCESS(result) )
+                 result = plain_part.InitFromFile(this->m_File);
+               
+               if ( KM_SUCCESS(result)
+                    && ( plain_part.IndexSID > 0 || plain_part.BodySID > 0 ) )
+                 {
+                   plain_part.PreviousPartition = previous_partition;
+                   plain_part.FooterPartition = footer_part.ThisPartition;
+                   previous_partition = plain_part.ThisPartition;
+                   result = this->m_File.Seek(i->ByteOffset);
+
+                   if ( KM_SUCCESS(result) )
+                     {
+                       UL tmp_ul = plain_part.GetUL();
+                       result = plain_part.WriteToFile(this->m_File, tmp_ul);
+                     }
+                 }
+             }
+         }
+       
+       this->m_File.Close();
+       return result;
+      }
+    };
 
   //
-  class h__Writer
-  {
-    ASDCP_NO_COPY_CONSTRUCT(h__Writer);
-    h__Writer();
-
-  public:
-    const Dictionary*  m_Dict;
-    Kumu::FileWriter   m_File;
-    ui32_t             m_HeaderSize;
-    OPAtomHeader       m_HeaderPart;
-    //more than one Body-Partition -> use a list of Partitions
-    std::vector<Partition*> m_BodyPartList;
-    //we don't use the footer like in the dcps but we need also a footer
-    AS_02::MXF::OP1aIndexFooter    m_FooterPart;
-    ui64_t             m_EssenceStart;
-
-    MaterialPackage*   m_MaterialPackage;
-    SourcePackage*     m_FilePackage;
-
-    FileDescriptor*    m_EssenceDescriptor;
-    std::list<InterchangeObject*> m_EssenceSubDescriptorList;
-
-    ui32_t             m_FramesWritten;
-    ui64_t             m_StreamOffset;
-    ASDCP::FrameBuffer m_CtFrameBuf;
-    h__WriterState     m_State;
-    WriterInfo         m_Info;
-    DurationElementList_t m_DurationUpdateList;
-
-    //new elements for AS-02
-    ui64_t             m_BodyOffset;
-    //TODO: Currently not used, delete if not necessary
-    // Counter values for BodySID and IndexSID
-    ui32_t             m_CurrentBodySID;
-    ui32_t             m_CurrentIndexSID;
-    //TODO: maybe set this to the lf__Writer class because this is only in JP2K creation necessary
-    //our computed PartitionSpace
-    ui32_t             m_PartitionSpace;
-    IndexStrategy_t    m_IndexStrategy; //Shim parameter index_strategy_frame/clip
-
-    //the EditRate
-    ASDCP::MXF::Rational      m_EditRate;
-    //the BytesPerEditUnit
-    ui32_t                        m_BytesPerEditUnit;
-    //pointer to the current Body Partition(Index)
-    AS_02::MXF::OP1aIndexBodyPartion*  m_CurrentIndexBodyPartition;
-
-    h__Writer(const Dictionary&);
-    virtual ~h__Writer();
-
-    //virtual methods, implementation details for JP2K or PCM are in the MXFWriter::h__Writer classes if they are different
-    virtual void InitHeader();
-    /*virtual*/ void AddSourceClip(const ASDCP::MXF::Rational& EditRate, ui32_t TCFrameRate,
-                                  const std::string& TrackName, const UL& EssenceUL,
-                                  const UL& DataDefinition, const std::string& PackageLabel);
-    /*virtual*/ void AddDMSegment(const ASDCP::MXF::Rational& EditRate, ui32_t TCFrameRate,
-                                 const std::string& TrackName, const UL& DataDefinition,
-                                 const std::string& PackageLabel);
-    /*virtual*/ void AddEssenceDescriptor(const ASDCP::UL& WrappingUL);
-    virtual Result_t CreateBodyPart(const ASDCP::MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
-
-    //new method to create BodyPartition for essence and index
-    virtual Result_t CreateBodyPartPair();
-    //new method to finalize BodyPartion(index)
-    virtual Result_t CompleteIndexBodyPart();
-
-    // all the above for a single source clip
-    virtual Result_t WriteMXFHeader(const std::string& PackageLabel, const UL& WrappingUL,
-                                   const std::string& TrackName, const UL& EssenceUL,
-                                   const UL& DataDefinition, const ASDCP::MXF::Rational& EditRate,
-                                   ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
-
-    virtual Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
-                                    const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
-
-    virtual Result_t WriteMXFFooter() = 0;
-
-  };
+  class h__AS02WriterFrame : public h__AS02Writer<AS_02::MXF::AS02IndexWriterVBR>
+    {
+      ASDCP_NO_COPY_CONSTRUCT(h__AS02WriterFrame);
+      h__AS02WriterFrame();
+
+    public:
+      IndexStrategy_t m_IndexStrategy; // per SMPTE ST 2067-5
+
+      h__AS02WriterFrame(const Dictionary&);
+      virtual ~h__AS02WriterFrame();
+
+      Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
+                              const ui32_t& MinEssenceElementBerLength,
+                              AESEncContext* Ctx, HMACContext* HMAC);
+    };
+
+  //
+  class h__AS02WriterClip : public h__AS02Writer<AS_02::MXF::AS02IndexWriterCBR>
+    {
+      ASDCP_NO_COPY_CONSTRUCT(h__AS02WriterClip);
+      h__AS02WriterClip();
+
+    public:
+      ui64_t  m_ECStart; // offset of the first essence element
+      ui64_t  m_ClipStart;  // state variable for clip-wrap-in-progress
+      IndexStrategy_t m_IndexStrategy; // per SMPTE ST 2067-5
+
+      h__AS02WriterClip(const Dictionary&);
+      virtual ~h__AS02WriterClip();
+
+      bool HasOpenClip() const;
+      Result_t StartClip(const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
+      Result_t WriteClipBlock(const ASDCP::FrameBuffer& FrameBuf);
+      Result_t FinalizeClip(ui32_t bytes_per_frame);
+    };
 
 } // namespace AS_02