{
if ( ! m_State.Test_BEGIN() )
{
- return RESULT_STATE;
+ KM_RESULT_STATE_HERE();
+ return RESULT_STATE;
}
if ( m_IndexStrategy != AS_02::IS_FOLLOW )
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);
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();
--- /dev/null
+/*
+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
+//
/*
-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
}
+
+//------------------------------------------------------------------------------------------
+//
+
+// 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
/*
-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
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
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
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
}
};
+ // 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!
if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
{
- DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
return RESULT_RANGE;
}
if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
{
- DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
return RESULT_RANGE;
}
if ( ASDCP_FAILURE(m_IndexAccess.Lookup(FrameNum, TmpEntry)) )
{
- DefaultLogSink().Error("Frame value out of range: %u\n", FrameNum);
return RESULT_RANGE;
}
}
}
+ 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
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;
}
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);
}
/*
-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
{
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;
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;
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() )
{
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;
}
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);
};
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
}
+//
+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);
+}
+
+
//------------------------------------------------------------------------------------------
//
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;
// 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
//------------------------------------------------------------------------------------------
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' )
{
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] == ':' )
{
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] == '+' )
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;
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;
}
#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
//
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
{
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 }
/*
-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
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
//
/*
-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
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
{
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],
{
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],
{
// 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],
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
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@
/*
-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
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;
}
--- /dev/null
+/*
+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
+//
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)
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);
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);
-#if 0
-// NOT YET, unfinished business with ST 2052-1
-
//------------------------------------------------------------------------------------------
// TimedText essence
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);
return result;
}
-#endif
-
//
int
main(int argc, const char** argv)
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)
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);
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);
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);
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);
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);
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);
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);
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
//
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;