imf bugs
authorjhurst <jhurst@cinecert.com>
Sun, 21 Sep 2014 13:27:43 +0000 (13:27 +0000)
committerjhurst <>
Sun, 21 Sep 2014 13:27:43 +0000 (13:27 +0000)
date parse bug
timed-text transform removed

28 files changed:
src/AS_02_JP2K.cpp
src/AS_02_TimedText.cpp [new file with mode: 0644]
src/AS_DCP.cpp
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/KLV.cpp
src/KLV.h
src/KM_error.h
src/KM_fileio.cpp
src/KM_fileio.h
src/KM_util.cpp
src/KM_xml.cpp
src/KM_xml.h
src/MDD.cpp
src/MXF.cpp
src/MXF.h
src/MXFTypes.cpp
src/Makefile.am
src/S12MTimecode.h
src/ST2052_TextParser.cpp [new file with mode: 0644]
src/as-02-wrap.cpp
src/asdcp-wrap.cpp
src/h__02_Writer.cpp
src/wavesplit.cpp

index 48b98fec44d44fd7538b754dc9728fbf447cc8c9..a2027d3a8407cb05c88bcd1187139cc8c86335c2 100644 (file)
@@ -264,7 +264,8 @@ AS_02::JP2K::MXFWriter::h__Writer::OpenWrite(const std::string& filename,
 {
   if ( ! m_State.Test_BEGIN() )
     {
-      return RESULT_STATE;
+      KM_RESULT_STATE_HERE();
+       return RESULT_STATE;
     }
 
   if ( m_IndexStrategy != AS_02::IS_FOLLOW )
@@ -319,7 +320,8 @@ AS_02::JP2K::MXFWriter::h__Writer::SetSourceStream(const std::string& label, con
   assert(m_Dict);
   if ( ! m_State.Test_INIT() )
     {
-      return RESULT_STATE;
+      KM_RESULT_STATE_HERE();
+       return RESULT_STATE;
     }
 
   memcpy(m_EssenceUL, m_Dict->ul(MDD_JPEG2000Essence), SMPTE_UL_LENGTH);
@@ -378,7 +380,10 @@ Result_t
 AS_02::JP2K::MXFWriter::h__Writer::Finalize()
 {
   if ( ! m_State.Test_RUNNING() )
-    return RESULT_STATE;
+    {
+      KM_RESULT_STATE_HERE();
+       return RESULT_STATE;
+    }
 
   Result_t result = m_State.Goto_FINAL();
 
diff --git a/src/AS_02_TimedText.cpp b/src/AS_02_TimedText.cpp
new file mode 100644 (file)
index 0000000..fc851ec
--- /dev/null
@@ -0,0 +1,739 @@
+/*
+Copyright (c) 2008-2014, 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_DCP_TimedText.cpp
+    \version $Id$       
+    \brief   AS-DCP library, PCM essence reader and writer implementation
+*/
+
+
+#include "AS_02_internal.h"
+#include "KM_xml.h"
+#include <iostream>
+#include <iomanip>
+
+using Kumu::GenRandomValue;
+
+static std::string TIMED_TEXT_PACKAGE_LABEL = "File Package: SMPTE ST 429-5 / ST 2067-5 clip wrapping of IMF Timed Text data";
+static std::string TIMED_TEXT_DEF_LABEL = "Timed Text Track";
+
+
+//------------------------------------------------------------------------------------------
+
+const char*
+MIME2str(TimedText::MIMEType_t m)
+{
+  if ( m == TimedText::MT_PNG )
+    return "image/png";
+
+  else if ( m == TimedText::MT_OPENTYPE )
+    return "application/x-font-opentype";
+
+  return "application/octet-stream";
+}
+
+//------------------------------------------------------------------------------------------
+
+typedef std::map<Kumu::UUID, Kumu::UUID> ResourceMap_t;
+
+class AS_02::TimedText::MXFReader::h__Reader : public AS_02::h__AS02Reader
+{
+  ASDCP::MXF::TimedTextDescriptor* m_EssenceDescriptor;
+  ResourceMap_t             m_ResourceMap;
+
+  ASDCP_NO_COPY_CONSTRUCT(h__Reader);
+
+public:
+  TimedTextDescriptor m_TDesc;    
+
+  h__Reader(const Dictionary& d) : AS_02::h__AS02Reader(d), m_EssenceDescriptor(0) {
+    memset(&m_TDesc.AssetID, 0, UUIDlen);
+  }
+
+  virtual ~h__Reader() {}
+
+  Result_t    OpenRead(const std::string&);
+  Result_t    MD_to_TimedText_TDesc(TimedTextDescriptor& TDesc);
+  Result_t    ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
+  Result_t    ReadAncillaryResource(const Kumu::UUID&, ASDCP::TimedText::FrameBuffer& FrameBuf, AESDecContext* Ctx, HMACContext* HMAC);
+};
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::h__Reader::MD_to_TimedText_TDesc(TimedTextDescriptor& TDesc)
+{
+  assert(m_EssenceDescriptor);
+  memset(&m_TDesc.AssetID, 0, UUIDlen);
+  ASDCP::MXF::TimedTextDescriptor* TDescObj = (ASDCP::MXF::TimedTextDescriptor*)m_EssenceDescriptor;
+
+  TDesc.EditRate = TDescObj->SampleRate;
+  assert(TDescObj->ContainerDuration <= 0xFFFFFFFFL);
+  TDesc.ContainerDuration = (ui32_t) TDescObj->ContainerDuration;
+  memcpy(TDesc.AssetID, TDescObj->ResourceID.Value(), UUIDlen);
+  TDesc.NamespaceName = TDescObj->NamespaceURI;
+  TDesc.EncodingName = TDescObj->UCSEncoding;
+
+  Batch<Kumu::UUID>::const_iterator sdi = TDescObj->SubDescriptors.begin();
+  TimedTextResourceSubDescriptor* DescObject = 0;
+  Result_t result = RESULT_OK;
+
+  for ( ; sdi != TDescObj->SubDescriptors.end() && KM_SUCCESS(result); sdi++ )
+    {
+      InterchangeObject* tmp_iobj = 0;
+      result = m_HeaderPart.GetMDObjectByID(*sdi, &tmp_iobj);
+      DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
+
+      if ( KM_SUCCESS(result) )
+       {
+         TimedTextResourceDescriptor TmpResource;
+         memcpy(TmpResource.ResourceID, DescObject->AncillaryResourceID.Value(), UUIDlen);
+
+         if ( DescObject->MIMEMediaType.find("application/x-font-opentype") != std::string::npos
+              || DescObject->MIMEMediaType.find("application/x-opentype") != std::string::npos
+              || DescObject->MIMEMediaType.find("font/opentype") != std::string::npos )
+           TmpResource.Type = ASDCP::TimedText::MT_OPENTYPE;
+
+         else if ( DescObject->MIMEMediaType.find("image/png") != std::string::npos )
+           TmpResource.Type = ASDCP::TimedText::MT_PNG;
+
+         else
+           TmpResource.Type = ASDCP::TimedText::MT_BIN;
+
+         TDesc.ResourceList.push_back(TmpResource);
+         m_ResourceMap.insert(ResourceMap_t::value_type(DescObject->AncillaryResourceID, *sdi));
+       }
+      else
+       {
+         DefaultLogSink().Error("Broken sub-descriptor link\n");
+         return RESULT_FORMAT;
+       }
+    }
+
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::h__Reader::OpenRead(const std::string& filename)
+{
+  Result_t result = OpenMXFRead(filename.c_str());
+  
+  if( ASDCP_SUCCESS(result) )
+    {
+      if ( m_EssenceDescriptor == 0 )
+       {
+         InterchangeObject* tmp_iobj = 0;
+         result = m_HeaderPart.GetMDObjectByType(OBJ_TYPE_ARGS(TimedTextDescriptor), &tmp_iobj);
+         m_EssenceDescriptor = static_cast<ASDCP::MXF::TimedTextDescriptor*>(tmp_iobj);
+       }
+
+      if( ASDCP_SUCCESS(result) )
+       result = MD_to_TimedText_TDesc(m_TDesc);
+    }
+
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::h__Reader::ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf,
+                                                             AESDecContext* Ctx, HMACContext* HMAC)
+{
+  if ( ! m_File.IsOpen() )
+    return RESULT_INIT;
+
+  assert(m_Dict);
+  Result_t result = ReadEKLVFrame(0, FrameBuf, m_Dict->ul(MDD_TimedTextEssence), Ctx, HMAC);
+
+ if( ASDCP_SUCCESS(result) )
+   {
+     FrameBuf.AssetID(m_TDesc.AssetID);
+     FrameBuf.MIMEType("text/xml");
+   }
+
+ return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::h__Reader::ReadAncillaryResource(const Kumu::UUID& uuid,
+                                                             ASDCP::TimedText::FrameBuffer& FrameBuf,
+                                                             AESDecContext* Ctx, HMACContext* HMAC)
+{
+  ResourceMap_t::const_iterator ri = m_ResourceMap.find(uuid);
+  if ( ri == m_ResourceMap.end() )
+    {
+      char buf[64];
+      DefaultLogSink().Error("No such resource: %s\n", uuid.EncodeHex(buf, 64));
+      return RESULT_RANGE;
+    }
+
+  TimedTextResourceSubDescriptor* DescObject = 0;
+  // get the subdescriptor
+  InterchangeObject* tmp_iobj = 0;
+  Result_t result = m_HeaderPart.GetMDObjectByID((*ri).second, &tmp_iobj);
+  DescObject = static_cast<TimedTextResourceSubDescriptor*>(tmp_iobj);
+
+  if ( KM_SUCCESS(result) )
+    {
+      Array<RIP::Pair>::const_iterator pi;
+      RIP::Pair TmpPair;
+      ui32_t sequence = 0;
+
+      // Look up the partition start in the RIP using the SID.
+      // Count the sequence length in because this is the sequence
+      // value needed to  complete the HMAC.
+      for ( pi = m_RIP.PairArray.begin(); pi != m_RIP.PairArray.end(); ++pi, ++sequence )
+       {
+         if ( (*pi).BodySID == DescObject->EssenceStreamID )
+           {
+             TmpPair = *pi;
+             break;
+           }
+       }
+
+      if ( TmpPair.ByteOffset == 0 )
+       {
+         DefaultLogSink().Error("Body SID not found in RIP set: %d\n", DescObject->EssenceStreamID);
+         return RESULT_FORMAT;
+       }
+
+      if ( KM_SUCCESS(result) )
+       {
+         FrameBuf.AssetID(uuid.Value());
+         FrameBuf.MIMEType(DescObject->MIMEMediaType);
+
+         // seek tp the start of the partition
+         if ( (Kumu::fpos_t)TmpPair.ByteOffset != m_LastPosition )
+           {
+             m_LastPosition = TmpPair.ByteOffset;
+             result = m_File.Seek(TmpPair.ByteOffset);
+           }
+
+         // read the partition header
+         ASDCP::MXF::Partition GSPart(m_Dict);
+         result = GSPart.InitFromFile(m_File);
+
+         if( ASDCP_SUCCESS(result) )
+           {
+             // check the SID
+             if ( DescObject->EssenceStreamID != GSPart.BodySID )
+               {
+                 char buf[64];
+                 DefaultLogSink().Error("Generic stream partition body differs: %s\n", uuid.EncodeHex(buf, 64));
+                 return RESULT_FORMAT;
+               }
+
+             // read the essence packet
+             assert(m_Dict);
+             if( ASDCP_SUCCESS(result) )
+               result = ReadEKLVPacket(0, sequence, FrameBuf, m_Dict->ul(MDD_GenericStream_DataElement), Ctx, HMAC);
+           }
+       }
+    }
+
+  return result;
+}
+
+
+//------------------------------------------------------------------------------------------
+
+AS_02::TimedText::MXFReader::MXFReader()
+{
+  m_Reader = new h__Reader(DefaultSMPTEDict());
+}
+
+
+AS_02::TimedText::MXFReader::~MXFReader()
+{
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper.  Caveat emptor!
+//
+ASDCP::MXF::OP1aHeader&
+AS_02::TimedText::MXFReader::OP1aHeader()
+{
+  if ( m_Reader.empty() )
+    {
+      assert(g_OP1aHeader);
+      return *g_OP1aHeader;
+    }
+
+  return m_Reader->m_HeaderPart;
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper.  Caveat emptor!
+//
+AS_02::MXF::AS02IndexReader&
+AS_02::TimedText::MXFReader::AS02IndexReader()
+{
+  if ( m_Reader.empty() )
+    {
+      assert(g_AS02IndexReader);
+      return *g_AS02IndexReader;
+    }
+
+  return m_Reader->m_IndexAccess;
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper.  Caveat emptor!
+//
+ASDCP::MXF::RIP&
+AS_02::TimedText::MXFReader::RIP()
+{
+  if ( m_Reader.empty() )
+    {
+      assert(g_RIP);
+      return *g_RIP;
+    }
+
+  return m_Reader->m_RIP;
+}
+
+// Open the file for reading. The file must exist. Returns error if the
+// operation cannot be completed.
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::OpenRead(const std::string& filename) const
+{
+  return m_Reader->OpenRead(filename);
+}
+
+// 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::TimedText::MXFReader::FillTimedTextDescriptor(TimedText::TimedTextDescriptor& TDesc) const
+{
+  if ( m_Reader && m_Reader->m_File.IsOpen() )
+    {
+      TDesc = m_Reader->m_TDesc;
+      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
+AS_02::TimedText::MXFReader::FillWriterInfo(WriterInfo& Info) const
+{
+  if ( m_Reader && m_Reader->m_File.IsOpen() )
+    {
+      Info = m_Reader->m_Info;
+      return RESULT_OK;
+    }
+
+  return RESULT_INIT;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::ReadTimedTextResource(std::string& s, AESDecContext* Ctx, HMACContext* HMAC) const
+{
+  ASDCP::TimedText::FrameBuffer FrameBuf(8*Kumu::Megabyte);
+
+  Result_t result = ReadTimedTextResource(FrameBuf, Ctx, HMAC);
+
+  if ( ASDCP_SUCCESS(result) )
+    s.assign((char*)FrameBuf.Data(), FrameBuf.Size());
+
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::ReadTimedTextResource(ASDCP::TimedText::FrameBuffer& FrameBuf,
+                                                  AESDecContext* Ctx, HMACContext* HMAC) const
+{
+  if ( m_Reader && m_Reader->m_File.IsOpen() )
+    return m_Reader->ReadTimedTextResource(FrameBuf, Ctx, HMAC);
+
+  return RESULT_INIT;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::ReadAncillaryResource(const Kumu::UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
+                                                  AESDecContext* Ctx, HMACContext* HMAC) const
+{
+  if ( m_Reader && m_Reader->m_File.IsOpen() )
+    return m_Reader->ReadAncillaryResource(uuid, FrameBuf, Ctx, HMAC);
+
+  return RESULT_INIT;
+}
+
+
+//
+void
+AS_02::TimedText::MXFReader::DumpHeaderMetadata(FILE* stream) const
+{
+  if ( m_Reader->m_File.IsOpen() )
+    m_Reader->m_HeaderPart.Dump(stream);
+}
+
+
+//
+void
+AS_02::TimedText::MXFReader::DumpIndex(FILE* stream) const
+{
+  if ( m_Reader->m_File.IsOpen() )
+    m_Reader->m_IndexAccess.Dump(stream);
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFReader::Close() const
+{
+  if ( m_Reader && m_Reader->m_File.IsOpen() )
+    {
+      m_Reader->Close();
+      return RESULT_OK;
+    }
+
+  return RESULT_INIT;
+}
+
+
+//------------------------------------------------------------------------------------------
+
+
+//
+class AS_02::TimedText::MXFWriter::h__Writer : public AS_02::h__AS02WriterClip
+{
+  ASDCP_NO_COPY_CONSTRUCT(h__Writer);
+  h__Writer();
+
+public:
+  TimedTextDescriptor m_TDesc;
+  byte_t m_EssenceUL[SMPTE_UL_LENGTH];
+  ui32_t m_EssenceStreamID;
+
+  h__Writer(const Dictionary& d) : AS_02::h__AS02WriterClip(d), m_EssenceStreamID(10)
+  {
+    memset(m_EssenceUL, 0, SMPTE_UL_LENGTH);
+  }
+
+  virtual ~h__Writer() {}
+
+  Result_t OpenWrite(const std::string&, ui32_t HeaderSize);
+  Result_t SetSourceStream(const ASDCP::TimedText::TimedTextDescriptor&);
+  Result_t WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* = 0, HMACContext* = 0);
+  Result_t WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer&, AESEncContext* = 0, HMACContext* = 0);
+  Result_t Finalize();
+  Result_t TimedText_TDesc_to_MD(ASDCP::TimedText::TimedTextDescriptor& TDesc);
+};
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::h__Writer::TimedText_TDesc_to_MD(TimedText::TimedTextDescriptor& TDesc)
+{
+  assert(m_EssenceDescriptor);
+  ASDCP::MXF::TimedTextDescriptor* TDescObj = (ASDCP::MXF::TimedTextDescriptor*)m_EssenceDescriptor;
+
+  TDescObj->SampleRate = TDesc.EditRate;
+  TDescObj->ContainerDuration = TDesc.ContainerDuration;
+  TDescObj->ResourceID.Set(TDesc.AssetID);
+  TDescObj->NamespaceURI = TDesc.NamespaceName;
+  TDescObj->UCSEncoding = TDesc.EncodingName;
+
+  return RESULT_OK;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::h__Writer::OpenWrite(const std::string& filename, ui32_t HeaderSize)
+{
+  if ( ! m_State.Test_BEGIN() )
+    {
+      KM_RESULT_STATE_HERE();
+      return RESULT_STATE;
+    }
+
+  Result_t result = m_File.OpenWrite(filename.c_str());
+
+  if ( ASDCP_SUCCESS(result) )
+    {
+      m_HeaderSize = HeaderSize;
+      m_EssenceDescriptor = new ASDCP::MXF::TimedTextDescriptor(m_Dict);
+      result = m_State.Goto_INIT();
+    }
+
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::h__Writer::SetSourceStream(ASDCP::TimedText::TimedTextDescriptor const& TDesc)
+{
+  if ( ! m_State.Test_INIT() )
+    {
+      KM_RESULT_STATE_HERE();
+      return RESULT_STATE;
+    }
+
+  assert(m_Dict);
+  m_TDesc = TDesc;
+
+  Result_t result = TimedText_TDesc_to_MD(m_TDesc);
+
+  if ( KM_SUCCESS(result) )
+    {
+      ResourceList_t::const_iterator i;
+      for ( i = m_TDesc.ResourceList.begin() ; i != m_TDesc.ResourceList.end(); ++i )
+       {
+         TimedTextResourceSubDescriptor* resourceSubdescriptor = new TimedTextResourceSubDescriptor(m_Dict);
+         GenRandomValue(resourceSubdescriptor->InstanceUID);
+         resourceSubdescriptor->AncillaryResourceID.Set((*i).ResourceID);
+         resourceSubdescriptor->MIMEMediaType = MIME2str((*i).Type);
+         resourceSubdescriptor->EssenceStreamID = m_EssenceStreamID++;
+         m_EssenceSubDescriptorList.push_back((FileDescriptor*)resourceSubdescriptor);
+         m_EssenceDescriptor->SubDescriptors.push_back(resourceSubdescriptor->InstanceUID);
+       }
+    }
+  
+  if ( KM_SUCCESS(result) )
+    {
+      result = WriteAS02Header(TIMED_TEXT_PACKAGE_LABEL, UL(m_Dict->ul(MDD_TimedTextWrappingClip)),
+                              "Data Track", UL(m_EssenceUL), UL(m_Dict->ul(MDD_TimedTextEssence)),
+                              TDesc.EditRate, derive_timecode_rate_from_edit_rate(TDesc.EditRate));
+    }
+  if ( KM_SUCCESS(result) )
+    {
+      this->m_IndexWriter.SetPrimerLookup(&this->m_HeaderPart.m_Primer);
+    }
+
+  if ( KM_SUCCESS(result) )
+    {
+      memcpy(m_EssenceUL, m_Dict->ul(MDD_TimedTextEssence), SMPTE_UL_LENGTH);
+      m_EssenceUL[SMPTE_UL_LENGTH-1] = 1; // first (and only) essence container
+      result = m_State.Goto_READY();
+    }
+
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::h__Writer::WriteTimedTextResource(const std::string& XMLDoc,
+                                                              ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
+{
+  Result_t result = m_State.Goto_RUNNING();
+
+  if ( KM_SUCCESS(result) )
+    {
+      // TODO: make sure it's XML
+
+      ui32_t str_size = XMLDoc.size();
+      ASDCP::TimedText::FrameBuffer FrameBuf(str_size);
+      
+      memcpy(FrameBuf.Data(), XMLDoc.c_str(), str_size);
+      FrameBuf.Size(str_size);
+
+      IndexTableSegment::IndexEntry Entry;
+      Entry.StreamOffset = m_StreamOffset;
+      
+      if ( KM_SUCCESS(result) )
+       {
+         ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
+
+         result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
+                                    m_StreamOffset, FrameBuf, m_EssenceUL, Ctx, HMAC);
+       }
+    }
+
+  return result;
+}
+
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::h__Writer::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf,
+                                                              ASDCP::AESEncContext* Ctx, ASDCP::HMACContext* HMAC)
+{
+  if ( ! m_State.Test_RUNNING() )
+    {
+      KM_RESULT_STATE_HERE();
+      return RESULT_STATE;
+    }
+
+  Kumu::fpos_t here = m_File.Tell();
+  assert(m_Dict);
+
+  // create generic stream partition header
+  static UL GenericStream_DataElement(m_Dict->ul(MDD_GenericStream_DataElement));
+  ASDCP::MXF::Partition GSPart(m_Dict);
+
+  GSPart.ThisPartition = here;
+  GSPart.PreviousPartition = m_RIP.PairArray.back().ByteOffset;
+  GSPart.BodySID = m_EssenceStreamID;
+  GSPart.OperationalPattern = m_HeaderPart.OperationalPattern;
+
+  m_RIP.PairArray.push_back(RIP::Pair(m_EssenceStreamID++, here));
+  GSPart.EssenceContainers.push_back(UL(m_Dict->ul(MDD_TimedTextEssence)));
+  UL TmpUL(m_Dict->ul(MDD_GenericStreamPartition));
+  Result_t result = GSPart.WriteToFile(m_File, TmpUL);
+
+  if ( KM_SUCCESS(result) )
+    {
+      ui64_t this_stream_offset = m_StreamOffset; // m_StreamOffset will be changed by the call to Write_EKLV_Packet
+      
+      result = Write_EKLV_Packet(m_File, *m_Dict, m_HeaderPart, m_Info, m_CtFrameBuf, m_FramesWritten,
+                                m_StreamOffset, FrameBuf, GenericStream_DataElement.Value(), Ctx, HMAC);
+    }
+
+  m_FramesWritten++;
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::h__Writer::Finalize()
+{
+  if ( ! m_State.Test_RUNNING() )
+    {
+      DefaultLogSink().Error("Cannot finalize file, the primary essence resource has not been written.\n");
+      return RESULT_STATE;
+    }
+
+  m_IndexWriter.m_Duration = m_FramesWritten = m_TDesc.ContainerDuration;
+  fprintf(stderr, "m_IndexWriter.m_Duration=%d\n", m_IndexWriter.m_Duration);
+
+  Result_t result = m_State.Goto_FINAL();
+
+  if ( KM_SUCCESS(result) )
+    {
+      result = WriteAS02Footer();
+    }
+
+  return result;
+}
+
+
+//------------------------------------------------------------------------------------------
+
+AS_02::TimedText::MXFWriter::MXFWriter()
+{
+}
+
+AS_02::TimedText::MXFWriter::~MXFWriter()
+{
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper.  Caveat emptor!
+//
+ASDCP::MXF::OP1aHeader&
+AS_02::TimedText::MXFWriter::OP1aHeader()
+{
+  if ( m_Writer.empty() )
+    {
+      assert(g_OP1aHeader);
+      return *g_OP1aHeader;
+    }
+
+  return m_Writer->m_HeaderPart;
+}
+
+// Warning: direct manipulation of MXF structures can interfere
+// with the normal operation of the wrapper.  Caveat emptor!
+//
+ASDCP::MXF::RIP&
+AS_02::TimedText::MXFWriter::RIP()
+{
+  if ( m_Writer.empty() )
+    {
+      assert(g_RIP);
+      return *g_RIP;
+    }
+
+  return m_Writer->m_RIP;
+}
+
+// Open the file for writing. The file must not exist. Returns error if
+// the operation cannot be completed.
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::OpenWrite(const std::string& filename, const WriterInfo& Info,
+                                      const TimedTextDescriptor& TDesc, ui32_t HeaderSize)
+{
+  if ( Info.LabelSetType != LS_MXF_SMPTE )
+    {
+      DefaultLogSink().Error("Timed Text support requires LS_MXF_SMPTE\n");
+      return RESULT_FORMAT;
+    }
+
+  m_Writer = new h__Writer(DefaultSMPTEDict());
+  m_Writer->m_Info = Info;
+  
+  Result_t result = m_Writer->OpenWrite(filename, HeaderSize);
+
+  if ( ASDCP_SUCCESS(result) )
+    result = m_Writer->SetSourceStream(TDesc);
+
+  if ( ASDCP_FAILURE(result) )
+    m_Writer.release();
+
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::WriteTimedTextResource(const std::string& XMLDoc, AESEncContext* Ctx, HMACContext* HMAC)
+{
+  if ( m_Writer.empty() )
+    return RESULT_INIT;
+
+  return m_Writer->WriteTimedTextResource(XMLDoc, Ctx, HMAC);
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::WriteAncillaryResource(const ASDCP::TimedText::FrameBuffer& FrameBuf, AESEncContext* Ctx, HMACContext* HMAC)
+{
+  if ( m_Writer.empty() )
+    return RESULT_INIT;
+
+  return m_Writer->WriteAncillaryResource(FrameBuf, Ctx, HMAC);
+}
+
+// Closes the MXF file, writing the index and other closing information.
+ASDCP::Result_t
+AS_02::TimedText::MXFWriter::Finalize()
+{
+  if ( m_Writer.empty() )
+    return RESULT_INIT;
+
+  return m_Writer->Finalize();
+}
+
+
+
+//
+// end AS_02_timedText.cpp
+//
index 223d52a90006bac6fbc77493aa40ff3572fed5eb..1abd0924e20b47e66aadc2ae83d327ada7485287 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2004-2009, John Hurst
+Copyright (c) 2004-2014, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -39,6 +39,46 @@ ASDCP::Version()
 }
 
 
+
+//------------------------------------------------------------------------------------------
+//
+
+// Encodes a rational number as a string having a single delimiter character between
+// numerator and denominator.  Retuns the buffer pointer to allow convenient in-line use.
+const char*
+ASDCP::EncodeRational(const Rational& rational, char* str_buf, ui32_t buf_len, char delimiter)
+{
+  assert(str_buf);
+  snprintf(str_buf, buf_len, "%u%c%u", rational.Numerator, delimiter, rational.Denominator);
+  return str_buf;
+}
+
+// Decodes a rational number havng a single non-digit delimiter character between
+// the numerator and denominator.  Returns false if the string does not contain
+// the expected syntax.
+bool
+ASDCP::DecodeRational(const char* str_rational, Rational& rational)
+{
+  assert(str_rational);
+  rational.Numerator = strtol(str_rational, 0, 10);
+
+  const char* p = str_rational;
+  while ( *p && isdigit(*p) )
+    {
+      ++p;
+    }
+
+  if ( p[0] == 0 || p[1] == 0 )
+    {
+      return false;
+    }
+
+  ++p;
+  rational.Denominator = strtol(p, 0, 10);
+  return  true;
+}
+
+
 //------------------------------------------------------------------------------------------
 //
 // frame buffer base class implementation
index 6b598cc301c14713763cbe10779b12e29d1bf876..7231fb56776759e7c4e7758e9c332989a4ab21ce 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2003-2013, John Hurst
+Copyright (c) 2003-2014, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -31,7 +31,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 The asdcplib library is a set of file access objects that offer simplified
 access to files conforming to the standards published by the SMPTE
 D-Cinema Technology Committee 21DC. The file format, labeled AS-DCP,
-is described in series of separate documents which include but may not
+is described in a series of documents which includes but may not
 be be limited to:
 
  o SMPTE ST 429-2:2011 DCP Operational Constraints
@@ -40,8 +40,10 @@ be be limited to:
  o SMPTE ST 429-5:2009 Timed Text Track File
  o SMPTE ST 429-6:2006 MXF Track File Essence Encryption
  o SMPTE ST 429-10:2008 Stereoscopic Picture Track File
+ o SMPTE ST 429-14:2008 Aux Data Track File
  o SMPTE ST 330:2004 - UMID
  o SMPTE ST 336:2001 - KLV
+ o SMPTE ST 377:2004 - MXF (old version, required)
  o SMPTE ST 377-1:2011 - MXF
  o SMPTE ST 377-4:2012 - MXF Multichannel Audio Labeling Framework
  o SMPTE ST 390:2011 - MXF OP-Atom
@@ -61,21 +63,13 @@ be be limited to:
 The following use cases are supported by the library:
 
  o Write essence to a plaintext or ciphertext AS-DCP file:
-     MPEG2 Video Elementary Stream
-     JPEG 2000 codestreams
-     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
      JPEG 2000 codestreams
      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
+     Aux Data (frame-wrapped synchronous blob)
      Proposed Dolby (TM) Atmos track file
 
  o Read header metadata from an AS-DCP file
@@ -280,6 +274,16 @@ namespace ASDCP {
     }
   };
 
+  // Encodes a rational number as a string having a single delimiter character between
+  // numerator and denominator.  Retuns the buffer pointer to allow convenient in-line use.
+  const char* EncodeRational(const Rational&, char* str_buf, ui32_t buf_len, char delimiter = ' ');
+
+  // Decodes a rational number havng a single non-digit delimiter character between
+  // the numerator and denominator.  Returns false if the string does not contain
+  // the expected syntax.
+  bool DecodeRational(const char* str_rat, Rational&);
+
+
   // common edit rates, use these instead of hard coded constants
   const Rational EditRate_24 = Rational(24,1);
   const Rational EditRate_23_98 = Rational(24000,1001); // Not a DCI-compliant value!
index 635eae5ae2c60200fc7942571204152bc58ef86a..6a41fe8f3581c5ccbe2316ca8f9b5772f0eea777 100755 (executable)
@@ -684,7 +684,6 @@ public:
 
     if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
       {
-       DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
        return RESULT_RANGE;
       }
 
index 8353ea4f1e7edae62bbd7df966291a63ba5b28dd..ef82c2ca888ce7e1d2675f5df3ebb3a6e6a6dad5 100755 (executable)
@@ -237,7 +237,6 @@ ASDCP::MPEG2::MXFReader::h__Reader::FindFrameGOPStart(ui32_t FrameNum, ui32_t& K
 
   if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
     {
-      DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
       return RESULT_RANGE;
     }
 
@@ -258,7 +257,6 @@ ASDCP::MPEG2::MXFReader::h__Reader::FrameType(ui32_t FrameNum, FrameType_t& type
 
   if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
     {
-      DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
       return RESULT_RANGE;
     }
 
index 88ccd1f5d671e5247134022f0fd30f48572144ca..9de8d53bd76ec2abb23a5014f3b6a46baa10ab66 100755 (executable)
@@ -276,6 +276,12 @@ ASDCP::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename)
        }
     }
 
+  if ( m_ADesc.ContainerDuration == 0 )
+    {
+      DefaultLogSink().Error("ContainerDuration unset.\n");
+      return RESULT_FORMAT;
+    }
+
   // check for sample/frame rate sanity
   if ( ASDCP_SUCCESS(result)
        && m_ADesc.EditRate != EditRate_24
@@ -297,14 +303,14 @@ ASDCP::PCM::MXFReader::h__Reader::OpenRead(const std::string& filename)
                             m_ADesc.EditRate.Numerator, m_ADesc.EditRate.Denominator);
 
       // oh, they gave us the audio sampling rate instead, assume 24/1
-      if ( m_ADesc.EditRate == SampleRate_48k )
+      if ( m_ADesc.EditRate == SampleRate_48k || m_ADesc.EditRate == SampleRate_96k )
        {
          DefaultLogSink().Warn("adjusting EditRate to 24/1\n"); 
          m_ADesc.EditRate = EditRate_24;
        }
       else
        {
-      DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
+         DefaultLogSink().Error("PCM EditRate not in expected value range.\n");
          // or we just drop the hammer
          return RESULT_FORMAT;
        }
@@ -325,6 +331,11 @@ ASDCP::PCM::MXFReader::h__Reader::ReadFrame(ui32_t FrameNum, FrameBuffer& FrameB
   if ( ! m_File.IsOpen() )
     return RESULT_INIT;
 
+  if ( (FrameNum+1) > m_ADesc.ContainerDuration )
+    {
+      return RESULT_RANGE;
+    }
+
   assert(m_Dict);
   return ReadEKLVFrame(FrameNum, FrameBuf, m_Dict->ul(MDD_WAVEssence), Ctx, HMAC);
 }
index e158cde25edf09e28e41b9f97652aedd5f6a8a24..e4619e09e64888078f557a050c74e88e9b328726 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2008-2013, John Hurst
+Copyright (c) 2008-2014, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
index 7875a4f7bdea40e9e08e44b7052b0efb481bc912..c1df8aded6b09910a19ea989fe31a4618d717d34 100755 (executable)
@@ -73,14 +73,14 @@ namespace ASDCP
       {
        assert(r >= pstr);
        if ( r > pstr )
-         result.push_back(atoi(pstr));
+         result.push_back(strtol(pstr, 0, 10));
 
        pstr = r + 1;
        r = strchr(pstr, '.');
       }
 
     if( strlen(pstr) > 0 )
-      result.push_back(atoi(pstr));
+      result.push_back(strtol(pstr, 0, 10));
 
     assert(result.size() == 3);
     return result;
index 6ccf50cfaa50cdab012d95e83975213c8d928d84..f78f3ead018a90b743fe5111620eb6c6a72693d3 100755 (executable)
@@ -106,10 +106,11 @@ ASDCP::KLVPacket::InitFromBuffer(const byte_t* buf, ui32_t buf_len)
 
   ui64_t tmp_size;
   if ( ! Kumu::read_BER(buf + SMPTE_UL_LENGTH, &tmp_size) )
-       return RESULT_FAIL;
+    {
+      return RESULT_FAIL;
+    }
 
-  assert (tmp_size <= 0xFFFFFFFFL);
-  m_ValueLength = (ui32_t) tmp_size;
+  m_ValueLength = tmp_size;
   m_KLLength = SMPTE_UL_LENGTH + Kumu::BER_length(buf + SMPTE_UL_LENGTH);
   m_KeyStart = buf;
   m_ValueStart = buf + m_KLLength;
@@ -170,10 +171,10 @@ ASDCP::KLVPacket::Dump(FILE* stream, const Dictionary& Dict, bool show_value)
       fprintf(stream, "%s", TmpUL.EncodeString(buf, 64));
 
       const MDDEntry* Entry = Dict.FindUL(m_KeyStart);
-      fprintf(stream, "  len: %7u (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown"));
+      fprintf(stream, "  len: %7qu (%s)\n", m_ValueLength, (Entry ? Entry->name : "Unknown"));
 
       if ( show_value && m_ValueLength < 1000 )
-       Kumu::hexdump(m_ValueStart, Kumu::xmin(m_ValueLength, (ui32_t)128), stream);
+       Kumu::hexdump(m_ValueStart, Kumu::xmin(m_ValueLength, (ui64_t)128), stream);
     }
   else if ( m_UL.HasValue() )
     {
index 878390508b98779b45ef15f6a2a1f3c62d0d7d22..b49cd07f9862e335b76da04cba495d137d7c0c73 100755 (executable)
--- a/src/KLV.h
+++ b/src/KLV.h
@@ -196,22 +196,22 @@ inline const char* ui64sz(ui64_t i, char* buf)
       const byte_t* m_KeyStart;
       ui32_t        m_KLLength;
       const byte_t* m_ValueStart;
-      ui32_t        m_ValueLength;
+      ui64_t        m_ValueLength;
       UL m_UL;
 
     public:
       KLVPacket() : m_KeyStart(0), m_KLLength(0), m_ValueStart(0), m_ValueLength(0) {}
       virtual ~KLVPacket() {}
 
-      ui32_t  PacketLength() {
+      inline ui64_t  PacketLength() {
        return m_KLLength + m_ValueLength;
       }
 
-      ui32_t   ValueLength() {
+      inline ui64_t   ValueLength() {
        return m_ValueLength;
       }
 
-      ui32_t   KLLength() {
+      inline ui32_t   KLLength() {
        return m_KLLength;
       }
 
@@ -221,10 +221,16 @@ inline const char* ui64sz(ui64_t i, char* buf)
       virtual Result_t InitFromBuffer(const byte_t*, ui32_t);
       virtual Result_t InitFromBuffer(const byte_t*, ui32_t, const UL& label);
       virtual Result_t WriteKLToBuffer(ASDCP::FrameBuffer&, const UL& label, ui32_t length);
-      virtual Result_t WriteKLToBuffer(ASDCP::FrameBuffer& fb, ui32_t length) {
+
+      virtual Result_t WriteKLToBuffer(ASDCP::FrameBuffer& fb, ui32_t length)
+      {
        if ( ! m_UL.HasValue() )
-         return RESULT_STATE;
-       return WriteKLToBuffer(fb, m_UL, length); }
+         {
+           return RESULT_STATE;
+         }
+
+       return WriteKLToBuffer(fb, m_UL, length);
+      }
 
       virtual void     Dump(FILE*, const Dictionary& Dict, bool show_value);
     };
index 0d051746c888835f0a4a179920f9823f5458bb2d..77a0846af9dccd41d47777c76e1e0c694316a864 100755 (executable)
@@ -105,7 +105,8 @@ namespace Kumu
   KM_DECLARE_RESULT(NOTAFILE,   -19,  "Filename not found.");
   KM_DECLARE_RESULT(UNKNOWN,    -20,  "Unknown result code.");
   KM_DECLARE_RESULT(DIR_CREATE, -21,  "Unable to create directory.");
-  // -22 is reserved
+  KM_DECLARE_RESULT(NOT_EMPTY,  -22,  "Unable to delete non-empty directory.");
+  // 23-100 are reserved
  
 } // namespace Kumu
 
index 417b17e60074038fc8e1e9df7c6e45a95e6b40f8..2b95d63070073af768961544c7f60bfb14b455ec 100644 (file)
@@ -1657,6 +1657,31 @@ Kumu::DeletePath(const std::string& pathname)
 }
 
 
+//
+Result_t
+Kumu::DeleteDirectoryIfEmpty(const std::string& path)
+{
+  DirScanner source_dir;
+  char next_file[Kumu::MaxFilePath];
+
+  Result_t result = source_dir.Open(path);
+
+  if ( KM_FAILURE(result) )
+    return result;
+
+  while ( KM_SUCCESS(source_dir.GetNext(next_file)) )
+    {
+      if ( ( next_file[0] == '.' && next_file[1] == 0 )
+          || ( next_file[0] == '.' && next_file[1] == '.' && next_file[2] == 0 ) )
+       continue;
+
+      return RESULT_NOT_EMPTY; // anything other than "." and ".." indicates a non-empty directory
+    }
+
+  return DeletePath(path);
+}
+
+
 //------------------------------------------------------------------------------------------
 //
 
@@ -1665,19 +1690,21 @@ Result_t
 Kumu::FreeSpaceForPath(const std::string& path, Kumu::fsize_t& free_space, Kumu::fsize_t& total_space)
 {
 #ifdef KM_WIN32
-       ULARGE_INTEGER lTotalNumberOfBytes;
-       ULARGE_INTEGER lTotalNumberOfFreeBytes;
+  ULARGE_INTEGER lTotalNumberOfBytes;
+  ULARGE_INTEGER lTotalNumberOfFreeBytes;
 
-       BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
-       if (fResult) {
+  BOOL fResult = ::GetDiskFreeSpaceExA(path.c_str(), NULL, &lTotalNumberOfBytes, &lTotalNumberOfFreeBytes);
+  if ( fResult )
+    {
       free_space = static_cast<Kumu::fsize_t>(lTotalNumberOfFreeBytes.QuadPart);
       total_space = static_cast<Kumu::fsize_t>(lTotalNumberOfBytes.QuadPart);
       return RESULT_OK;
-       }
-       HRESULT LastError = ::GetLastError();
+    }
 
-       DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), ::GetLastError());
-       return RESULT_FAIL;
+  HRESULT last_error = ::GetLastError();
+
+  DefaultLogSink().Error("FreeSpaceForPath GetDiskFreeSpaceEx %s: %lu\n", path.c_str(), last_error);
+  return RESULT_FAIL;
 #else // KM_WIN32
   struct statfs s;
 
index 1bf2822077c416b9af998a93645ea3daa8f1e881..a6970bfb4b2e32996b5ea1888aa3ed41c7b7a1f1 100755 (executable)
@@ -274,6 +274,9 @@ namespace Kumu
   // Recursively remove a file or directory
   Result_t DeletePath(const std::string& pathname);
 
+  // Remove the path only if it is a directory that is empty.
+  Result_t DeleteDirectoryIfEmpty(const std::string& path);
+
   //------------------------------------------------------------------------------------------
   // File I/O Wrappers
   //------------------------------------------------------------------------------------------
index 333b00a8c4876dd57f0c8205ad86a12c9133d903..8f8107f6875371c54a4d239c4970409a5ba5c2a0 100755 (executable)
@@ -829,9 +829,9 @@ Kumu::Timestamp::DecodeString(const char* datestr)
   YMDhms.minute = 0;
   YMDhms.second = 0;
   YMDhms.offset = 0;
-  YMDhms.date.year = atoi(datestr);
-  YMDhms.date.month = atoi(datestr + 5);
-  YMDhms.date.day = atoi(datestr + 8);
+  YMDhms.date.year = strtol(datestr, 0, 10);
+  YMDhms.date.month = strtol(datestr + 5, 0, 10);
+  YMDhms.date.day = strtol(datestr + 8, 0, 10);
  
   if ( datestr[10] == 'T' )
     {
@@ -841,8 +841,8 @@ Kumu::Timestamp::DecodeString(const char* datestr)
        return false;
 
       char_count += 6;
-      YMDhms.hour = atoi(datestr + 11);
-      YMDhms.minute = atoi(datestr + 14);
+      YMDhms.hour = strtol(datestr + 11, 0, 10);
+      YMDhms.minute = strtol(datestr + 14, 0, 10);
 
       if ( datestr[16] == ':' )
        {
@@ -850,16 +850,23 @@ Kumu::Timestamp::DecodeString(const char* datestr)
            return false;
 
          char_count += 3;
-         YMDhms.second = atoi(datestr + 17);
+         YMDhms.second = strtol(datestr + 17, 0, 10);
        }
 
       if ( datestr[19] == '.' )
        {
-         if ( ! ( isdigit(datestr[20]) && isdigit(datestr[21]) && isdigit(datestr[22]) ) )
-           return false;
-         
+         if ( ! isdigit(datestr[20]) )
+           {
+             return false;
+           }
+
          // we don't carry the ms value
-         datestr += 4;
+         while ( isdigit(datestr[20]) )
+           {
+             ++datestr;
+           }
+
+         ++datestr;
        }
 
       if ( datestr[19] == '-' || datestr[19] == '+' )
@@ -871,8 +878,8 @@ Kumu::Timestamp::DecodeString(const char* datestr)
 
          char_count += 6;
 
-         ui32_t TZ_hh = atoi(datestr + 20);
-         ui32_t TZ_mm = atoi(datestr + 23);
+         ui32_t TZ_hh = strtol(datestr + 20, 0, 10);
+         ui32_t TZ_mm = strtol(datestr + 23, 0, 10);
          if ((TZ_hh > 14) || (TZ_mm > 59) || ((TZ_hh == 14) && (TZ_mm > 0)))
            return false;
 
@@ -1182,8 +1189,10 @@ Kumu::km_token_split(const std::string& str, const std::string& separator)
       r = strstr(pstr, separator.c_str());
     }
       
-  if( strlen(pstr) > 0 )
-    components.push_back(std::string(pstr));
+  if ( strlen(pstr) > 0 )
+    {
+      components.push_back(std::string(pstr));
+    }
 
   return components;
 }
index 121f0a6ad02437d1a17b3b81dc596a51387e2f8a..eb9c25fb5c1f909f34b43e7629c33888076a91af 100644 (file)
@@ -1017,6 +1017,126 @@ Kumu::StringIsXML(const char* document, ui32_t len)
 #endif
 
 
+//----------------------------------------------------------------------------------------------------
+
+//
+bool
+Kumu::GetXMLDocType(const ByteString& buf, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
+                   AttributeList& doc_attr_list)
+{
+  return GetXMLDocType(buf.RoData(), buf.Length(), ns_prefix, type_name, namespace_name, doc_attr_list);
+}
+
+//
+bool
+Kumu::GetXMLDocType(const std::string& buf, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
+                   AttributeList& doc_attr_list)
+{
+  return GetXMLDocType((const byte_t*)buf.c_str(), buf.size(), ns_prefix, type_name, namespace_name, doc_attr_list);
+}
+
+//
+bool
+Kumu::GetXMLDocType(const byte_t* buf, ui32_t buf_len, std::string& ns_prefix, std::string& type_name, std::string& namespace_name,
+                   AttributeList& doc_attr_list)
+{
+  assert(buf);
+  const byte_t *p1 = buf, *p2;
+  const byte_t *end_p = buf + buf_len;
+
+  while ( p1 < end_p && *p1 )
+    {
+      if ( *p1 == '<' && isalpha(*(p1+1)) )
+        {
+          p2 = ++p1;
+
+          // collect element name
+          while ( p2 < end_p && *p2 && ! ( isspace(*p2) || *p2 == '>' ) )
+            ++p2;
+
+          if ( p2 < end_p )
+            {
+              const byte_t* separator = (byte_t*)strchr(reinterpret_cast<const char*>(p1), ':');
+              if ( separator != 0 && separator < p2 )
+                {
+                  ns_prefix.assign(reinterpret_cast<const char*>(p1), separator - p1);
+                  p1 = separator + 1;
+                }
+
+              type_name.assign(reinterpret_cast<const char*>(p1), p2 - p1);
+              break;
+            }
+        }
+
+      p1++;
+    }
+
+  if ( *p2 == ' ' )
+    {
+      const byte_t *p3 = p2+1;
+      while ( p3 < end_p && *p3 && *p3 != '>'  )
+       {       
+         ++p3;
+       }
+
+      if ( *p3 != '>' )
+       {
+         return false; // not well-formed XML
+       }
+
+      std::string attr_str;
+      attr_str.assign(reinterpret_cast<const char*>(p2+1), p3 - p2 - 1);
+      
+      // normalize whitespace so the subesquent split works properly
+      for ( int j = 0; j < attr_str.length(); ++j )
+       {
+         if ( attr_str[j] != ' ' && isspace(attr_str[j]) )
+           {
+             attr_str[j] = ' ';
+           }
+       }
+
+      std::list<std::string> doc_attr_nvpairs = km_token_split(attr_str, " ");
+      
+      std::list<std::string>::iterator i;
+      std::map<std::string, std::string> ns_map;
+
+      for ( i = doc_attr_nvpairs.begin(); i != doc_attr_nvpairs.end(); ++i )
+        {
+         // trim leading and trailing whitespace an right-most character, i.e., \"
+         std::string trimmed = i->substr(i->find_first_not_of(" "), i->find_last_not_of(" "));
+          std::list<std::string> nv_tokens = km_token_split(trimmed, "=\"");
+
+          if ( nv_tokens.size() != 2 )
+           {
+             continue;
+           }
+
+         NVPair nv_pair;
+         nv_pair.name = nv_tokens.front();
+         nv_pair.value = nv_tokens.back();
+         doc_attr_list.push_back(nv_pair);
+          ns_map.insert(std::map<std::string,std::string>::value_type(nv_pair.name, nv_pair.value));
+        }
+
+      std::string doc_ns_name_selector = ns_prefix.empty() ? "xmlns" : "xmlns:"+ns_prefix;
+      std::map<std::string,std::string>::iterator j = ns_map.find(doc_ns_name_selector);
+
+      if ( j != ns_map.end() )
+        {
+          namespace_name = j->second;
+        }
+    }
+ else if ( *p2 != '>' )
+   {
+     return false; // not well-formed XML
+   }
+
+  return ! type_name.empty();
+}
+
+
+
 //
 // end KM_xml.cpp
 //
index 5d499e5f2c1e501a5aabf53c486373281d761a87..0f8ba3cd553741ebd3e972ed1528a40822bb1b2c 100644 (file)
@@ -57,6 +57,15 @@ namespace Kumu
   typedef std::list<XMLElement*> ElementList;
   typedef ElementList::const_iterator Elem_i;
 
+  bool GetXMLDocType(const ByteString& buf, std::string& ns_prefix, std::string& type_name,
+                    std::string& namespace_name, AttributeList& doc_attr_list);
+
+  bool GetXMLDocType(const std::string& buf, std::string& ns_prefix, std::string& type_name,
+                    std::string& namespace_name, AttributeList& doc_attr_list);
+
+  bool GetXMLDocType(const byte_t* buf, ui32_t buf_len, std::string& ns_prefix, std::string& type_name,
+                    std::string& namespace_name, AttributeList& doc_attr_list);
+
   //
   class XMLNamespace
   {
index c879d0f839bf2d4541922ebaeac5e334cdba4053..f3e8fc68aa6017042d85627d1c7605232d9b74be 100644 (file)
@@ -1163,24 +1163,24 @@ static const ASDCP::MDDEntry s_MDD_Table[] = {
       0x0d, 0x01, 0x01, 0x01, 0x01, 0x01, 0x67, 0x00 },
     {0}, false, "ContainerConstraintSubDescriptor" },
 
-  // protype for high dynamic range
+  // protype for high dynamic range, values recorded in Dolby registry
   { { 0x06, 0x0e, 0x2b, 0x34, 0x04, 0x01, 0x01, 0x05, // 374
-      0x0e, 0x09, 0x06, 0xe1, 0x00, 0x00, 0x00, 0x00 },
+      0x0e, 0x09, 0x06, 0x07, 0x01, 0x01, 0x01, 0x01 },
     {0}, false, "PHDRImageMetadataWrappingFrame" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x05, // 375xxx
-      0x0e, 0x09, 0x06, 0xe2, 0x00, 0x00, 0x01, 0x00 },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x02, 0x01, 0x05, // 375
+      0x0e, 0x09, 0x06, 0x07, 0x01, 0x01, 0x01, 0x02 },
     {0}, false, "PHDRImageMetadataItem" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x05, // 376xxx
-      0x0e, 0x09, 0x05, 0xe3, 0x00, 0x00, 0x00, 0x00 },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x02, 0x53, 0x01, 0x05, // 376
+      0x0e, 0x09, 0x06, 0x07, 0x01, 0x01, 0x01, 0x03 },
     {0}, false, "PHDRMetadataTrackSubDescriptor" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 377xxx
-      0x0e, 0x09, 0x05, 0xe3, 0x01, 0x00, 0x00, 0x00 },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 377
+      0x0e, 0x09, 0x06, 0x07, 0x01, 0x01, 0x01, 0x04 },
     {0}, false, "PHDRMetadataTrackSubDescriptor_DataDefinition" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 378xxx
-      0x0e, 0x09, 0x05, 0xe3, 0x02, 0x00, 0x00, 0x00 },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 378
+      0x0e, 0x09, 0x06, 0x07, 0x01, 0x01, 0x01, 0x05 },
     {0}, false, "PHDRMetadataTrackSubDescriptor_SourceTrackID" },
-  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 379xxx
-      0x0e, 0x09, 0x05, 0xe3, 0x03, 0x00, 0x00, 0x00 },
+  { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x05, // 379
+      0x0e, 0x09, 0x06, 0x07, 0x01, 0x01, 0x01, 0x06 },
     {0}, false, "PHDRMetadataTrackSubDescriptor_SimplePayloadSID" },
 
   { {0}, {0}, false, 0 }
index f113444b4654a124b46ab183def9f8ba6475630d..c3a3a5d6d28139063522533ea946ebf63f15c69a 100755 (executable)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005-2013, John Hurst
+Copyright (c) 2005-2014, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -1716,6 +1716,127 @@ ASDCP::MXF::AS02_MCAConfigParser::AS02_MCAConfigParser(const Dictionary*& d) : A
   m_LabelMap.insert(pair("VIN",   label_traits("VIN", true,  m_Dict->ul(MDD_IMFAudioSoundfield_VIN))));
 }
 
+
+
+//
+bool
+ASDCP::MXF::GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate)
+{
+  bool has_first_item = false;
+
+  MXF::InterchangeObject* temp_item;
+  std::list<MXF::InterchangeObject*> temp_items;
+
+  Result_t result = header.GetMDObjectsByType(DefaultCompositeDict().ul(MDD_SourcePackage), temp_items);
+
+  if ( KM_FAILURE(result) )
+    {
+      DefaultLogSink().Error("The MXF header does not contain a FilePackage item.\n");
+      return false;
+    }
+
+  if ( temp_items.size() != 1 )
+    {
+      DefaultLogSink().Error("The MXF header must contain one FilePackage item, found %d.\n", temp_items.size());
+      return false;
+    }
+
+  char buf[64];
+  MXF::Batch<UUID>::const_iterator i;
+  MXF::SourcePackage *source_package = dynamic_cast<MXF::SourcePackage*>(temp_items.front());
+  assert(source_package);
+
+  for ( i = source_package->Tracks.begin(); i != source_package->Tracks.end(); ++i )
+    {
+      // Track
+      result = header.GetMDObjectByID(*i, &temp_item);
+
+      if ( KM_FAILURE(result) )
+       {
+         DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
+                                i->EncodeHex(buf, 64));
+         return false;
+       }
+
+      MXF::Track *track = dynamic_cast<MXF::Track*>(temp_item);
+
+      if ( track == 0 )
+       {
+         DefaultLogSink().Error("The MXF header is incomplete: %s is not a Track item.\n",
+                                i->EncodeHex(buf, 64));
+         return false;
+       }
+
+      // Sequence
+      result = header.GetMDObjectByID(track->Sequence, &temp_item);
+
+      if ( KM_FAILURE(result) )
+       {
+         DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
+                                i->EncodeHex(buf, 64));
+         return false;
+       }
+
+      MXF::Sequence *sequence = dynamic_cast<MXF::Sequence*>(temp_item);
+
+      if ( sequence == 0 )
+       {
+         DefaultLogSink().Error("The MXF header is incomplete: %s is not a Sequence item.\n",
+                                track->Sequence.get().EncodeHex(buf, 64));
+         return false;
+       }
+
+      if ( sequence->StructuralComponents.size() != 1 )
+       {
+         DefaultLogSink().Error("The Sequence item must contain one reference to an esence item, found %d.\n",
+                                sequence->StructuralComponents.size());
+         return false;
+       }
+
+      // SourceClip
+      result = header.GetMDObjectByID(sequence->StructuralComponents.front(), &temp_item);
+
+      if ( KM_FAILURE(result) )
+       {
+         DefaultLogSink().Error("The MXF header is incomplete: strong reference %s leads nowhere.\n",
+                                sequence->StructuralComponents.front().EncodeHex(buf, 64));
+         return false;
+       }
+
+      if ( temp_item->IsA(DefaultCompositeDict().ul(MDD_SourceClip)) )
+       {
+         MXF::SourceClip *source_clip = dynamic_cast<MXF::SourceClip*>(temp_item);
+
+         if ( source_clip == 0 )
+           {
+             DefaultLogSink().Error("The MXF header is incomplete: %s is not a SourceClip item.\n",
+                                    sequence->StructuralComponents.front().EncodeHex(buf, 64));
+             return false;
+           }
+
+         if ( ! has_first_item )
+           {
+             edit_rate = track->EditRate;
+             has_first_item = true;
+           }
+         else if ( edit_rate != track->EditRate )
+           {
+             DefaultLogSink().Error("The MXF header is incomplete: %s EditRate value does not match others in the file.\n",
+                                    sequence->StructuralComponents.front().EncodeHex(buf, 64));
+             return false;
+           }
+       }
+      else if ( ! temp_item->IsA(DefaultCompositeDict().ul(MDD_TimecodeComponent)) )
+       {
+         DefaultLogSink().Error("Reference from Sequence to an unexpected type: %s.\n", temp_item->ObjectName());
+         return false;
+       }
+    }
+
+  return true;
+}
+
+
 //
 // end MXF.cpp
 //
