big change rollup
authorjhurst <jhurst@cinecert.com>
Fri, 8 Feb 2013 19:11:58 +0000 (19:11 +0000)
committerjhurst <>
Fri, 8 Feb 2013 19:11:58 +0000 (19:11 +0000)
20 files changed:
README
src/AS_DCP.h
src/AS_DCP_JP2K.cpp
src/AS_DCP_MPEG2.cpp
src/AS_DCP_MXF.cpp
src/AS_DCP_PCM.cpp
src/AS_DCP_TimedText.cpp
src/AS_DCP_internal.h
src/KM_fileio.cpp
src/KM_fileio.h
src/KM_util.cpp
src/KM_util.h
src/Makefile.am
src/asdcp-info.cpp
src/asdcp-test.cpp
src/asdcp-unwrap.cpp
src/asdcp-wrap.cpp
src/blackwave.cpp
src/h__Reader.cpp
src/path-test.cpp

diff --git a/README b/README
index c056ce12ff63dbb5e3588ec5c081a2a88c8be762..2e43b679db3ba80d20eff3f47b1bb385dcbee699 100755 (executable)
--- a/README
+++ b/README
@@ -139,16 +139,46 @@ command-line utilities all respond to -h.
 
 Change History
 
-YYYY-MM-DD - bug fix, first publication of AS-02 EC support 1.10.46
- o Added support for much of IMF Essence Component (SMPTE draft ST 2067-5,
-   AKA "AS-02".  This code was developed and contributed by Robert Scheler,
-   Fraunhofer IIS. Very special thanks to Siegfried Foessel and Heiko
-   Sparenberg for their support of this essential IMF component.
+YYYY-MM-DD - bug fixes, enhancements M.N.RR
+ o Refactored internals of the AS-DCP file readers.  While no
+   changes in behavior are intended, users are cautioned to test
+   thouroughly before use in production.
+ o Fixed a bug in ReadAncillaryResource that was causing bogus HMAC
+   failures when reading resources from a file.
+ o Fixed premature-release bug in the Expat version of the XML parser.
+   Thanks to Carsten Feldheim (IIS) for the tip.
+ o Fixed -W option in asdcp-unwrap.  Thanks to RGB.
+ o Added P-HFR support to asdcp-wrap (see URL for details:
+   http://isdcf.com/papers/ISDCF-HighFrameRate-DCP.pdf).
+ o Added support for SMPTE ST 428-21 "Archival Frame Rates".
+ o Added -P option to asdcp-wrap (inserts arbitrary UL into the
+   PictureEssenceCoding property when wrapping JP2K files.)
+ o Added support for 96 kHz files to blackwave.
+ o Added new path and string manglers to Kumu.
+ o Updated MCA ULs (I warned you...).  Again please take some
+   time to proof this work against ST 477-4 including the latest
+   drafts of the registries.
+       Changed the version byte (8 0f 16) to 0x0e:
+           MCALabelSubDescriptor
+           AudioChannelLabelSubDescriptor
+           SoundfieldGroupLabelSubDescriptor
+           GroupOfSoundfieldGroupsLabelSubDescriptor
+          GroupOfSoundfieldGroupsLinkID
+       Changed bytes 8 and and 13 of SoundfieldGroupLinkID
+       Added items to the UL dictionary:
+          MCAPartitionKind
+          MCAPartitionNumber
+          MCATitle
+          MCATitleVersion
+          MCATitleSubVersion
+          MCAEpisode
+          MCAAudioContentKind
+          MCAAudioElementKind
 
 2012-08-07 - bug fix, 1.10.46
- o Added zero-initializers to time values when parsing a timestamp string
-   (in the case where the (T...) option was not present the time was
-   uninitialized).
+ o Added missing zero-initializers to time values when parsing a
+   timestamp string (in the case where the optional [Thh:mm.[:ss]]
+   syntax is not present in an encoded string).
 
 2012-03-06 - bug fixes, enhancements 1.9.45
  o Removed ASDCP::Timestamp, all items that were of that class are now 
@@ -268,7 +298,8 @@ YYYY-MM-DD - bug fix, first publication of AS-02 EC support 1.10.46
  o ST 429-5 files have corrected ULs for DCTimedTextDescriptor and
    GenericStream DataElement. Files made with previous versions of
    the library are incompatible with this and future versions.
- o Fixed File Package TrackNumber values. Thanks to Sankar.
+ o Fixed File Package TrackNumber values. Th
+anks to Sankar.
  o Added edit rate constants to AS_DCP.h (25, 30, 50, 60).
  o Changed AudioDescriptor "SampleRate" element name to "EditRate"
    to make it consistent with the other types.
index 72d52615d738b069dfa23ed898bfc9119e3a7e7b..c0556ba7d71c9c91fed2baa45098b072bc7bc484 100755 (executable)
@@ -279,6 +279,15 @@ namespace ASDCP {
   const Rational EditRate_100 = Rational(100,1);
   const Rational EditRate_120 = Rational(120,1);
 
+  // Archival frame rates, see ST 428-21
+  // These rates are new and not supported by all systems. Do not assume that
+  // a package made using one of these rates will work just anywhere!
+  const Rational EditRate_16 = Rational(16,1);
+  const Rational EditRate_18 = Rational(200,11); // 18.182
+  const Rational EditRate_20 = Rational(20,1);
+  const Rational EditRate_22 = Rational(240,11); // 21.818
+
+
   // Non-reference counting container for internal member objects.
   // Please do not use this class for any other purpose.
   template <class T>
index 076ba5c41a35b2002b5837dd90f928c6ee3825bb..33d5b9355618acd5b25cc8a6335588fcc6f9a7ef 100755 (executable)
@@ -205,7 +205,7 @@ ASDCP::JP2K::PictureDescriptorDump(const PictureDescriptor& PDesc, FILE* stream)
 // hidden, internal implementation of JPEG 2000 reader
 
 
-class lh__Reader : public ASDCP::h__Reader
+class lh__Reader : public ASDCP::h__ASDCPReader
 {
   RGBAEssenceDescriptor*        m_EssenceDescriptor;
   JPEG2000PictureSubDescriptor* m_EssenceSubDescriptor;
@@ -219,7 +219,7 @@ public:
   PictureDescriptor m_PDesc;        // codestream parameter list
 
   lh__Reader(const Dictionary& d) :
-    ASDCP::h__Reader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
+    ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0), m_EssenceSubDescriptor(0), m_Format(ESS_UNKNOWN) {}
   Result_t    OpenRead(const char*, EssenceType_t);
   Result_t    ReadFrame(ui32_t, JP2K::FrameBuffer&, AESDecContext*, HMACContext*);
   Result_t    MD_to_JP2K_PDesc(JP2K::PictureDescriptor& PDesc);
@@ -595,7 +595,7 @@ public:
       }
 
     // get frame position
-    Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
+    Kumu::fpos_t FilePosition = m_HeaderPart.BodyOffset + TmpEntry.StreamOffset;
     Result_t result = RESULT_OK;
 
     if ( phase == SP_LEFT )
index 18102d43e692c0daa5e659d1b68aa1878a971d4a..208163066a0f002aaa1ff4b758b53115c47009fe 100755 (executable)
@@ -160,7 +160,7 @@ ASDCP::MPEG2::VideoDescriptorDump(const VideoDescriptor& VDesc, FILE* stream)
 //
 // hidden, internal implementation of MPEG2 reader
 
-class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
+class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
 {
   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
   h__Reader();
@@ -168,7 +168,7 @@ class ASDCP::MPEG2::MXFReader::h__Reader : public ASDCP::h__Reader
 public:
   VideoDescriptor m_VDesc;        // video parameter list
 
-  h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
+  h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d) {}
   ~h__Reader() {}
   Result_t    OpenRead(const char*);
   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
index 95e219e1df331de0f392e3cd600f6ab8013ff45b..96fc95930e2ec3f48db0a81e1da454894306a8fc 100755 (executable)
@@ -439,7 +439,7 @@ ASDCP::DecryptFrameBuffer(const ASDCP::FrameBuffer& FBin, ASDCP::FrameBuffer& FB
 
 //
 Result_t
-ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
+ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
                                 ui32_t sequence, HMACContext* HMAC)
 {
   ASDCP_TEST_NULL(AssetID);
@@ -489,7 +489,7 @@ ASDCP::IntegrityPack::CalcValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
 
 
 Result_t
-ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, byte_t* AssetID,
+ASDCP::IntegrityPack::TestValues(const ASDCP::FrameBuffer& FB, const byte_t* AssetID,
                                 ui32_t sequence, HMACContext* HMAC)
 {
   ASDCP_TEST_NULL(AssetID);
index 06a47b49878fb0a8981ba1f6d0f85f3591c4bdd2..606a6f5d4356fe7a04a90a3762680744c54b7849 100755 (executable)
@@ -196,7 +196,7 @@ calc_CBR_frame_size(ASDCP::WriterInfo& Info, const ASDCP::PCM::AudioDescriptor&
 //------------------------------------------------------------------------------------------
 
 
-class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
+class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
 {
   ASDCP_NO_COPY_CONSTRUCT(h__Reader);
   h__Reader();
@@ -204,7 +204,7 @@ class ASDCP::PCM::MXFReader::h__Reader : public ASDCP::h__Reader
 public:
   AudioDescriptor m_ADesc;
 
-  h__Reader(const Dictionary& d) : ASDCP::h__Reader(d) {}
+  h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d) {}
   ~h__Reader() {}
   Result_t    OpenRead(const char*);
   Result_t    ReadFrame(ui32_t, FrameBuffer&, AESDecContext*, HMACContext*);
@@ -239,6 +239,10 @@ ASDCP::PCM::MXFReader::h__Reader::OpenRead(const char* filename)
        && m_ADesc.EditRate != EditRate_96
        && m_ADesc.EditRate != EditRate_100
        && m_ADesc.EditRate != EditRate_120
+       && m_ADesc.EditRate != EditRate_16
+       && m_ADesc.EditRate != EditRate_18
+       && m_ADesc.EditRate != EditRate_20
+       && m_ADesc.EditRate != EditRate_22
        && m_ADesc.EditRate != EditRate_23_98 )
     {
       DefaultLogSink().Error("PCM file EditRate is not a supported value: %d/%d\n", // lu
@@ -488,6 +492,10 @@ ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
        && ADesc.EditRate != EditRate_96
        && ADesc.EditRate != EditRate_100
        && ADesc.EditRate != EditRate_120
+       && ADesc.EditRate != EditRate_16
+       && ADesc.EditRate != EditRate_18
+       && ADesc.EditRate != EditRate_20
+       && ADesc.EditRate != EditRate_22
        && ADesc.EditRate != EditRate_23_98 )
     {
       DefaultLogSink().Error("AudioDescriptor.EditRate is not a supported value: %d/%d\n",
@@ -516,7 +524,14 @@ ASDCP::PCM::MXFWriter::h__Writer::SetSourceStream(const AudioDescriptor& ADesc)
 
   if ( ASDCP_SUCCESS(result) )
     {
-      ui32_t TCFrameRate = ( m_ADesc.EditRate == EditRate_23_98  ) ? 24 : m_ADesc.EditRate.Numerator;
+      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 = WriteMXFHeader(PCM_PACKAGE_LABEL, UL(m_Dict->ul(MDD_WAVWrapping)),
                              SOUND_DEF_LABEL, UL(m_EssenceUL), UL(m_Dict->ul(MDD_SoundDataDef)),
index 11995549dbb418a958e48587915c8885454abee0..971fe08bf164f1fa2700f6676af93815438afe66 100644 (file)
@@ -123,7 +123,7 @@ ASDCP::TimedText::FrameBuffer::Dump(FILE* stream, ui32_t dump_len) const
 
 typedef std::map<UUID, UUID> ResourceMap_t;
 
-class ASDCP::TimedText::MXFReader::h__Reader : public ASDCP::h__Reader
+class ASDCP::TimedText::MXFReader::h__Reader : public ASDCP::h__ASDCPReader
 {
   MXF::TimedTextDescriptor* m_EssenceDescriptor;
   ResourceMap_t             m_ResourceMap;
@@ -133,7 +133,7 @@ class ASDCP::TimedText::MXFReader::h__Reader : public ASDCP::h__Reader
 public:
   TimedTextDescriptor m_TDesc;    
 
-  h__Reader(const Dictionary& d) : ASDCP::h__Reader(d), m_EssenceDescriptor(0) {
+  h__Reader(const Dictionary& d) : ASDCP::h__ASDCPReader(d), m_EssenceDescriptor(0) {
     memset(&m_TDesc.AssetID, 0, UUIDlen);
   }
 
index 082cd83b223c4af1ea8dceae063c7d274feaa2e3..af6b55317be1f58b922ad9a3f960e748674fa481 100755 (executable)
@@ -128,6 +128,11 @@ 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::OPAtomHeader& HeaderPart,
+                           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);
+  
   //
  class KLReader : public ASDCP::KLVPacket
     {
@@ -145,38 +150,147 @@ namespace ASDCP
       Result_t ReadKLFromFile(Kumu::FileReader& Reader);
     };
 
+  namespace MXF
+  {
+      //---------------------------------------------------------------------------------
+      //
+
+    ///      void default_md_object_init();
+
+      template <class HeaderType, class FooterType>
+      class TrackFileReader
+      {
+       KM_NO_COPY_CONSTRUCT(TrackFileReader);
+       TrackFileReader();
+
+      public:
+       const Dictionary*  m_Dict;
+       Kumu::FileReader   m_File;
+       HeaderType         m_HeaderPart;
+       FooterType         m_FooterPart;
+       WriterInfo         m_Info;
+       ASDCP::FrameBuffer m_CtFrameBuf;
+       Kumu::fpos_t       m_LastPosition;
+
+      TrackFileReader(const Dictionary& d) :
+       m_HeaderPart(m_Dict), m_FooterPart(m_Dict), m_Dict(&d)
+         {
+           default_md_object_init();
+         }
+
+       virtual ~TrackFileReader() {
+         Close();
+       }
+
+       Result_t InitInfo()
+       {
+         assert(m_Dict);
+         InterchangeObject* Object;
+
+         // Identification
+         Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
+
+         // Writer Info and SourcePackage
+         if ( KM_SUCCESS(result) )
+           {
+             MD_to_WriterInfo((Identification*)Object, m_Info);
+             result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
+           }
+
+         if ( KM_SUCCESS(result) )
+           {
+             SourcePackage* SP = (SourcePackage*)Object;
+             memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
+           }
+
+         // optional CryptographicContext
+         if ( KM_SUCCESS(result) )
+           {
+             Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
+             
+             if ( KM_SUCCESS(cr_result) )
+               MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
+           }
+
+         return result;
+       }
+
+       //
+       Result_t OpenMXFRead(const char* filename)
+       {
+         m_LastPosition = 0;
+         Result_t result = m_File.OpenRead(filename);
+
+         if ( KM_SUCCESS(result) )
+           result = m_HeaderPart.InitFromFile(m_File);
+         
+         return result;
+       }
+
+       // positions file before reading
+       Result_t ReadEKLVFrame(const ASDCP::MXF::Partition& CurrentPartition,
+                              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_FooterPart.Lookup(FrameNum, TmpEntry)) )
+           {
+             DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
+             return RESULT_RANGE;
+           }
+
+         // get frame position and go read the frame's key and length
+         Kumu::fpos_t FilePosition = CurrentPartition.BodyOffset + TmpEntry.StreamOffset;
+         Result_t result = RESULT_OK;
+
+         if ( FilePosition != m_LastPosition )
+           {
+             m_LastPosition = FilePosition;
+             result = m_File.Seek(FilePosition);
+           }
+
+         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,
+                                 FrameNum, SequenceNum, FrameBuf, EssenceUL, Ctx, HMAC);
+       }
+
+       //
+       void     Close() {
+         m_File.Close();
+       }
+      };
+
+  }/// namespace MXF
+
   //
-  class h__Reader
+  class h__ASDCPReader : public MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>
     {
-      ASDCP_NO_COPY_CONSTRUCT(h__Reader);
-      h__Reader();
+      ASDCP_NO_COPY_CONSTRUCT(h__ASDCPReader);
+      h__ASDCPReader();
 
     public:
-      const Dictionary*  m_Dict;
-      Kumu::FileReader   m_File;
-      OPAtomHeader       m_HeaderPart;
-      Partition          m_BodyPart;
-      OPAtomIndexFooter  m_FooterPart;
-      ui64_t             m_EssenceStart;
-      WriterInfo         m_Info;
-      ASDCP::FrameBuffer m_CtFrameBuf;
-      Kumu::fpos_t       m_LastPosition;
+      Partition m_BodyPart;
 
-      h__Reader(const Dictionary&);
-      virtual ~h__Reader();
+      h__ASDCPReader(const Dictionary&);
+      virtual ~h__ASDCPReader();
 
-      Result_t InitInfo();
       Result_t OpenMXFRead(const char* filename);
+      Result_t InitInfo();
       Result_t InitMXFIndex();
-
-      // 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);
-      void     Close();
     };
 
 
@@ -285,8 +399,8 @@ namespace ASDCP
 
       ~IntegrityPack() {}
   
-      Result_t CalcValues(const ASDCP::FrameBuffer&, byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
-      Result_t TestValues(const ASDCP::FrameBuffer&, byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
+      Result_t CalcValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
+      Result_t TestValues(const ASDCP::FrameBuffer&, const byte_t* AssetID, ui32_t sequence, HMACContext* HMAC);
     };
 
 
index 5d728ccb5401741e54129f364f1bfa860f7db4e1..fdd47198f34627d49fc952233a910b1b27dd338f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2011, John Hurst
+Copyright (c) 2004-2012, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -70,31 +70,6 @@ struct iovec {
 typedef struct stat     fstat_t;
 #endif
 
-//
-static void
-split(const std::string& str, char separator, std::list<std::string>& components)
-{
-  const char* pstr = str.c_str();
-  const char* r = strchr(pstr, separator);
-
-  while ( r != 0 )
-    {
-      assert(r >= pstr);
-      if ( r > pstr )
-       {
-         std::string tmp_str;
-         tmp_str.assign(pstr, (r - pstr));
-         components.push_back(tmp_str);
-       }
-
-      pstr = r + 1;
-      r = strchr(pstr, separator);
-    }
-
-  if( strlen(pstr) > 0 )
-    components.push_back(std::string(pstr));
-}
-
 
 //
 static Kumu::Result_t
@@ -216,61 +191,55 @@ Kumu::FileSize(const std::string& pathname)
 }
 
 //
-static PathCompList_t&
-s_PathMakeCanonical(PathCompList_t& CList, bool is_absolute)
+static void
+make_canonical_list(const PathCompList_t& in_list, PathCompList_t& out_list)
 {
-  PathCompList_t::iterator ci, ri; // component and removal iterators
-
-  for ( ci = CList.begin(); ci != CList.end(); ci++ )
+  PathCompList_t::const_iterator i;
+  for ( i = in_list.begin(); i != in_list.end(); ++i )
     {
-      if ( *ci == "." && ( CList.size() > 1 || is_absolute ) )
-        {
-          ri = ci++;
-          CList.erase(ri);
-        }
-      else if ( *ci == ".." && ci != CList.begin() )
+      if ( *i == ".." )
        {
-         ri = ci;
-         ri--;
-             
-         if ( *ri != ".." )
+         if ( ! out_list.empty() )
            {
-             CList.erase(ri);
-             ri = ci++;
-             CList.erase(ri);
-            }
-        }
+             out_list.pop_back();
+           }
+       }
+      else if ( *i != "." )
+       {
+         out_list.push_back(*i);
+       }
     }
-
-  return CList;
 }
 
 //
 std::string
 Kumu::PathMakeCanonical(const std::string& Path, char separator)
 {
-  PathCompList_t CList;
+  PathCompList_t in_list, out_list;
   bool is_absolute = PathIsAbsolute(Path, separator);
-  s_PathMakeCanonical(PathToComponents(Path, CList, separator), is_absolute);
+  PathToComponents(Path, in_list, separator);
+  make_canonical_list(in_list, out_list);
 
   if ( is_absolute )
-    return ComponentsToAbsolutePath(CList, separator);
+    return ComponentsToAbsolutePath(out_list, separator);
 
-  return ComponentsToPath(CList, separator);
+  return ComponentsToPath(out_list, separator);
 }
 
 //
 bool
 Kumu::PathsAreEquivalent(const std::string& lhs, const std::string& rhs)
 {
-  return PathMakeCanonical(lhs) == PathMakeCanonical(rhs);
+  return PathMakeAbsolute(lhs) == PathMakeAbsolute(rhs);
 }
 
 //
 Kumu::PathCompList_t&
 Kumu::PathToComponents(const std::string& Path, PathCompList_t& CList, char separator)
 {
-  split(Path, separator, CList);
+  std::string s;
+  s = separator;
+  CList = km_token_split(Path, s);
   return CList;
 }
 
@@ -334,18 +303,8 @@ Kumu::PathIsAbsolute(const std::string& Path, char separator)
 
 //
 std::string
-Kumu::PathMakeAbsolute(const std::string& Path, char separator)
+Kumu::PathCwd()
 {
-  if ( Path.empty() )
-    {
-      std::string out_path;
-      out_path = separator;
-      return out_path;
-    }
-
-  if ( PathIsAbsolute(Path, separator) )
-    return Path;
-
   char cwd_buf [MaxFilePath];
   if ( _getcwd(cwd_buf, MaxFilePath) == 0 )
     {
@@ -353,11 +312,28 @@ Kumu::PathMakeAbsolute(const std::string& Path, char separator)
       return "";
     }
 
-  PathCompList_t CList;
-  PathToComponents(cwd_buf, CList);
-  CList.push_back(Path);
+  return cwd_buf;
+}
 
-  return ComponentsToAbsolutePath(s_PathMakeCanonical(CList, true), separator);
+//
+std::string
+Kumu::PathMakeAbsolute(const std::string& Path, char separator)
+{
+  if ( Path.empty() )
+    {
+      std::string tmpstr;
+      tmpstr = separator;
+      return tmpstr;
+    }
+
+  if ( PathIsAbsolute(Path, separator) )
+    return PathMakeCanonical(Path);
+
+  PathCompList_t in_list, out_list;
+  PathToComponents(PathJoin(PathCwd(), Path), in_list);
+  make_canonical_list(in_list, out_list);
+
+  return ComponentsToAbsolutePath(out_list);
 }
 
 //
@@ -455,6 +431,69 @@ Kumu::PathJoin(const std::string& Path1, const std::string& Path2,
   return Path1 + separator + Path2 + separator + Path3 + separator + Path4;
 }
 
+#ifndef KM_WIN32
+// returns false if link cannot be read
+//
+bool
+Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
+{
+  PathCompList_t in_list, out_list;
+  PathToComponents(PathMakeCanonical(link_path), in_list, separator);
+  PathCompList_t::iterator i;
+  char link_buf[MaxFilePath];
+
+  for ( i = in_list.begin(); i != in_list.end(); ++i )
+    {
+      assert ( *i != ".." && *i != "." );
+      out_list.push_back(*i);
+
+      for (;;)
+       {
+         std::string next_link = ComponentsToAbsolutePath(out_list, separator);
+         ssize_t link_size = readlink(next_link.c_str(), link_buf, MaxFilePath);
+
+         if ( link_size == -1 )
+           {
+             if ( errno == EINVAL )
+               break;
+
+             DefaultLogSink().Error("%s: readlink: %s\n", next_link.c_str(), strerror(errno));
+             return false;
+           }
+         
+         assert(link_size < MaxFilePath);
+         link_buf[link_size] = 0;
+         std::string tmp_path;
+         out_list.clear();
+
+         if ( PathIsAbsolute(link_buf) )
+           {
+             tmp_path = link_buf;
+           }
+         else
+           {
+             tmp_path = PathJoin(PathDirname(next_link), link_buf);
+           }
+
+         PathToComponents(PathMakeCanonical(tmp_path), out_list, separator);
+       }
+    }
+
+  resolved_path = ComponentsToAbsolutePath(out_list, separator);
+  return true;
+}
+
+#else // KM_WIN32
+// TODO: is there a reasonable equivalent to be written for win32?
+//
+bool
+Kumu::PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator)
+{
+  resolved_path = link_path;
+  return true;
+}
+#endif
+
 //
 Kumu::PathList_t&
 Kumu::FindInPaths(const IPathMatch& Pattern, const Kumu::PathList_t& SearchPaths,
@@ -1520,7 +1559,7 @@ h__DeletePath(const std::string& pathname)
 Result_t
 Kumu::DeletePath(const std::string& pathname)
 {
-  std::string c_pathname = PathMakeAbsolute(PathMakeCanonical(pathname));
+  std::string c_pathname = PathMakeCanonical(PathMakeAbsolute(pathname));
   DefaultLogSink().Debug("DeletePath (%s) c(%s)\n", pathname.c_str(), c_pathname.c_str());
   return h__DeletePath(c_pathname);
 }
index 0eb921b7227d2a377fa4741e5cc83f267c17fd74..a051e8e99b0a66eb96818a7c0f4eb70a562b39e5 100755 (executable)
@@ -133,6 +133,7 @@ namespace Kumu
   bool        PathIsFile(const std::string& Path); // true if the path exists in the filesystem and is a file
   bool        PathIsDirectory(const std::string& Path); // true if the path exists in the filesystem and is a directory
   fsize_t     FileSize(const std::string& Path); // returns the size of a regular file, 0 for a directory or device
+  std::string PathCwd();
   bool        PathsAreEquivalent(const std::string& lhs, const std::string& rhs); // true if paths point to the same filesystem entry
 
   // Returns free space and total space available for the given path
@@ -148,6 +149,7 @@ namespace Kumu
   std::string PathMakeAbsolute(const std::string& Path, char separator = '/'); // compute position of relative path using getcwd()
   std::string PathMakeLocal(const std::string& Path, const std::string& Parent); // remove Parent from front of Path, if it exists
   std::string PathMakeCanonical(const std::string& Path, char separator = '/'); // remove '.' and '..'
+  bool        PathResolveLinks(const std::string& link_path, std::string& resolved_path, char separator = '/');
 
   // common operations
   std::string PathBasename(const std::string& Path, char separator = '/'); // returns right-most path element (list back())
index 803b0ed6a5cf09df2e1c946589ed81ff94e86ce2..333b00a8c4876dd57f0c8205ad86a12c9133d903 100755 (executable)
@@ -1129,6 +1129,64 @@ Kumu::ByteString::Append(const byte_t* buf, ui32_t buf_len)
   return result;
 }
 
+//------------------------------------------------------------------------------------------
+
+//
+const char*
+Kumu::km_strnstr(const char *s, const char *find, size_t slen)
+{
+  char c, sc;
+  size_t len;
+
+  if ( ( c = *find++ ) != '\0' )
+    {
+      len = strlen(find);
+      do
+       {
+         do
+           {
+             if ( slen-- < 1 || ( sc = *s++ ) == '\0' )
+               return 0;
+           }
+         while ( sc != c );
+
+         if ( len > slen )
+           return 0;
+       }
+      while ( strncmp(s, find, len) != 0 );
+      --s;
+    }
+
+  return s;
+}
+
+//
+std::list<std::string>
+Kumu::km_token_split(const std::string& str, const std::string& separator)
+{
+  std::list<std::string> components;
+  const char* pstr = str.c_str();
+  const char* r = strstr(pstr, separator.c_str());
+
+  while ( r != 0 )
+    {
+      assert(r >= pstr);
+      if ( r > pstr )
+       {
+         std::string tmp_str;
+         tmp_str.assign(pstr, r - pstr);
+         components.push_back(tmp_str);
+       }
+
+      pstr = r + separator.size();
+      r = strstr(pstr, separator.c_str());
+    }
+      
+  if( strlen(pstr) > 0 )
+    components.push_back(std::string(pstr));
+
+  return components;
+}
 
 //
 // end KM_util.cpp
index f4de54eb95bd8878144ae260a007afabbcce44dc..a7667fc524d379b9421b45f978e36fff30e48feb 100755 (executable)
@@ -534,6 +534,14 @@ namespace Kumu
     hexdump(buf.RoData(), buf.Length());
   }
 
+  // Locates the first occurrence of the null-terminated string s2 in the string s1, where not more
+  // than n characters are searched.  Characters that appear after a `\0' character are not searched.
+  // Reproduced here from BSD for portability.
+  const char *km_strnstr(const char *s1, const char *s2, size_t n);
+
+  // Split the input string into tokens using the given separator. If the separator is not found the
+  // entire string will be returned as a single-item list.
+  std::list<std::string> km_token_split(const std::string& str, const std::string& separator);
 
 } // namespace Kumu
 
index 81f1eeb0cbae024d6f80450ad17e904bb66dafff..6cd2da3465cac768cd3e16153f4751680495ff47 100644 (file)
@@ -39,17 +39,34 @@ AM_CPPFLAGS += -DCONFIG_RANDOM_UUID
 endif
 
 # list of all the header files that should be installed
-include_HEADERS = KM_error.h KM_fileio.h KM_log.h KM_memio.h KM_mutex.h \
-               KM_platform.h KM_prng.h KM_util.h KM_tai.h KM_xml.h AS_DCP.h AS_02.h
+include_HEADERS = \
+       KM_error.h \
+       KM_fileio.h \
+       KM_log.h \
+       KM_memio.h \
+       KM_mutex.h \
+       KM_platform.h \
+       KM_prng.h \
+       KM_util.h \
+       KM_tai.h \
+       KM_xml.h \
+       AS_DCP.h
+
 if DEV_HEADERS
-include_HEADERS += S12MTimecode.h MDD.h Metadata.h KLV.h MXFTypes.h MXF.h Wav.h \
-               PCMParserList.h
+include_HEADERS += \
+       S12MTimecode.h \
+       MDD.h \
+       Metadata.h \
+       KLV.h \
+       MXFTypes.h \
+       MXF.h \
+       Wav.h \
+       PCMParserList.h
 nodist_include_HEADERS = TimedText_Transform.h
 endif
 
-
 # list of the libraries to build and install
-lib_LTLIBRARIES = libkumu.la libasdcp.la libas02.la
+lib_LTLIBRARIES = libkumu.la libasdcp.la
 
 # sources for kumu library
 libkumu_la_SOURCES = KM_error.h KM_fileio.cpp KM_fileio.h KM_log.cpp KM_log.h \
@@ -76,20 +93,12 @@ libasdcp_la_SOURCES = MPEG2_Parser.cpp MPEG.cpp JP2K_Codestream_Parser.cpp \
 if DEV_HEADERS
 nodist_libasdcp_la_SOURCES += TimedText_Transform.h TimedText_Transform.cpp
 endif
+
 libasdcp_la_LDFLAGS = -release @VERSION@
 # additional libraries to link against for a library
 libasdcp_la_LIBADD = libkumu.la
 libasdcp_la_CPPFLAGS = -DASDCP_PLATFORM=\"@host@\"
 
-
-# sources for as-02 library
-libas02_la_SOURCES     = \
-               AS_02.h AS_02_MXF.cpp AS_02_JP2K.cpp AS_02_PCM.cpp h__02_Reader.cpp h__02_Writer.cpp AS_02_internal.h
-libas02_la_LDFLAGS = -release @VERSION@
-libas02_la_LIBADD = libasdcp.la libkumu.la
-libas02_la_CPPFLAGS = -DASDCP_PLATFORM=\"@host@\"
-
-
 # Python extension
 if PYTHON_USE
 lib_LTLIBRARIES += libpyasdcp.la
@@ -132,7 +141,6 @@ endif
 # list of programs to be built and installed
 bin_PROGRAMS = \
        asdcp-wrap asdcp-unwrap asdcp-util asdcp-info asdcp-test \
-       as-02-wrap as-02-unwrap \
        j2c-test blackwave klvwalk wavesplit \
        kmfilegen kmrandgen kmuuidgen
 
@@ -140,12 +148,6 @@ bin_PROGRAMS = \
 asdcp_test_SOURCES = asdcp-test.cpp
 asdcp_test_LDADD = libasdcp.la
 
-as_02_wrap_SOURCES = as-02-wrap.cpp
-as_02_wrap_LDADD = libas02.la
-
-as_02_unwrap_SOURCES = as-02-unwrap.cpp
-as_02_unwrap_LDADD = libas02.la
-
 asdcp_wrap_SOURCES = asdcp-wrap.cpp
 asdcp_wrap_LDADD = libasdcp.la
 
index 4cd755ad8fdab626169753eb30e8336b454b7712..05e2c79a353a93c9928bf764256cf6ccd7d1dc01 100755 (executable)
@@ -92,15 +92,16 @@ USAGE:%s [-h|-help] [-V]\n\
        %s [options] <input-file>+\n\
 \n\
 Options:\n\
-  -3                - Force stereoscopic interpretation of a JP2K file\n\
-  -C                - Do not show essence coding UL\n\
-  -d                - Show essence descriptor info\n\
-  -h | -help        - Show help\n\
-  -H                - Show MXF header metadata\n\
-  -i                - Show identity info\n\
-  -n                - Show index\n\
-  -R                - Do not show bit-rate (Mb/s)\n\
-  -V                - Show version information\n\
+  -3          - Force stereoscopic interpretation of a JP2K file\n\
+  -C          - Do not show essence coding UL\n\
+  -d          - Show essence descriptor info\n\
+  -h | -help  - Show help\n\
+  -H          - Show MXF header metadata\n\
+  -i          - Show identity info\n\
+  -n          - Show index\n\
+  -r          - Show bit-rate (Mb/s)\n\
+  -t <int>    - Set high-bitrate threshold (Mb/s)\n\
+  -V          - Show version information\n\
 \n\
   NOTES: o There is no option grouping, all options must be distinct arguments.\n\
          o All option arguments must be separated from the option by whitespace.\n\n",
@@ -122,17 +123,19 @@ public:
   bool   showindex_flag; // true if index is to be displayed
   bool   showheader_flag; // true if MXF file header is to be displayed
   bool   stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
-  bool   showid_flag;
-  bool   showdescriptor_flag;
-  bool   showcoding_flag;
-  bool   showrate_flag;
+  bool   showid_flag;          // if true, show file identity info (the WriterInfo struct)
+  bool   showdescriptor_flag;  // if true, show the essence descriptor
+  bool   showcoding_flag;      // if true, show the coding UL 
+  bool   showrate_flag;        // if true and is image file, show bit rate
+  bool   max_bitrate_flag;     // true if -t option given
+  double max_bitrate;          // if true and is image file, max bit rate for rate test
 
   //
   CommandOptions(int argc, const char** argv) :
     error_flag(true), version_flag(false), help_flag(false), verbose_flag(false),
     showindex_flag(), showheader_flag(), stereo_image_flag(false),
-    showid_flag(false), showdescriptor_flag(false), showcoding_flag(true),
-    showrate_flag(true)
+    showid_flag(false), showdescriptor_flag(false), showcoding_flag(false),
+    showrate_flag(false), max_bitrate_flag(false), max_bitrate(0.0)
   {
     for ( int i = 1; i < argc; ++i )
       {
@@ -150,13 +153,20 @@ public:
            switch ( argv[i][1] )
              {
              case '3': stereo_image_flag = true; break;
-             case 'C': showcoding_flag = false; break;
+             case 'c': showcoding_flag = true; break;
              case 'd': showdescriptor_flag = true; break;
              case 'H': showheader_flag = true; break;
              case 'h': help_flag = true; break;
              case 'i': showid_flag = true; break;
              case 'n': showindex_flag = true; break;
-             case 'R': showrate_flag = false; break;
+             case 'r': showrate_flag = true; break;
+
+             case 't':
+               TEST_EXTRA_ARG(i, 't');
+               max_bitrate = abs(atoi(argv[i]));
+               max_bitrate_flag = true;
+               break;
+
              case 'V': version_flag = true; break;
              case 'v': verbose_flag = true; break;
 
@@ -260,16 +270,21 @@ class MyTextDescriptor : public TimedText::TimedTextDescriptor
   }
 };
 
-// MSVC didn't like the function template, so now it's a static class method
+//
+//
 template<class ReaderT, class DescriptorT>
 class FileInfoWrapper
 {
   ReaderT  m_Reader;
   DescriptorT m_Desc;
+  WriterInfo m_WriterInfo;
+  double m_MaxBitrate, m_AvgBitrate;
+  UL m_PictureEssenceCoding;
+
   KM_NO_COPY_CONSTRUCT(FileInfoWrapper);
 
 public:
-  FileInfoWrapper() {}
+  FileInfoWrapper() : m_MaxBitrate(0.0), m_AvgBitrate(0.0) {}
   virtual ~FileInfoWrapper() {}
 
   Result_t
@@ -285,19 +300,16 @@ public:
     if ( ASDCP_SUCCESS(result) )
       {
        m_Desc.FillDescriptor(m_Reader);
+       m_Reader.FillWriterInfo(m_WriterInfo);
 
-       fprintf(stdout, "File essence type is %s, (%d frame%s).\n",
+       fprintf(stdout, "File essence type is %s, (%d edit unit%s).\n",
                type_string, m_Desc.ContainerDuration, (m_Desc.ContainerDuration==1?"":"s"));
 
        if ( Options.showheader_flag )
          m_Reader.DumpHeaderMetadata(stream);
 
        if ( Options.showid_flag )
-         {
-           WriterInfo WI;
-           m_Reader.FillWriterInfo(WI);
-           WriterInfoDump(WI, stream);
-         }
+         WriterInfoDump(m_WriterInfo, stream);
 
        if ( Options.showdescriptor_flag )
          m_Desc.Dump(stream);
@@ -314,7 +326,7 @@ public:
   }
 
   //
-  void dump_PictureEssenceCoding(FILE* stream = 0)
+  void get_PictureEssenceCoding(FILE* stream = 0)
   {
     const Dictionary& Dict = DefaultCompositeDict();
     MXF::RGBAEssenceDescriptor *descriptor = 0;
@@ -323,26 +335,108 @@ public:
                                                                reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
     
     if ( KM_SUCCESS(result) )
+      m_PictureEssenceCoding = descriptor->PictureEssenceCoding;
+  }
+
+
+  //
+  void dump_PictureEssenceCoding(FILE* stream = 0)
+  {
+    char buf[64];
+
+    if ( m_PictureEssenceCoding.HasValue() )
       {
        const char *encoding_ul_type = "**UNKNOWN**";
 
-       if ( descriptor->PictureEssenceCoding == UL(P_HFR_UL_2K) )
+       if ( m_PictureEssenceCoding == UL(P_HFR_UL_2K) )
          encoding_ul_type = "P-HFR-2K";
-       else if ( descriptor->PictureEssenceCoding == UL(P_HFR_UL_4K) )
+       else if ( m_PictureEssenceCoding == UL(P_HFR_UL_4K) )
          encoding_ul_type = "**P-HFR-4K**";
-       else if ( descriptor->PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_2K) )
+       else if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_2K) )
          encoding_ul_type = "ST-429-4-2K";
-       else if ( descriptor->PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_4K) )
+       else if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_4K) )
          encoding_ul_type = "ST-429-4-4K";
 
-       char buf[64];
-       fprintf(stream, "PictureEssenceCoding: %s (%s)\n", descriptor->PictureEssenceCoding.EncodeString(buf, 64), encoding_ul_type);
+       fprintf(stream, "PictureEssenceCoding: %s (%s)\n", m_PictureEssenceCoding.EncodeString(buf, 64), encoding_ul_type);
       }
   }
 
