tweezes
authorjhurst <jhurst@cinecert.com>
Mon, 17 Jun 2013 17:55:54 +0000 (17:55 +0000)
committerjhurst <>
Mon, 17 Jun 2013 17:55:54 +0000 (17:55 +0000)
28 files changed:
README
configure.ac
src/AS_02.h
src/AS_02_JP2K.cpp
src/AS_02_PCM.cpp
src/AS_02_internal.h
src/AS_DCP.h
src/AS_DCP_JP2K.cpp
src/AS_DCP_MPEG2.cpp
src/AS_DCP_PCM.cpp
src/AS_DCP_TimedText.cpp
src/AS_DCP_internal.h
src/Index.cpp
src/KM_log.h
src/MDD.cpp
src/MDD.h
src/MXF.cpp
src/MXF.h
src/MXFTypes.h
src/WavFileWriter.h
src/as-02-unwrap.cpp
src/as-02-wrap.cpp
src/asdcp-wrap.cpp
src/h__02_Reader.cpp
src/h__02_Writer.cpp
src/h__Reader.cpp
src/h__Writer.cpp
src/klvsplit.cpp

diff --git a/README b/README
index 7def63f12d5b3633103dbae3ad408dfbe9e1c35a..f39fd19306163139d3c8372cb4c542e00f2fcedf 100755 (executable)
--- a/README
+++ b/README
@@ -139,6 +139,28 @@ command-line utilities all respond to -h.
 
 Change History
 
+YYYY-MM-DD - IMF/AS-02 support, bug fixes
+  o Fixed missing return statement in ArchivableString::ArchiveLength
+    (thanks to both Kristof Provost and Franck Chopin) 
+  o Fixed broken sample alignment in RF64Writer (thanks to Dolby)
+  o Fixed win32 build (thanks to Dolby)
+  o Massive refactoring of internals to allow easier implementation
+    of AS-02.  Some API changes were made as well (note that
+    OPAtomHeader is now OP1aHeader and RIP is no longer part of the
+    OP1aHeader.)  If you are using this project as a library (and
+    especially if you are keeping patches against it) PLEASE TAKE
+    TIME TO EVALUATE THIS RELEASE THOUROUGHLY BEFORE ADDING IT TO
+    YOUR RELEASE PATH.
+  o Final integration of Fraunhoffer IIS code contribution.  AS-02
+    files are now fully supported with some TODOs and one major
+    exception: LEAD indexes are not supported by the MXF writers
+    and interlace images are not yet supported.
+  o Added support for MCA labels (ST 428-12) to asdcp-wrap.  Note
+    that this project is still in the early stages of interop testing
+    so errors are likely present and don't expect any server to
+    understand this feature.
+
+
 2013-04-12 - Dolby Atmos support and more audio labels 1.11.49
   o Significant code contribution from Dolby Laboratories to add
     support for generic data track files as proposed in ST 21DC
@@ -150,7 +172,6 @@ Change History
   o Added ULs for ST 428-12 and Amd. 429-2 2013.  Please check!
 
 
-
 2013-02-20 - bug fixes, enhancements 1.10.48
  o Refactored internals of the AS-DCP file readers.  While no
    changes in behavior are intended, users are cautioned to test
@@ -187,6 +208,7 @@ Change History
           MCAAudioContentKind
           MCAAudioElementKind
 
+
 2012-08-07 - bug fix, 1.10.46
  o Added missing zero-initializers to time values when parsing a
    timestamp string (in the case where the optional [Thh:mm.[:ss]]
index 623ccc3bee2fdebb24139b24559fee0a8d05c306..5968dd8e030bf9eba23c97dcfd3299ccda0c4783 100644 (file)
@@ -37,7 +37,7 @@ AC_PREREQ([2.59])
 # For example, if asdcplib version 1.0.0 were modified to accomodate changes
 # in file format, and if no changes were made to AS_DCP.h, the new version would be
 # 1.0.1. If changes were also required in AS_DCP.h, the new version would be 1.1.1.
-AC_INIT([asdcplib], [1.12.50a], [asdcplib@cinecert.com])
+AC_INIT([asdcplib], [1.12.50b], [asdcplib@cinecert.com])
 
 AC_CONFIG_AUX_DIR([build-aux])
 AC_CONFIG_SRCDIR([src/KM_error.h])
index e4be1b872b450d979100971fa6b57e54b566adff..ff6b692525a433c5143c2dcfdb4240ffb840f55f 100644 (file)
@@ -1,5 +1,7 @@
 /*
-Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+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
@@ -34,7 +36,7 @@ by the SMPTE Media and Packaging Technology Committee 35PM. The file
 format, labeled IMF Essence Component (AKA "AS-02" for historical
 reasons), is described in the following document:
 
- o SMPTE 2067-5:201X (draft at this time) IMF Essence Component
+ o SMPTE 2067-5:2013 IMF Essence Component
 
 The following use cases are supported by the module:
 
@@ -47,49 +49,46 @@ The following use cases are supported by the module:
      PCM audio streams
 
  o Read header metadata from an AS-02 file
+
+NOTE: ciphertext support for clip-wrapped PCM is not yet complete.
 */
 
 #ifndef _AS_02_H_
 #define _AS_02_H_
 
-#include "AS_DCP.h"
-#include "MXF.h"
-
+#include "Metadata.h"
 
-namespace ASDCP {
-  namespace MXF {
-    // #include<Metadata.h> to use this
-    class OPAtomHeader;
-  };
-};
 
 namespace AS_02
 {
   using Kumu::Result_t;
 
+  KM_DECLARE_RESULT(AS02_FORMAT,        -116, "The file format is not proper OP-1a/AS-02.");
+
   namespace MXF {
     //
+    // reads distributed index tables and provides a uniform lookup with
+    // translated StreamOffest values (that is, StreamOffest is adjusted
+    // to the actual file position
     class AS02IndexReader : public ASDCP::MXF::Partition
     {
       Kumu::ByteString m_IndexSegmentData;
       ui32_t m_Duration;
+      ui32_t m_BytesPerEditUnit;
 
-      //      ui32_t              m_BytesPerEditUnit;
-
-      Result_t InitFromBuffer(const byte_t* p, ui32_t l);
+      Result_t InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset);
 
       ASDCP_NO_COPY_CONSTRUCT(AS02IndexReader);
       AS02IndexReader();
          
     public:
       const ASDCP::Dictionary*&   m_Dict;
-      Kumu::fpos_t  m_ECOffset;
       ASDCP::IPrimerLookup *m_Lookup;
     
       AS02IndexReader(const ASDCP::Dictionary*&);
       virtual ~AS02IndexReader();
     
-      Result_t InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip);
+      Result_t InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence);
       ui32_t GetDuration() const;
       void     Dump(FILE* = 0);
       Result_t GetMDObjectByID(const Kumu::UUID&, ASDCP::MXF::InterchangeObject** = 0);
@@ -98,6 +97,27 @@ namespace AS_02
       Result_t Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegment::IndexEntry&) const;
     };
 
+
+    
+    // Returns size in bytes of a single sample of data described by ADesc
+    inline ui32_t CalcSampleSize(const ASDCP::MXF::WaveAudioDescriptor& d)
+    {
+      return (d.QuantizationBits / 8) * d.ChannelCount;
+    }
+
+      // Returns number of samples per frame of data described by ADesc
+    inline ui32_t CalcSamplesPerFrame(const ASDCP::MXF::WaveAudioDescriptor& d, const ASDCP::Rational& edit_rate)
+    {
+      double tmpd = d.AudioSamplingRate.Quotient() / edit_rate.Quotient();
+      return (ui32_t)ceil(tmpd);
+    }
+
+    // Returns the size in bytes of a frame of data described by ADesc
+    inline ui32_t CalcFrameBufferSize(const ASDCP::MXF::WaveAudioDescriptor& d, const ASDCP::Rational& edit_rate)
+    {
+      return CalcSampleSize(d) * CalcSamplesPerFrame(d, edit_rate);
+    }
+
   } // namespace MXF
 
   //---------------------------------------------------------------------------------
@@ -110,6 +130,7 @@ namespace AS_02
     IS_LEAD,
     IS_FOLLOW,
     IS_FILE_SPECIFIC,
+    IS_MAX
   };
  
   namespace JP2K
@@ -133,11 +154,11 @@ namespace AS_02
       // Open the file for writing. The file must not exist. Returns error if
       // the operation cannot be completed or if nonsensical data is discovered
       // in the essence descriptor.
-      Result_t OpenWrite(const char* filename, const ASDCP::WriterInfo&,
-                        const ASDCP::JP2K::PictureDescriptor&,
-                        const IndexStrategy_t& Strategy = IS_FOLLOW,
-                        const ui32_t& PartitionSpace = 60, /* seconds per partition */
-                        const ui32_t& HeaderSize = 16384);
+      Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
+                        ASDCP::MXF::FileDescriptor* essence_descriptor,
+                        ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+                        const ASDCP::Rational& edit_rate, const ui32_t& header_size = 16384,
+                        const IndexStrategy_t& strategy = IS_FOLLOW, const ui32_t& partition_space = 10);
       
       // Writes a frame of essence to the MXF file. If the optional AESEncContext
       // argument is present, the essence is encrypted prior to writing.
@@ -168,15 +189,11 @@ namespace AS_02
 
       // Open the file for reading. The file must exist. Returns error if the
       // operation cannot be completed.
-      Result_t OpenRead(const char* filename) const;
+      Result_t OpenRead(const std::string& filename) const;
 
       // Returns RESULT_INIT if the file is not open.
       Result_t Close() const;
 
-      // Fill an AudioDescriptor struct with the values from the file's header.
-      // Returns RESULT_INIT if the file is not open.
-      Result_t FillPictureDescriptor(ASDCP::JP2K::PictureDescriptor&) const;
-
       // Fill a WriterInfo struct with the values from the file's header.
       // Returns RESULT_INIT if the file is not open.
       Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
@@ -204,6 +221,14 @@ namespace AS_02
   {
     // see AS_DCP.h for related data types
 
+    // An AS-02 PCM file is clip-wrapped, but the interface defined below mimics that used
+    // for frame-wrapped essence elsewhere in this library.  The concept of frame rate
+    // therefore is only relevant to these classes and is not reflected in or affected by
+    // the contents of the MXF file.  The frame rate that is set on the writer is only
+    // for compatibility with the existing parsers, samples are packed contiguously into
+    // the same clip-wrapped packet.  Similarly, the edit rate must be set when initializing
+    // the reader to signal the number of samples to be read by each call to ReadFrame();
+
     //
     class MXFWriter
     {
@@ -223,8 +248,10 @@ namespace AS_02
       // Open the file for writing. The file must not exist. Returns error if
       // the operation cannot be completed or if nonsensical data is discovered
       // in the essence descriptor.
-      Result_t OpenWrite(const char* filename, const ASDCP::WriterInfo&,
-                               const ASDCP::PCM::AudioDescriptor&, ui32_t HeaderSize = 16384);
+      Result_t OpenWrite(const std::string& filename, const ASDCP::WriterInfo&,
+                        ASDCP::MXF::FileDescriptor* essence_descriptor,
+                        ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+                        const ASDCP::Rational& edit_rate, ui32_t HeaderSize = 16384);
 
       // Writes a frame of essence to the MXF file. If the optional AESEncContext
       // argument is present, the essence is encrypted prior to writing.
@@ -255,15 +282,11 @@ namespace AS_02
 
       // Open the file for reading. The file must exist. Returns error if the
       // operation cannot be completed.
-      Result_t OpenRead(const char* filename) const;
+      Result_t OpenRead(const std::string& filename, const ASDCP::Rational& EditRate);
 
       // Returns RESULT_INIT if the file is not open.
       Result_t Close() const;
 
-      // Fill an AudioDescriptor struct with the values from the file's header.
-      // Returns RESULT_INIT if the file is not open.
-      Result_t FillAudioDescriptor(ASDCP::PCM::AudioDescriptor&) const;
-
       // Fill a WriterInfo struct with the values from the file's header.
       // Returns RESULT_INIT if the file is not open.
       Result_t FillWriterInfo(ASDCP::WriterInfo&) const;
index aeb6dbc6550aea4a27596ba20bdfb29971bc298a..79ea382bdb03f85c05ef2f6e974744fccceacfb8 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    AS_02_JP2K.cpp
   \version $Id$
@@ -50,60 +52,47 @@ static std::string PICT_DEF_LABEL = "Image Track";
 
 class AS_02::JP2K::MXFReader::h__Reader : public AS_02::h__AS02Reader
 {
-  RGBAEssenceDescriptor*        m_RGBAEssenceDescriptor;
-  CDCIEssenceDescriptor*        m_CDCIEssenceDescriptor;
-  JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
-  ASDCP::Rational               m_EditRate;
-  ASDCP::Rational               m_SampleRate;
-  EssenceType_t                 m_Format;
-
   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
 
 public:
   PictureDescriptor m_PDesc;        // codestream parameter list
 
   h__Reader(const Dictionary& d) :
-    AS_02::h__AS02Reader(d), m_RGBAEssenceDescriptor(0), m_CDCIEssenceDescriptor(0),
-    m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
+    AS_02::h__AS02Reader(d) {}
 
   virtual ~h__Reader() {}
 
-  Result_t    OpenRead(const char*);
+  Result_t    OpenRead(const std::string&);
   Result_t    ReadFrame(ui32_t, ASDCP::JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
 };
 
 //
 Result_t
-AS_02::JP2K::MXFReader::h__Reader::OpenRead(const char* filename)
+AS_02::JP2K::MXFReader::h__Reader::OpenRead(const std::string& filename)
 {
-  Result_t result = OpenMXFRead(filename);
+  Result_t result = OpenMXFRead(filename.c_str());
 
-  if( ASDCP_SUCCESS(result) )
+  if( KM_SUCCESS(result) )
     {
       InterchangeObject* tmp_iobj = 0;
 
       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CDCIEssenceDescriptor), &tmp_iobj);
-      m_CDCIEssenceDescriptor = static_cast<CDCIEssenceDescriptor*>(tmp_iobj);
 
-      if ( m_CDCIEssenceDescriptor == 0 )
+      if ( tmp_iobj == 0 )
        {
          m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(RGBAEssenceDescriptor), &tmp_iobj);
-         m_RGBAEssenceDescriptor = static_cast<RGBAEssenceDescriptor*>(tmp_iobj);
        }
 
-      if ( m_CDCIEssenceDescriptor == 0 && m_RGBAEssenceDescriptor == 0 )
+      if ( tmp_iobj == 0 )
        {
          DefaultLogSink().Error("RGBAEssenceDescriptor nor CDCIEssenceDescriptor found.\n");
-         return RESULT_FORMAT;
        }
 
       m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(JPEG2000PictureSubDescriptor), &tmp_iobj);
-      m_EssenceSubDescriptor = static_cast<JPEG2000PictureSubDescriptor*>(tmp_iobj);
 
-      if ( m_EssenceSubDescriptor == 0 )
+      if ( tmp_iobj == 0 )
        {
          DefaultLogSink().Error("JPEG2000PictureSubDescriptor not found.\n");
-         return RESULT_FORMAT;
        }
 
       std::list<InterchangeObject*> ObjectList;
@@ -112,25 +101,7 @@ AS_02::JP2K::MXFReader::h__Reader::OpenRead(const char* filename)
       if ( ObjectList.empty() )
        {
          DefaultLogSink().Error("MXF Metadata contains no Track Sets.\n");
-         return RESULT_FORMAT;
-       }
-
-      if ( m_CDCIEssenceDescriptor != 0 )
-       {
-         m_EditRate = ((Track*)ObjectList.front())->EditRate;
-         m_SampleRate = m_CDCIEssenceDescriptor->SampleRate;
-         result = MD_to_JP2K_PDesc(*m_CDCIEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
-       }
-      else if ( m_RGBAEssenceDescriptor != 0 )
-       {
-         m_EditRate = ((Track*)ObjectList.front())->EditRate;
-         m_SampleRate = m_RGBAEssenceDescriptor->SampleRate;
-         result = MD_to_JP2K_PDesc(*m_RGBAEssenceDescriptor, *m_EssenceSubDescriptor, m_EditRate, m_SampleRate, m_PDesc);
-       }
-
-      if ( m_PDesc.ContainerDuration == 0 )
-       {
-         m_PDesc.ContainerDuration = m_IndexAccess.GetDuration();
+         return RESULT_AS02_FORMAT;
        }
     }
 
@@ -211,38 +182,35 @@ AS_02::JP2K::MXFReader::RIP()
 // Open the file for reading. The file must exist. Returns error if the
 // operation cannot be completed.
 Result_t
-AS_02::JP2K::MXFReader::OpenRead(const char* filename) const
+AS_02::JP2K::MXFReader::OpenRead(const std::string& filename) const
 {
   return m_Reader->OpenRead(filename);
 }
 
 //
 Result_t
-AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
-                                          ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
+AS_02::JP2K::MXFReader::Close() const
 {
   if ( m_Reader && m_Reader->m_File.IsOpen() )
-    return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
+    {
+      m_Reader->Close();
+      return RESULT_OK;
+    }
 
   return RESULT_INIT;
 }
 
-
-// Fill the struct with the values from the file's header.
-// Returns RESULT_INIT if the file is not open.
+//
 Result_t
-AS_02::JP2K::MXFReader::FillPictureDescriptor(PictureDescriptor& PDesc) const
+AS_02::JP2K::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::JP2K::FrameBuffer& FrameBuf,
+                                          ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC) const
 {
   if ( m_Reader && m_Reader->m_File.IsOpen() )
-    {
-      PDesc = m_Reader->m_PDesc;
-      return RESULT_OK;
-    }
+    return m_Reader->ReadFrame(FrameNum, FrameBuf, Ctx, HMAC);
 
   return RESULT_INIT;
 }
 
