summaryrefslogtreecommitdiff
path: root/asdcplib/src/ST2052_TextParser.cpp
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2016-01-05 15:32:10 +0000
committerCarl Hetherington <cth@carlh.net>2016-01-05 15:32:10 +0000
commit3ec4338ce90ea0549409312f24f8b28c07a5d2da (patch)
tree09aaaff66dd860abbeacc20793145e1a27f4defd /asdcplib/src/ST2052_TextParser.cpp
parent342aad2ddf893aaaafa9a2c9980579d2dc4ec125 (diff)
asdcplib 2.5.11
Diffstat (limited to 'asdcplib/src/ST2052_TextParser.cpp')
-rw-r--r--asdcplib/src/ST2052_TextParser.cpp498
1 files changed, 498 insertions, 0 deletions
diff --git a/asdcplib/src/ST2052_TextParser.cpp b/asdcplib/src/ST2052_TextParser.cpp
new file mode 100644
index 00000000..b5d7fe6d
--- /dev/null
+++ b/asdcplib/src/ST2052_TextParser.cpp
@@ -0,0 +1,498 @@
+/*
+Copyright (c) 2013-2015, 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: ST2052_TextParser.cpp,v 1.4 2015/10/07 16:41:23 jhurst Exp $
+ \brief AS-DCP library, PCM essence reader and writer implementation
+*/
+
+
+#include "AS_02_internal.h"
+#include "KM_xml.h"
+#include <openssl/sha.h>
+
+using namespace Kumu;
+using namespace ASDCP;
+
+using Kumu::DefaultLogSink;
+
+const char* c_tt_namespace_name = "http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt";
+
+
+//------------------------------------------------------------------------------------------
+
+//
+int const NS_ID_LENGTH = 16;
+
+//
+static byte_t s_png_id_prefix[NS_ID_LENGTH] = {
+ // RFC 4122 type 5
+ // 2067-2 5.4.5 / RFC4122 Appendix C
+ 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1,
+ 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
+};
+
+//
+static byte_t s_font_id_prefix[NS_ID_LENGTH] = {
+ // RFC 4122 type 5
+ // 2067-2 5.4.6
+ 0xb6, 0xcc, 0x57, 0xa0, 0x87, 0xe7, 0x4e, 0x75,
+ 0xb1, 0xc3, 0x33, 0x59, 0xf3, 0xae, 0x88, 0x17
+};
+
+//
+static Kumu::UUID
+create_4122_type5_id(const std::string& subject_name, const byte_t* ns_id)
+{
+ SHA_CTX ctx;
+ SHA1_Init(&ctx);
+ SHA1_Update(&ctx, ns_id, NS_ID_LENGTH);
+ SHA1_Update(&ctx, (byte_t*)subject_name.c_str(), subject_name.size());
+
+ const ui32_t sha_len = 20;
+ byte_t bin_buf[sha_len];
+ SHA1_Final(bin_buf, &ctx);
+
+ // Derive the asset ID from the digest. Make it a type-5 UUID
+ byte_t buf[UUID_Length];
+ memcpy(buf, bin_buf, UUID_Length);
+ buf[6] &= 0x0f; // clear bits 4-7
+ buf[6] |= 0x50; // set UUID version 'digest'
+ buf[8] &= 0x3f; // clear bits 6&7
+ buf[8] |= 0x80; // set bit 7
+ return Kumu::UUID(buf);
+}
+
+//
+static Kumu::UUID
+create_png_name_id(const std::string& image_name)
+{
+ return create_4122_type5_id(image_name, s_png_id_prefix);
+}
+
+//
+static Kumu::UUID
+create_font_name_id(const std::string& font_name)
+{
+ return create_4122_type5_id(font_name, s_font_id_prefix);
+}
+
+//------------------------------------------------------------------------------------------
+
+
+AS_02::TimedText::Type5UUIDFilenameResolver::Type5UUIDFilenameResolver() {}
+AS_02::TimedText::Type5UUIDFilenameResolver::~Type5UUIDFilenameResolver() {}
+
+const byte_t PNGMagic[8] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
+const byte_t OpenTypeMagic[5] = { 0x4f, 0x54, 0x54, 0x4f, 0x00 };
+const byte_t TrueTypeMagic[5] = { 0x00, 0x01, 0x00, 0x00, 0x00 };
+
+//
+Result_t
+AS_02::TimedText::Type5UUIDFilenameResolver::OpenRead(const std::string& dirname)
+{
+ DirScannerEx dir_reader;
+ DirectoryEntryType_t ft;
+ std::string next_item;
+ std::string abs_dirname = PathMakeCanonical(dirname);
+ byte_t read_buffer[16];
+
+ if ( abs_dirname.empty() )
+ {
+ abs_dirname = ".";
+ }
+
+ Result_t result = dir_reader.Open(abs_dirname);
+
+ if ( KM_SUCCESS(result) )
+ {
+ while ( KM_SUCCESS(dir_reader.GetNext(next_item, ft)) )
+ {
+ if ( next_item[0] == '.' ) continue; // no hidden files
+ std::string tmp_path = PathJoin(abs_dirname, next_item);
+
+ if ( ft == DET_FILE )
+ {
+ FileReader reader;
+ Result_t read_result = reader.OpenRead(tmp_path);
+
+ if ( KM_SUCCESS(read_result) )
+ {
+ read_result = reader.Read(read_buffer, 16);
+ }
+
+ if ( KM_SUCCESS(read_result) )
+ {
+ // is it PNG?
+ if ( memcmp(read_buffer, PNGMagic, sizeof(PNGMagic)) == 0 )
+ {
+ UUID asset_id = create_png_name_id(next_item);
+ m_ResourceMap.insert(ResourceMap::value_type(asset_id, next_item));
+ }
+ // is it a font?
+ else if ( memcmp(read_buffer, OpenTypeMagic, sizeof(OpenTypeMagic)) == 0
+ || memcmp(read_buffer, TrueTypeMagic, sizeof(TrueTypeMagic)) == 0 )
+ {
+ fprintf(stderr, "wrap font!\n");
+ UUID asset_id = create_font_name_id(next_item);
+ m_ResourceMap.insert(ResourceMap::value_type(asset_id, next_item));
+ }
+ }
+ }
+ }
+ }
+
+ return result;
+}
+
+//
+Result_t
+AS_02::TimedText::Type5UUIDFilenameResolver::ResolveRID(const byte_t* uuid, ASDCP::TimedText::FrameBuffer& FrameBuf) const
+{
+ Kumu::UUID tmp_id(uuid);
+ char buf[64];
+
+ ResourceMap::const_iterator i = m_ResourceMap.find(tmp_id);
+
+ if ( i == m_ResourceMap.end() )
+ {
+ DefaultLogSink().Debug("Missing timed-text resource \"%s\"\n", tmp_id.EncodeHex(buf, 64));
+ return RESULT_NOT_FOUND;
+ }
+
+ FileReader Reader;
+
+ DefaultLogSink().Debug("Retrieving resource %s from file %s\n", tmp_id.EncodeHex(buf, 64), i->second.c_str());
+
+ Result_t result = Reader.OpenRead(i->second.c_str());
+
+ if ( KM_SUCCESS(result) )
+ {
+ ui32_t read_count, read_size = Reader.Size();
+ result = FrameBuf.Capacity(read_size);
+
+ if ( KM_SUCCESS(result) )
+ result = Reader.Read(FrameBuf.Data(), read_size, &read_count);
+
+ if ( KM_SUCCESS(result) )
+ FrameBuf.Size(read_count);
+ }
+
+ return result;
+}
+
+//------------------------------------------------------------------------------------------
+
+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::IResourceResolver> m_DefaultResolver;
+
+ h__TextParser() : m_Root("**ParserRoot**")
+ {
+ memset(&m_TDesc.AssetID, 0, UUIDlen);
+ }
+
+ ~h__TextParser() {}
+
+ ASDCP::TimedText::IResourceResolver* GetDefaultResolver()
+ {
+ if ( m_DefaultResolver.empty() )
+ {
+ AS_02::TimedText::Type5UUIDFilenameResolver *resolver = new AS_02::TimedText::Type5UUIDFilenameResolver;
+ 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 byte_t *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();
+}
+
+//
+template <class VisitorType>
+bool
+apply_visitor(const XMLElement& element, VisitorType& visitor)
+{
+ const ElementList& l = element.GetChildren();
+ ElementList::const_iterator i;
+
+ for ( i = l.begin(); i != l.end(); ++i )
+ {
+ if ( ! visitor.Element(**i) )
+ {
+ return false;
+ }
+
+ if ( ! apply_visitor(**i, visitor) )
+ {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//
+class AttributeVisitor
+{
+ std::string attr_name;
+
+public:
+ AttributeVisitor(const std::string& n) : attr_name(n) {}
+ std::set<std::string> value_list;
+
+ bool Element(const XMLElement& e)
+ {
+ const AttributeList& l = e.GetAttributes();
+ AttributeList::const_iterator i;
+
+ for ( i = l.begin(); i != l.end(); ++i )
+ {
+ if ( i->name == attr_name )
+ {
+ value_list.insert(i->value);
+ }
+ }
+
+ return true;
+ }
+};
+
+//
+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_tt_namespace_name);
+ m_TDesc.NamespaceName = c_tt_namespace_name;
+ }
+ else
+ {
+ m_TDesc.NamespaceName = ns->Name();
+ }
+
+ AttributeVisitor png_visitor("backgroundImage");
+ apply_visitor(m_Root, png_visitor);
+ std::set<std::string>::const_iterator i;
+
+ for ( i = png_visitor.value_list.begin(); i != png_visitor.value_list.end(); ++i )
+ {
+ UUID asset_id = create_png_name_id(*i);
+ TimedTextResourceDescriptor png_resource;
+ memcpy(png_resource.ResourceID, asset_id.Value(), UUIDlen);
+ png_resource.Type = ASDCP::TimedText::MT_PNG;
+ m_TDesc.ResourceList.push_back(png_resource);
+ m_ResourceTypes.insert(ResourceTypeMap_t::value_type(UUID(png_resource.ResourceID),
+ ASDCP::TimedText::MT_PNG));
+ }
+
+ AttributeVisitor font_visitor("fontFamily");
+ apply_visitor(m_Root, font_visitor);
+
+ for ( i = font_visitor.value_list.begin(); i != font_visitor.value_list.end(); ++i )
+ {
+ UUID font_id = create_font_name_id(*i);
+ TimedTextResourceDescriptor font_resource;
+ memcpy(font_resource.ResourceID, font_id.Value(), UUIDlen);
+ font_resource.Type = ASDCP::TimedText::MT_OPENTYPE;
+ m_TDesc.ResourceList.push_back(font_resource);
+ m_ResourceTypes.insert(ResourceTypeMap_t::value_type(UUID(font_resource.ResourceID),
+ ASDCP::TimedText::MT_OPENTYPE));
+ }
+
+ return RESULT_OK;
+}
+
+//
+Result_t
+AS_02::TimedText::ST2052_TextParser::h__TextParser::ReadAncillaryResource(const byte_t* uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
+ const ASDCP::TimedText::IResourceResolver& Resolver) const
+{
+ FrameBuf.AssetID(uuid);
+ UUID TmpID(uuid);
+ char buf[64];
+
+ ResourceTypeMap_t::const_iterator rmi = m_ResourceTypes.find(TmpID);
+
+ if ( rmi == m_ResourceTypes.end() )
+ {
+ DefaultLogSink().Error("Unknown ancillary resource id: %s\n", TmpID.EncodeHex(buf, 64));
+ return RESULT_RANGE;
+ }
+
+ Result_t result = Resolver.ResolveRID(uuid, FrameBuf);
+
+ if ( KM_SUCCESS(result) )
+ {
+ if ( (*rmi).second == ASDCP::TimedText::MT_PNG )
+ {
+ FrameBuf.MIMEType("image/png");
+ }
+ else if ( (*rmi).second == ASDCP::TimedText::MT_OPENTYPE )
+ {
+ FrameBuf.MIMEType("application/x-font-opentype");
+ }
+ else
+ {
+ FrameBuf.MIMEType("application/octet-stream");
+ }
+ }
+
+ return result;
+}
+
+
+
+//------------------------------------------------------------------------------------------
+
+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
+{
+ if ( m_Parser.empty() )
+ return RESULT_INIT;
+
+ if ( Resolver == 0 )
+ Resolver = m_Parser->GetDefaultResolver();
+
+ return m_Parser->ReadAncillaryResource(uuid.Value(), FrameBuf, *Resolver);
+}
+
+
+//
+// end ST2052_TextParser.cpp
+//