+  //
+  Result_t
+  test_rates(CommandOptions& Options, FILE* stream = 0)
+  {
+    static const double dci_max_bitrate = 250.0;
+    static const double p_hfr_max_bitrate = 400.0;
+
+    double max_bitrate = Options.max_bitrate_flag ? Options.max_bitrate : dci_max_bitrate;
+    ui32_t errors = 0;
+
+    if ( m_PictureEssenceCoding == UL(P_HFR_UL_2K) )
+      {
+       if ( m_Desc.StoredWidth > 2048 ) // 4k
+         {
+           fprintf(stream, "4k images marked as 2k HFR.\n");
+           ++errors;
+         }
+
+       if ( m_Desc.SampleRate < ASDCP::EditRate_96 )
+         {
+           fprintf(stream, "HFR UL used for fps < 96.\n");
+           ++errors;
+         }
+
+       if ( ! Options.max_bitrate_flag )
+         max_bitrate = p_hfr_max_bitrate;
+      }
+    else if ( m_PictureEssenceCoding == UL(P_HFR_UL_4K) )
+      {
+       fprintf(stream, "4k HFR support undefined.\n");
+       ++errors;
+
+       if ( m_Desc.StoredWidth <= 2048 ) // 2k
+         {
+           fprintf(stream, "2k images marked as 4k HFR.\n");
+           ++errors;
+         }
+      }
+    else if ( m_PictureEssenceCoding != DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_2K)
+             && m_PictureEssenceCoding != DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_4K) )
+      {
+       fprintf(stream, "Unknown PictureEssenceCoding UL value.\n");
+       ++errors;
+      }
+    else
+      {
+       if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_2K) )
+         {
+           if ( m_Desc.StoredWidth > 2048 ) // 4k
+             {
+               fprintf(stream, "4k images marked as 2k ST 429-4.\n");
+               ++errors;
+             }
+         }
+       else if ( m_PictureEssenceCoding == DefaultCompositeDict().ul(MDD_JP2KEssenceCompression_4K) )
+         {
+           if ( m_Desc.StoredWidth <= 2048 ) // 2k
+             {
+               fprintf(stream, "2k images marked as 4k ST 429-4.\n");
+               ++errors;
+             }
+         }
+      }
+
+    if ( m_MaxBitrate > max_bitrate )
+      {
+       fprintf(stream, "Bitrate %0.0f exceeds maximum %0.0f (see option -r).\n", m_MaxBitrate, max_bitrate);
+       ++errors;
+      }
+
+    return errors ? RESULT_FAIL : RESULT_OK;
+  }
+
   //
   void