-
 // Fill the struct with the values from the file's header.
 // Returns RESULT_INIT if the file is not open.
 Result_t
@@ -278,10 +246,11 @@ public:
 
   virtual ~h__Writer(){}
 
-  Result_t OpenWrite(const char*, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
+  Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
+                    ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+                    const AS_02::IndexStrategy_t& IndexStrategy,
                     const ui32_t& PartitionSpace, const ui32_t& HeaderSize);
-  Result_t SetSourceStream(const PictureDescriptor&, const std::string& label,
-                          ASDCP::Rational LocalEditRate = ASDCP::Rational(0,0));
+  Result_t SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate);
   Result_t WriteFrame(const ASDCP::JP2K::FrameBuffer&, ASDCP::AESEncContext*, ASDCP::HMACContext*);
   Result_t Finalize();
 };
@@ -290,11 +259,16 @@ public:
 // Open the file for writing. The file must not exist. Returns error if
 // the operation cannot be completed.
 Result_t
-AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, EssenceType_t type, const AS_02::IndexStrategy_t& IndexStrategy,
+AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
+                                            ASDCP::MXF::FileDescriptor* essence_descriptor,
+                                            ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+                                            const AS_02::IndexStrategy_t& IndexStrategy,
                                             const ui32_t& PartitionSpace_sec, const ui32_t& HeaderSize)
 {
   if ( ! m_State.Test_BEGIN() )
-    return RESULT_STATE;
+    {
+      return RESULT_STATE;
+    }
 
   if ( m_IndexStrategy != AS_02::IS_FOLLOW )
     {
@@ -302,20 +276,39 @@ AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, EssenceType_t
       return Kumu::RESULT_NOTIMPL;
     }
 
-  Result_t result = m_File.OpenWrite(filename);
+  Result_t result = m_File.OpenWrite(filename.c_str());
 
-  if ( ASDCP_SUCCESS(result) )
+  if ( KM_SUCCESS(result) )
     {
       m_IndexStrategy = IndexStrategy;
       m_PartitionSpace = PartitionSpace_sec; // later converted to edit units by SetSourceStream()
       m_HeaderSize = HeaderSize;
 
-      m_EssenceDescriptor = new RGBAEssenceDescriptor(m_Dict);
-      m_EssenceSubDescriptor = new JPEG2000PictureSubDescriptor(m_Dict);
-      m_EssenceSubDescriptorList.push_back((InterchangeObject*)m_EssenceSubDescriptor);
+      if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_RGBAEssenceDescriptor))
+          && essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_CDCIEssenceDescriptor)) )
+       {
+         DefaultLogSink().Error("Essence descriptor is not a RGBAEssenceDescriptor or CDCIEssenceDescriptor.\n");
+         essence_descriptor->Dump();
+         return RESULT_AS02_FORMAT;
+       }
+
+      m_EssenceDescriptor = essence_descriptor;
+
+      ASDCP::MXF::InterchangeObject_list_t::iterator i;
+      for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
+       {
+         if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_JPEG2000PictureSubDescriptor)) )
+           {
+             DefaultLogSink().Error("Essence sub-descriptor is not a JPEG2000PictureSubDescriptor.\n");
+             (*i)->Dump();
+           }
+
+         m_EssenceSubDescriptorList.push_back(*i);
+         GenRandomValue((*i)->InstanceUID);
+         m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
+         *i = 0; // parent will only free the ones we don't keep
+       }
 
-      GenRandomValue(m_EssenceSubDescriptor->InstanceUID);
-      m_EssenceDescriptor->SubDescriptors.push_back(m_EssenceSubDescriptor->InstanceUID);
       result = m_State.Goto_INIT();
     }
 
@@ -324,38 +317,23 @@ AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const char* filename, EssenceType_t
 
 // Automatically sets the MXF file's metadata from the first jpeg codestream stream.
 Result_t
-AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& label, ASDCP::Rational LocalEditRate)
+AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, const ASDCP::Rational& edit_rate)
 {
   assert(m_Dict);
   if ( ! m_State.Test_INIT() )
-    return RESULT_STATE;
-
-  if ( LocalEditRate == ASDCP::Rational(0,0) )
-    LocalEditRate = PDesc.EditRate;
-
-  m_PDesc = PDesc;
-  assert(m_Dict);
-  Result_t result = JP2K_PDesc_to_MD(m_PDesc, *m_Dict,
-                                    static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor),
-                                    m_EssenceSubDescriptor);
-
-  static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMaxRef = 4095; /// TODO: set with magic or some such thing
-  static_cast<ASDCP::MXF::RGBAEssenceDescriptor*>(m_EssenceDescriptor)->ComponentMinRef = 0;
-
-  if ( ASDCP_SUCCESS(result) )
     {
-      memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
-      m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
-      result = m_State.Goto_READY();
+      return RESULT_STATE;
     }
 
-  if ( ASDCP_SUCCESS(result) )
-    {
-      ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98  ) ? 24 : m_PDesc.EditRate.Numerator;
+  memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
+  m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
+  Result_t result = m_State.Goto_READY();
 
+  if ( KM_SUCCESS(result) )
+    {
       result = WriteAS02Header(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
                               PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
-                              LocalEditRate, TCFrameRate);
+                              edit_rate, derive_timecode_rate_from_edit_rate(edit_rate));
     }
 
   return result;
@@ -368,26 +346,27 @@ AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const PictureDescriptor& PDes
 //
 Result_t
 AS_02::JP2K::MXFWriter::h__Writer::WriteFrame(const ASDCP::JP2K::FrameBuffer& FrameBuf,
-                      AESEncContext* Ctx, HMACContext* HMAC)
+                                             AESEncContext* Ctx, HMACContext* HMAC)
 {
+  if ( FrameBuf.Size() == 0 )
+    {
+      DefaultLogSink().Error("The frame buffer size is zero.\n");
+      return RESULT_PARAM;
+    }
+
   Result_t result = RESULT_OK;
 
   if ( m_State.Test_READY() )
-    result = m_State.Goto_RUNNING(); // first time through
-  ui64_t StreamOffset = m_StreamOffset; // m_StreamOffset will be changed by the call to WriteEKLVPacket
-
-  if ( ASDCP_SUCCESS(result) )
-    result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
-
-  if ( ASDCP_SUCCESS(result) )
-    {  
-      IndexTableSegment::IndexEntry Entry;
-      Entry.StreamOffset = StreamOffset;
-      m_IndexWriter.PushIndexEntry(Entry);
+    {
+      result = m_State.Goto_RUNNING(); // first time through
+    }
+
+  if ( KM_SUCCESS(result) )
+    {
+      result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
+      m_FramesWritten++;
     }
 
-  m_FramesWritten++;
   return result;
 }
 
@@ -401,8 +380,10 @@ AS_02::JP2K::MXFWriter::h__Writer::Finalize()
 
   Result_t result = m_State.Goto_FINAL();
 
-  if ( ASDCP_SUCCESS(result) )
-    result = WriteAS02Footer();
+  if ( KM_SUCCESS(result) )
+    {
+      result = WriteAS02Footer();
+    }
 
   return result;
 }
@@ -453,21 +434,28 @@ AS_02::JP2K::MXFWriter::RIP()
 // Open the file for writing. The file must not exist. Returns error if
 // the operation cannot be completed.
 Result_t
-AS_02::JP2K::MXFWriter::OpenWrite(const char* filename, const ASDCP::WriterInfo& Info,
-                                 const ASDCP::JP2K::PictureDescriptor& PDesc,
-                                 const IndexStrategy_t& Strategy,
-                                 const ui32_t& PartitionSpace,
-                                 const ui32_t& HeaderSize)
+AS_02::JP2K::MXFWriter::OpenWrite(const std::string& filename, const ASDCP::WriterInfo& Info,
+                                 ASDCP::MXF::FileDescriptor* essence_descriptor,
+                                 ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+                                 const ASDCP::Rational& edit_rate, const ui32_t& header_size,
+                                 const IndexStrategy_t& strategy, const ui32_t& partition_space)
 {
+  if ( essence_descriptor == 0 )
+    {
+      DefaultLogSink().Error("Essence descriptor object required.\n");
+      return RESULT_PARAM;
+    }
+
   m_Writer = new AS_02::JP2K::MXFWriter::h__Writer(DefaultSMPTEDict());
   m_Writer->m_Info = Info;
 
-  Result_t result = m_Writer->OpenWrite(filename, ASDCP::ESS_JPEG_2000, Strategy, PartitionSpace, HeaderSize);
+  Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list,
+                                       strategy, partition_space, header_size);
 
-  if ( ASDCP_SUCCESS(result) )
-    result = m_Writer->SetSourceStream(PDesc, JP2K_PACKAGE_LABEL);
+  if ( KM_SUCCESS(result) )
+    result = m_Writer->SetSourceStream(JP2K_PACKAGE_LABEL, edit_rate);
 
-  if ( ASDCP_FAILURE(result) )
+  if ( KM_FAILURE(result) )
     m_Writer.release();
 
   return result;
index b371fcf72553eeed83d45823fbef9793823d1d8e..66de6e9d7eebfe2462bcc64819b31c4ac5b11a93 100644 (file)
@@ -1,29 +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    AS_02_PCM.cpp
   \version $Id$       
   \brief   AS-02 library, PCM essence reader and writer implementation
 static std::string PCM_PACKAGE_LABEL = "File Package: SMPTE 382M clip wrapping of wave audio";
 static std::string SOUND_DEF_LABEL = "Sound Track";
 
-//this must be changed because the CBR_frame_size is only   
-//
-static ui32_t
-calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor& ADesc)
-{
-  ui32_t CBR_frame_size = 0;
-
-  if ( Info.EncryptedEssence )
-    {
-      CBR_frame_size =
-       //TODO: correct?
-       /*SMPTE_UL_LENGTH  
-         + MXF_BER_LENGTH
-         + */klv_cryptinfo_size
-       + calc_esv_length(ASDCP::PCM::CalcFrameBufferSize(ADesc), 0)
-       + ( Info.UsesHMAC ? klv_intpack_size : (MXF_BER_LENGTH * 3) );
-    }
-  else
-    {
-      CBR_frame_size = ASDCP::PCM::CalcFrameBufferSize(ADesc);
-    }
-
-  return CBR_frame_size;
-}
-
 
 //------------------------------------------------------------------------------------------
 
 
 class AS_02::PCM::MXFReader::h__Reader : public AS_02::h__AS02Reader
 {
+  ui64_t m_ClipEssenceBegin;
+  ui64_t m_SamplesPerFrame;
+  ui32_t m_ContainerDuration;
+
   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
   h__Reader();
 
 public:
-  ASDCP::PCM::AudioDescriptor m_ADesc;
-
-  h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d) {}
+  h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_ClipEssenceBegin(0),
+                                  m_SamplesPerFrame(0), m_ContainerDuration(0) {}
   virtual ~h__Reader() {}
 
-  ASDCP::Result_t    OpenRead(const char*);
+  ASDCP::Result_t    OpenRead(const std::string&, const ASDCP::Rational& edit_rate);
   ASDCP::Result_t    ReadFrame(ui32_t, ASDCP::PCM::FrameBuffer&, ASDCP::AESDecContext*, ASDCP::HMACContext*);
-
-  
-  ///  Result_t OpenMXFRead(const char* filename);
-  // positions file before reading
-  ///  Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
-  ///                   const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
-
-  // reads from current position
-  Result_t ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
-                         const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC);
-
-
 };
 
-
+// TODO: This will ignore any body partitions past the first
 //
 //
 ASDCP::Result_t
-AS_02::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
+AS_02::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
 {
-  Result_t result = OpenMXFRead(filename);
+  ASDCP::MXF::WaveAudioDescriptor* wave_descriptor = 0;
+  IndexTableSegment::IndexEntry tmp_entry;
+  Result_t result = OpenMXFRead(filename.c_str());
 
-  if( ASDCP_SUCCESS(result) )
+  if( KM_SUCCESS(result) )
     {
-      InterchangeObject* Object = 0;
-      if ( ASDCP_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor), &Object)) )
+      if ( KM_SUCCESS(m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(WaveAudioDescriptor),
+                                                    reinterpret_cast<InterchangeObject**>(&wave_descriptor))) )
        {
-         if ( Object == 0 )
+         if ( wave_descriptor == 0 )
            {
              DefaultLogSink().Error("WaveAudioDescriptor object not found.\n");
-             return RESULT_FORMAT;
+             return RESULT_AS02_FORMAT;
            }
-
-         result = MD_to_PCM_ADesc((ASDCP::MXF::WaveAudioDescriptor*)Object, m_ADesc);
        }
     }
 
-  // check for sample/frame rate sanity
-  if ( ASDCP_SUCCESS(result)
-       && m_ADesc.EditRate != EditRate_24
-       && m_ADesc.EditRate != EditRate_25
-       && m_ADesc.EditRate != EditRate_30
-       && m_ADesc.EditRate != EditRate_48
-       && m_ADesc.EditRate != EditRate_50
-       && m_ADesc.EditRate != EditRate_60
-       && m_ADesc.EditRate != EditRate_23_98 )
+  if ( KM_SUCCESS(result) )
+    result = m_IndexAccess.Lookup(0, tmp_entry);
+
+  if ( KM_SUCCESS(result) )
+    result = m_File.Seek(tmp_entry.StreamOffset);
+
+  if ( KM_SUCCESS(result) )
     {
-      DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
-                            m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
+      assert(wave_descriptor);
+      KLReader reader;
+      result = reader.ReadKLFromFile(m_File);
 
-      // oh, they gave us the audio sampling rate instead, assume 24/1
-      if ( m_ADesc.EditRate == SampleRate_48k )
+      if ( KM_SUCCESS(result) )
        {
-         DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
-         m_ADesc.EditRate = EditRate_24;
-       }
-      else
-       {
-         // or we just drop the hammer
-         return RESULT_FORMAT;
+         if ( ! UL(reader.Key()).MatchIgnoreStream(m_Dict->ul(MDD_WAVEssenceClip)) )
+           {
+             const MDDEntry *entry = m_Dict->FindUL(reader.Key());
+
+             if ( entry == 0 )
+               {
+                 char buf[64];
+                 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", UL(reader.Key()).EncodeString(buf, 64));
+               }
+             else
+               {
+                 DefaultLogSink().Error("Essence wrapper key is not WAVEssenceClip: %s\n", entry->name);
+               }
+             
+             return RESULT_AS02_FORMAT;
+           }
+
+         if ( wave_descriptor->BlockAlign == 0 )
+           {
+             DefaultLogSink().Error("EssenceDescriptor has corrupt BlockAlign value, unable to continue.\n");
+             return RESULT_AS02_FORMAT;
+           }
+
+         if ( reader.Length() % wave_descriptor->BlockAlign != 0 )
+           {
+             DefaultLogSink().Error("Clip length is not an even multiple of BlockAlign, unable to continue.\n");
+             return RESULT_AS02_FORMAT;
+           }
+
+         m_ClipEssenceBegin = m_File.Tell();
+         m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*wave_descriptor, edit_rate);
+         m_ContainerDuration = reader.Length() / m_SamplesPerFrame;
        }
     }
 
