/*
-Copyright (c) 2013-2015, John Hurst
+Copyright (c) 2013-2016, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
\brief AS-DCP library, PCM essence reader and writer implementation
*/
-
#include "AS_02_internal.h"
#include "KM_xml.h"
#include <openssl/sha.h>
//------------------------------------------------------------------------------------------
//
+int const NS_ID_LENGTH = 16;
+
//
-static byte_t s_id_prefix[16] = {
+static byte_t s_png_id_prefix[NS_ID_LENGTH] = {
// RFC 4122 type 5
- // 2067-2 5.4.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
};
//
-void
-gen_png_name_id(const std::string& image_name, UUID& asset_id)
+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, s_id_prefix, 16);
- SHA1_Update(&ctx, (byte_t*)image_name.c_str(), image_name.length());
+ 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];
buf[6] |= 0x50; // set UUID version 'digest'
buf[8] &= 0x3f; // clear bits 6&7
buf[8] |= 0x80; // set bit 7
- asset_id.Set(buf);
+ return Kumu::UUID(buf);
}
+//
+Kumu::UUID
+AS_02::TimedText::CreatePNGNameId(const std::string& image_name)
+{
+ return create_4122_type5_id(image_name, s_png_id_prefix);
+}
+
+//
+Kumu::UUID
+AS_02::TimedText::CreateFontNameId(const std::string& font_name)
+{
+ return create_4122_type5_id(font_name, s_font_id_prefix);
+}
+
+//
+static Kumu::Mutex sg_default_font_family_list_lock;
+static std::set<std::string> sg_default_font_family_list;
+
+static void
+setup_default_font_family_list()
+{
+ AutoMutex l(sg_default_font_family_list_lock);
+ sg_default_font_family_list.insert("default");
+ sg_default_font_family_list.insert("monospace");
+ sg_default_font_family_list.insert("sansSerif");
+ sg_default_font_family_list.insert("serif");
+ sg_default_font_family_list.insert("monospaceSansSerif");
+ sg_default_font_family_list.insert("monospaceSerif");
+ sg_default_font_family_list.insert("proportionalSansSerif");
+ sg_default_font_family_list.insert("proportionalSerif");
+}
+
+
//------------------------------------------------------------------------------------------
abs_dirname = ".";
}
- if ( KM_SUCCESS(dir_reader.Open(abs_dirname.c_str())) )
+ Result_t result = dir_reader.Open(abs_dirname);
+
+ if ( KM_SUCCESS(result) )
{
while ( KM_SUCCESS(dir_reader.GetNext(next_item, ft)) )
{
if ( ft == DET_FILE )
{
FileReader reader;
- Result_t result = reader.OpenRead(tmp_path);
+ Result_t read_result = reader.OpenRead(tmp_path);
- if ( KM_SUCCESS(result) )
+ if ( KM_SUCCESS(read_result) )
{
- result = reader.Read(read_buffer, 16);
+ read_result = reader.Read(read_buffer, 16);
}
- if ( KM_SUCCESS(result) )
+ if ( KM_SUCCESS(read_result) )
{
// is it PNG?
if ( memcmp(read_buffer, PNGMagic, sizeof(PNGMagic)) == 0 )
{
- UUID asset_id;
- gen_png_name_id(next_item, asset_id);
+ UUID asset_id = CreatePNGNameId(PathBasename(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 )
+ {
+ std::string font_root_name = PathSetExtension(next_item, "");
+ UUID asset_id = CreateFontNameId(PathBasename(font_root_name));
m_ResourceMap.insert(ResourceMap::value_type(asset_id, next_item));
}
}
}
}
}
+
+ return result;
}
//
if ( i == m_ResourceMap.end() )
{
+ DefaultLogSink().Debug("Missing timed-text resource \"%s\"\n", tmp_id.EncodeHex(buf, 64));
return RESULT_NOT_FOUND;
}
{
XMLElement m_Root;
ResourceTypeMap_t m_ResourceTypes;
- Result_t OpenRead();
+ Result_t OpenRead(const std::string& profile_name);
ASDCP_NO_COPY_CONSTRUCT(h__TextParser);
return m_DefaultResolver;
}
- Result_t OpenRead(const std::string& filename);
- Result_t OpenRead(const std::string& xml_doc, const std::string& filename);
+ Result_t OpenRead(const std::string& filename, const std::string& profile_name);
+ Result_t OpenRead(const std::string& xml_doc, const std::string& filename, const std::string& profile_name);
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)
+AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead(const std::string& filename, const std::string& profile_name)
{
Result_t result = ReadFileIntoString(filename, m_XMLDoc);
if ( KM_SUCCESS(result) )
{
m_Filename = filename;
- result = OpenRead();
+ result = OpenRead(profile_name);
}
return result;
//
Result_t
-AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead(const std::string& xml_doc, const std::string& filename)
+AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead(const std::string& xml_doc, const std::string& filename,
+ const std::string& profile_name)
{
m_XMLDoc = xml_doc;
m_Filename = filename;
- return OpenRead();
+ return OpenRead(profile_name);
}
//
//
Result_t
-AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead()
+AS_02::TimedText::ST2052_TextParser::h__TextParser::OpenRead(const std::string& profile_name)
{
+ setup_default_font_family_list();
+
if ( ! m_Root.ParseString(m_XMLDoc.c_str()) )
{
+ DefaultLogSink(). Error("ST 2052-1 document is not well-formed.\n");
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();
- }
+ m_TDesc.NamespaceName = profile_name;
AttributeVisitor png_visitor("backgroundImage");
apply_visitor(m_Root, png_visitor);
for ( i = png_visitor.value_list.begin(); i != png_visitor.value_list.end(); ++i )
{
- UUID asset_id;
- gen_png_name_id(*i, asset_id);
- TimedTextResourceDescriptor TmpResource;
- memcpy(TmpResource.ResourceID, asset_id.Value(), UUIDlen);
- TmpResource.Type = ASDCP::TimedText::MT_PNG;
- m_TDesc.ResourceList.push_back(TmpResource);
- m_ResourceTypes.insert(ResourceTypeMap_t::value_type(UUID(TmpResource.ResourceID), ASDCP::TimedText::MT_PNG));
+ UUID asset_id = CreatePNGNameId(PathBasename(*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);
+ char buf[64];
+
+ for ( i = font_visitor.value_list.begin(); i != font_visitor.value_list.end(); ++i )
+ {
+ UUID font_id = CreateFontNameId(PathBasename(*i));
+
+ if ( PathIsFile(font_id.EncodeHex(buf, 64))
+ || PathIsFile(*i+".ttf")
+ || PathIsFile(*i+".otf") )
+ {
+ 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));
+ }
+ else
+ {
+ AutoMutex l(sg_default_font_family_list_lock);
+ if ( sg_default_font_family_list.find(*i) == sg_default_font_family_list.end() )
+ {
+ DefaultLogSink(). Error("Unable to locate external font resource \"%s\".\n", i->c_str());
+ return RESULT_FORMAT;
+ }
+ }
}
return RESULT_OK;
// 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
+AS_02::TimedText::ST2052_TextParser::OpenRead(const std::string& filename, const std::string& profile_name) const
{
const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = new h__TextParser;
- Result_t result = m_Parser->OpenRead(filename);
+ Result_t result = m_Parser->OpenRead(filename, profile_name);
if ( ASDCP_FAILURE(result) )
const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = 0;
// 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
+AS_02::TimedText::ST2052_TextParser::OpenRead(const std::string& xml_doc, const std::string& filename,
+ const std::string& profile_name) const
{
const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = new h__TextParser;
- Result_t result = m_Parser->OpenRead(xml_doc, filename);
+ Result_t result = m_Parser->OpenRead(xml_doc, filename, profile_name);
if ( ASDCP_FAILURE(result) )
const_cast<AS_02::TimedText::ST2052_TextParser*>(this)->m_Parser = 0;