-  dump_Bitrate(FILE* stream = 0)
+  calc_Bitrate(FILE* stream = 0)
   {
     MXF::OPAtomIndexFooter& footer = m_Reader.OPAtomIndexFooter();
     ui64_t total_frame_bytes = 0, last_stream_offset = 0;
@@ -369,13 +463,22 @@ public:
          }
       }
 
-    // we did not test the first or last frame; scale to return bits when the input is bytes
+    // scale bytes to megabits
     static const double mega_const = 1 / ( 1024.0 * 1024.0 / 8.0 );
+
+    // we did not accumulate the first or last frame, so duration -= 2
     double avg_bytes_frame = total_frame_bytes / ( m_Desc.ContainerDuration - 2 );
-    double avg_mbits_second = avg_bytes_frame * mega_const * m_Desc.EditRate.Quotient();
 
-    fprintf(stream, "Max BitRate: %0.2f Mb/s\n", largest_frame * mega_const * m_Desc.EditRate.Quotient());
-    fprintf(stream, "Average BitRate: %0.2f Mb/s\n", avg_bytes_frame * mega_const * m_Desc.EditRate.Quotient());
+    m_MaxBitrate = largest_frame * mega_const * m_Desc.EditRate.Quotient();
+    m_AvgBitrate = avg_bytes_frame * mega_const * m_Desc.EditRate.Quotient();
+  }
+
+  //
+  void
+  dump_Bitrate(FILE* stream = 0)
+  {
+    fprintf(stream, "Max BitRate: %0.2f Mb/s\n", m_MaxBitrate);
+    fprintf(stream, "Average BitRate: %0.2f Mb/s\n", m_AvgBitrate);
   }
 
   //