-  // TODO: test file for sane CBR index BytesPerEditUnit
-
   return result;
 }
 
-
-//
 //
 ASDCP::Result_t
 AS_02::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& FrameBuf,
                                            ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC)
 {
   if ( ! m_File.IsOpen() )
-    return RESULT_INIT;
+    {
+      return RESULT_INIT;
+    }
+
+  if ( FrameNum > m_ContainerDuration )
+    {
+      return RESULT_RANGE;
+    }
+
+  assert(m_ClipEssenceBegin);
+  Result_t result = RESULT_OK;
+  ui64_t position = m_ClipEssenceBegin + ( FrameNum * m_SamplesPerFrame );
 
-  assert(m_Dict);
-  return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
+  if ( m_File.Tell() != position )
+    {
+      result = m_File.Seek(position);
+    }
+
+  if ( KM_SUCCESS(result) )
+    {
+      result = m_File.Read(FrameBuf.Data(), m_SamplesPerFrame);
+    }
+
+  if ( KM_SUCCESS(result) )
+    {
+      FrameBuf.Size(m_SamplesPerFrame);
+    }
+
+  return result;
 }
 
 
+//------------------------------------------------------------------------------------------
+//
+
+
 AS_02::PCM::MXFReader::MXFReader()
 {
   m_Reader = new h__Reader(DefaultCompositeDict());
 }
 
-
 AS_02::PCM::MXFReader::~MXFReader()
 {
-  if ( m_Reader && m_Reader->m_File.IsOpen() )
-    m_Reader->Close();
 }
 
 // Warning: direct manipulation of MXF structures can interfere
@@ -225,9 +237,22 @@ AS_02::PCM::MXFReader::RIP()
 // Open the file for reading. The file must exist. Returns error if the
 // operation cannot be completed.
 ASDCP::Result_t
-AS_02::PCM::MXFReader::OpenRead(const char* filename) const
+AS_02::PCM::MXFReader::OpenRead(const std::string& filename, const ASDCP::Rational& edit_rate)
 {
-  return m_Reader->OpenRead(filename);
+  return m_Reader->OpenRead(filename, edit_rate);
+}
+
+//
+Result_t
+AS_02::PCM::MXFReader::Close() const
+{
+  if ( m_Reader && m_Reader->m_File.IsOpen() )
+    {
+      m_Reader->Close();
+      return RESULT_OK;
+    }
+
+  return RESULT_INIT;
 }
 
 // Reads a frame of essence from the MXF file. If the optional AESEncContext
@@ -245,20 +270,6 @@ AS_02::PCM::MXFReader::ReadFrame(ui32_t FrameNum, ASDCP::PCM::FrameBuffer& Frame
 }
 
 
-// Fill the struct with the values from the file's header.
-// Returns RESULT_INIT if the file is not open.
-ASDCP::Result_t
-AS_02::PCM::MXFReader::FillAudioDescriptor(ASDCP::PCM::AudioDescriptor& ADesc) const
-{
-  if ( m_Reader && m_Reader->m_File.IsOpen() )
-    {
-      ADesc = m_Reader->m_ADesc;
-      return RESULT_OK;
-    }
-
-  return RESULT_INIT;
-}
-
 // Fill the struct with the values from the file's header.
 // Returns RESULT_INIT if the file is not open.
 ASDCP::Result_t
@@ -300,58 +311,72 @@ class AS_02::PCM::MXFWriter::h__Writer : public AS_02::h__AS02Writer
   h__Writer();
 
 public:
-  ASDCP::PCM::AudioDescriptor m_ADesc;
-  byte_t          m_EssenceUL[SMPTE_UL_LENGTH];
-  ui64_t                       m_KLV_start;
+  ASDCP::MXF::WaveAudioDescriptor *m_WaveAudioDescriptor;
+  byte_t m_EssenceUL[SMPTE_UL_LENGTH];
+  ui32_t m_BytesPerFrame;
+  ui32_t m_SamplesPerFrame;
 
-  h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_KLV_start(0){
+  h__Writer(const Dictionary& d) : AS_02::h__AS02Writer(d), m_WaveAudioDescriptor(0), m_BytesPerFrame(0), m_SamplesPerFrame(0)
+  {
     memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
-
   }
 
   virtual ~h__Writer(){}
 
-  Result_t OpenWrite(const char*, ui32_t HeaderSize);
-  Result_t SetSourceStream(const ASDCP::PCM::AudioDescriptor&);
+  Result_t OpenWrite(const std::string&, ASDCP::MXF::FileDescriptor* essence_descriptor,
+                    ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size);
+  Result_t SetSourceStream(const ASDCP::Rational&);
   Result_t WriteFrame(const FrameBuffer&, ASDCP::AESEncContext* = 0, ASDCP::HMACContext* = 0);
-
   Result_t Finalize();
-
-  //void AddSourceClip(const MXF::Rational& EditRate, ui32_t TCFrameRate,
-  // const std::string& TrackName, const UL& EssenceUL,
-  // const UL& DataDefinition, const std::string& PackageLabel);
-  //void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
-  // const std::string& TrackName, const UL& DataDefinition,
-  // const std::string& PackageLabel);
-  //void AddEssenceDescriptor(const UL& WrappingUL);
-  //Result_t CreateBodyPart(const MXF::Rational& EditRate, ui32_t BytesPerEditUnit = 0);
-
-  ////new method to create BodyPartition for essence and index
-  //Result_t CreateBodyPartPair();
-  ////new method to finalize BodyPartion(index)
-  //Result_t CompleteIndexBodyPart();
-
-  // reimplement these functions in AS_02_PCM to support modifications for AS-02
-  //  Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,
-  //                      const byte_t* EssenceUL, AESEncContext* Ctx, HMACContext* HMAC);
-  //  Result_t WriteAS02Footer();
-
 };
 
 // Open the file for writing. The file must not exist. Returns error if
 // the operation cannot be completed.
 ASDCP::Result_t
-AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderSize)
+AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ASDCP::MXF::FileDescriptor* essence_descriptor,
+                                           ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list, const ui32_t& header_size)
 {
+  assert(essence_descriptor);
+
+  if ( essence_descriptor->GetUL() != UL(m_Dict->ul(MDD_WaveAudioDescriptor)) )
+    {
+      DefaultLogSink().Error("Essence descriptor is not a WaveAudioDescriptor.\n");
+      essence_descriptor->Dump();
+      return RESULT_AS02_FORMAT;
+    }
+
+  m_WaveAudioDescriptor = reinterpret_cast<ASDCP::MXF::WaveAudioDescriptor*>(essence_descriptor);
+
   if ( ! m_State.Test_BEGIN() )
-    return RESULT_STATE;
+    {
+      return RESULT_STATE;
+    }
 
-  Result_t result = m_File.OpenWrite(filename);
+  Result_t result = m_File.OpenWrite(filename.c_str());
 
-  if ( ASDCP_SUCCESS(result) )
+  if ( KM_SUCCESS(result) )
     {
-      m_HeaderSize = HeaderSize;
-      m_EssenceDescriptor = new WaveAudioDescriptor(m_Dict);
+      m_HeaderSize = header_size;
+      m_EssenceDescriptor = essence_descriptor;
+      m_WaveAudioDescriptor->SampleRate = m_WaveAudioDescriptor->AudioSamplingRate;
+
+      ASDCP::MXF::InterchangeObject_list_t::iterator i;
+      for ( i = essence_sub_descriptor_list.begin(); i != essence_sub_descriptor_list.end(); ++i )
+       {
+         if ( (*i)->GetUL() != UL(m_Dict->ul(MDD_AudioChannelLabelSubDescriptor))
+              && (*i)->GetUL() != UL(m_Dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
+              && (*i)->GetUL() != UL(m_Dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
+           {
+             DefaultLogSink().Error("Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
+             (*i)->Dump();
+           }
+
+         m_EssenceSubDescriptorList.push_back(*i);
+         GenRandomValue((*i)->InstanceUID);
+         m_EssenceDescriptor->SubDescriptors.push_back((*i)->InstanceUID);
+         *i = 0; // parent will only free the ones we don't keep
+       }
+
       result = m_State.Goto_INIT();
     }
 
@@ -361,52 +386,31 @@ AS_02::PCM::MXFWriter::h__Writer::OpenWrite(const char* filename, ui32_t HeaderS
 
 // Automatically sets the MXF file's metadata from the WAV parser info.
 ASDCP::Result_t
-AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::PCM::AudioDescriptor& ADesc)
+AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::Rational& edit_rate)
 {
   if ( ! m_State.Test_INIT() )
-    return RESULT_STATE;
-
-  if ( ADesc.EditRate != EditRate_24
-       && ADesc.EditRate != EditRate_25
-       && ADesc.EditRate != EditRate_30
-       && ADesc.EditRate != EditRate_48
-       && ADesc.EditRate != EditRate_50
-       && ADesc.EditRate != EditRate_60
-       && ADesc.EditRate != EditRate_23_98 )
     {
-      DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
-                            ADesc.EditRate.Numerator, ADesc.EditRate.Denominator);
-      return RESULT_RAW_FORMAT;
+      return RESULT_STATE;
     }
 
-  if ( ADesc.AudioSamplingRate != SampleRate_48k && ADesc.AudioSamplingRate != SampleRate_96k )
-    {
-      DefaultLogSink().Error("AudioDescriptor.AudioSamplingRate is not 48000/1 or 96000/1: %d/%d\n",
-                            ADesc.AudioSamplingRate.Numerator, ADesc.AudioSamplingRate.Denominator);
-      return RESULT_RAW_FORMAT;
-    }
+  fprintf(stderr, "edit_rate=%d/%d\n", edit_rate.Numerator, edit_rate.Denominator);
 
-  assert(m_Dict);
-  m_ADesc = ADesc;
+  memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssenceClip), SMPTE_UL_LENGTH);
+  m_EssenceUL[15] = 1; // set the stream identifier
+  Result_t result = m_State.Goto_READY();
 
-  Result_t result = PCM_ADesc_to_MD(m_ADesc, (WaveAudioDescriptor*)m_EssenceDescriptor);
-
-  if ( ASDCP_SUCCESS(result) )
+  if ( KM_SUCCESS(result) )
     {
-      memcpy(m_EssenceUL, m_Dict->ul(MDD_WAVEssence), SMPTE_UL_LENGTH);
-      //SMPTE 382M-2007: ByteNo. 15 - 02h - Wave Clip-Wrapped Element
-      m_EssenceUL[SMPTE_UL_LENGTH-2] = 2; // 02h - Wave Clip-Wrapped Element
-      m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
-      result = m_State.Goto_READY();
-    }
+      assert(m_WaveAudioDescriptor);
+      m_BytesPerFrame = AS_02::MXF::CalcFrameBufferSize(*m_WaveAudioDescriptor, edit_rate);
+      m_SamplesPerFrame = AS_02::MXF::CalcSamplesPerFrame(*m_WaveAudioDescriptor, edit_rate);
+      m_WaveAudioDescriptor->ContainerDuration = 0;
 
-  if ( ASDCP_SUCCESS(result) )
-    {
-      ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
+      fprintf(stderr, "m_BytesPerFrame=%d, m_SamplesPerFrame=%d\n", m_BytesPerFrame, m_SamplesPerFrame);
 
       result = WriteAS02Header(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
                               SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
-                              m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
+                              m_EssenceDescriptor->SampleRate, derive_timecode_rate_from_edit_rate(edit_rate), m_BytesPerFrame);
     }
 
   return result;
@@ -416,19 +420,42 @@ AS_02::PCM::MXFWriter::h__Writer::SetSourceStream(const ASDCP::PCM::AudioDescrip
 //
 //
 ASDCP::Result_t
-AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& FrameBuf, AESEncContext* Ctx,
+AS_02::PCM::MXFWriter::h__Writer::WriteFrame(const FrameBuffer& frame_buf, AESEncContext* Ctx,
                                             HMACContext* HMAC)
 {
+  if ( frame_buf.Size() == 0 )
+    {
+      DefaultLogSink().Error("The frame buffer size is zero.\n");
+      return RESULT_PARAM;
+    }
+
+  if ( frame_buf.Size() % m_BytesPerFrame != 0 )
+    {
+      DefaultLogSink().Error("The frame buffer does not contain an integral number of sample sets.\n");
+      return RESULT_AS02_FORMAT;
+    }
+
   Result_t result = RESULT_OK;
 
   if ( m_State.Test_READY() )
-    result = m_State.Goto_RUNNING(); // first time through
+    {
+      result = m_State.Goto_RUNNING(); // first time through
+    }
 
-  if ( ASDCP_SUCCESS(result) )
-    result = WriteEKLVPacket(FrameBuf, m_EssenceUL, Ctx, HMAC);
+  if ( KM_SUCCESS(result) && ! HasOpenClip() )
+    {
+      result = StartClip(m_EssenceUL, Ctx, HMAC);
+    }
 
-  if ( ASDCP_SUCCESS(result) )
-    m_FramesWritten++;
+  if ( KM_SUCCESS(result) )
+    {
+      result = WriteClipBlock(frame_buf);
+    }
+
+  if ( KM_SUCCESS(result) )
+    {
+      m_FramesWritten++;
+    }
 
   return result;
 }
@@ -443,7 +470,16 @@ AS_02::PCM::MXFWriter::h__Writer::Finalize()
 
   m_State.Goto_FINAL();
 
-  return WriteAS02Footer();
+  Result_t result = FinalizeClip(m_BytesPerFrame);
+
+  if ( KM_SUCCESS(result) )
+    {
+      fprintf(stderr, "m_FramesWritten=%d, m_SamplesPerFrame=%d\n", m_FramesWritten, m_SamplesPerFrame);
+      m_FramesWritten = m_FramesWritten * m_SamplesPerFrame;
+      WriteAS02Footer();
+    }
+
+  return result;
 }
 
 
@@ -494,16 +530,30 @@ AS_02::PCM::MXFWriter::RIP()
 // Open the file for writing. The file must not exist. Returns error if
 // the operation cannot be completed.
 ASDCP::Result_t
-AS_02::PCM::MXFWriter::OpenWrite(const char* filename, const WriterInfo& Info,
-                                const ASDCP::PCM::AudioDescriptor& ADesc, ui32_t HeaderSize)
+AS_02::PCM::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
+                                ASDCP::MXF::FileDescriptor* essence_descriptor,
+                                ASDCP::MXF::InterchangeObject_list_t& essence_sub_descriptor_list,
+                                const ASDCP::Rational& edit_rate, ui32_t header_size)
 {
+  if ( essence_descriptor == 0 )
+    {
+      DefaultLogSink().Error("Essence descriptor object required.\n");
+      return RESULT_PARAM;
+    }
+
+  if ( Info.EncryptedEssence )
+    {
+      DefaultLogSink().Error("Encryption not supported for ST 382 clip-wrap.\n");
+      return Kumu::RESULT_NOTIMPL;
+    }
+
   m_Writer = new h__Writer(DefaultSMPTEDict());
   m_Writer->m_Info = Info;
 
-  Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
+  Result_t result = m_Writer->OpenWrite(filename, essence_descriptor, essence_sub_descriptor_list, header_size);
 
-  if ( ASDCP_SUCCESS(result) )
-    result = m_Writer->SetSourceStream(ADesc);
+  if ( KM_SUCCESS(result) )
+    result = m_Writer->SetSourceStream(edit_rate);
 
   if ( ASDCP_FAILURE(result) )
     m_Writer.release();
index cf9c3681fdda8415e48d05d01d79b59f69c86595..748e7954b40635fa4b78dcde7aed7745d8195297 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    AS_02_internal.h
   \version $Id: AS_02_internal.h ***       
@@ -50,34 +52,35 @@ namespace AS_02
 
   void default_md_object_init();
 
-
-  //
-  class AS02IndexWriter : public ASDCP::MXF::Partition
-    {
-      ASDCP::MXF::IndexTableSegment*  m_CurrentSegment;
-      ui32_t  m_BytesPerEditUnit;
-      ASDCP::MXF::Rational m_EditRate;
-
-      KM_NO_COPY_CONSTRUCT(AS02IndexWriter);
-      AS02IndexWriter();
-
-    public:
-      const ASDCP::Dictionary*&  m_Dict;
-      Kumu::fpos_t        m_ECOffset;
-      ASDCP::IPrimerLookup*      m_Lookup;
+  namespace MXF
+  {
+    //
+    class AS02IndexWriter : public ASDCP::MXF::Partition
+      {
+       ASDCP::MXF::IndexTableSegment*  m_CurrentSegment;
+       ui32_t  m_BytesPerEditUnit;
+       ASDCP::MXF::Rational m_EditRate;
+
+       KM_NO_COPY_CONSTRUCT(AS02IndexWriter);
+       AS02IndexWriter();
+
+      public:
+       const ASDCP::Dictionary*&  m_Dict;
+       ASDCP::IPrimerLookup*      m_Lookup;
       
-      AS02IndexWriter(const ASDCP::Dictionary*&);
-      virtual ~AS02IndexWriter();
+       AS02IndexWriter(const ASDCP::Dictionary*&);
+       virtual ~AS02IndexWriter();
 
-      Result_t WriteToFile(Kumu::FileWriter& Writer);
-      void     ResetCBR(Kumu::fpos_t offset);
-      void     Dump(FILE* = 0);
+       Result_t WriteToFile(Kumu::FileWriter& Writer);
+       void     ResetCBR(Kumu::fpos_t offset);
+       void     Dump(FILE* = 0);
 
-      ui32_t GetDuration() const;
-      void PushIndexEntry(const ASDCP::MXF::IndexTableSegment::IndexEntry&);
-      void SetIndexParamsCBR(ASDCP::IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate);
-      void SetIndexParamsVBR(ASDCP::IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset);
-    };
+       ui32_t GetDuration() const;
+       void PushIndexEntry(const ASDCP::MXF::IndexTableSegment::IndexEntry&);
+       void SetIndexParamsCBR(ASDCP::IPrimerLookup* lookup, ui32_t size, const ASDCP::Rational& Rate);
+       void SetIndexParamsVBR(ASDCP::IPrimerLookup* lookup, const ASDCP::Rational& Rate, Kumu::fpos_t offset);
+      };
+  }
 
   //
   class h__AS02Reader : public ASDCP::MXF::TrackFileReader<ASDCP::MXF::OP1aHeader, AS_02::MXF::AS02IndexReader>
@@ -86,20 +89,21 @@ namespace AS_02
       h__AS02Reader();
 
     public:
-      //      Partition *m_pCurrentBodyPartition;
-      //      ui64_t     m_EssenceStart;
-      //      std::vector<Partition*> m_BodyPartList;
-      //      ui32_t     m_start_pos;
-
       h__AS02Reader(const ASDCP::Dictionary&);
       virtual ~h__AS02Reader();
 
       Result_t OpenMXFRead(const char* filename);
 
+      // USE FRAME WRAPPING...
       Result_t ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
                             const byte_t* EssenceUL, ASDCP::AESDecContext* Ctx, ASDCP::HMACContext* HMAC);
 
-      Result_t LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset, i8_t& temporalOffset, i8_t& keyFrameOffset);
+     // 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!
     };
 
   //