index 88fc0af69776bf83cb4eaa1f104ab7c4339d4161..6041772a08c7abbd5e6ddbc20d7f6c61b18e3ad7 100755 (executable)
--- a/src/MXF.h
+++ b/src/MXF.h
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2005-2013, John Hurst
+Copyright (c) 2005-2014, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -405,6 +405,10 @@ namespace ASDCP
          SourcePackage*   GetSourcePackage();
        };
 
+      // Searches the header object and returns the edit rate based on the contents of the
+      // File Package items.  Logs an error message and returns false if anthing goes wrong.
+      bool GetEditRateFromFP(ASDCP::MXF::OP1aHeader& header, ASDCP::Rational& edit_rate);
+
       //
       class OPAtomIndexFooter : public Partition
        {
index 483eb7048150762b031e4ed0919b3d71f4bcf752..c575b963472bcbf13514a5478fd62567aba55162 100755 (executable)
@@ -123,7 +123,7 @@ ASDCP::UL::EncodeString(char* str_buf, ui32_t buf_len) const
   if ( buf_len > 38 ) // room for dotted notation?
     {
       snprintf(str_buf, buf_len,
-              "%02x%02x%02x%02x.%02x%02x.%02x%02x.%02x%02x%02x%02x.%02x%02x%02x%02x",
+              "%02x%02x%02x%02x.%02x%02x%02x%02x.%02x%02x%02x%02x.%02x%02x%02x%02x",
               m_Value[0],  m_Value[1],  m_Value[2],  m_Value[3],
               m_Value[4],  m_Value[5],  m_Value[6],  m_Value[7],
               m_Value[8],  m_Value[9],  m_Value[10], m_Value[11],
@@ -189,7 +189,7 @@ ASDCP::UMID::EncodeString(char* str_buf, ui32_t buf_len) const
 {
   assert(str_buf);
 
-  snprintf(str_buf, buf_len, "[%02x%02x%02x%02x.%02x%02x.%02x%02x.%02x%02x%02x%02x],%02x,%02x,%02x,%02x,",
+  snprintf(str_buf, buf_len, "[%02x%02x%02x%02x.%02x%02x%02x%02x.%02x%02x%02x%02x],%02x,%02x,%02x,%02x,",
           m_Value[0],  m_Value[1],  m_Value[2],  m_Value[3],
           m_Value[4],  m_Value[5],  m_Value[6],  m_Value[7],
           m_Value[8],  m_Value[9],  m_Value[10], m_Value[11],
@@ -202,7 +202,7 @@ ASDCP::UMID::EncodeString(char* str_buf, ui32_t buf_len) const
     {
       // half-swapped UL, use [bbaa9988.ddcc.ffee.00010203.04050607]
       snprintf(str_buf + offset, buf_len - offset,
-              "[%02x%02x%02x%02x.%02x%02x.%02x%02x.%02x%02x%02x%02x.%02x%02x%02x%02x]",
+              "[%02x%02x%02x%02x.%02x%02x%02x%02x.%02x%02x%02x%02x.%02x%02x%02x%02x]",
                m_Value[24], m_Value[25], m_Value[26], m_Value[27],
               m_Value[28], m_Value[29], m_Value[30], m_Value[31],
                m_Value[16], m_Value[17], m_Value[18], m_Value[19],
index ef44ffd0357aad6e04b7025b10b28740998f409d..4592918db5fb04a4510312b503715f4ed3d664b3 100644 (file)
@@ -132,7 +132,9 @@ libas02_la_SOURCES  = \
        h__02_Reader.cpp \
        h__02_Writer.cpp \
        AS_02_JP2K.cpp \
-       AS_02_PCM.cpp
+       AS_02_PCM.cpp \
+       ST2052_TextParser.cpp \
+       AS_02_TimedText.cpp
 
 libas02_la_LDFLAGS = -release @VERSION@
 libas02_la_LIBADD = libasdcp.la libkumu.la
@@ -155,7 +157,7 @@ nodist_libpyasdcp_la_SOURCES = \
        asdcp_python_mxf.cpp \
        asdcp_python_mxf.h \
        asdcp_python_mxf_text.cpp \
-       asdcp_python_mxf_metadata.cpp 
+       asdcp_python_mxf_metadata.cpp
 
 
 libpyasdcp_la_CPPFLAGS = @PYTHON_CPPFLAGS@
index e750f2549f4253807b5ffce2ca9cb2450da482e2..6abe3f6e8a51a4b2aed9c8b59c84f3002e796bef 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (c) 2007-2009, John Hurst
+Copyright (c) 2007-2014, John Hurst
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
@@ -106,10 +106,10 @@ public:
 
     if ( *p != 0 )
       {
-       ui32_t hours = atoi(p);
-       ui32_t minutes = atoi(p+3);
-       ui32_t seconds = atoi(p+6);
-       ui32_t frames = atoi(p+9);
+       ui32_t hours = strtol(p, 0, 10);
+       ui32_t minutes = strtol(p+3, 0, 10);
+       ui32_t seconds = strtol(p+6, 0, 10);
+       ui32_t frames = strtol(p+9, 0, 10);
 
        m_FrameCount = (((((hours * 60) + minutes) * 60) + seconds) * m_FPS)+ frames;
       }
diff --git a/src/ST2052_TextParser.cpp b/src/ST2052_TextParser.cpp
new file mode 100644 (file)
index 0000000..312c9d9
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+Copyright (c) 2013-2014, 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    ST2052_TimedText.cpp
+    \version $Id$       
+    \brief   AS-DCP library, PCM essence reader and writer implementation
+*/
+
+
+#include "AS_02_internal.h"
+#include "KM_xml.h"
+
+using namespace Kumu;
+using namespace ASDCP;
+
+using Kumu::DefaultLogSink;
+
+// TODO: 
+const char* c_dcst_namespace_name = "http://www.smpte-ra.org/schemas/428-7/2007/DCST";
+
+//------------------------------------------------------------------------------------------
+
+typedef std::map<Kumu::UUID, ASDCP::TimedText::MIMEType_t> ResourceTypeMap_t;
+
+class AS_02::TimedText::ST2052_TextParser::h__TextParser
+{
+  XMLElement  m_Root;
+  ResourceTypeMap_t m_ResourceTypes;
+  Result_t OpenRead();
+
+  ASDCP_NO_COPY_CONSTRUCT(h__TextParser);
+
+public:
+  std::string m_Filename;
+  std::string m_XMLDoc;
+  TimedTextDescriptor  m_TDesc;
+  ASDCP::mem_ptr<ASDCP::TimedText::LocalFilenameResolver> m_DefaultResolver;
+
+  h__TextParser() : m_Root("**ParserRoot**")
+  {
+    memset(&m_TDesc.AssetID, 0, UUIDlen);
+  }
+
+  ~h__TextParser() {}
+
+  ASDCP::TimedText::IResourceResolver* GetDefaultResolver()
+  {
+    if ( m_DefaultResolver.empty() )
+      {
+       ASDCP::TimedText::LocalFilenameResolver *resolver = new ASDCP::TimedText::LocalFilenameResolver;
+       resolver->OpenRead(PathDirname(m_Filename));
+       m_DefaultResolver = resolver;
+      }
+    
+    return m_DefaultResolver;
+  }
+
+  Result_t OpenRead(const std::string& filename);
+  Result_t OpenRead(const std::string& xml_doc, const std::string& filename);
+  Result_t ReadAncillaryResource(const UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
+                                const ASDCP::TimedText::IResourceResolver& Resolver) const;
+};
+
+//
+Result_t
+AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead(const std::string& filename)
+{
+  Result_t result = ReadFileIntoString(filename, m_XMLDoc);
+
+  if ( KM_SUCCESS(result) )
+    {
+      m_Filename = filename;
+      result = OpenRead();
+    }
+
+  return result;
+}
+
+//
+Result_t
+AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead(const std::string& xml_doc, const std::string& filename)
+{
+  m_XMLDoc = xml_doc;
+  m_Filename = filename;
+  return OpenRead();
+}
+
+//
+Result_t
+AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead()
+{
+  if ( ! m_Root.ParseString(m_XMLDoc.c_str()) )
+    return RESULT_FORMAT;
+
+  m_TDesc.EncodingName = "UTF-8"; // the XML parser demands UTF-8
+  m_TDesc.ResourceList.clear();
+  m_TDesc.ContainerDuration = 0;
+  const XMLNamespace* ns = m_Root.Namespace();
+
+  if ( ns == 0 )
+    {
+      DefaultLogSink(). Warn("Document has no namespace name, assuming %s\n", c_dcst_namespace_name);
+      m_TDesc.NamespaceName = c_dcst_namespace_name;
+    }
+  else
+    {
+      m_TDesc.NamespaceName = ns->Name();
+    }
+
+  return RESULT_OK;
+}
+
+
+//------------------------------------------------------------------------------------------
+
+AS_02::TimedText::ST2052_TextParser::ST2052_TextParser()
+{
+}
+
+AS_02::TimedText::ST2052_TextParser::~ST2052_TextParser()
+{
+}
+
+// Opens the stream for reading, parses enough data to provide a complete
+// set of stream metadata for the MXFWriter below.
+ASDCP::Result_t
+AS_02::TimedText::ST2052_TextParser::OpenRead(const std::string& filename) const
+{
+  const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = new h__TextParser;
+
+  Result_t result = m_Parser->OpenRead(filename);
+
+  if ( ASDCP_FAILURE(result) )
+    const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = 0;
+
+  return result;
+}
+
+// Parses an XML document to provide a complete set of stream metadata for the MXFWriter below.
+Result_t
+AS_02::TimedText::ST2052_TextParser::OpenRead(const std::string& xml_doc, const std::string& filename) const
+{
+  const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = new h__TextParser;
+
+  Result_t result = m_Parser->OpenRead(xml_doc, filename);
+
+  if ( ASDCP_FAILURE(result) )
+    const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = 0;
+
+  return result;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::ST2052_TextParser::FillTimedTextDescriptor(TimedTextDescriptor& TDesc) const
+{
+  if ( m_Parser.empty() )
+    return RESULT_INIT;
+
+  TDesc = m_Parser->m_TDesc;
+  return RESULT_OK;
+}
+
+// Reads the complete Timed Text Resource into the given string.
+ASDCP::Result_t
+AS_02::TimedText::ST2052_TextParser::ReadTimedTextResource(std::string& s) const
+{
+  if ( m_Parser.empty() )
+    return RESULT_INIT;
+
+  s = m_Parser->m_XMLDoc;
+  return RESULT_OK;
+}
+
+//
+ASDCP::Result_t
+AS_02::TimedText::ST2052_TextParser::ReadAncillaryResource(const Kumu::UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
+                                                          const ASDCP::TimedText::IResourceResolver* Resolver) const
+{
+  return RESULT_NOTIMPL;
+}
+
+
+//
+// end ST2052_TextParser.cpp
+//
index 5a51eaddfc532e3ffb893a829eeb5f78ab287f32..66aacd043f590df522dc4ab382f95c5ae04bfe64 100755 (executable)
@@ -91,6 +91,16 @@ public:
     return;                                                            \
   }
 
+
+//
+static void
+create_random_uuid(byte_t* uuidbuf)
+{
+  Kumu::UUID tmp_id;
+  GenRandomValue(tmp_id);
+  memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
+}
+
 //
 void
 banner(FILE* stream = stdout)
@@ -558,9 +568,13 @@ write_JP2K_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -705,9 +719,13 @@ write_PCM_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -778,9 +796,6 @@ write_PCM_file(CommandOptions& Options)
 
 
 
-#if 0
-// NOT YET, unfinished business with ST 2052-1
-
 
 //------------------------------------------------------------------------------------------
 // TimedText essence
@@ -832,9 +847,13 @@ write_timed_text_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -897,8 +916,6 @@ write_timed_text_file(CommandOptions& Options)
   return result;
 }
 
-#endif
-
 //
 int
 main(int argc, const char** argv)
index b87e4c7392902c95f62fe5132ee96f4eaf45d119..0a749b0d1dfbec9cfbab4533aabf4f3e2d022302 100755 (executable)
@@ -98,6 +98,15 @@ public:
     return;                                                            \
   }
 
+//
+static void
+create_random_uuid(byte_t* uuidbuf)
+{
+  Kumu::UUID tmp_id;
+  GenRandomValue(tmp_id);
+  memcpy(uuidbuf, tmp_id.Value(), tmp_id.Size());
+}
+
 //
 void
 banner(FILE* stream = stdout)
@@ -514,9 +523,13 @@ write_MPEG2_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -679,9 +692,13 @@ write_JP2K_S_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -814,9 +831,13 @@ write_JP2K_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -954,9 +975,13 @@ write_PCM_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -1122,9 +1147,13 @@ write_PCM_with_ATMOS_sync_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -1245,9 +1274,13 @@ write_timed_text_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
@@ -1365,9 +1398,13 @@ write_dolby_atmos_file(CommandOptions& Options)
          Info.EncryptedEssence = true;
 
          if ( Options.key_id_flag )
-           memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           {
+             memcpy(Info.CryptographicKeyID, Options.key_id_value, UUIDlen);
+           }
          else
-           RNG.FillRandom(Info.CryptographicKeyID, UUIDlen);
+           {
+             create_random_uuid(Info.CryptographicKeyID);
+           }
 
          Context = new AESEncContext;
          result = Context->InitKey(Options.key_value);
index 1de439214f363b48cf015cda61dc926855173278..ff8d000e71f668d3c77566c22bebb77f70f76086 100644 (file)
@@ -354,22 +354,26 @@ AS_02::h__AS02WriterClip::FinalizeClip(ui32_t bytes_per_frame)
   ui64_t current_position = m_File.Tell();
   Result_t result = m_File.Seek(m_ClipStart+16);
 
-  if ( ASDCP_SUCCESS(result) )
+  if ( KM_SUCCESS(result) )
     {
       byte_t clip_buffer[8] = {0};
-      bool check = Kumu::write_BER(clip_buffer, m_FramesWritten * bytes_per_frame, 8);
+      ui64_t size = static_cast<ui64_t>(m_FramesWritten) * bytes_per_frame;
+      bool check = Kumu::write_BER(clip_buffer, size, 8);
       assert(check);
       result = m_File.Write(clip_buffer, 8);
     }
 
-  m_File.Seek(current_position);
-  m_ClipStart = 0;
+  if ( KM_SUCCESS(result) )
+    {
+      result = m_File.Seek(current_position);
+      m_ClipStart = 0;
+    }
+  
   return result;
 }
 
 
 
-
 //
 // end h__02_Writer.cpp
 //
index f3dd3fd8bc7119c414a9077a05301e7a8ee4b502..3fc689910fc16fa6ac9fe16a78587984d627ee17 100755 (executable)
@@ -138,12 +138,12 @@ public:
 
              case 'd':
                TEST_EXTRA_ARG(i, 'd');
-               duration = atoi(argv[i]); // TODO: test for negative value, should use strtol()
+               duration = abs(strtol(argv[i], 0, 10));
                break;
 
              case 'f':
                TEST_EXTRA_ARG(i, 'f');
-               start_frame = atoi(argv[i]); // TODO: test for negative value, should use strtol()
+               start_frame = abs(strtol(argv[i], 0, 10));
                break;
 
              case 'h': help_flag = true; break;