@@ -431,22 +534,38 @@ show_file_info(CommandOptions& Options)
          FileInfoWrapper<ASDCP::JP2K::MXFSReader, MyStereoPictureDescriptor> wrapper;
          result = wrapper.file_info(Options, "JPEG 2000 stereoscopic pictures");
 
-         if ( ASDCP_SUCCESS(result) && Options.showcoding_flag )
-           wrapper.dump_PictureEssenceCoding(stdout);
+         if ( KM_SUCCESS(result) )
+           {
+             wrapper.get_PictureEssenceCoding();
+             wrapper.calc_Bitrate();
 
-         if ( ASDCP_SUCCESS(result) && Options.showrate_flag )
-           wrapper.dump_Bitrate(stdout);
+             if ( Options.showcoding_flag )
+               wrapper.dump_PictureEssenceCoding(stdout);
+
+             if ( Options.showrate_flag )
+               wrapper.dump_Bitrate(stdout);
+
+             result = wrapper.test_rates(Options, stdout);
+           }
        }
       else
        {
          FileInfoWrapper<ASDCP::JP2K::MXFReader, MyPictureDescriptor>wrapper;
          result = wrapper.file_info(Options, "JPEG 2000 pictures");
 
-         if ( ASDCP_SUCCESS(result) && Options.showcoding_flag )
-           wrapper.dump_PictureEssenceCoding(stdout);
+         if ( KM_SUCCESS(result) )
+           {
+             wrapper.get_PictureEssenceCoding();
+             wrapper.calc_Bitrate();
 
-         if ( ASDCP_SUCCESS(result) && Options.showrate_flag )
-           wrapper.dump_Bitrate(stdout);
+             if ( Options.showcoding_flag )
+               wrapper.dump_PictureEssenceCoding(stdout);
+
+             if ( Options.showrate_flag )
+               wrapper.dump_Bitrate(stdout);
+
+             result = wrapper.test_rates(Options, stdout);
+           }
        }
     }
   else if ( EssenceType == ESS_JPEG_2000_S )