@@ -109,9 +113,11 @@ namespace AS_02
       h__AS02Writer();
 
     public:
-      AS02IndexWriter m_IndexWriter;
-      ui32_t     m_PartitionSpace;  // edit units per partition
-      IndexStrategy_t    m_IndexStrategy; //Shim parameter index_strategy_frame/clip
+      ui64_t  m_ECStart; // offset of the first essence element
+      ui64_t  m_ClipStart;  // state variable for clip-wrap-in-progress
+      ui32_t  m_PartitionSpace;  // edit units per partition
+      AS_02::MXF::AS02IndexWriter m_IndexWriter;
+      IndexStrategy_t m_IndexStrategy; // per SMPTE ST 2067-5
 
       h__AS02Writer(const Dictionary&);
       virtual ~h__AS02Writer();
@@ -122,9 +128,18 @@ namespace AS_02
                               const ASDCP::UL& DataDefinition, const ASDCP::Rational& EditRate,
                               ui32_t TCFrameRate, ui32_t BytesPerEditUnit = 0);
 
+      // USE FRAME WRAPPING...
       Result_t WriteEKLVPacket(const ASDCP::FrameBuffer& FrameBuf,const byte_t* EssenceUL,
                               AESEncContext* Ctx, HMACContext* HMAC);
 
+      // OR CLIP WRAPPING...
+      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);
+
+      // NOT BOTH!
+
       Result_t WriteAS02Footer();
     };
 
index 4f6b0974f06bfe9554256bc5a16749c8576fea9a..1763d9cde4dbdf5629b65c9f782022ee17b5320f 100755 (executable)
@@ -66,6 +66,8 @@ The following use cases are supported by the library:
      JPEG 2000 stereoscopic codestream pairs
      PCM audio streams
      SMPTE 429-7 Timed Text XML with font and image resources
+     Proposed SMPTE Aux Data track file
+     Proposed Dolby (TM) Atmos track file
 
  o Read essence from a plaintext or ciphertext AS-DCP file:
      MPEG2 Video Elementary Stream
@@ -73,6 +75,8 @@ The following use cases are supported by the library:
      JPEG 2000 stereoscopic codestream pairs
      PCM audio streams
      SMPTE 429-7 Timed Text XML with font and image resources
+     Proposed SMPTE Aux Data track file
+     Proposed Dolby (TM) Atmos track file
 
  o Read header metadata from an AS-DCP file
 
@@ -213,6 +217,7 @@ namespace ASDCP {
     ESS_JPEG_2000_S,          // the file contains one or more JPEG 2000 codestream pairs (stereoscopic)
     ESS_DCDATA_UNKNOWN,       // the file contains one or more D-Cinema Data bytestreams
     ESS_DCDATA_DOLBY_ATMOS,   // the file contains one or more DolbyATMOS bytestreams
+    ESS_MAX
   };
 
   // Determine the type of essence contained in the given MXF file. RESULT_OK
@@ -346,7 +351,8 @@ namespace ASDCP {
   {
     LS_MXF_UNKNOWN,
     LS_MXF_INTEROP,
-    LS_MXF_SMPTE
+    LS_MXF_SMPTE,
+    LS_MAX
   };
 
   //
@@ -842,6 +848,7 @@ namespace ASDCP {
        CF_CFG_3, // 7.1 (SDDS) with optional HI/VI
        CF_CFG_4, // Wild Track Format
        CF_CFG_5, // 7.1 DS with optional HI/VI
+       CF_MAX
       };
 
       struct AudioDescriptor
index 1aec48eb0ba990a69960c014e674a20603de7214..956a867ea7dbd5d1640c1269f852eeb0b3c00d84 100755 (executable)
@@ -1167,11 +1167,9 @@ lh__Writer::SetSourceStream(const PictureDescriptor& PDesc, const std::string& l
 
   if ( ASDCP_SUCCESS(result) )
     {
-      ui32_t TCFrameRate = ( m_PDesc.EditRate == EditRate_23_98  ) ? 24 : m_PDesc.EditRate.Numerator;
-
       result = WriteASDCPHeader(label, UL(m_Dict->ul(MDD_JPEG_2000Wrapping)),
                                PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
-                               LocalEditRate, TCFrameRate);
+                               LocalEditRate, derive_timecode_rate_from_edit_rate(m_PDesc.EditRate));
     }
 
   return result;
index 6dc34f2f9ac5594d81f22d26b71279dafbf6a4dc..b10c8b53b995b619df6164e1f2b399ebac3e9960 100755 (executable)
@@ -568,11 +568,9 @@ ASDCP::MPEG2::MXFWriter::h__Writer::SetSourceStream(const VideoDescriptor& VDesc
 
   if ( ASDCP_SUCCESS(result) )
     {
-      ui32_t TCFrameRate = ( m_VDesc.EditRate == EditRate_23_98  ) ? 24 : m_VDesc.EditRate.Numerator;
-
       result = WriteASDCPHeader(MPEG_PACKAGE_LABEL, UL(m_Dict->ul(MDD_MPEG2_VESWrapping)), 
                                PICT_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_PictureDataDef)),
-                               m_VDesc.EditRate, TCFrameRate);
+                               m_VDesc.EditRate, derive_timecode_rate_from_edit_rate(m_VDesc.EditRate));
     }
 
   return result;
index c126f2515878482cd8801f983c72923fb3564f50..d683ed07feead910e2c75942b9b8673e3b0bf626 100755 (executable)
@@ -577,18 +577,10 @@ ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
 
   if ( ASDCP_SUCCESS(result) )
     {
-      ui32_t TCFrameRate = m_ADesc.EditRate.Numerator;
-
-      if ( m_ADesc.EditRate == EditRate_23_98  )
-       TCFrameRate = 24;
-      else if ( m_ADesc.EditRate == EditRate_18  )
-       TCFrameRate = 18;
-      else if ( m_ADesc.EditRate == EditRate_22  )
-       TCFrameRate = 22;
-      
       result = WriteASDCPHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
                                SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
-                               m_ADesc.EditRate, TCFrameRate, calc_CBR_frame_size(m_Info, m_ADesc));
+                               m_ADesc.EditRate, derive_timecode_rate_from_edit_rate(m_ADesc.EditRate),
+                               calc_CBR_frame_size(m_Info, m_ADesc));
     }
 
   return result;
index 56407ed4e026f0a18980e8b1d81c1d29c143da76..e77207fb9297bd4c3b943bee5266e1cd5cff7bd4 100644 (file)
@@ -577,7 +577,8 @@ ASDCP::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedT
   if ( ASDCP_SUCCESS(result) )
     {
       InitHeader();
-      AddDMSegment(m_TDesc.EditRate, 24, TIMED_TEXT_DEF_LABEL,
+      // timecode rate and essence rate are the same
+      AddDMSegment(m_TDesc.EditRate, m_TDesc.EditRate, derive_timecode_rate_from_edit_rate(m_TDesc.EditRate), TIMED_TEXT_DEF_LABEL,
                   UL(m_Dict->ul(MDD_DataDataDef)), TIMED_TEXT_PACKAGE_LABEL);
 
       AddEssenceDescriptor(UL(m_Dict->ul(MDD_TimedTextWrapping)));
index 6fa4fa19b58901de33943cc18e6584849becac63..df342bf113cfdda87f0e681c84829e54777a2b32 100755 (executable)
@@ -126,6 +126,8 @@ namespace ASDCP
   //------------------------------------------------------------------------------------------
   //
 
+  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&);
 
@@ -158,7 +160,7 @@ namespace ASDCP
   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 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);
@@ -168,7 +170,6 @@ namespace ASDCP
                             ui64_t & StreamOffset, const ASDCP::FrameBuffer& FrameBuf, const byte_t* EssenceUL,
                             AESEncContext* Ctx, HMACContext* HMAC);
 
-
   //
  class KLReader : public ASDCP::KLVPacket
     {
@@ -295,7 +296,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 +310,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 +326,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,7 +379,7 @@ 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;
          
@@ -380,13 +411,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 +434,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 +461,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
@@ -540,7 +574,8 @@ 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)
        {
@@ -571,13 +606,13 @@ namespace ASDCP
 
          TrackSet<TimecodeComponent> MPTCTrack =
            CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
-                                                EditRate, TCFrameRate, 0, m_Dict);
+                                                tc_edit_rate, 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,
+                                                               TrackName, clip_edit_rate, DataDefinition,
                                                                2, m_Dict);
          m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
 
@@ -603,13 +638,13 @@ namespace ASDCP
 
          TrackSet<TimecodeComponent> FPTCTrack =
            CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
-                                              EditRate, TCFrameRate,
+                                              tc_edit_rate, 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,
+                                                             TrackName, clip_edit_rate, DataDefinition,
                                                              2, m_Dict);
          m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
 
@@ -630,7 +665,8 @@ namespace ASDCP
        }
 
        //
-       void AddDMSegment(const MXF::Rational& EditRate, ui32_t TCFrameRate,
+       void AddDMSegment(const MXF::Rational& clip_edit_rate,
+                         const MXF::Rational& tc_edit_rate, ui32_t tc_frame_rate,
                          const std::string& TrackName, const UL& DataDefinition,
                          const std::string& PackageLabel)
        {
@@ -661,13 +697,13 @@ namespace ASDCP
 
          TrackSet<TimecodeComponent> MPTCTrack =
            CreateTimecodeTrack<MaterialPackage>(m_HeaderPart, *m_MaterialPackage,
-                                                EditRate, TCFrameRate, 0, m_Dict);
+                                                tc_edit_rate, tc_frame_rate, 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,
+                                                              TrackName, clip_edit_rate, DataDefinition,
                                                               2, m_Dict);
          m_DurationUpdateList.push_back(&(MPTrack.Sequence->Duration));
 
@@ -693,14 +729,14 @@ namespace ASDCP
 
          TrackSet<TimecodeComponent> FPTCTrack =
            CreateTimecodeTrack<SourcePackage>(m_HeaderPart, *m_FilePackage,
-                                              EditRate, TCFrameRate,
-                                              ui64_C(3600) * TCFrameRate, m_Dict);
+                                              clip_edit_rate, tc_frame_rate,
+                                              ui64_C(3600) * tc_frame_rate, 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,
+                                                            TrackName, clip_edit_rate, DataDefinition,
                                                             2, m_Dict);
          m_DurationUpdateList.push_back(&(FPTrack.Sequence->Duration));
 
@@ -708,7 +744,7 @@ namespace ASDCP
          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->EventComment = "ST 429-5 Timed Text";
 
          m_DurationUpdateList.push_back(&(FPTrack.Clip->Duration));
          m_EssenceDescriptor->LinkedTrackID = FPTrack.Track->TrackID;
index 42bdce0d4e984a7282e74096f1b3b0a8f687203f..b7796bcd0a807ed324e05f35ed0b8e833999411a 100755 (executable)
@@ -35,7 +35,7 @@ const ui32_t kl_length = ASDCP::SMPTE_UL_LENGTH + ASDCP::MXF_BER_LENGTH;
 
 //
 ASDCP::MXF::IndexTableSegment::IndexTableSegment(const Dictionary*& d) :
-  InterchangeObject(d), m_Dict(d),
+  InterchangeObject(d), m_Dict(d), RtFileOffset(0), RtEntryOffset(0),
   IndexStartPosition(0), IndexDuration(0), EditUnitByteCount(0),
   IndexSID(129), BodySID(1), SliceCount(0), PosTableCount(0)
 {
index bc99cd1cb27b5ac781f37b8a97698abe64648f66..9279f300a41c6d0e9736a53be8f969adf7e22184 100755 (executable)
@@ -88,6 +88,7 @@ namespace Kumu
     LOG_NOTICE,   // application user info
     LOG_ALERT,    // application non-fatal or near-miss error
     LOG_CRIT,     // application fatal error
+    LOG_MAX
   };
 
 
index 5afb75edc2216ddb707af064911613ee5bc6e463..c60fc061181e1311ca6204dab3bddfdc47aed6ae 100644 (file)
@@ -995,6 +995,24 @@ static const ASDCP::MDDEntry s_MDD_Table[] = {
   { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 319
       0x03, 0x02, 0x01, 0x0f, 0x00, 0x00, 0x00, 0x00 },
       {0}, false, "DCAudioChannel_VIN" },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 320
+      0x03, 0x02, 0x02, 0x01, 0x00, 0x00, 0x00, 0x00 },
+      {0}, false, "DCAudioSoundfield_51" },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 321
+      0x03, 0x02, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00 },
+      {0}, false, "DCAudioSoundfield_71" },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 322
+      0x03, 0x02, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00 },
+      {0}, false, "DCAudioSoundfield_SDS" },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 323
+      0x03, 0x02, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00 },
+      {0}, false, "DCAudioSoundfield_61" },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d, // 324
+      0x03, 0x02, 0x02, 0x05, 0x00, 0x00, 0x00, 0x00 },
+      {0}, false, "DCAudioSoundfield_M" },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x01, // 325
+      0x0d, 0x01, 0x03, 0x01, 0x16, 0x01, 0x02, 0x00 },
+      {0}, false, "WAVEssenceClip" },
 
   { {0}, {0}, false, 0 }
 };
