using ASDCP::TimedText::TimedTextResourceDescriptor;
using ASDCP::TimedText::ResourceList_t;
+ //
+ class Type5UUIDFilenameResolver : public ASDCP::TimedText::IResourceResolver
+ {
+ typedef std::map<Kumu::UUID, std::string> ResourceMap;
+
+ ResourceMap m_ResourceMap;
+ std::string m_Dirname;
+ KM_NO_COPY_CONSTRUCT(Type5UUIDFilenameResolver);
+
+ public:
+ Type5UUIDFilenameResolver();
+ virtual ~Type5UUIDFilenameResolver();
+ Result_t OpenRead(const std::string& dirname);
+ Result_t ResolveRID(const byte_t* uuid, ASDCP::TimedText::FrameBuffer& FrameBuf) const;
+ };
+
//
class ST2052_TextParser
{
}
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();
public:
LocalFilenameResolver();
+ virtual ~LocalFilenameResolver();
Result_t OpenRead(const std::string& dirname);
Result_t ResolveRID(const byte_t* uuid, FrameBuffer& FrameBuf) const;
};
{
type = ESS_JPEG_2000;
}
- else if ( ASDCP_SUCCESS(WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
+ else if ( std::string((const char*)FB.RoData() + 8, 4) == "WAVE" )
{
- switch ( WavHeader.samplespersec )
+ if ( std::string((const char*)FB.RoData(), 4) == "RIFF" )
{
- case 48000: type = ESS_PCM_24b_48k; break;
- case 96000: type = ESS_PCM_24b_96k; break;
- default:
- return RESULT_FORMAT;
+ result = WavHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset);
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ switch ( WavHeader.samplespersec )
+ {
+ case 48000: type = ESS_PCM_24b_48k; break;
+ case 96000: type = ESS_PCM_24b_96k; break;
+ default:
+ DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec);
+ result = RESULT_FORMAT;
+ }
+ }
}
- }
- else if ( ASDCP_SUCCESS(RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
- {
- switch ( RF64Header.samplespersec )
+ else
{
- case 48000: type = ESS_PCM_24b_48k; break;
- case 96000: type = ESS_PCM_24b_96k; break;
- default:
- return RESULT_FORMAT;
+ result = RF64Header.ReadFromBuffer(FB.RoData(), read_count, &data_offset);
+
+ if ( ASDCP_SUCCESS(result) )
+ {
+ switch ( RF64Header.samplespersec )
+ {
+ case 48000: type = ESS_PCM_24b_48k; break;
+ case 96000: type = ESS_PCM_24b_96k; break;
+ default:
+ DefaultLogSink().Error("Unexpected sample rate: %d\n", WavHeader.samplespersec);
+ result = RESULT_FORMAT;
+ }
+ }
}
}
else if ( ASDCP_SUCCESS(AIFFHeader.ReadFromBuffer(FB.RoData(), read_count, &data_offset)) )
{ { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 362
0x04, 0x01, 0x03, 0x02, 0x0b, 0x00, 0x00, 0x00 },
{0}, false, "GenericPictureEssenceDescriptor_AlternativeCenterCuts" },
- { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 363
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 363
0x04, 0x01, 0x05, 0x01, 0x13, 0x00, 0x00, 0x00 },
{0x32, 0x05}, true, "GenericPictureEssenceDescriptor_ActiveHeight" },
- { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 364
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 364
0x04, 0x01, 0x05, 0x01, 0x14, 0x00, 0x00, 0x00 },
{0x32, 0x04}, true, "GenericPictureEssenceDescriptor_ActiveWidth" },
- { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 365
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 365
0x04, 0x01, 0x05, 0x01, 0x15, 0x00, 0x00, 0x00 },
{0x32, 0x06}, true, "GenericPictureEssenceDescriptor_ActiveXOffset" },
- { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x01, // 366
+ { { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 366
0x04, 0x01, 0x05, 0x01, 0x16, 0x00, 0x00, 0x00 },
{0x32, 0x07}, true, "GenericPictureEssenceDescriptor_ActiveYOffset" },
{ { 0x06, 0x0e, 0x2b, 0x34, 0x01, 0x01, 0x01, 0x0e, // 367
/*
-Copyright (c) 2013-2014, John Hurst
+Copyright (c) 2013-2015, John Hurst
All rights reserved.
Redistribution and use in source and binary forms, with or without
#include "AS_02_internal.h"
#include "KM_xml.h"
+#include <openssl/sha.h>
using namespace Kumu;
using namespace ASDCP;
const char* c_tt_namespace_name = "http://www.smpte-ra.org/schemas/2052-1/2010/smpte-tt";
+
+//------------------------------------------------------------------------------------------
+
+//
+//
+static byte_t s_id_prefix[16] = {
+ // RFC 4122 type 5
+ // 2067-2 5.4.5
+ 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)
+{
+ 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());
+
+ 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
+ asset_id.Set(buf);
+}
+
+//------------------------------------------------------------------------------------------
+
+
+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 = ".";
+ }
+
+ if ( KM_SUCCESS(dir_reader.Open(abs_dirname.c_str())) )
+ {
+ 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 result = reader.OpenRead(tmp_path);
+
+ if ( KM_SUCCESS(result) )
+ {
+ result = reader.Read(read_buffer, 16);
+ }
+
+ if ( KM_SUCCESS(result) )
+ {
+ // is it PNG?
+ if ( memcmp(read_buffer, PNGMagic, sizeof(PNGMagic)) == 0 )
+ {
+ UUID asset_id;
+ gen_png_name_id(next_item, asset_id);
+ m_ResourceMap.insert(ResourceMap::value_type(asset_id, next_item));
+ }
+ }
+ }
+ }
+ }
+}
+
+//
+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() )
+ {
+ 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;
std::string m_Filename;
std::string m_XMLDoc;
TimedTextDescriptor m_TDesc;
- ASDCP::mem_ptr<ASDCP::TimedText::LocalFilenameResolver> m_DefaultResolver;
+ ASDCP::mem_ptr<ASDCP::TimedText::IResourceResolver> m_DefaultResolver;
h__TextParser() : m_Root("**ParserRoot**")
{
{
if ( m_DefaultResolver.empty() )
{
- ASDCP::TimedText::LocalFilenameResolver *resolver = new ASDCP::TimedText::LocalFilenameResolver;
+ AS_02::TimedText::Type5UUIDFilenameResolver *resolver = new AS_02::TimedText::Type5UUIDFilenameResolver;
resolver->OpenRead(PathDirname(m_Filename));
m_DefaultResolver = resolver;
}
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,
+ Result_t ReadAncillaryResource(const byte_t *uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
const ASDCP::TimedText::IResourceResolver& Resolver) const;
};
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;
+ {
+ return RESULT_FORMAT;
+ }
m_TDesc.EncodingName = "UTF-8"; // the XML parser demands UTF-8
m_TDesc.ResourceList.clear();
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;
+ 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));
+ }
+
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::ReadAncillaryResource(const Kumu::UUID& uuid, ASDCP::TimedText::FrameBuffer& FrameBuf,
const ASDCP::TimedText::IResourceResolver* Resolver) const
{
- return RESULT_NOTIMPL;
+ if ( m_Parser.empty() )
+ return RESULT_INIT;
+
+ if ( Resolver == 0 )
+ Resolver = m_Parser->GetDefaultResolver();
+
+ return m_Parser->ReadAncillaryResource(uuid.Value(), FrameBuf, *Resolver);
}
ASDCP::TimedText::LocalFilenameResolver::LocalFilenameResolver() {}
+ASDCP::TimedText::LocalFilenameResolver::~LocalFilenameResolver() {}
//
Result_t
{
ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2;
- if ( format != WAVE_FORMAT_PCM && format != WAVE_FORMAT_EXTENSIBLE )
+ if ( format != ASDCP_WAVE_FORMAT_PCM && format != ASDCP_WAVE_FORMAT_EXTENSIBLE )
{
DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format);
return RESULT_RAW_FORMAT;
{
ui16_t format = KM_i16_LE(*(ui16_t*)p); p += 2;
- if ( format != Wav::WAVE_FORMAT_PCM && format != Wav::WAVE_FORMAT_EXTENSIBLE )
+ if ( format != Wav::ASDCP_WAVE_FORMAT_PCM && format != Wav::ASDCP_WAVE_FORMAT_EXTENSIBLE )
{
DefaultLogSink().Error("Expecting uncompressed PCM data, got format type %hd\n", format);
return RESULT_RAW_FORMAT;
const fourcc FCC_fmt_("fmt ");
const fourcc FCC_data("data");
- const ui16_t WAVE_FORMAT_PCM = 1;
- const ui16_t WAVE_FORMAT_EXTENSIBLE = 65534;
+ const ui16_t ASDCP_WAVE_FORMAT_PCM = 1;
+ const ui16_t ASDCP_WAVE_FORMAT_EXTENSIBLE = 65534;
//
class SimpleWaveHeader
%s [-1|-2] [-b <buffer-size>] [-d <duration>]\n\
[-f <starting-frame>] [-m] [-p <frame-rate>] [-R] [-s <size>] [-v] [-W]\n\
[-w] <input-file> [<file-prefix>]\n\n",
- PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
+ PROGRAM_NAME, PROGRAM_NAME);
fprintf(stream, "\
Options:\n\
%s -d <input-file>\n\
\n\
%s -g | -u\n",
- PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
+ PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
fprintf(stream, "\
Major modes:\n\
case ESS_DCDATA_UNKNOWN:
if ( ! Options.aux_data_coding.HasValue() )
{
- fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n",
- Options.filenames.front().c_str());
+ fprintf(stderr, "Option \"-A <UL>\" is required for Aux Data essence.\n");
return 3;
}
else
snprintf(filename_buf, 1024, "%s%010qu%s", this_prefix.c_str(), item_counter, Options.suffix.c_str());
if ( Options.verbose_flag )
- fprintf(stderr, "%s (%d bytes)\n", filename_buf, packet.ValueLength());
+ fprintf(stderr, "%s (%llu bytes)\n", filename_buf, packet.ValueLength());
Kumu::FileWriter writer;
writer.OpenWrite(filename_buf);
%s [-b <buffer-size>] [-d <duration>]\n\
[-f <starting-frame>] [-m] [-R] [-s <size>] [-v] [-W]\n\
[-w] <input-file> [<file-prefix>]\n\n",
- PROGRAM_NAME, PROGRAM_NAME, PROGRAM_NAME);
+ PROGRAM_NAME, PROGRAM_NAME);
fprintf(stream, "\
Options:\n\