@@ -454,11 +573,19 @@ show_file_info(CommandOptions& Options)
       FileInfoWrapper<ASDCP::JP2K::MXFSReader, MyStereoPictureDescriptor>wrapper;
       result = wrapper.file_info(Options, "JPEG 2000 stereoscopic pictures");
       
-      if ( ASDCP_SUCCESS(result) && Options.showcoding_flag )
-       wrapper.dump_PictureEssenceCoding(stdout);
+      if ( KM_SUCCESS(result) )
+       {
+         wrapper.get_PictureEssenceCoding();
+         wrapper.calc_Bitrate();
 
-      if ( ASDCP_SUCCESS(result) && Options.showrate_flag )
-       wrapper.dump_Bitrate(stdout);
+         if ( Options.showcoding_flag )
+           wrapper.dump_PictureEssenceCoding(stdout);
+
+         if ( Options.showrate_flag )
+           wrapper.dump_Bitrate(stdout);
+
+         result = wrapper.test_rates(Options, stdout);
+       }
     }
   else if ( EssenceType == ESS_TIMED_TEXT )
     {
index 384324d6ef079242cbae7f57a4a1db373662ac9a..56bc5ab5b8cd4c9ec6d91ac6979836350bcc8a45 100755 (executable)
@@ -292,6 +292,10 @@ public:
   //
   Rational PictureRate()
   {
+    if ( picture_rate == 16 ) return EditRate_16;
+    if ( picture_rate == 18 ) return EditRate_18;
+    if ( picture_rate == 20 ) return EditRate_20;
+    if ( picture_rate == 22 ) return EditRate_22;
     if ( picture_rate == 23 ) return EditRate_23_98;
     if ( picture_rate == 24 ) return EditRate_24;
     if ( picture_rate == 25 ) return EditRate_25;
@@ -308,6 +312,10 @@ public:
   //
   const char* szPictureRate()
   {
+    if ( picture_rate == 16 ) return "16";
+    if ( picture_rate == 18 ) return "18.182";
+    if ( picture_rate == 20 ) return "20";
+    if ( picture_rate == 22 ) return "21.818";
     if ( picture_rate == 23 ) return "23.976";
     if ( picture_rate == 24 ) return "24";
     if ( picture_rate == 25 ) return "25";
index ebe483c8fdbb2cbe893a3a3db93ddebabd773a18..e9b9feeada1f6869bf9ef3fddeb6df237241fdd1 100755 (executable)
@@ -157,6 +157,10 @@ public:
   //
   Rational PictureRate()
   {
+    if ( picture_rate == 16 ) return EditRate_16;
+    if ( picture_rate == 18 ) return EditRate_18;
+    if ( picture_rate == 20 ) return EditRate_20;
+    if ( picture_rate == 22 ) return EditRate_22;
     if ( picture_rate == 23 ) return EditRate_23_98;
     if ( picture_rate == 24 ) return EditRate_24;
     if ( picture_rate == 25 ) return EditRate_25;
@@ -204,10 +208,6 @@ public:
              case 'b':
                TEST_EXTRA_ARG(i, 'b');
                fb_size = abs(atoi(argv[i]));
-
-               if ( verbose_flag )
-                 fprintf(stderr, "Frame Buffer size: %u bytes.\n", fb_size);
-
                break;
 
              case 'd':
@@ -325,7 +325,7 @@ read_MPEG2_file(CommandOptions& Options)
        }
     }
 
-  if ( ASDCP_SUCCESS(result) )
+  if ( ASDCP_SUCCESS(result) && ( ! Options.no_write_flag ) )
     {
       char filename[256];
       snprintf(filename, 256, "%s.ves", Options.file_prefix);
@@ -367,8 +367,11 @@ read_MPEG2_file(CommandOptions& Options)
          if ( Options.verbose_flag )
            FrameBuffer.Dump(stderr, Options.fb_dump_size);
 
-         ui32_t write_count = 0;
-         result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+         if ( ! Options.no_write_flag )
+           {
+             ui32_t write_count = 0;
+             result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+           }
        }
     }
 
@@ -494,13 +497,16 @@ read_JP2K_S_file(CommandOptions& Options)
 
       if ( ASDCP_SUCCESS(result) )
        {
-         Kumu::FileWriter OutFile;
-         ui32_t write_count;
-         snprintf(filename, filename_max, left_format, Options.file_prefix, i);
-         result = OutFile.OpenWrite(filename);
+         if ( ! Options.no_write_flag )
+           {
+             Kumu::FileWriter OutFile;
+             ui32_t write_count;
+             snprintf(filename, filename_max, left_format, Options.file_prefix, i);
+             result = OutFile.OpenWrite(filename);
 
-         if ( ASDCP_SUCCESS(result) )
-           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+             if ( ASDCP_SUCCESS(result) )
+               result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+           }
 
          if ( Options.verbose_flag )
            FrameBuffer.Dump(stderr, Options.fb_dump_size);
@@ -511,13 +517,19 @@ read_JP2K_S_file(CommandOptions& Options)
 
       if ( ASDCP_SUCCESS(result) )
        {
-         Kumu::FileWriter OutFile;
-         ui32_t write_count;
-         snprintf(filename, filename_max, right_format, Options.file_prefix, i);
-         result = OutFile.OpenWrite(filename);
+         if ( ! Options.no_write_flag )
+           {
+             Kumu::FileWriter OutFile;
+             ui32_t write_count;
+             snprintf(filename, filename_max, right_format, Options.file_prefix, i);
+             result = OutFile.OpenWrite(filename);
 
-         if ( ASDCP_SUCCESS(result) )
-           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+             if ( ASDCP_SUCCESS(result) )
+               result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+           }
+
+         if ( Options.verbose_flag )
+           FrameBuffer.Dump(stderr, Options.fb_dump_size);
        }
     }
 
@@ -588,14 +600,17 @@ read_JP2K_file(CommandOptions& Options)
 
       if ( ASDCP_SUCCESS(result) )
        {
-         Kumu::FileWriter OutFile;
-         char filename[256];
-         ui32_t write_count;
-         snprintf(filename, 256, name_format, Options.file_prefix, i);
-         result = OutFile.OpenWrite(filename);
-
-         if ( ASDCP_SUCCESS(result) )
-           result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+         if ( ! Options.no_write_flag )
+           {
+             Kumu::FileWriter OutFile;
+             char filename[256];
+             ui32_t write_count;
+             snprintf(filename, 256, name_format, Options.file_prefix, i);
+             result = OutFile.OpenWrite(filename);
+
+             if ( ASDCP_SUCCESS(result) )
+               result = OutFile.Write(FrameBuffer.Data(), FrameBuffer.Size(), &write_count);
+           }
 
          if ( Options.verbose_flag )
            FrameBuffer.Dump(stderr, Options.fb_dump_size);
@@ -638,10 +653,20 @@ read_PCM_file(CommandOptions& Options)
           && ADesc.EditRate != EditRate_60 )
        ADesc.EditRate = Options.PictureRate();
 
-      FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
+      if ( Options.fb_size != FRAME_BUFFER_SIZE )
+       {
+         FrameBuffer.Capacity(Options.fb_size);
+       }
+      else
+       {
+         FrameBuffer.Capacity(PCM::CalcFrameBufferSize(ADesc));
+       }
 
       if ( Options.verbose_flag )
-       PCM::AudioDescriptorDump(ADesc);
+       {
+         fprintf(stderr, "Frame Buffer size: %u\n", Options.fb_size);
+         PCM::AudioDescriptorDump(ADesc);
+       }
     }
 
   if ( ASDCP_SUCCESS(result) )
