From ab3e3df49a9d4a44a3bf11211e31bdeac3ef7bcf Mon Sep 17 00:00:00 2001 From: jhurst Date: Sun, 21 Sep 2014 13:27:43 +0000 Subject: [PATCH] imf bugs date parse bug timed-text transform removed --- src/AS_02_JP2K.cpp | 11 +- src/AS_02_TimedText.cpp | 739 ++++++++++++++++++++++++++++++++++++++ src/AS_DCP.cpp | 42 ++- src/AS_DCP.h | 26 +- src/AS_DCP_JP2K.cpp | 1 - src/AS_DCP_MPEG2.cpp | 2 - src/AS_DCP_PCM.cpp | 15 +- src/AS_DCP_TimedText.cpp | 2 +- src/AS_DCP_internal.h | 4 +- src/KLV.cpp | 11 +- src/KLV.h | 20 +- src/KM_error.h | 3 +- src/KM_fileio.cpp | 43 ++- src/KM_fileio.h | 3 + src/KM_util.cpp | 37 +- src/KM_xml.cpp | 120 +++++++ src/KM_xml.h | 9 + src/MDD.cpp | 24 +- src/MXF.cpp | 123 ++++++- src/MXF.h | 6 +- src/MXFTypes.cpp | 6 +- src/Makefile.am | 6 +- src/S12MTimecode.h | 10 +- src/ST2052_TextParser.cpp | 209 +++++++++++ src/as-02-wrap.cpp | 39 +- src/asdcp-wrap.cpp | 65 +++- src/h__02_Writer.cpp | 14 +- src/wavesplit.cpp | 4 +- 28 files changed, 1480 insertions(+), 114 deletions(-) create mode 100644 src/AS_02_TimedText.cpp create mode 100644 src/ST2052_TextParser.cpp diff --git a/src/AS_02_JP2K.cpp b/src/AS_02_JP2K.cpp index 48b98fe..a2027d3 100644 --- a/src/AS_02_JP2K.cpp +++ b/src/AS_02_JP2K.cpp @@ -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 index 0000000..fc851ec --- /dev/null +++ b/src/AS_02_TimedText.cpp @@ -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 +#include + +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 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::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(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(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(tmp_iobj); + + if ( KM_SUCCESS(result) ) + { + Array::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 +// diff --git a/src/AS_DCP.cpp b/src/AS_DCP.cpp index 223d52a..1abd092 100755 --- a/src/AS_DCP.cpp +++ b/src/AS_DCP.cpp @@ -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 diff --git a/src/AS_DCP.h b/src/AS_DCP.h index 6b598cc..7231fb5 100755 --- a/src/AS_DCP.h +++ b/src/AS_DCP.h @@ -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! diff --git a/src/AS_DCP_JP2K.cpp b/src/AS_DCP_JP2K.cpp index 635eae5..6a41fe8 100755 --- a/src/AS_DCP_JP2K.cpp +++ b/src/AS_DCP_JP2K.cpp @@ -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; } diff --git a/src/AS_DCP_MPEG2.cpp b/src/AS_DCP_MPEG2.cpp index 8353ea4..ef82c2c 100755 --- a/src/AS_DCP_MPEG2.cpp +++ b/src/AS_DCP_MPEG2.cpp @@ -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; } diff --git a/src/AS_DCP_PCM.cpp b/src/AS_DCP_PCM.cpp index 88ccd1f..9de8d53 100755 --- a/src/AS_DCP_PCM.cpp +++ b/src/AS_DCP_PCM.cpp @@ -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); } diff --git a/src/AS_DCP_TimedText.cpp b/src/AS_DCP_TimedText.cpp index e158cde..e4619e0 100644 --- a/src/AS_DCP_TimedText.cpp +++ b/src/AS_DCP_TimedText.cpp @@ -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 diff --git a/src/AS_DCP_internal.h b/src/AS_DCP_internal.h index 7875a4f..c1df8ad 100755 --- a/src/AS_DCP_internal.h +++ b/src/AS_DCP_internal.h @@ -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; diff --git a/src/KLV.cpp b/src/KLV.cpp index 6ccf50c..f78f3ea 100755 --- a/src/KLV.cpp +++ b/src/KLV.cpp @@ -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() ) { diff --git a/src/KLV.h b/src/KLV.h index 8783905..b49cd07 100755 --- 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); }; diff --git a/src/KM_error.h b/src/KM_error.h index 0d05174..77a0846 100755 --- a/src/KM_error.h +++ b/src/KM_error.h @@ -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 diff --git a/src/KM_fileio.cpp b/src/KM_fileio.cpp index 417b17e..2b95d63 100644 --- a/src/KM_fileio.cpp +++ b/src/KM_fileio.cpp @@ -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(lTotalNumberOfFreeBytes.QuadPart); total_space = static_cast(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; diff --git a/src/KM_fileio.h b/src/KM_fileio.h index 1bf2822..a6970bf 100755 --- a/src/KM_fileio.h +++ b/src/KM_fileio.h @@ -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 //------------------------------------------------------------------------------------------ diff --git a/src/KM_util.cpp b/src/KM_util.cpp index 333b00a..8f8107f 100755 --- a/src/KM_util.cpp +++ b/src/KM_util.cpp @@ -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; } diff --git a/src/KM_xml.cpp b/src/KM_xml.cpp index 121f0a6..eb9c25f 100644 --- a/src/KM_xml.cpp +++ b/src/KM_xml.cpp @@ -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(p1), ':'); + if ( separator != 0 && separator < p2 ) + { + ns_prefix.assign(reinterpret_cast(p1), separator - p1); + p1 = separator + 1; + } + + type_name.assign(reinterpret_cast(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(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 doc_attr_nvpairs = km_token_split(attr_str, " "); + + std::list::iterator i; + std::map 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 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::value_type(nv_pair.name, nv_pair.value)); + } + + std::string doc_ns_name_selector = ns_prefix.empty() ? "xmlns" : "xmlns:"+ns_prefix; + std::map::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 // diff --git a/src/KM_xml.h b/src/KM_xml.h index 5d499e5..0f8ba3c 100644 --- a/src/KM_xml.h +++ b/src/KM_xml.h @@ -57,6 +57,15 @@ namespace Kumu typedef std::list 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 { diff --git a/src/MDD.cpp b/src/MDD.cpp index c879d0f..f3e8fc6 100644 --- a/src/MDD.cpp +++ b/src/MDD.cpp @@ -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 } diff --git a/src/MXF.cpp b/src/MXF.cpp index f113444..c3a3a5d 100755 --- a/src/MXF.cpp +++ b/src/MXF.cpp @@ -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 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::const_iterator i; + MXF::SourcePackage *source_package = dynamic_cast(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(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(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(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 // diff --git a/src/MXF.h b/src/MXF.h index 88fc0af..6041772 100755 --- 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 { diff --git a/src/MXFTypes.cpp b/src/MXFTypes.cpp index 483eb70..c575b96 100755 --- a/src/MXFTypes.cpp +++ b/src/MXFTypes.cpp @@ -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], diff --git a/src/Makefile.am b/src/Makefile.am index ef44ffd..4592918 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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@ diff --git a/src/S12MTimecode.h b/src/S12MTimecode.h index e750f25..6abe3f6 100644 --- a/src/S12MTimecode.h +++ b/src/S12MTimecode.h @@ -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 index 0000000..312c9d9 --- /dev/null +++ b/src/ST2052_TextParser.cpp @@ -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 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 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(this)->m_Parser = new h__TextParser; + + Result_t result = m_Parser->OpenRead(filename); + + if ( ASDCP_FAILURE(result) ) + const_cast(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(this)->m_Parser = new h__TextParser; + + Result_t result = m_Parser->OpenRead(xml_doc, filename); + + if ( ASDCP_FAILURE(result) ) + const_cast(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 +// diff --git a/src/as-02-wrap.cpp b/src/as-02-wrap.cpp index 5a51ead..66aacd0 100755 --- a/src/as-02-wrap.cpp +++ b/src/as-02-wrap.cpp @@ -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) diff --git a/src/asdcp-wrap.cpp b/src/asdcp-wrap.cpp index b87e4c7..0a749b0 100755 --- a/src/asdcp-wrap.cpp +++ b/src/asdcp-wrap.cpp @@ -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); diff --git a/src/h__02_Writer.cpp b/src/h__02_Writer.cpp index 1de4392..ff8d000 100644 --- a/src/h__02_Writer.cpp +++ b/src/h__02_Writer.cpp @@ -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(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 // diff --git a/src/wavesplit.cpp b/src/wavesplit.cpp index f3dd3fd..3fc6899 100755 --- a/src/wavesplit.cpp +++ b/src/wavesplit.cpp @@ -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; -- 2.30.2