index ab8482f2775cead3bd92357fe3005d0ccbeb0be0..7cbe35e6a385a9f7c01108f475be54b791b79dc1 100755 (executable)
--- a/src/MDD.h
+++ b/src/MDD.h
@@ -355,6 +355,12 @@ namespace ASDCP {
        MDD_DCAudioChannel_Cs, // 317
        MDD_DCAudioChannel_HI, // 318
        MDD_DCAudioChannel_VIN, // 319
+       MDD_DCAudioSoundfield_51, // 320
+       MDD_DCAudioSoundfield_71, // 321
+       MDD_DCAudioSoundfield_SDS, // 322
+       MDD_DCAudioSoundfield_61, // 323
+       MDD_DCAudioSoundfield_M, // 324
+       MDD_WAVEssenceClip, // 325
         MDD_Max
 
     }; // enum MDD_t
index ad34095de8a91ffcb144764b383872162abefaf7..6686e6aafa6ac2a326fbc22096d7273826eff871 100755 (executable)
@@ -781,6 +781,11 @@ ASDCP::MXF::OP1aHeader::InitFromBuffer(const byte_t* p, ui32_t l)
          if ( object->IsA(m_Dict->ul(MDD_KLVFill)) )
            {
              delete object;
+
+             if ( p > end_p )
+               {
+                 DefaultLogSink().Error("Fill item short read: %d.\n", p - end_p);
+               }
            }
          else if ( object->IsA(m_Dict->ul(MDD_Primer)) ) // TODO: only one primer should be found
            {
@@ -1442,6 +1447,207 @@ ASDCP::MXF::CreateObject(const Dictionary*& Dict, const UL& label)
 }
 
 
+//------------------------------------------------------------------------------------------
+
+
+ASDCP::MXF::MCAConfigParser::MCAConfigParser(const Dictionary*& d) : m_Dict(d), m_ChannelCount(0)
+{
+  label_map.insert(label_map_t::value_type("L", m_Dict->ul(MDD_DCAudioChannel_L)));
+  label_map.insert(label_map_t::value_type("R", m_Dict->ul(MDD_DCAudioChannel_R)));
+  label_map.insert(label_map_t::value_type("C", m_Dict->ul(MDD_DCAudioChannel_C)));
+  label_map.insert(label_map_t::value_type("LFE", m_Dict->ul(MDD_DCAudioChannel_LFE)));
+  label_map.insert(label_map_t::value_type("Ls", m_Dict->ul(MDD_DCAudioChannel_Ls)));
+  label_map.insert(label_map_t::value_type("Rs", m_Dict->ul(MDD_DCAudioChannel_Rs)));
+  label_map.insert(label_map_t::value_type("Lss", m_Dict->ul(MDD_DCAudioChannel_Lss)));
+  label_map.insert(label_map_t::value_type("Rss", m_Dict->ul(MDD_DCAudioChannel_Rss)));
+  label_map.insert(label_map_t::value_type("Lrs", m_Dict->ul(MDD_DCAudioChannel_Lrs)));
+  label_map.insert(label_map_t::value_type("Rrs", m_Dict->ul(MDD_DCAudioChannel_Rrs)));
+  label_map.insert(label_map_t::value_type("Lc", m_Dict->ul(MDD_DCAudioChannel_Lc)));
+  label_map.insert(label_map_t::value_type("Rc", m_Dict->ul(MDD_DCAudioChannel_Rc)));
+  label_map.insert(label_map_t::value_type("Cs", m_Dict->ul(MDD_DCAudioChannel_Cs)));
+  label_map.insert(label_map_t::value_type("HI", m_Dict->ul(MDD_DCAudioChannel_HI)));
+  label_map.insert(label_map_t::value_type("VIN", m_Dict->ul(MDD_DCAudioChannel_VIN)));
+  label_map.insert(label_map_t::value_type("51", m_Dict->ul(MDD_DCAudioSoundfield_51)));
+  label_map.insert(label_map_t::value_type("71", m_Dict->ul(MDD_DCAudioSoundfield_71)));
+  label_map.insert(label_map_t::value_type("SDS", m_Dict->ul(MDD_DCAudioSoundfield_SDS)));
+  label_map.insert(label_map_t::value_type("61", m_Dict->ul(MDD_DCAudioSoundfield_61)));
+  label_map.insert(label_map_t::value_type("M", m_Dict->ul(MDD_DCAudioSoundfield_M)));
+}
+
+//
+ui32_t
+ASDCP::MXF::MCAConfigParser::ChannelCount() const
+{
+  return m_ChannelCount;
+}
+
+// 51(L,R,C,LFE,Ls,Rs),HI,VIN
+bool
+ASDCP::MXF::MCAConfigParser::DecodeString(const std::string& s, const std::string& language)
+{
+  std::string symbol_buf;
+  m_ChannelCount = 0;
+  ASDCP::MXF::SoundfieldGroupLabelSubDescriptor *current_soundfield = 0;
+  std::string::const_iterator i;
+
+  for ( i = s.begin(); i != s.end(); ++i )
+    {
+      if ( *i == '(' )
+       {
+         if ( current_soundfield != 0 )
+           {
+             fprintf(stderr, "Encountered '(', already processing a soundfield group.\n");
+             return false;
+           }
+
+         if ( symbol_buf.empty() )
+           {
+             fprintf(stderr, "Encountered '(', without leading soundfield group symbol.\n");
+             return false;
+           }
+
+         label_map_t::const_iterator i = label_map.find(symbol_buf);
+      
+         if ( i == label_map.end() )
+           {
+             fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+             return false;
+           }
+      
+         if ( i->second.Value()[10] != 2 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
+           {
+             fprintf(stderr, "Not a soundfield group symbol: '%s'\n", symbol_buf.c_str());
+             return false;
+           }
+
+         current_soundfield = new ASDCP::MXF::SoundfieldGroupLabelSubDescriptor(m_Dict);
+
+         GenRandomValue(current_soundfield->InstanceUID);
+         GenRandomValue(current_soundfield->MCALinkID);
+         current_soundfield->MCATagSymbol = "sg" + i->first;
+         current_soundfield->MCATagName = i->first;
+         current_soundfield->RFC5646SpokenLanguage = language;
+         current_soundfield->MCALabelDictionaryID = i->second;
+         push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(current_soundfield));
+         symbol_buf.clear();
+       }
+      else if ( *i == ')' )
+       {
+         if ( current_soundfield == 0 )
+           {
+             fprintf(stderr, "Encountered ')', not currently processing a soundfield group.\n");
+             return false;
+           }
+
+         if ( symbol_buf.empty() )
+           {
+             fprintf(stderr, "Soundfield group description contains no channels.\n");
+             return false;
+           }
+
+         label_map_t::const_iterator i = label_map.find(symbol_buf);
+      
+         if ( i == label_map.end() )
+           {
+             fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+             return false;
+           }
+
+         ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
+           new ASDCP::MXF::AudioChannelLabelSubDescriptor(m_Dict);
+
+         GenRandomValue(channel_descr->InstanceUID);
+         assert(current_soundfield);
+         channel_descr->MCALinkID = current_soundfield->MCALinkID;
+         channel_descr->MCAChannelID = m_ChannelCount++;
+         channel_descr->MCATagSymbol = "ch" + i->first;
+         channel_descr->MCATagName = i->first;
+         channel_descr->RFC5646SpokenLanguage = language;
+         channel_descr->MCALabelDictionaryID = i->second;
+         push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
+         symbol_buf.clear();
+         current_soundfield = 0;
+       }
+      else if ( *i == ',' )
+       {
+         if ( ! symbol_buf.empty() )
+           {
+             label_map_t::const_iterator i = label_map.find(symbol_buf);
+
+             if ( i == label_map.end() )
+               {
+                 fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+                 return false;
+               }
+
+             if ( i->second.Value()[10] != 1 ) // magic depends on UL "Essence Facet" byte (see ST 428-12)
+               {
+                 fprintf(stderr, "Not a channel symbol: '%s'\n", symbol_buf.c_str());
+                 return false;
+               }
+
+             ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
+               new ASDCP::MXF::AudioChannelLabelSubDescriptor(m_Dict);
+
+             GenRandomValue(channel_descr->InstanceUID);
+
+             if ( current_soundfield != 0 )
+               {
+                 channel_descr->MCALinkID = current_soundfield->MCALinkID;
+               }
+
+             channel_descr->MCAChannelID = m_ChannelCount++;
+             channel_descr->MCATagSymbol = "ch" + i->first;
+             channel_descr->MCATagName = i->first;
+             channel_descr->RFC5646SpokenLanguage = language;
+             channel_descr->MCALabelDictionaryID = i->second;
+             push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
+             symbol_buf.clear();
+           }
+       }
+      else if ( isalnum(*i) )
+       {
+         symbol_buf += *i;
+       }
+      else if ( ! isspace(*i) )
+       {
+         fprintf(stderr, "Unexpected character '%c'.\n", *i);
+         return false;
+       }
+    }
+
+  if ( ! symbol_buf.empty() )
+    {
+      label_map_t::const_iterator i = label_map.find(symbol_buf);
+      
+      if ( i == label_map.end() )
+       {
+         fprintf(stderr, "Unknown symbol: '%s'\n", symbol_buf.c_str());
+         return false;
+       }
+
+      ASDCP::MXF::AudioChannelLabelSubDescriptor *channel_descr =
+       new ASDCP::MXF::AudioChannelLabelSubDescriptor(m_Dict);
+
+      GenRandomValue(channel_descr->InstanceUID);
+
+      if ( current_soundfield != 0 )
+       {
+         channel_descr->MCALinkID = current_soundfield->MCALinkID;
+       }
+
+      channel_descr->MCAChannelID = m_ChannelCount++;
+      channel_descr->MCATagSymbol = "ch" + i->first;
+      channel_descr->MCATagName = i->first;
+      channel_descr->RFC5646SpokenLanguage = language;
+      channel_descr->MCALabelDictionaryID = i->second;
+      push_back(reinterpret_cast<ASDCP::MXF::InterchangeObject*>(channel_descr));
+    }
+
+  return true;
+}
+
+
 //
 // end MXF.cpp
 //
index 50574c29b700dcff39c2ea1e81ac69459618de7a..3b901ac93ef9a9c6dd3fa51c8251749f4d3afdbd 100755 (executable)
--- a/src/MXF.h
+++ b/src/MXF.h
@@ -242,6 +242,9 @@ namespace ASDCP
          virtual void     Dump(FILE* stream = 0);
        };
 
+      //
+      typedef std::list<InterchangeObject*> InterchangeObject_list_t;
+
       //
       class Preface : public InterchangeObject
        {
@@ -321,6 +324,8 @@ namespace ASDCP
            };
 
          const Dictionary*& m_Dict;
+         ui64_t  RtFileOffset; // not part of the MXF structure: used to manage runtime index access 
+         ui64_t  RtEntryOffset;
 
          Rational    IndexEditRate;
          ui64_t      IndexStartPosition;
@@ -410,6 +415,42 @@ namespace ASDCP
          virtual void     SetIndexParamsVBR(IPrimerLookup* lookup, const Rational& Rate, Kumu::fpos_t offset);
        };
 
+      //---------------------------------------------------------------------------------
+      //
+
+      //
+      inline std::string to_lower(std::string str) {
+       std::transform(str.begin(), str.end(), str.begin(), ::tolower);
+       return str;
+      }
+
+      // ignore case when searching for audio labels
+      struct ci_comp
+      {
+       inline bool operator()(const std::string& a, const std::string& b) const {
+         return to_lower(a) < to_lower(b);
+       }
+      };
+
+      //
+      class MCAConfigParser : public InterchangeObject_list_t
+       {
+         typedef std::map<const std::string, const UL, ci_comp> label_map_t;
+         label_map_t label_map;
+         ui32_t m_ChannelCount;
+
+         const Dictionary*& m_Dict;
+
+         KM_NO_COPY_CONSTRUCT(MCAConfigParser);
+         MCAConfigParser();
+         
+       public:
+         MCAConfigParser(const Dictionary*&);
+         bool DecodeString(const std::string& s, const std::string& language = "en");
+
+         // Valid only after a successful call to DecodeString
+         ui32_t ChannelCount() const;
+       };
 
     } // namespace MXF
 } // namespace ASDCP