@@ -663,9 +688,13 @@ read_PCM_file(CommandOptions& Options)
        }
 
       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 ) ));
+
+      if ( ! Options.no_write_flag )
+       {
+         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 )
@@ -699,7 +728,10 @@ read_PCM_file(CommandOptions& Options)
          if ( Options.verbose_flag )
            FrameBuffer.Dump(stderr, Options.fb_dump_size);
 
-         result = OutWave.WriteFrame(FrameBuffer);
+         if ( ! Options.no_write_flag )
+           {
+             result = OutWave.WriteFrame(FrameBuffer);
+           }
        }
     }
 
@@ -767,7 +799,7 @@ read_timed_text_file(CommandOptions& Options)
 
   result = Reader.ReadTimedTextResource(XMLDoc, Context, HMAC);
 
-  if ( ASDCP_SUCCESS(result) )
+  if ( ASDCP_SUCCESS(result) && ( ! Options.no_write_flag ) )
     {
       Kumu::FileWriter Writer;
       result = Writer.OpenWrite(Options.file_prefix);
@@ -780,18 +812,16 @@ read_timed_text_file(CommandOptions& Options)
     {
       result = Reader.ReadAncillaryResource(ri->ResourceID, FrameBuffer, Context, HMAC);
 
-      if ( ASDCP_SUCCESS(result) )
+      if ( ASDCP_SUCCESS(result) && ( ! Options.no_write_flag ) )
        {
          Kumu::FileWriter Writer;
          result = Writer.OpenWrite(Kumu::PathJoin(out_path, Kumu::UUID(ri->ResourceID).EncodeHex(buf, 64)).c_str());
 
          if ( ASDCP_SUCCESS(result) )
-           {
-             if ( Options.verbose_flag )
-               FrameBuffer.Dump(stderr, Options.fb_dump_size);
+           result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count);
 
-             result = Writer.Write(FrameBuffer.RoData(), FrameBuffer.Size(), &write_count);
-           }
+         if ( Options.verbose_flag )
+           FrameBuffer.Dump(stderr, Options.fb_dump_size);
        }
     }
 
index b6880ac73d6e6436907b70b5970ed984f96d5c23..bbff82a6c6eaae1e962d818f146d39b9f43f6a5d 100755 (executable)
@@ -56,6 +56,11 @@ using namespace ASDCP;
 
 const ui32_t FRAME_BUFFER_SIZE = 4 * Kumu::Megabyte;
 
+const byte_t P_HFR_UL_2K[16] = {
+  0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x0d,
+  0x0e, 0x16, 0x02, 0x02, 0x03, 0x01, 0x01, 0x03
+};
+
 //------------------------------------------------------------------------------------------
 //
 // command line option parser class
@@ -120,8 +125,8 @@ USAGE: %s [-h|-help] [-V]\n\
 Options:\n\
   -3                - Create a stereoscopic image file. Expects two\n\
                       directories of JP2K codestreams (directories must have\n\
-                      an equal number of frames; left eye is first).\n\
-  -C <UL>           - Set ChannelAssignment UL value\n\
+                      an equal number of frames; the left eye is first)\n\
+  -C <UL>           - Set ChannelAssignment UL value in a PCM file\n\
   -h | -help        - Show help\n\
   -V                - Show version information\n\
   -e                - Encrypt MPEG or JP2K headers (default)\n\
@@ -139,6 +144,7 @@ Options:\n\
                       '7.1DS', 'WTF'\n\
                       Default is no label (valid for Interop only).\n\
   -L                - Write SMPTE UL values instead of MXF Interop\n\
+  -P <UL>           - Set PictureEssenceCoding UL value in a JP2K file\n\
   -p <rate>         - fps of picture when wrapping PCM or JP2K:\n\
                       Use one of [23|24|25|30|48|50|60], 24 is default\n\
   -v                - Verbose, prints informative messages to stderr\n\
@@ -189,18 +195,12 @@ public:
   bool   asset_id_flag;  // true if an asset ID was given
   bool   encrypt_header_flag; // true if mpeg headers are to be encrypted
   bool   write_hmac;     // true if HMAC values are to be generated and written
-  ///  bool   read_hmac;      // true if HMAC values are to be validated
-  ///  bool   split_wav;      // true if PCM is to be extracted to stereo WAV files
-  ///  bool   mono_wav;       // true if PCM is to be extracted to mono WAV files
   bool   verbose_flag;   // true if the verbose option was selected
   ui32_t fb_dump_size;   // number of bytes of frame buffer to dump
-  ///  bool   showindex_flag; // true if index is to be displayed
-  ///  bool   showheader_flag; // true if MXF file header is to be displayed
   bool   no_write_flag;  // true if no output files are to be written
   bool   version_flag;   // true if the version display option was selected
   bool   help_flag;      // true if the help display option was selected
   bool   stereo_image_flag; // if true, expect stereoscopic JP2K input (left eye first)
-  ///  ui32_t number_width;   // number of digits in a serialized filename (for JPEG extract)
   ui32_t start_frame;    // frame number to begin processing
   ui32_t duration;       // number of frames to be processed
   bool   use_smpte_labels; // if true, SMPTE UL values will be written instead of MXF Interop values
@@ -216,10 +216,15 @@ public:
   bool show_ul_values;    /// 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;
 
   //
   Rational PictureRate()
   {
+    if ( picture_rate == 16 ) return EditRate_16;
+    if ( picture_rate == 18 ) return EditRate_18;
+    if ( picture_rate == 20 ) return EditRate_20;
+    if ( picture_rate == 22 ) return EditRate_22;
     if ( picture_rate == 23 ) return EditRate_23_98;
     if ( picture_rate == 24 ) return EditRate_24;
     if ( picture_rate == 25 ) return EditRate_25;
@@ -236,6 +241,10 @@ public:
   //
   const char* szPictureRate()
   {
+    if ( picture_rate == 16 ) return "16";
+    if ( picture_rate == 18 ) return "18.182";
+    if ( picture_rate == 20 ) return "20";
+    if ( picture_rate == 22 ) return "21.818";
     if ( picture_rate == 23 ) return "23.976";
     if ( picture_rate == 24 ) return "24";
     if ( picture_rate == 25 ) return "25";
@@ -306,7 +315,7 @@ public:
                break;
 
              case 'C':
-               TEST_EXTRA_ARG(i, 'U');
+               TEST_EXTRA_ARG(i, 'C');
                if ( ! channel_assignment.DecodeHex(argv[i]) )
                  {
                    fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
@@ -365,6 +374,15 @@ public:
              case 'L': use_smpte_labels = true; break;
              case 'M': write_hmac = false; break;
 
+             case 'P':
+               TEST_EXTRA_ARG(i, 'P');
+               if ( ! picture_coding.DecodeHex(argv[i]) )
+                 {
+                   fprintf(stderr, "Error decoding UL value: %s\n", argv[i]);
+                   return;
+                 }
+               break;
+
              case 'p':
                TEST_EXTRA_ARG(i, 'p');
                picture_rate = abs(atoi(argv[i]));
@@ -535,6 +553,35 @@ write_MPEG2_file(CommandOptions& Options)
 }
 
 
+//------------------------------------------------------------------------------------------
+
+// return false if an error is discovered
+bool
+check_phfr_params(CommandOptions& Options, JP2K::PictureDescriptor& PDesc)
+{
+  Rational rate = Options.PictureRate();
+  if ( rate != EditRate_96 && rate != EditRate_100 && rate != EditRate_120 )
+    return true;
+
+  if ( PDesc.StoredWidth > 2048 )
+    {
+      fprintf(stderr, "P-HFR files currently limited to 2K.\n");
+      return false;
+    }
+
+  if ( ! Options.use_smpte_labels )
+    {
+      fprintf(stderr, "P-HFR files must be written using SMPTE labels. Use option '-L'.\n");
+      return false;
+    }
+
+  // do not set the label if the user has already done so
+  if ( ! Options.picture_coding.HasValue() )
+    Options.picture_coding = UL(P_HFR_UL_2K);
+  
+  return true;
+}
+
 //------------------------------------------------------------------------------------------
 // JPEG 2000 essence
 
@@ -582,6 +629,9 @@ write_JP2K_S_file(CommandOptions& Options)
        }
     }
 
+  if ( ! check_phfr_params(Options, PDesc) )
+    return RESULT_FAIL;
+
   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
     {
       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
@@ -623,6 +673,14 @@ write_JP2K_S_file(CommandOptions& Options)
 
       if ( ASDCP_SUCCESS(result) )
        result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
+      
+      if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
+       {
+         MXF::RGBAEssenceDescriptor *descriptor = 0;
+         Writer.OPAtomHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
+                                                 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
+         descriptor->PictureEssenceCoding = Options.picture_coding;
+       }
     }
 
   if ( ASDCP_SUCCESS(result) )
@@ -706,6 +764,9 @@ write_JP2K_file(CommandOptions& Options)
        }
     }
 
+  if ( ! check_phfr_params(Options, PDesc) )
+    return RESULT_FAIL;
+
   if ( ASDCP_SUCCESS(result) && ! Options.no_write_flag )
     {
       WriterInfo Info = s_MyInfo;  // fill in your favorite identifiers here
@@ -747,6 +808,14 @@ write_JP2K_file(CommandOptions& Options)
 
       if ( ASDCP_SUCCESS(result) )
        result = Writer.OpenWrite(Options.out_file.c_str(), Info, PDesc);
+
+      if ( ASDCP_SUCCESS(result) && Options.picture_coding.HasValue() )
+       {
+         MXF::RGBAEssenceDescriptor *descriptor = 0;
+         Writer.OPAtomHeader().GetMDObjectByType(DefaultSMPTEDict().ul(MDD_RGBAEssenceDescriptor),
+                                                 reinterpret_cast<MXF::InterchangeObject**>(&descriptor));
+         descriptor->PictureEssenceCoding = Options.picture_coding;
+       }
     }
 
   if ( ASDCP_SUCCESS(result) )