index 82a5c6860fd0199e738108e3ee9507a6f3130881..05672d6861b00dca95d802a21875795c246db7e0 100755 (executable)
@@ -325,7 +325,7 @@ namespace ASDCP
       class VersionType : public Kumu::IArchive
        {
        public:
-         enum Release_t { RL_UNKNOWN, RL_RELEASE, RL_DEVELOPMENT, RL_PATCHED, RL_BETA, RL_PRIVATE };
+         enum Release_t { RL_UNKNOWN, RL_RELEASE, RL_DEVELOPMENT, RL_PATCHED, RL_BETA, RL_PRIVATE, RL_MAX };
          ui16_t Major;
          ui16_t Minor;
          ui16_t Patch;
index 9611adc05adb6c53a6341df6d242d110d606c02d..6f507c8e9b6b715606ac2d8a56bd783131313696 100755 (executable)
@@ -98,7 +98,8 @@ class WavFileWriter
   enum SplitType_t {
     ST_NONE,   // write all channels to a single WAV file
     ST_MONO,   // write each channel a separate WAV file
-    ST_STEREO  // write channel pairs to separate WAV files
+    ST_STEREO, // write channel pairs to separate WAV files
+    ST_MAX
   };
 
   ASDCP::Result_t
index 6e6850773a4627a85939200e58e7d0a0eaac9922..42d48205f292f8fe0a6a3d1697b74da7b0914147 100755 (executable)
@@ -1,5 +1,7 @@
 /*
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+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
@@ -38,6 +40,10 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 #include <AS_02.h>
 #include <WavFileWriter.h>
 
+namespace ASDCP {
+  Result_t MD_to_PCM_ADesc(ASDCP::MXF::WaveAudioDescriptor* ADescObj, ASDCP::PCM::AudioDescriptor& ADesc);
+}
+
 using namespace ASDCP;
 
 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
@@ -63,7 +69,7 @@ banner(FILE* stream = stdout)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
 asdcplib may be copied only under the terms of the license found at\n\
 the top of every file in the asdcplib distribution kit.\n\n\
 Specify the -h (help) option for further information about %s\n\n",
@@ -132,6 +138,7 @@ public:
   bool   j2c_pedantic;   // passed to JP2K::SequenceParser::OpenRead
   ui32_t picture_rate;   // fps of picture when wrapping PCM
   ui32_t fb_size;        // size of picture frame buffer
+  Rational edit_rate;    // frame buffer size for reading clip-wrapped PCM
   const char* file_prefix; // filename pre for files written by the extract mode
   byte_t key_value[KeyLen];  // value of given encryption key (when key_flag is true)
   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
@@ -281,15 +288,58 @@ read_JP2K_file(CommandOptions& Options)
 
   if ( ASDCP_SUCCESS(result) )
     {
-      JP2K::PictureDescriptor PDesc;
-      Reader.FillPictureDescriptor(PDesc);
-
-      frame_count = PDesc.ContainerDuration;
-
       if ( Options.verbose_flag )
        {
          fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
-         JP2K::PictureDescriptorDump(PDesc);
+       }
+
+      ASDCP::MXF::RGBAEssenceDescriptor *rgba_descriptor = 0;
+      ASDCP::MXF::CDCIEssenceDescriptor *cdci_descriptor = 0;
+
+      result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_RGBAEssenceDescriptor),
+                                                    reinterpret_cast<MXF::InterchangeObject**>(&rgba_descriptor));
+
+      if ( KM_SUCCESS(result) )
+       {
+         assert(rgba_descriptor);
+         frame_count = rgba_descriptor->ContainerDuration;
+
+         if ( Options.verbose_flag )
+           {
+             rgba_descriptor->Dump();
+           }
+       }
+      else
+       {
+         result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_CDCIEssenceDescriptor),
+                                                        reinterpret_cast<MXF::InterchangeObject**>(&cdci_descriptor));
+
+         if ( KM_SUCCESS(result) )
+           {
+             assert(cdci_descriptor);
+             frame_count = cdci_descriptor->ContainerDuration;
+
+             if ( Options.verbose_flag )
+               {
+                 cdci_descriptor->Dump();
+               }
+           }
+         else
+           {
+             fprintf(stderr, "File does not contain an essence descriptor.\n");
+             frame_count = Reader.AS02IndexReader().GetDuration();
+           }
+       }
+
+      if ( frame_count == 0 )
+       {
+         frame_count = Reader.AS02IndexReader().GetDuration();
+       }
+
+      if ( frame_count == 0 )
+       {
+         fprintf(stderr, "Unable to determine file duration.\n");
+         return RESULT_FAIL;
        }
     }
 
@@ -360,43 +410,87 @@ read_PCM_file(CommandOptions& Options)
   AS_02::PCM::MXFReader     Reader;
   PCM::FrameBuffer   FrameBuffer;
   WavFileWriter      OutWave;
-  PCM::AudioDescriptor ADesc;
   ui32_t last_frame = 0;
+  ASDCP::MXF::WaveAudioDescriptor *wave_descriptor = 0;
 
-  Result_t result = Reader.OpenRead(Options.input_filename);
+  if ( Options.edit_rate == Rational(0,0) ) // todo, make this available to the CLI
+    {
+      Options.edit_rate = EditRate_24;
+    }
+
+  Result_t result = Reader.OpenRead(Options.input_filename, Options.edit_rate);
 
   if ( ASDCP_SUCCESS(result) )
     {
-      Reader.FillAudioDescriptor(ADesc);
-      ADesc.EditRate = Rational(1, 1);
-      FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
+      if ( Options.verbose_flag )
+       {
+         fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+       }
+
+      result = Reader.OP1aHeader().GetMDObjectByType(DefaultCompositeDict().ul(MDD_WaveAudioDescriptor),
+                                                    reinterpret_cast<MXF::InterchangeObject**>(&wave_descriptor));
+
+      if ( KM_SUCCESS(result) )
+       {
+         assert(wave_descriptor);
+         last_frame = wave_descriptor->ContainerDuration;
+
+         if ( Options.verbose_flag )
+           {
+             wave_descriptor->Dump();
+           }
+       }
+      else
+       {
+         fprintf(stderr, "File does not contain an essence descriptor.\n");
+         last_frame = Reader.AS02IndexReader().GetDuration();
+       }
+
+      if ( last_frame == 0 )
+       {
+         fprintf(stderr, "Unable to determine file duration.\n");
+         return RESULT_FAIL;
+       }
+
+      FrameBuffer.Capacity(AS_02::MXF::CalcFrameBufferSize(*wave_descriptor, Options.edit_rate));
 
       if ( Options.verbose_flag )
-       PCM::AudioDescriptorDump(ADesc);
+       {
+         wave_descriptor->Dump();
+       }
     }
 
   if ( ASDCP_SUCCESS(result) )
     {
-      last_frame = ADesc.ContainerDuration;
-
       if ( Options.duration > 0 && Options.duration < last_frame )
        last_frame = Options.duration;
 
       if ( Options.start_frame > 0 )
        {
-         if ( Options.start_frame > ADesc.ContainerDuration )
+         if ( Options.start_frame > last_frame )
            {
              fprintf(stderr, "Start value greater than file duration.\n");
              return RESULT_FAIL;
            }
 
-         last_frame = Kumu::xmin(Options.start_frame + last_frame, ADesc.ContainerDuration);
+         last_frame = Kumu::xmin(Options.start_frame + last_frame, last_frame);
        }
 
-      ADesc.ContainerDuration = last_frame - Options.start_frame;
-      OutWave.OpenWrite(ADesc, Options.file_prefix,
-                       ( Options.split_wav ? WavFileWriter::ST_STEREO : 
-                         ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
+      last_frame = last_frame - Options.start_frame;
+
+      PCM::AudioDescriptor ADesc;
+
+      result = MD_to_PCM_ADesc(wave_descriptor, ADesc);
+
+      if ( ASDCP_SUCCESS(result) )
+       {
+         ADesc.ContainerDuration = last_frame;
+         ADesc.EditRate = Options.edit_rate;
+
+         result = OutWave.OpenWrite(ADesc, Options.file_prefix,
+                                    ( Options.split_wav ? WavFileWriter::ST_STEREO : 
+                                      ( Options.mono_wav ? WavFileWriter::ST_MONO : WavFileWriter::ST_NONE ) ));
+       }
     }
 
   if ( ASDCP_SUCCESS(result) && Options.key_flag )
index 6f7901281fe4f7df339f2f56bf1cdf0b1121b188..b5cb138dd932cd19ed4a63300affdc80bc026d93 100755 (executable)
@@ -1,5 +1,7 @@
 /*
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst
+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
@@ -43,6 +45,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace ASDCP;
 
 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
+const ASDCP::Dictionary *g_dict = 0;
 
 
 const char*
@@ -52,6 +55,8 @@ RationalToString(const ASDCP::Rational& r, char* buf, const ui32_t& len)
   return buf;
 }
 
+
+
 //------------------------------------------------------------------------------------------
 //
 // command line option parser class
@@ -92,7 +97,7 @@ banner(FILE* stream = stdout)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2011-2012, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
+Copyright (c) 2011-2013, Robert Scheler, Heiko Sparenberg Fraunhofer IIS, John Hurst\n\n\
 asdcplib may be copied only under the terms of the license found at\n\
 the top of every file in the asdcplib distribution kit.\n\n\
 Specify the -h (help) option for further information about %s\n\n",
@@ -108,7 +113,7 @@ USAGE: %s [-h|-help] [-V]\n\
 \n\
        %s [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
           [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
-            [-M] [-p <n>/<d>] [-s <seconds>] [-v] [-W]\n\
+            [-M] [-m <expr>] [-r <n>/<d>] [-s <seconds>] [-v] [-W]\n\
           [-z|-Z] <input-file>+ <output-file>\n\n",
          PROGRAM_NAME, PROGRAM_NAME);
 
@@ -122,12 +127,14 @@ Options:\n\
   -j <key-id-str>   - Write key ID instead of creating a random value\n\
   -k <key-string>   - Use key for ciphertext operations\n\
   -M                - Do not create HMAC values when writing\n\
+  -m <expr>         - Write MCA labels using <expr>.  Example:\n\
+                        51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
   -a <UUID>         - Specify the Asset ID of the file\n\
   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
                       Defaults to 4,194,304 (4MB)\n\
   -d <duration>     - Number of frames to process, default all\n\
   -f <start-frame>  - Starting frame number, default 0\n\
-  -p <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
+  -r <n>/<d>        - Edit Rate of the output file.  24/1 is the default\n\
   -s <seconds>      - Duration of a frame-wrapped partition (default 60)\n\
   -v                - Verbose, prints informative messages to stderr\n\
   -W                - Read input file only, do not write source file\n\
@@ -138,6 +145,20 @@ Options:\n\
          o All option arguments must be separated from the option by whitespace.\n\n");
 }
 
+//
+static ASDCP::Rational
+decode_rational(const char* str_rat)
+{
+  assert(str_rat);
+  ui32_t Num = atoi(str_rat);
+  ui32_t Den = 0;
+
+  const char* den_str = strrchr(str_rat, ' ');
+  if ( den_str != 0 )
+    Den = atoi(den_str+1);
+
+  return ASDCP::Rational(Num, Den);
+}
 //
 //
 class CommandOptions
@@ -165,9 +186,10 @@ public:
   byte_t key_id_value[UUIDlen];// value of given key ID (when key_id_flag is true)
   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
   std::string out_file; //
-  bool show_ul_values;    /// if true, dump the UL table before going tp work.
+  bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
   Kumu::PathList_t filenames;  // list of filenames to be processed
   UL channel_assignment;
+  ASDCP::MXF::MCAConfigParser mca_config;
 
   //new attributes for AS-02 support 
   AS_02::IndexStrategy_t index_strategy; //Shim parameter index_strategy_frame/clip
@@ -178,8 +200,9 @@ public:
     error_flag(true), key_flag(false), key_id_flag(false), asset_id_flag(false),
     encrypt_header_flag(true), write_hmac(true), verbose_flag(false), fb_dump_size(0),
     no_write_flag(false), version_flag(false), help_flag(false), start_frame(0),
-    duration(0xffffffff), j2c_pedantic(true), edit_rate(30,1), fb_size(FRAME_BUFFER_SIZE),
-    show_ul_values(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60)
+    duration(0xffffffff), j2c_pedantic(true), edit_rate(24,1), fb_size(FRAME_BUFFER_SIZE),
+    show_ul_values_flag(false), index_strategy(AS_02::IS_FOLLOW), partition_space(60),
+    mca_config(g_dict)
   {
     memset(key_value, 0, KeyLen);
     memset(key_id_value, 0, UUIDlen);
@@ -277,11 +300,17 @@ public:
 
              case 'M': write_hmac = false; break;
 
-             case 'p':
-               TEST_EXTRA_ARG(i, 'p');
-               /// TODO: VERY BROKEN, WANT RATIONAL
-               edit_rate.Numerator = abs(atoi(argv[i]));
-               edit_rate.Denominator = 1;
+             case 'm':
+               TEST_EXTRA_ARG(i, 'm');
+               if ( ! mca_config.DecodeString(argv[i]) )
+                 {
+                   return;
+                 }
+               break;
+
+             case 'r':
+               TEST_EXTRA_ARG(i, 'r');
+               edit_rate = decode_rational(argv[i]);
                break;
 
              case 's':
@@ -289,6 +318,7 @@ public:
                partition_space = abs(atoi(argv[i]));
                break;
 
+             case 'u': show_ul_values_flag = true; break;
              case 'V': version_flag = true; break;
              case 'v': verbose_flag = true; break;
              case 'W': no_write_flag = true; break;
@@ -334,6 +364,15 @@ public:
 //------------------------------------------------------------------------------------------
 // JPEG 2000 essence
 
+namespace ASDCP {
+  Result_t JP2K_PDesc_to_MD(const ASDCP::JP2K::PictureDescriptor& PDesc,
+                           const ASDCP::Dictionary& dict,
+                           ASDCP::MXF::RGBAEssenceDescriptor *EssenceDescriptor,
+                           ASDCP::MXF::JPEG2000PictureSubDescriptor *EssenceSubDescriptor);
+
+  Result_t PCM_ADesc_to_MD(ASDCP::PCM::AudioDescriptor& ADesc, ASDCP::MXF::WaveAudioDescriptor* ADescObj);
+}
+
 // Write one or more plaintext JPEG 2000 codestreams to a plaintext AS-02 file
 // Write one or more plaintext JPEG 2000 codestreams to a ciphertext AS-02 file
 //
@@ -344,10 +383,11 @@ write_JP2K_file(CommandOptions& Options)
   HMACContext*            HMAC = 0;
   AS_02::JP2K::MXFWriter  Writer;
   JP2K::FrameBuffer       FrameBuffer(Options.fb_size);
-  JP2K::PictureDescriptor PDesc;
   JP2K::SequenceParser    Parser;
   byte_t                  IV_buf[CBC_BLOCK_SIZE];
   Kumu::FortunaRNG        RNG;
+  ASDCP::MXF::RGBAEssenceDescriptor *essence_descriptor = 0;
+  ASDCP::MXF::InterchangeObject_list_t essence_sub_descriptors;
 
   // set up essence parser
   Result_t result = Parser.OpenRead(Options.filenames.front().c_str(), Options.j2c_pedantic);
@@ -355,6 +395,7 @@ write_JP2K_file(CommandOptions& Options)
   // set up MXF writer
   if ( ASDCP_SUCCESS(result) )
     {
+      ASDCP::JP2K::PictureDescriptor PDesc;
       Parser.FillPictureDescriptor(PDesc);
       PDesc.EditRate = Options.edit_rate;
 
@@ -365,6 +406,17 @@ write_JP2K_file(CommandOptions& Options)
           fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
          JP2K::PictureDescriptorDump(PDesc);
        }
+
+      // TODO: optionally set up CDCIEssenceDescriptor
+      essence_descriptor = new ASDCP::MXF::RGBAEssenceDescriptor(g_dict);
+      essence_sub_descriptors.push_back(new ASDCP::MXF::JPEG2000PictureSubDescriptor(g_dict));
+
+      result = ASDCP::JP2K_PDesc_to_MD(PDesc, *g_dict, essence_descriptor,
+                                      reinterpret_cast<ASDCP::MXF::JPEG2000PictureSubDescriptor*>(essence_sub_descriptors.back()));
+
+      /// TODO: set with magic or some such thing
+      essence_descriptor->ComponentMaxRef = 4095;
+      essence_descriptor->ComponentMinRef = 0;
     }
 
   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
@@ -403,7 +455,13 @@ write_JP2K_file(CommandOptions& Options)
        }
 
       if ( ASDCP_SUCCESS(result) )
-       result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc, Options.index_strategy, Options.partition_space);
+       {
+         result = Writer.OpenWrite(Options.out_file, Info,
+                                   static_cast<ASDCP::MXF::FileDescriptor*>(essence_descriptor),
+                                   essence_sub_descriptors,
+                                   Options.edit_rate, 16384, Options.index_strategy, Options.partition_space);
+         // TODO: make 16384 part of CommandOptions
+       }
     }
 
   if ( ASDCP_SUCCESS(result) )
@@ -462,16 +520,17 @@ write_PCM_file(CommandOptions& Options)
   PCMParserList     Parser;
   AS_02::PCM::MXFWriter    Writer;
   PCM::FrameBuffer  FrameBuffer;
-  PCM::AudioDescriptor ADesc;
   byte_t            IV_buf[CBC_BLOCK_SIZE];
   Kumu::FortunaRNG  RNG;
+  ASDCP::MXF::WaveAudioDescriptor *essence_descriptor = 0;
 
   // set up essence parser
-  Result_t result = Parser.OpenRead(Options.filenames, Rational(1, 1));
+  Result_t result = Parser.OpenRead(Options.filenames, Options.edit_rate);
 
   // set up MXF writer
   if ( ASDCP_SUCCESS(result) )
     {
+      ASDCP::PCM::AudioDescriptor ADesc;
       Parser.FillAudioDescriptor(ADesc);
 
       ADesc.EditRate = Options.edit_rate;
@@ -487,6 +546,27 @@ write_PCM_file(CommandOptions& Options)
          fputs("AudioDescriptor:\n", stderr);
          PCM::AudioDescriptorDump(ADesc);
        }
+
+      essence_descriptor = new ASDCP::MXF::WaveAudioDescriptor(g_dict);
+
+      result = ASDCP::PCM_ADesc_to_MD(ADesc, essence_descriptor);
+
+      if ( Options.mca_config.empty() )
+       {
+         essence_descriptor->ChannelAssignment = Options.channel_assignment;
+       }
+      else
+       {
+         if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
+           {
+             fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
+                     Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
+             return RESULT_FAIL;
+           }
+
+         // this is the d-cinema MCA label, what is the one for IMF?
+         essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
+       }
     }
 
   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
@@ -525,14 +605,9 @@ write_PCM_file(CommandOptions& Options)
        }
 
       if ( ASDCP_SUCCESS(result) )
-       result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
-
-      if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
        {
-         MXF::WaveAudioDescriptor *descriptor = 0;
-         Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
-                                               reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
-         descriptor->ChannelAssignment = Options.channel_assignment;
+         result = Writer.OpenWrite(Options.out_file.c_str(), Info, essence_descriptor,
+                                   Options.mca_config, Options.edit_rate);
        }
     }
 
@@ -589,6 +664,8 @@ main(int argc, const char** argv)
 {
   Result_t result = RESULT_OK;
   char     str_buf[64];
+  g_dict = &ASDCP::DefaultSMPTEDict();
+
   CommandOptions Options(argc, argv);
 
   if ( Options.version_flag )
@@ -597,7 +674,12 @@ main(int argc, const char** argv)
   if ( Options.help_flag )
     usage();
 
-  if ( Options.version_flag || Options.help_flag )
+  if ( Options.show_ul_values_flag )
+    {
+      g_dict->Dump(stdout);
+    }
+
+  if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
     return 0;
 
   if ( Options.error_flag )
@@ -606,11 +688,6 @@ main(int argc, const char** argv)
       return 3;
     }
 
-  if ( Options.show_ul_values )
-    {
-      DefaultSMPTEDict().Dump(stdout);
-    }
-
   EssenceType_t EssenceType;
   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
 
index 4439f7efc200c8a2af2b4479c1783b354d812733..77e7845bb3862bd940695220b9fdd72a70a15c9c 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2003-2012, John Hurst
+Copyright (c) 2003-2013, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -62,6 +62,8 @@ const byte_t P_HFR_UL_2K[16] = {
   0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
 };
 
+const ASDCP::Dictionary *g_dict = 0;
+
 //------------------------------------------------------------------------------------------
 //
 // command line option parser class
@@ -118,8 +120,8 @@ USAGE: %s [-h|-help] [-V]\n\
 \n\
        %s [-3] [-a <uuid>] [-b <buffer-size>] [-C <UL>] [-d <duration>]\n\
           [-e|-E] [-f <start-frame>] [-j <key-id-string>] [-k <key-string>]\n\
-          [-l <label>] [-L] [-M] [-p <frame-rate>] [-s] [-v] [-W]\n\
-          [-z|-Z] <input-file>+ <output-file>\n\n",
+          [-l <label>] [-L] [-M] [-m <expr>] [-p <frame-rate>] [-s] [-v]\n\
+          [-W] [-z|-Z] <input-file>+ <output-file>\n\n",
          PROGRAM_NAME, PROGRAM_NAME);
 
   fprintf(stream, "\
@@ -135,6 +137,8 @@ Options:\n\
   -j <key-id-str>   - Write key ID instead of creating a random value\n\
   -k <key-string>   - Use key for ciphertext operations\n\
   -M                - Do not create HMAC values when writing\n\
+  -m <expr>         - Write MCA labels using <expr>.  Example:\n\
+                        51(L,R,C,LFE,Ls,Rs,),HI,VIN\n\
   -a <UUID>         - Specify the Asset ID of the file\n\
   -b <buffer-size>  - Specify size in bytes of picture frame buffer\n\
                       Defaults to 4,194,304 (4MB)\n\
@@ -218,7 +222,7 @@ public:
   byte_t asset_id_value[UUIDlen];// value of asset ID (when asset_id_flag is true)
   PCM::ChannelFormat_t channel_fmt; // audio channel arrangement
   std::string out_file; //
-  bool show_ul_values;    /// if true, dump the UL table before going tp work.
+  bool show_ul_values_flag;    /// if true, dump the UL table before going tp work.
   Kumu::PathList_t filenames;  // list of filenames to be processed
   UL channel_assignment;
   UL picture_coding;
@@ -226,7 +230,7 @@ public:
   ui32_t ffoa;  /// first frame of action for atmos wrapping
   ui32_t max_channel_count; /// max channel count for atmos wrapping
   ui32_t max_object_count; /// max object count for atmos wrapping
-
+  ASDCP::MXF::MCAConfigParser mca_config;
 
   //
   Rational PictureRate()
@@ -280,7 +284,8 @@ public:
     channel_fmt(PCM::CF_NONE),
     ffoa(0), max_channel_count(10), max_object_count(118), // hard-coded sample atmos properties
     dolby_atmos_sync_flag(false),
-    show_ul_values(false)
+    show_ul_values_flag(false),
+    mca_config(g_dict)
   {
     memset(key_value, 0, KeyLen);
     memset(key_id_value, 0, UUIDlen);
@@ -386,6 +391,14 @@ public:
              case 'L': use_smpte_labels = true; break;
              case 'M': write_hmac = false; break;
 
+             case 'm':
+               TEST_EXTRA_ARG(i, 'm');
+               if ( ! mca_config.DecodeString(argv[i]) )
+                 {
+                   return;
+                 }
+               break;
+
              case 'P':
                TEST_EXTRA_ARG(i, 'P');
                if ( ! picture_coding.DecodeHex(argv[i]) )
@@ -399,7 +412,9 @@ public:
                TEST_EXTRA_ARG(i, 'p');
                picture_rate = abs(atoi(argv[i]));
                break;
-          case 's': dolby_atmos_sync_flag = true; break;
+
+             case 's': dolby_atmos_sync_flag = true; break;
+             case 'u': show_ul_values_flag = true; break;
              case 'V': version_flag = true; break;
              case 'v': verbose_flag = true; break;
              case 'W': no_write_flag = true; break;
@@ -686,7 +701,7 @@ write_JP2K_S_file(CommandOptions& Options)
       if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
        {
          MXF::RGBAEssenceDescriptor *descriptor = 0;
-         Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
+         Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
                                                reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
          descriptor->PictureEssenceCoding = Options.picture_coding;
        }
@@ -821,7 +836,7 @@ write_JP2K_file(CommandOptions& Options)
       if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
        {
          MXF::RGBAEssenceDescriptor *descriptor = 0;
-         Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
+         Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_RGBAEssenceDescriptor),
                                                reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
          descriptor->PictureEssenceCoding = Options.picture_coding;
        }
@@ -958,12 +973,47 @@ write_PCM_file(CommandOptions& Options)
       if ( ASDCP_SUCCESS(result) )
        result = Writer.OpenWrite(Options.out_file.c_str(), Info, ADesc);
 
-      if ( ASDCP_SUCCESS(result) && Options.channel_assignment.HasValue() )
+      if ( ASDCP_SUCCESS(result)
+          && ( Options.channel_assignment.HasValue()
+               || ! Options.mca_config.empty() ) )
        {
-         MXF::WaveAudioDescriptor *descriptor = 0;
-         Writer.OP1aHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_WaveAudioDescriptor),
-                                               reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
-         descriptor->ChannelAssignment = Options.channel_assignment;
+         MXF::WaveAudioDescriptor *essence_descriptor = 0;
+         Writer.OP1aHeader().GetMDObjectByType(g_dict->ul(MDD_WaveAudioDescriptor),
+                                               reinterpret_cast<MXF::InterchangeObject**>(&essence_descriptor));
+         assert(essence_descriptor);
+
+         if ( Options.mca_config.empty() )
+           {
+             essence_descriptor->ChannelAssignment = Options.channel_assignment;
+           }
+         else
+           {
+             if ( Options.mca_config.ChannelCount() != essence_descriptor->ChannelCount )
+               {
+                 fprintf(stderr, "MCA label count (%d) differs from essence stream channel count (%d).\n",
+                         Options.mca_config.ChannelCount(), essence_descriptor->ChannelCount);
+                 return RESULT_FAIL;
+               }
+
+             essence_descriptor->ChannelAssignment = g_dict->ul(MDD_DCAudioChannelCfg_MCA);
+
+             // add descriptors to the essence_descriptor and header
+             ASDCP::MXF::InterchangeObject_list_t::iterator i;
+             for ( i = Options.mca_config.begin(); i != Options.mca_config.end(); ++i )
+               {
+                 if ( (*i)->GetUL() != UL(g_dict->ul(MDD_AudioChannelLabelSubDescriptor))
+                      && (*i)->GetUL() != UL(g_dict->ul(MDD_SoundfieldGroupLabelSubDescriptor))
+                      && (*i)->GetUL() != UL(g_dict->ul(MDD_GroupOfSoundfieldGroupsLabelSubDescriptor)) )
+                   {
+                     fprintf(stderr, "Essence sub-descriptor is not an MCALabelSubDescriptor.\n");
+                     (*i)->Dump();
+                   }
+
+                 Writer.OP1aHeader().AddChildObject(*i);
+                 essence_descriptor->SubDescriptors.push_back((*i)->InstanceUID);
+                 *i = 0; // parent will only free the ones we don't keep
+               }
+           }
        }
     }
 
@@ -1384,6 +1434,8 @@ main(int argc, const char** argv)
 {
   Result_t result = RESULT_OK;
   char     str_buf[64];
+  g_dict = &ASDCP::DefaultSMPTEDict();
+
   CommandOptions Options(argc, argv);
 
   if ( Options.version_flag )
@@ -1392,7 +1444,15 @@ main(int argc, const char** argv)
   if ( Options.help_flag )
     usage();
 
-  if ( Options.version_flag || Options.help_flag )
+  if ( Options.show_ul_values_flag )
+    {
+      if ( Options.use_smpte_labels )
+       DefaultSMPTEDict().Dump(stdout);
+      else
+       DefaultInteropDict().Dump(stdout);
+    }
+
+  if ( Options.version_flag || Options.help_flag || Options.show_ul_values_flag )
     return 0;
 
   if ( Options.error_flag )
@@ -1401,14 +1461,6 @@ main(int argc, const char** argv)
       return 3;
     }
 
-  if ( Options.show_ul_values )
-    {
-      if ( Options.use_smpte_labels )
-       DefaultSMPTEDict().Dump(stdout);
-      else
-       DefaultInteropDict().Dump(stdout);
-    }
-
   EssenceType_t EssenceType;
   result = ASDCP::RawEssenceType(Options.filenames.front().c_str(), EssenceType);
 
index 77f0cdbbcb1c01403fef3ab89301f1415565e341..3d385ec84972fc9a9c24b6fa97c741112984550c 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_Reader.cpp
   \version $Id$
@@ -35,7 +37,6 @@
 using namespace ASDCP;
 using namespace ASDCP::MXF;
 
-
 static Kumu::Mutex sg_DefaultMDInitLock;
 static bool        sg_DefaultMDTypesInit = false;
 static const ASDCP::Dictionary *sg_dict;
@@ -62,16 +63,68 @@ AS_02::default_md_object_init()
 //
 
     
-AS_02::MXF::AS02IndexReader::AS02IndexReader(const ASDCP::Dictionary*& d) : m_Duration(0), ASDCP::MXF::Partition(m_Dict), m_Dict(d) {}
+AS_02::MXF::AS02IndexReader::AS02IndexReader(const ASDCP::Dictionary*& d) :
+  m_Duration(0), m_BytesPerEditUnit(0),
+  ASDCP::MXF::Partition(m_Dict), m_Dict(d) {}
+
 AS_02::MXF::AS02IndexReader::~AS02IndexReader() {}
 
 //    
 Result_t
-AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip)
+AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const ASDCP::MXF::RIP& rip, const bool has_header_essence)
 {
+  typedef std::list<Kumu::mem_ptr<ASDCP::MXF::Partition> > body_part_array_t;
+  body_part_array_t body_part_array;
+  body_part_array_t::const_iterator body_part_iter;
+
   ASDCP::MXF::Array<ASDCP::MXF::RIP::Pair>::const_iterator i;
+  Result_t result = m_IndexSegmentData.Capacity(128*Kumu::Kilobyte); // will be grown if needed
+  ui32_t first_body_sid = 0;
+
+  // create a list of body parts and index parts
+  for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
+    {
+      if ( i->BodySID == 0 )
+       continue;
+
+      if ( first_body_sid == 0 )
+       {
+         first_body_sid = i->BodySID;
+       }
+      else if ( i->BodySID != first_body_sid )
+       {
+         DefaultLogSink().Error("RIP contains multipls BodySID identities.\n");
+       }
+
+      reader.Seek(i->ByteOffset);
+      ASDCP::MXF::Partition *this_partition = new ASDCP::MXF::Partition(m_Dict);
+      assert(this_partition);
+
+      result = this_partition->InitFromFile(reader);
+
+      if ( KM_FAILURE(result) )
+       {
+         delete this_partition;
+         return result;
+       }
+
+      if ( this_partition->BodySID != i->BodySID )
+       {
+         DefaultLogSink().Error("Partition BodySID %d does not match RIP BodySID %d.\n",
+                                this_partition->BodySID, i->BodySID);
+       }
 
-  Result_t result = m_IndexSegmentData.Capacity(128*Kumu::Kilobyte);
+      body_part_array.push_back(0);
+      body_part_array.back().set(this_partition);
+    }
+
+  if ( body_part_array.empty() )
+    {
+      DefaultLogSink().Error("File has no partitions with essence data.\n");
+      return RESULT_AS02_FORMAT;
+    }
+
+  body_part_iter = body_part_array.begin();
 
   for ( i = rip.PairArray.begin(); KM_SUCCESS(result) && i != rip.PairArray.end(); ++i )
     {
@@ -79,9 +132,23 @@ AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const
       ASDCP::MXF::Partition plain_part(m_Dict);
       result = plain_part.InitFromFile(reader);
 
-      if ( KM_SUCCESS(result) && plain_part.IndexByteCount > 0 )
+      if ( KM_FAILURE(result) )
+       return result;
+
+      if ( plain_part.IndexByteCount > 0 )
        {
-         // slurp up the remainder of the footer
+         if ( body_part_iter == body_part_array.end() )
+           {
+             DefaultLogSink().Error("Index and Body partitions do not match.\n");
+             break;
+           }
+
+         if ( plain_part.ThisPartition == plain_part.FooterPartition )
+           {
+             DefaultLogSink().Warn("File footer partition contains index data.\n");
+           }
+
+         // slurp up the remainder of the partition
          ui32_t read_count = 0;
 
          assert (plain_part.IndexByteCount <= 0xFFFFFFFFL);
@@ -89,36 +156,102 @@ AS_02::MXF::AS02IndexReader::InitFromFile(const Kumu::FileReader& reader, const
 
          result = m_IndexSegmentData.Capacity(m_IndexSegmentData.Length() + bytes_this_partition);
 
-         if ( ASDCP_SUCCESS(result) )
+         if ( KM_SUCCESS(result) )
            result = reader.Read(m_IndexSegmentData.Data() + m_IndexSegmentData.Length(),
                                 bytes_this_partition, &read_count);
 
-         if ( ASDCP_SUCCESS(result) && read_count != bytes_this_partition )
+         if ( KM_SUCCESS(result) && read_count != bytes_this_partition )
            {
-             DefaultLogSink().Error("Short read of footer partition: got %u, expecting %u\n",
+             DefaultLogSink().Error("Short read of index partition: got %u, expecting %u\n",
                                     read_count, bytes_this_partition);
-             return RESULT_FAIL;
+             return RESULT_AS02_FORMAT;
            }
 
-         if ( ASDCP_SUCCESS(result) )
+         if ( KM_SUCCESS(result) )
            {
-             result = InitFromBuffer(m_IndexSegmentData.RoData() + m_IndexSegmentData.Length(), bytes_this_partition);
+             ui64_t current_body_offset = 0;
+             ui64_t current_ec_offset = 0;
+             assert(body_part_iter != body_part_array.end());
+
+             assert(!body_part_iter->empty());
+             ASDCP::MXF::Partition *tmp_partition = body_part_iter->get();
+
+             if ( has_header_essence && tmp_partition->ThisPartition == 0 )
+               {
+                 current_body_offset = 0;
+                 current_ec_offset = tmp_partition->HeaderByteCount + tmp_partition->ArchiveSize();
+               }
+             else
+               {
+                 current_body_offset = tmp_partition->BodyOffset;
+                 current_ec_offset += tmp_partition->ThisPartition + tmp_partition->ArchiveSize();
+               }
+
+             result = InitFromBuffer(m_IndexSegmentData.RoData() + m_IndexSegmentData.Length(), bytes_this_partition, current_body_offset, current_ec_offset);
              m_IndexSegmentData.Length(m_IndexSegmentData.Length() + bytes_this_partition);
+             ++body_part_iter;
+           }
+       }
+    }
+
+  if ( KM_SUCCESS(result) )
+    {
+      std::list<InterchangeObject*>::const_iterator ii;
+  
+      for ( ii = m_PacketList->m_List.begin(); ii != m_PacketList->m_List.end(); ++ii )
+       {
+         if ( (*ii)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
+           {
+             m_Duration += static_cast<IndexTableSegment*>(*ii)->IndexDuration;
            }
        }
     }
 
+#if 0
+  char identbuf[IdentBufferLen];
+  std::list<InterchangeObject*>::iterator j;
+  std::vector<ASDCP::MXF::IndexTableSegment::IndexEntry>::iterator k;
+  ui32_t entry_count = 0;
+
+  for ( j = m_PacketList->m_List.begin(); j != m_PacketList->m_List.end(); ++j )
+    {
+      assert(*j);
+      ASDCP::MXF::IndexTableSegment* segment = static_cast<ASDCP::MXF::IndexTableSegment*>(*j);
+
+      fprintf(stderr, "  --------------------------------------\n");
+      fprintf(stderr, "  IndexEditRate      = %d/%d\n",  segment->IndexEditRate.Numerator, segment->IndexEditRate.Denominator);
+      fprintf(stderr, "  IndexStartPosition = %s\n",  i64sz(segment->IndexStartPosition, identbuf));
+      fprintf(stderr, "  IndexDuration      = %s\n",  i64sz(segment->IndexDuration, identbuf));
+      fprintf(stderr, "  EditUnitByteCount  = %u\n",  segment->EditUnitByteCount);
+      fprintf(stderr, "  IndexSID           = %u\n",  segment->IndexSID);
+      fprintf(stderr, "  BodySID            = %u\n",  segment->BodySID);
+      fprintf(stderr, "  SliceCount         = %hu\n", segment->SliceCount);
+      fprintf(stderr, "  PosTableCount      = %hu\n", segment->PosTableCount);
+      fprintf(stderr, "  RtFileOffset       = %s\n",  i64sz(segment->RtFileOffset, identbuf));
+      fprintf(stderr, "  RtEntryOffset      = %s\n",  i64sz(segment->RtEntryOffset, identbuf));
+      fprintf(stderr, "  IndexEntryArray:\n");
+
+      for ( k = segment->IndexEntryArray.begin(); k != segment->IndexEntryArray.end(); ++k )
+       {
+         fprintf(stderr, "  0x%010qx\n", k->StreamOffset);
+         ++entry_count;
+       }
+    }
+
+  fprintf(stderr, "Actual entries: %d\n", entry_count);
+#endif
+
   return result;
 }
 
 //
 ASDCP::Result_t
-AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l)
+AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l, const ui64_t& body_offset, const ui64_t& essence_container_offset)
 {
   Result_t result = RESULT_OK;
   const byte_t* end_p = p + l;
 
-  while ( ASDCP_SUCCESS(result) && p < end_p )
+  while ( KM_SUCCESS(result) && p < end_p )
     {
       // parse the packets and index them by uid, discard KLVFill items
       InterchangeObject* object = CreateObject(m_Dict, p);
@@ -128,9 +261,18 @@ AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l)
       result = object->InitFromBuffer(p, end_p - p);
       p += object->PacketLength();
 
-      if ( ASDCP_SUCCESS(result) )
+      if ( KM_SUCCESS(result) )
        {
-         m_PacketList->AddPacket(object); // takes ownership
+         if ( object->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
+           {
+             static_cast<IndexTableSegment*>(object)->RtFileOffset = essence_container_offset;
+             static_cast<IndexTableSegment*>(object)->RtEntryOffset = body_offset;
+             m_PacketList->AddPacket(object); // takes ownership
+           }
+         else
+           {
+             delete object;
+           }
        }
       else
        {
@@ -139,19 +281,9 @@ AS_02::MXF::AS02IndexReader::InitFromBuffer(const byte_t* p, ui32_t l)
        }
     }
 
-  if ( ASDCP_FAILURE(result) )
+  if ( KM_FAILURE(result) )
     DefaultLogSink().Error("Failed to initialize AS02IndexReader\n");
 
-  std::list<InterchangeObject*>::const_iterator i;
-  
-  for ( i = m_PacketList->m_List.begin(); i != m_PacketList->m_List.end(); ++i )
-    {
-      if ( (*i)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
-       {
-         m_Duration += static_cast<IndexTableSegment*>(*i)->IndexDuration;
-       }
-    }
-
   return result;
 }
 
@@ -200,7 +332,6 @@ AS_02::MXF::AS02IndexReader::GetDuration() const
 {
   return m_Duration;
 }
-   
 
 //
 Result_t
@@ -211,7 +342,7 @@ AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegm
     {
       if ( (*li)->IsA(OBJ_TYPE_ARGS(IndexTableSegment)) )
        {
-         IndexTableSegment* Segment = (IndexTableSegment*)(*li);
+         IndexTableSegment* Segment = static_cast<IndexTableSegment*>(*li);
          ui64_t start_pos = Segment->IndexStartPosition;
 
          if ( Segment->EditUnitByteCount > 0 )
@@ -222,7 +353,7 @@ AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegm
              if ( ! Segment->IndexEntryArray.empty() )
                DefaultLogSink().Error("Unexpected IndexEntryArray contents in CBR file\n");
 
-             Entry.StreamOffset = (ui64_t)frame_num * Segment->EditUnitByteCount;
+             Entry.StreamOffset = ((ui64_t)frame_num * Segment->EditUnitByteCount) + Segment->RtFileOffset;
              return RESULT_OK;
            }
          else if ( (ui64_t)frame_num >= start_pos
@@ -231,6 +362,7 @@ AS_02::MXF::AS02IndexReader::Lookup(ui32_t frame_num, ASDCP::MXF::IndexTableSegm
              ui64_t tmp = frame_num - start_pos;
              assert(tmp <= 0xFFFFFFFFL);
              Entry = Segment->IndexEntryArray[(ui32_t) tmp];
+             Entry.StreamOffset = Entry.StreamOffset - Segment->RtEntryOffset + Segment->RtFileOffset;
              return RESULT_OK;
            }
        }
@@ -252,6 +384,7 @@ AS_02::h__AS02Reader::~h__AS02Reader() {}
 Result_t
 AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
 {
+  bool has_header_essence = false;
   Result_t result = ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::OpenMXFRead(filename);
 
   if ( KM_SUCCESS(result) )
@@ -284,18 +417,48 @@ AS_02::h__AS02Reader::OpenMXFRead(const char* filename)
       if ( m_RIP.PairArray.front().ByteOffset != 0 )
        {
          DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
-         result = RESULT_FORMAT;
+         return RESULT_AS02_FORMAT;
+       }
+
+      Kumu::fpos_t first_partition_after_header = 0;
+      bool has_body_sid = false;
+      Array<RIP::Pair>::iterator r_i;
+
+      for ( r_i = m_RIP.PairArray.begin(); r_i != m_RIP.PairArray.end(); ++r_i )
+       {
+         if ( r_i->BodySID != 0 )
+           {
+             has_body_sid = true;
+           }
+
+         if ( first_partition_after_header == 0 && r_i->ByteOffset != 0 )
+           {
+             first_partition_after_header = r_i->ByteOffset;
+           }
+       }
+
+      // essence in header partition?
+      Kumu::fpos_t header_end = m_HeaderPart.HeaderByteCount + m_HeaderPart.ArchiveSize();
+      has_header_essence = header_end < first_partition_after_header;
+
+      if ( has_header_essence )
+       {
+         DefaultLogSink().Warn("File header partition contains essence data.\n");
+       }
+
+      if ( ! has_body_sid )
+       {
+         DefaultLogSink().Error("File contains no essence.\n");
+         return RESULT_AS02_FORMAT;
        }
     }
 
   if ( KM_SUCCESS(result) )
     {
-      m_HeaderPart.BodyOffset = m_File.Tell();
       m_IndexAccess.m_Lookup = &m_HeaderPart.m_Primer;
-      result = m_IndexAccess.InitFromFile(m_File, m_RIP);
+      result = m_IndexAccess.InitFromFile(m_File, m_RIP, has_header_essence);
     }
 
-  m_File.Seek(m_HeaderPart.BodyOffset);
   return result;
 }
 
@@ -304,19 +467,9 @@ Result_t
 AS_02::h__AS02Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
                                     const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
 {
-  return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
-                                                                                    EssenceUL, Ctx, HMAC);
+  return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::ReadEKLVFrame(FrameNum, FrameBuf, EssenceUL, Ctx, HMAC);
 }
 
-Result_t
-AS_02::h__AS02Reader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
-                           i8_t& temporalOffset, i8_t& keyFrameOffset)
-{
-  return ASDCP::MXF::TrackFileReader<OP1aHeader, AS_02::MXF::AS02IndexReader>::LocateFrame(m_HeaderPart, FrameNum,
-                                                                                   streamOffset, temporalOffset, keyFrameOffset);
-}
-
-
 //
 // end h__02_Reader.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()
index 1ba43232f0cf3dada21ece244a032f4f5f4f657f..b1c055591aaf0c478a8c4f54111f399d82ca4625 100755 (executable)
@@ -108,6 +108,12 @@ ASDCP::h__ASDCPReader::OpenMXFRead(const char* filename)
            }
        }
 
+      if ( m_RIP.PairArray.front().ByteOffset != 0 )
+       {
+         DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
+         result = RESULT_FORMAT;
+       }
+
       //
       if ( m_RIP.PairArray.size() < 2 )
        {
@@ -130,15 +136,13 @@ ASDCP::h__ASDCPReader::OpenMXFRead(const char* filename)
              DefaultLogSink().Error("ASDCP::h__ASDCPReader::OpenMXFRead, m_BodyPart.InitFromFile failed\n");
             }
         }
-      else if ( m_RIP.PairArray.front().ByteOffset != 0 )
-       {
-         DefaultLogSink().Error("First Partition in RIP is not at offset 0.\n");
-         result = RESULT_FORMAT;
-       }
     }
 
   if ( KM_SUCCESS(result) )
     {
+      // this position will be at either
+      //     a) the spot in the header partition where essence units appear, or
+      //     b) right after the body partition header (where essence units appear)
       m_HeaderPart.BodyOffset = m_File.Tell();
 
       result = m_File.Seek(m_HeaderPart.FooterPartition);
@@ -159,7 +163,7 @@ Result_t
 ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
                                     const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
 {
-  return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
+  return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart.BodyOffset, FrameNum, FrameBuf,
                                                                                     EssenceUL, Ctx, HMAC);
 }
 
@@ -167,7 +171,7 @@ Result_t
 ASDCP::h__ASDCPReader::LocateFrame(ui32_t FrameNum, Kumu::fpos_t& streamOffset,
                            i8_t& temporalOffset, i8_t& keyFrameOffset)
 {
-  return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart, FrameNum,
+  return ASDCP::MXF::TrackFileReader<OP1aHeader, OPAtomIndexFooter>::LocateFrame(m_HeaderPart.BodyOffset, FrameNum,
                                                                                    streamOffset, temporalOffset, keyFrameOffset);
 }
 
@@ -208,7 +212,7 @@ ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
 
   if ( ber_size < MXF_BER_LENGTH )
     {
-      DefaultLogSink().Error("BER size %d shorter than AS-DCP minimum %d.\n",
+      DefaultLogSink().Error("BER size %d shorter than AS-DCP/AS-02 minimum %d.\n",
                             ber_size, MXF_BER_LENGTH);
       return RESULT_FORMAT;
     }
@@ -231,9 +235,14 @@ ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
   return InitFromBuffer(m_KeyBuf, header_length);
 }
 
+
+//------------------------------------------------------------------------------------------
+//
+
+
 // base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
 Result_t
-ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OP1aHeader& HeaderPart,
+ASDCP::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)
index 952972e0320ac396367d89069e18106e21cbdf3a..5009ffa75ef5a12d97c3c5539cb21e252d0477e2 100755 (executable)
@@ -35,6 +35,19 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 using namespace ASDCP;
 using namespace ASDCP::MXF;
 
+//
+ui32_t
+ASDCP::derive_timecode_rate_from_edit_rate(const ASDCP::Rational& edit_rate)
+{
+  double edit_rate_real = edit_rate.Quotient();
+  if ( ceil(edit_rate_real) == floor(edit_rate_real) )
+    {
+      return ceil(edit_rate_real);
+    }
+
+  return ( edit_rate_real - floor(edit_rate_real) < 0.5 ) ? floor(edit_rate_real) : ceil(edit_rate_real);
+}
+
 //
 // add DMS CryptographicFramework entry to source package
 void
@@ -146,7 +159,8 @@ ASDCP::h__ASDCPWriter::WriteASDCPHeader(const std::string& PackageLabel, const U
       m_RIP.PairArray.push_back(RIP::Pair(1, 0)); // 2-part, essence in header
     }
 
-  AddSourceClip(EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
+  // timecode rate and essence rate are the same
+  AddSourceClip(EditRate, EditRate, TCFrameRate, TrackName, EssenceUL, DataDefinition, PackageLabel);
   AddEssenceDescriptor(WrappingUL);
 
   Result_t result = m_HeaderPart.WriteToFile(m_File, m_HeaderSize);
index 12886cd95c188037989ce5e87930d3a740eb583c..4f77dbb6eb107f832544077c2ef70d57c8b5f25f 100755 (executable)
@@ -72,7 +72,7 @@ void
 usage(FILE* stream = stdout)
 {
   fprintf(stream, "\
-USAGE: %s [-l <limit>] [-p <prefix>] [-s <suffix>] [-u] [-v] \n\
+USAGE: %s [-l <limit>] [-p <prefix>] [-s <suffix>] [-u|-U] [-v] \n\
            (<type-name>|<type-ul>) <mxf-filename>+\n\
 \n\
        %s -d\n\
@@ -86,6 +86,7 @@ USAGE: %s [-l <limit>] [-p <prefix>] [-s <suffix>] [-u] [-v] \n\
                    uses the input filename minus any extension\n\
   -s <suffix>  - Append <suffix> to output filenames\n\
   -u           - Unwrap the packet value (i.e., do not output KL)\n\
+  -U           - Do not unwrap (default)\n\
   -v           - Verbose. Prints informative messages to stderr\n\
   -V           - Show version information\n\
 \n\
@@ -94,12 +95,6 @@ USAGE: %s [-l <limit>] [-p <prefix>] [-s <suffix>] [-u] [-v] \n\
 \n", PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
 }
 
-enum MajorMode_t {
-  MMT_NONE,
-  MMT_LIST,
-  MMT_EXTRACT
-};
-
 //
 //
  class CommandOptions
@@ -112,7 +107,7 @@ enum MajorMode_t {
    bool   help_flag;                // true if the help display option was selected
    bool   verbose_flag;             // true if the informative messages option was selected
    bool   unwrap_mode;              // true if we are to strip the K and L before writing
-   MajorMode_t   mode;
+   bool   list_mode;
    ASDCP::UL target_ul;             // a UL value identifying the packets to be extracted
    ui64_t  extract_limit;           // limit extraction to the given number of packets
    std::string prefix;              // output filename prefix
@@ -121,7 +116,7 @@ enum MajorMode_t {
 
    CommandOptions(int argc, const char** argv, const ASDCP::Dictionary& dict) :
      error_flag(true), version_flag(false), help_flag(false),
-     verbose_flag(false), unwrap_mode(false), mode(MMT_EXTRACT), extract_limit(ui64_C(-1))
+     verbose_flag(false), unwrap_mode(false), list_mode(false), extract_limit(ui64_C(-1))
    {
      for ( int i = 1; i < argc; ++i )
        {
@@ -136,7 +131,7 @@ enum MajorMode_t {
           {
             switch ( argv[i][1] )
               {
-              case 'd': mode = MMT_LIST; break;
+              case 'd': list_mode = true; break;
               case 'h': help_flag = true; break;
 
               case 'l':
@@ -155,6 +150,7 @@ enum MajorMode_t {
                 break;
 
               case 'u': unwrap_mode = true; break;
+              case 'U': unwrap_mode = false; break;
               case 'V': version_flag = true; break;
               case 'v': verbose_flag = true; break;
 
@@ -196,7 +192,7 @@ enum MajorMode_t {
      if ( help_flag || version_flag )
        return;
      
-     if ( mode == MMT_EXTRACT )
+     if ( ! list_mode )
        {
         if ( inFileList.empty() )
           {
@@ -223,7 +219,6 @@ int
 main(int argc, const char** argv)
 {
   const Dictionary *dict = &DefaultCompositeDict();
-
   CommandOptions Options(argc, argv, *dict);
 
   if ( Options.version_flag )
@@ -241,7 +236,7 @@ main(int argc, const char** argv)
       return 3;
     }
 
-  if ( Options.mode == MMT_LIST )
+  if ( Options.list_mode )
     {
       DefaultLogSink().UnsetFilterFlag(Kumu::LOG_ALLOW_WARN);
       char buf[64];
@@ -273,7 +268,7 @@ main(int argc, const char** argv)
   Result_t result = RESULT_OK;
   FileList_t::iterator fi;
 
-  for ( fi = Options.inFileList.begin(); ASDCP_SUCCESS(result) && fi != Options.inFileList.end(); ++fi )
+  for ( fi = Options.inFileList.begin(); KM_SUCCESS(result) && fi != Options.inFileList.end(); ++fi )
     {
       if ( Options.verbose_flag )
        fprintf(stderr, "Opening file %s\n", (fi->c_str()));
@@ -281,7 +276,7 @@ main(int argc, const char** argv)
       std::string this_prefix =  Options.prefix.empty() ? Kumu::PathSetExtension(*fi, "") + "_" : Options.prefix;
       Kumu::FileReader reader;
       KLVFilePacket packet;
-      char filename_buf[1024], buf1[64], buf2[64];
+      char filename_buf[1024];
       ui64_t item_counter = 0;
 
       result = reader.OpenRead(fi->c_str());
@@ -325,7 +320,7 @@ main(int argc, const char** argv)
        result = RESULT_OK;
     }
 
-  if ( ASDCP_FAILURE(result) )
+  if ( KM_FAILURE(result) )
     {
       fputs("Program stopped on error.\n", stderr);