index 3e75c3e76b3e6f1ea7dad781b85b3f15437b8592..35e5e776eb097e3bd069cb7ff33cc85508c33003 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005-2009, John Hurst
+Copyright (c) 2005-2012, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -56,7 +56,7 @@ banner(FILE* stream = stderr)
 {
   fprintf(stream, "\n\
 %s (asdcplib %s)\n\n\
-Copyright (c) 2005-2009 John Hurst\n\n\
+Copyright (c) 2005-2012 John Hurst\n\n\
 %s is part of asdcplib.\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\
@@ -73,7 +73,8 @@ USAGE: %s [-v|-h[-d]] <filename>\n\
 \n\
   -V              - Show version\n\
   -h              - Show help\n\
-  -d <duration>   - Number of 2k-sample frames to process, default 1440\n\
+  -d <duration>   - Number of edit units to process, default 1440\n\
+  -9              - Make a 96 kHz file (default 48 kHz)\n\
 \n\
 Other Options:\n\
   -v              - Verbose, show extra detail during run\n\
@@ -94,6 +95,7 @@ public:
   bool   verbose_flag;   // true if the verbose option was selected
   bool   version_flag;   // true if the version display option was selected
   bool   help_flag;      // true if the help display option was selected
+  bool   s96_flag;       // true if the samples should be at 96 kHz
   ui32_t duration;       // number of frames to be processed
   const char* filename;  // filename prefix for files written by the extract mode
 
@@ -103,7 +105,7 @@ public:
   {
     for ( int i = 1; i < argc; i++ )
       {
-       if ( argv[i][0] == '-' && isalpha(argv[i][1]) && argv[i][2] == 0 )
+       if ( argv[i][0] == '-' && ( isalpha(argv[i][1]) || isdigit(argv[i][1]) ) && argv[i][2] == 0 )
          {
            switch ( argv[i][1] )
              {
@@ -116,6 +118,10 @@ public:
                duration = atoi(argv[i]); // TODO: test for negative value, should use strtol()
                break;
 
+             case '9':
+               s96_flag = true;
+               break;
+
              default:
                fprintf(stderr, "Unrecognized option: %c\n", argv[i][1]);
                return;
@@ -153,12 +159,12 @@ make_black_wav_file(CommandOptions& Options)
   PCM::AudioDescriptor ADesc;
 
   ADesc.EditRate = Rational(24,1);
-  ADesc.AudioSamplingRate = ASDCP::SampleRate_48k;
+  ADesc.AudioSamplingRate = Options.s96_flag ? ASDCP::SampleRate_96k : ASDCP::SampleRate_48k;
   ADesc.Locked = 0;
   ADesc.ChannelCount = 1;
   ADesc.QuantizationBits = 24;
   ADesc.BlockAlign = 3;
-  ADesc.AvgBps = 14400;
+  ADesc.AvgBps = ADesc.BlockAlign * ADesc.AudioSamplingRate.Quotient();
   ADesc.LinkedTrackID = 1;
   ADesc.ContainerDuration = Options.duration;
 
@@ -169,8 +175,8 @@ make_black_wav_file(CommandOptions& Options)
 
   if ( Options.verbose_flag )
     {
-      fprintf(stderr, "48Khz PCM Audio, %s fps (%u spf)\n", "24",
-             PCM::CalcSamplesPerFrame(ADesc));
+      fprintf(stderr, "%s kHz PCM Audio, %s fps (%u spf)\n", "24",
+             (Options.s96_flag?"96":"48"), PCM::CalcSamplesPerFrame(ADesc));
       fputs("AudioDescriptor:\n", stderr);
       PCM::AudioDescriptorDump(ADesc);
     }
index 23b1e1a24490cec7d21ed356a4777d6a203f7f0c..da2aca231019172eb08d2da2c962a4b28eb2270f 100755 (executable)
@@ -59,81 +59,21 @@ ASDCP::default_md_object_init()
 }
 
 
-//
-ASDCP::h__Reader::h__Reader(const Dictionary& d) :
-  m_HeaderPart(m_Dict), m_BodyPart(m_Dict), m_FooterPart(m_Dict), m_Dict(&d), m_EssenceStart(0)
-{
-  default_md_object_init();
-}
-
-ASDCP::h__Reader::~h__Reader()
-{
-  Close();
-}
-
-void
-ASDCP::h__Reader::Close()
-{
-  m_File.Close();
-}
-
 //------------------------------------------------------------------------------------------
 //
 
 //
-Result_t
-ASDCP::h__Reader::InitInfo()
-{
-  assert(m_Dict);
-  InterchangeObject* Object;
-
-  m_Info.LabelSetType = LS_MXF_UNKNOWN;
-
-  if ( m_HeaderPart.OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
-    m_Info.LabelSetType = LS_MXF_INTEROP;
-  else if ( m_HeaderPart.OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
-    m_Info.LabelSetType = LS_MXF_SMPTE;
-
-  // Identification
-  Result_t result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(Identification), &Object);
-
-  if( ASDCP_SUCCESS(result) )
-    MD_to_WriterInfo((Identification*)Object, m_Info);
-
-  // SourcePackage
-  if( ASDCP_SUCCESS(result) )
-    result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(SourcePackage), &Object);
-
-  if( ASDCP_SUCCESS(result) )
-    {
-      SourcePackage* SP = (SourcePackage*)Object;
-      memcpy(m_Info.AssetUUID, SP->PackageUID.Value() + 16, UUIDlen);
-    }
-
-  // optional CryptographicContext
-  if( ASDCP_SUCCESS(result) )
-    {
-      Result_t cr_result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(CryptographicContext), &Object);
-
-      if( ASDCP_SUCCESS(cr_result) )
-       MD_to_CryptoInfo((CryptographicContext*)Object, m_Info, *m_Dict);
-    }
+ASDCP::h__ASDCPReader::h__ASDCPReader(const Dictionary& d) : MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>(d), m_BodyPart(m_Dict) {}
+ASDCP::h__ASDCPReader::~h__ASDCPReader() {}
 
-  return result;
-}
 
-
-// standard method of opening an MXF file for read
+// AS-DCP method of opening an MXF file for read
 Result_t
-ASDCP::h__Reader::OpenMXFRead(const char* filename)
+ASDCP::h__ASDCPReader::OpenMXFRead(const char* filename)
 {
-  m_LastPosition = 0;
-  Result_t result = m_File.OpenRead(filename);
+  Result_t result = ASDCP::MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>::OpenMXFRead(filename);
 
-  if ( ASDCP_SUCCESS(result) )
-    result = m_HeaderPart.InitFromFile(m_File);
-
-  if ( ASDCP_SUCCESS(result) )
+  if ( KM_SUCCESS(result) )
     {
       // if this is a three partition file, go to the body
       // partition and read the partition pack
@@ -142,20 +82,44 @@ ASDCP::h__Reader::OpenMXFRead(const char* filename)
          Array<RIP::Pair>::iterator r_i = m_HeaderPart.m_RIP.PairArray.begin();
          r_i++;
          m_File.Seek((*r_i).ByteOffset);
-
          result = m_BodyPart.InitFromFile(m_File);
        }
-
-      m_EssenceStart = m_File.Tell();
     }
 
+  if ( KM_SUCCESS(result) )
+    m_HeaderPart.BodyOffset = m_File.Tell();
+
   return result;
 }
 
+//
+Result_t
+ASDCP::h__ASDCPReader::InitInfo()
+{
+  Result_t result = ASDCP::MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>::InitInfo();
+
+  if( KM_SUCCESS(result) )
+    {
+      InterchangeObject* Object;
+
+      m_Info.LabelSetType = LS_MXF_UNKNOWN;
+
+      if ( m_HeaderPart.OperationalPattern.ExactMatch(MXFInterop_OPAtom_Entry().ul) )
+       {
+       m_Info.LabelSetType = LS_MXF_INTEROP;
+       }
+      else if ( m_HeaderPart.OperationalPattern.ExactMatch(SMPTE_390_OPAtom_Entry().ul) )
+       {
+         m_Info.LabelSetType = LS_MXF_SMPTE;
+       }
+    }
 
-// standard method of populating the in-memory index
+  return result;
+}
+
+// AS-DCP method of populating the in-memory index
 Result_t
-ASDCP::h__Reader::InitMXFIndex()
+ASDCP::h__ASDCPReader::InitMXFIndex()
 {
   if ( ! m_File.IsOpen() )
     return RESULT_INIT;
@@ -169,11 +133,26 @@ ASDCP::h__Reader::InitMXFIndex()
     }
 
   if ( ASDCP_SUCCESS(result) )
-    m_File.Seek(m_EssenceStart);
+    m_File.Seek(m_HeaderPart.BodyOffset);
 
   return result;
 }
 
+
+// AS-DCP method of reading a plaintext or encrypted frame
+Result_t
+ASDCP::h__ASDCPReader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
+                                    const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
+{
+  return ASDCP::MXF::TrackFileReader<OPAtomHeader, OPAtomIndexFooter>::ReadEKLVFrame(m_HeaderPart, FrameNum, FrameBuf,
+                                                                                    EssenceUL, Ctx, HMAC);
+}
+
+
+//------------------------------------------------------------------------------------------
+//
+
+
 //
 Result_t
 ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
@@ -229,55 +208,26 @@ ASDCP::KLReader::ReadKLFromFile(Kumu::FileReader& Reader)
   return InitFromBuffer(m_KeyBuf, header_length);
 }
 
-// standard method of reading a plaintext or encrypted frame
+// base subroutine for reading a KLV packet, assumes file position is at the first byte of the packet
 Result_t
-ASDCP::h__Reader::ReadEKLVFrame(ui32_t FrameNum, ASDCP::FrameBuffer& FrameBuf,
-                               const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
-{
-  // look up frame index node
-  IndexTableSegment::IndexEntry TmpEntry;
-
-  if ( ASDCP_FAILURE(m_FooterPart.Lookup(FrameNum, TmpEntry)) )
-    {
-      DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
-      return RESULT_RANGE;
-    }
-
-  // get frame position and go read the frame's key and length
-  Kumu::fpos_t FilePosition = m_EssenceStart + TmpEntry.StreamOffset;
-  Result_t result = RESULT_OK;
-
-  if ( FilePosition != m_LastPosition )
-    {
-      m_LastPosition = FilePosition;
-      result = m_File.Seek(FilePosition);
-    }
-
-  if( ASDCP_SUCCESS(result) )
-    result = ReadEKLVPacket(FrameNum, FrameNum + 1, FrameBuf, EssenceUL, Ctx, HMAC);
-
-  return result;
-}
-
-
-Result_t
-ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::FrameBuffer& FrameBuf,
-                                const byte_t* EssenceUL, AESDecContext* Ctx, HMACContext* HMAC)
+ASDCP::Read_EKLV_Packet(Kumu::FileReader& File, const ASDCP::Dictionary& Dict, const MXF::OPAtomHeader& HeaderPart,
+                       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)
 {
   KLReader Reader;
-  Result_t result = Reader.ReadKLFromFile(m_File);
+  Result_t result = Reader.ReadKLFromFile(File);
 
-  if ( ASDCP_FAILURE(result) )
+  if ( KM_FAILURE(result) )
     return result;
 
   UL Key(Reader.Key());
   ui64_t PacketLength = Reader.Length();
-  m_LastPosition = m_LastPosition + Reader.KLLength() + PacketLength;
-  assert(m_Dict);
+  LastPosition = LastPosition + Reader.KLLength() + PacketLength;
 
-  if ( Key.MatchIgnoreStream(m_Dict->ul(MDD_CryptEssence)) )  // ignore the stream numbers
+  if ( Key.MatchIgnoreStream(Dict.ul(MDD_CryptEssence)) )  // ignore the stream numbers
     {
-      if ( ! m_Info.EncryptedEssence )
+      if ( ! Info.EncryptedEssence )
        {
          DefaultLogSink().Error("EKLV packet found, no Cryptographic Context in header.\n");
          return RESULT_FORMAT;
@@ -285,10 +235,9 @@ ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::Fra
 
       // read encrypted triplet value into internal buffer
       assert(PacketLength <= 0xFFFFFFFFL);
-      m_CtFrameBuf.Capacity((ui32_t) PacketLength);
+      CtFrameBuf.Capacity((ui32_t) PacketLength);
       ui32_t read_count;
-      result = m_File.Read(m_CtFrameBuf.Data(), (ui32_t) PacketLength,
-                          &read_count);
+      result = File.Read(CtFrameBuf.Data(), (ui32_t) PacketLength, &read_count);
 
       if ( ASDCP_FAILURE(result) )
        return result;
@@ -299,17 +248,17 @@ ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::Fra
           return RESULT_FORMAT;
         }
 
-      m_CtFrameBuf.Size((ui32_t) PacketLength);
+      CtFrameBuf.Size((ui32_t) PacketLength);
 
       // should be const but mxflib::ReadBER is not
-      byte_t* ess_p = m_CtFrameBuf.Data();
+      byte_t* ess_p = CtFrameBuf.Data();
 
       // read context ID length
       if ( ! Kumu::read_test_BER(&ess_p, UUIDlen) )
        return RESULT_FORMAT;
 
       // test the context ID
-      if ( memcmp(ess_p, m_Info.ContextID, UUIDlen) != 0 )
+      if ( memcmp(ess_p, Info.ContextID, UUIDlen) != 0 )
        {
          DefaultLogSink().Error("Packet's Cryptographic Context ID does not match the header.\n");
          return RESULT_FORMAT;
@@ -331,11 +280,11 @@ ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::Fra
       if ( ! UL(ess_p).MatchIgnoreStream(EssenceUL) ) // ignore the stream number
        {
          char strbuf[IntBufferLen];
-         const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
+         const MDDEntry* Entry = Dict.FindUL(Key.Value());
          if ( Entry == 0 )
-           DefaultLogSink().Warn("Unexpected Encrypted Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
+           DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
          else
-           DefaultLogSink().Warn("Unexpected Encrypted Essence UL found: %s.\n", Entry->name);
+           DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
          return RESULT_FORMAT;
        }
       ess_p += SMPTE_UL_LENGTH;
@@ -363,7 +312,7 @@ ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::Fra
          return RESULT_FORMAT;
        }
 
-      ui32_t tmp_len = esv_length + (m_Info.UsesHMAC ? klv_intpack_size : 0);
+      ui32_t tmp_len = esv_length + (Info.UsesHMAC ? klv_intpack_size : 0);
 
       if ( PacketLength < tmp_len )
        {
@@ -385,10 +334,10 @@ ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::Fra
          FrameBuf.FrameNumber(FrameNum);
   
          // detect and test integrity pack
-         if ( ASDCP_SUCCESS(result) && m_Info.UsesHMAC && HMAC )
+         if ( ASDCP_SUCCESS(result) && Info.UsesHMAC && HMAC )
            {
              IntegrityPack IntPack;
-             result = IntPack.TestValues(TmpWrapper, m_Info.AssetUUID, SequenceNum, HMAC);
+             result = IntPack.TestValues(TmpWrapper, Info.AssetUUID, SequenceNum, HMAC);
            }
        }
       else // return ciphertext to caller
@@ -421,7 +370,7 @@ ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::Fra
       // read the data into the supplied buffer
       ui32_t read_count;
       assert(PacketLength <= 0xFFFFFFFFL);
-      result = m_File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
+      result = File.Read(FrameBuf.Data(), (ui32_t) PacketLength, &read_count);
          
       if ( ASDCP_FAILURE(result) )
        return result;
@@ -443,11 +392,12 @@ ASDCP::h__Reader::ReadEKLVPacket(ui32_t FrameNum, ui32_t SequenceNum, ASDCP::Fra
   else
     {
       char strbuf[IntBufferLen];
-      const MDDEntry* Entry = m_Dict->FindUL(Key.Value());
+      const MDDEntry* Entry = Dict.FindUL(Key.Value());
       if ( Entry == 0 )
         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Key.EncodeString(strbuf, IntBufferLen));
       else
         DefaultLogSink().Warn("Unexpected Essence UL found: %s.\n", Entry->name);
+
       return RESULT_FORMAT;
     }
 
index ce81b8faabb5d00f93985370d7fec15e8a62a5e6..30a5e09f81864d0f9fd1a2f8cc0d507ec085315f 100644 (file)
@@ -111,8 +111,15 @@ main(int argc, const char** argv)
   FindInPaths(PathMatchAny(), InList, OutList);
   PathList_t::iterator pi;
 
-  for ( pi = OutList.begin(); pi != OutList.end(); pi++ )
-    cerr << *pi << endl;
+  if ( false )
+    {
+      for ( pi = OutList.begin(); pi != OutList.end(); pi++ )
+       cerr << *pi << endl;
+    }
+  else
+    {
+      cerr << OutList.size() << ( ( OutList.size() == 1 ) ? " file" : " files" ) << endl;
+    }
 
   cerr << "----------------------------------" << endl;
   OutList.clear();