summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2015-06-09 14:02:20 +0100
committerCarl Hetherington <cth@carlh.net>2015-06-09 14:02:20 +0100
commite4b8bed37b4fcfb932e2b899003f2a95df908ba0 (patch)
tree6af8acd73fdfdaa0025dca758cc29e488c4ce8a4
parent2ac1dbfeaffa5166f104ef4a01fedc41f542cacf (diff)
Handle storing/recovery of fonts in SMPTE MXF files.
-rwxr-xr-xrun/tools/dcpinfo2
-rw-r--r--src/interop_subtitle_asset.cc4
-rw-r--r--src/smpte_load_font_node.cc10
-rw-r--r--src/smpte_load_font_node.h1
-rw-r--r--src/smpte_subtitle_asset.cc84
-rw-r--r--src/subtitle_asset.cc2
-rw-r--r--src/subtitle_asset.h14
-rw-r--r--test/data/dummy.mxfbin0 -> 17714 bytes
-rw-r--r--test/dcp_font_test.cc39
9 files changed, 135 insertions, 21 deletions
diff --git a/run/tools/dcpinfo b/run/tools/dcpinfo
index 54e3c659..c0d13ba4 100755
--- a/run/tools/dcpinfo
+++ b/run/tools/dcpinfo
@@ -1,6 +1,6 @@
#!/bin/bash
-export LD_LIBRARY_PATH=build/src:build/asdcplib/src
+export LD_LIBRARY_PATH=build/src:build/asdcplib/src:$LD_LIBRARY_PATH
if [ "$1" == "--debug" ]; then
shift
gdb --args build/tools/dcpinfo "$@"
diff --git a/src/interop_subtitle_asset.cc b/src/interop_subtitle_asset.cc
index 9565b12d..5be4c53c 100644
--- a/src/interop_subtitle_asset.cc
+++ b/src/interop_subtitle_asset.cc
@@ -36,6 +36,7 @@ using std::cout;
using std::cerr;
using std::map;
using boost::shared_ptr;
+using boost::shared_array;
using boost::optional;
using boost::dynamic_pointer_cast;
using namespace dcp;
@@ -192,6 +193,7 @@ void
InteropSubtitleAsset::add_font_assets (list<shared_ptr<Asset> >& assets)
{
for (map<string, FontData>::const_iterator i = _fonts.begin(); i != _fonts.end(); ++i) {
- assets.push_back (shared_ptr<Font> (new Font (i->second.file)));
+ DCP_ASSERT (i->second.file);
+ assets.push_back (shared_ptr<Font> (new Font (i->second.file.get ())));
}
}
diff --git a/src/smpte_load_font_node.cc b/src/smpte_load_font_node.cc
index 11f7d5a4..28a020df 100644
--- a/src/smpte_load_font_node.cc
+++ b/src/smpte_load_font_node.cc
@@ -24,8 +24,16 @@ using std::string;
using boost::shared_ptr;
using namespace dcp;
+SMPTELoadFontNode::SMPTELoadFontNode (string id, string urn_)
+ : LoadFontNode (id)
+ , urn (urn_)
+{
+
+}
+
SMPTELoadFontNode::SMPTELoadFontNode (shared_ptr<const cxml::Node> node)
: LoadFontNode (node->string_attribute ("ID"))
+ , urn (node->content().substr (9))
{
- urn = node->content().substr (9);
+
}
diff --git a/src/smpte_load_font_node.h b/src/smpte_load_font_node.h
index a150d9be..e6b87f30 100644
--- a/src/smpte_load_font_node.h
+++ b/src/smpte_load_font_node.h
@@ -38,6 +38,7 @@ class SMPTELoadFontNode : public LoadFontNode
{
public:
SMPTELoadFontNode () {}
+ SMPTELoadFontNode (std::string id, std::string urn);
SMPTELoadFontNode (boost::shared_ptr<const cxml::Node> node);
std::string urn;
diff --git a/src/smpte_subtitle_asset.cc b/src/smpte_subtitle_asset.cc
index 9878ea1f..026e9f63 100644
--- a/src/smpte_subtitle_asset.cc
+++ b/src/smpte_subtitle_asset.cc
@@ -26,9 +26,11 @@
#include "font_node.h"
#include "exceptions.h"
#include "xml.h"
+#include "raw_convert.h"
+#include "dcp_assert.h"
+#include "util.h"
#include "AS_DCP.h"
#include "KM_util.h"
-#include "raw_convert.h"
#include <libxml++/libxml++.h>
#include <boost/foreach.hpp>
#include <boost/algorithm/string.hpp>
@@ -38,13 +40,16 @@ using std::list;
using std::stringstream;
using std::cout;
using std::vector;
+using std::map;
using boost::shared_ptr;
using boost::split;
using boost::is_any_of;
+using boost::shared_array;
using namespace dcp;
SMPTESubtitleAsset::SMPTESubtitleAsset ()
- : _time_code_rate (0)
+ : _edit_rate (24, 1)
+ , _time_code_rate (24)
{
}
@@ -55,24 +60,25 @@ SMPTESubtitleAsset::SMPTESubtitleAsset ()
SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file)
: SubtitleAsset (file)
{
- shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
-
ASDCP::TimedText::MXFReader reader;
Kumu::Result_t r = reader.OpenRead (file.string().c_str ());
if (ASDCP_FAILURE (r)) {
boost::throw_exception (MXFFileError ("could not open MXF file for reading", file, r));
}
+
+ /* Read the subtitle XML */
string s;
reader.ReadTimedTextResource (s, 0, 0);
stringstream t;
t << s;
+ shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
xml->read_stream (t);
ASDCP::WriterInfo info;
reader.FillWriterInfo (info);
_id = read_writer_info (info);
-
+
_load_font_nodes = type_children<dcp::SMPTELoadFontNode> (xml, "LoadFont");
_content_title_text = xml->string_child ("ContentTitleText");
@@ -107,6 +113,42 @@ SMPTESubtitleAsset::SMPTESubtitleAsset (boost::filesystem::path file)
}
parse_subtitles (xml, font_nodes);
+
+ /* Read fonts */
+
+ ASDCP::TimedText::TimedTextDescriptor text_descriptor;
+ reader.FillTimedTextDescriptor (text_descriptor);
+ for (
+ ASDCP::TimedText::ResourceList_t::const_iterator i = text_descriptor.ResourceList.begin();
+ i != text_descriptor.ResourceList.end();
+ ++i) {
+
+ if (i->Type == ASDCP::TimedText::MT_OPENTYPE) {
+ ASDCP::TimedText::FrameBuffer buffer;
+ buffer.Capacity (10 * 1024 * 1024);
+ reader.ReadAncillaryResource (i->ResourceID, buffer);
+
+ char id[64];
+ Kumu::bin2UUIDhex (i->ResourceID, ASDCP::UUIDlen, id, sizeof (id));
+
+ shared_array<uint8_t> data (new uint8_t[buffer.Size()]);
+ memcpy (data.get(), buffer.RoData(), buffer.Size());
+
+ /* The IDs in the MXF have a 9 character prefix of unknown origin and meaning... */
+ string check_id = string (id).substr (9);
+
+ list<shared_ptr<SMPTELoadFontNode> >::const_iterator j = _load_font_nodes.begin ();
+ while (j != _load_font_nodes.end() && (*j)->urn != check_id) {
+ ++j;
+ }
+
+ if (j != _load_font_nodes.end ()) {
+ _fonts[(*j)->id] = FontData (data, buffer.Size ());
+ }
+ }
+ }
+
+
}
list<shared_ptr<LoadFontNode> >
@@ -172,13 +214,23 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const
ASDCP::TimedText::TimedTextDescriptor descriptor;
descriptor.EditRate = ASDCP::Rational (_edit_rate.numerator, _edit_rate.denominator);
descriptor.EncodingName = "UTF-8";
- descriptor.ResourceList.clear ();
+
+ BOOST_FOREACH (shared_ptr<dcp::SMPTELoadFontNode> i, _load_font_nodes) {
+ map<string, FontData>::const_iterator j = _fonts.find (i->id);
+ if (j != _fonts.end ()) {
+ ASDCP::TimedText::TimedTextResourceDescriptor res;
+ unsigned int c;
+ Kumu::hex2bin (i->urn.c_str(), res.ResourceID, Kumu::UUID_Length, &c);
+ DCP_ASSERT (c == Kumu::UUID_Length);
+ res.Type = ASDCP::TimedText::MT_OPENTYPE;
+ descriptor.ResourceList.push_back (res);
+ }
+ }
+
descriptor.NamespaceName = "dcst";
memcpy (descriptor.AssetID, writer_info.AssetUUID, ASDCP::UUIDlen);
descriptor.ContainerDuration = latest_subtitle_out().as_editable_units (_edit_rate.numerator / _edit_rate.denominator);
- /* XXX: should write fonts into the file somehow */
-
ASDCP::TimedText::MXFWriter writer;
ASDCP::Result_t r = writer.OpenWrite (p.string().c_str(), writer_info, descriptor);
if (ASDCP_FAILURE (r)) {
@@ -191,6 +243,19 @@ SMPTESubtitleAsset::write (boost::filesystem::path p) const
boost::throw_exception (MXFFileError ("could not write XML to timed text resource", p.string(), r));
}
+ BOOST_FOREACH (shared_ptr<dcp::SMPTELoadFontNode> i, _load_font_nodes) {
+ map<string, FontData>::const_iterator j = _fonts.find (i->id);
+ if (j != _fonts.end ()) {
+ ASDCP::TimedText::FrameBuffer buffer;
+ buffer.SetData (j->second.data.get(), j->second.size);
+ buffer.Size (j->second.size);
+ r = writer.WriteAncillaryResource (buffer);
+ if (ASDCP_FAILURE (r)) {
+ boost::throw_exception (MXFFileError ("could not write font to timed text resource", p.string(), r));
+ }
+ }
+ }
+
writer.Finalize ();
_file = p;
@@ -206,5 +271,6 @@ SMPTESubtitleAsset::equals (shared_ptr<const Asset> other_asset, EqualityOptions
void
SMPTESubtitleAsset::add_font (string id, boost::filesystem::path file)
{
- /* XXX */
+ add_font_data (id, file);
+ _load_font_nodes.push_back (shared_ptr<SMPTELoadFontNode> (new SMPTELoadFontNode (id, make_uuid ())));
}
diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc
index 70d7a955..085e5959 100644
--- a/src/subtitle_asset.cc
+++ b/src/subtitle_asset.cc
@@ -324,5 +324,5 @@ SubtitleAsset::add_font_data (string id, boost::filesystem::path file)
throw FileError ("could not read font file", file, -1);
}
- _fonts[id] = FontData (data, size, file);
+ _fonts[id] = FontData (data, size);
}
diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h
index 481556aa..4cbbff0d 100644
--- a/src/subtitle_asset.h
+++ b/src/subtitle_asset.h
@@ -31,6 +31,7 @@ namespace xmlpp {
}
struct interop_dcp_font_test;
+struct smpte_dcp_font_test;
namespace dcp
{
@@ -78,6 +79,7 @@ public:
protected:
friend struct ::interop_dcp_font_test;
+ friend struct ::smpte_dcp_font_test;
void parse_subtitles (boost::shared_ptr<cxml::Document> xml, std::list<boost::shared_ptr<FontNode> > font_nodes);
void subtitles_as_xml (xmlpp::Element* root, int time_code_rate, std::string xmlns) const;
@@ -94,20 +96,16 @@ protected:
: data (data_)
, size (size_)
{}
-
- FontData (boost::shared_array<uint8_t> data_, boost::uintmax_t size_, boost::filesystem::path file_)
- : data (data_)
- , size (size_)
- , file (file_)
- {}
boost::shared_array<uint8_t> data;
boost::uintmax_t size;
- mutable boost::filesystem::path file;
+ /** .ttf file that this data was last written to */
+ mutable boost::optional<boost::filesystem::path> file;
};
/** Font data, keyed by a subclass-dependent identifier.
- * For Interop fonts, the string is the font ID from the subtitle file.
+ * For Interop, the string is the font ID from the subtitle file.
+ * For SMPTE, the string is the font's URN from the subtitle file.
*/
std::map<std::string, FontData> _fonts;
diff --git a/test/data/dummy.mxf b/test/data/dummy.mxf
new file mode 100644
index 00000000..3c604548
--- /dev/null
+++ b/test/data/dummy.mxf
Binary files differ
diff --git a/test/dcp_font_test.cc b/test/dcp_font_test.cc
index e50219a0..a21552de 100644
--- a/test/dcp_font_test.cc
+++ b/test/dcp_font_test.cc
@@ -18,6 +18,7 @@
*/
#include "interop_subtitle_asset.h"
+#include "smpte_subtitle_asset.h"
#include "dcp.h"
#include "cpl.h"
#include "test.h"
@@ -70,3 +71,41 @@ BOOST_AUTO_TEST_CASE (interop_dcp_font_test)
BOOST_CHECK_EQUAL (memcmp (subs2->_fonts["theFontId"].data.get(), ref.get(), size), 0);
}
+
+/** Create a DCP with SMPTE subtitles and check that the font is written and read back correctly */
+BOOST_AUTO_TEST_CASE (smpte_dcp_font_test)
+{
+ boost::filesystem::path directory = "build/test/smpte_dcp_font_test";
+ dcp::DCP dcp (directory);
+
+ shared_ptr<dcp::SMPTESubtitleAsset> subs (new dcp::SMPTESubtitleAsset ());
+ subs->add_font ("theFontId", "test/data/dummy.ttf");
+ subs->write (directory / "frobozz.mxf");
+
+ shared_ptr<dcp::Reel> reel (new dcp::Reel ());
+ reel->add (shared_ptr<dcp::ReelAsset> (new dcp::ReelSubtitleAsset (subs, dcp::Fraction (24, 1), 24, 0)));
+
+ shared_ptr<dcp::CPL> cpl (new dcp::CPL ("", dcp::TRAILER));
+ cpl->add (reel);
+
+ dcp.add (cpl);
+ dcp.write_xml (dcp::SMPTE);
+
+ dcp::DCP dcp2 (directory);
+ dcp2.read ();
+ shared_ptr<dcp::SubtitleAsset> subs2 = dynamic_pointer_cast<dcp::SubtitleAsset> (
+ dcp2.cpls().front()->reels().front()->main_subtitle()->asset_ref().object()
+ );
+ BOOST_REQUIRE (subs2);
+ BOOST_REQUIRE_EQUAL (subs2->_fonts.size(), 1);
+
+ boost::uintmax_t const size = boost::filesystem::file_size ("test/data/dummy.ttf");
+ FILE* f = dcp::fopen_boost ("test/data/dummy.ttf", "r");
+ BOOST_REQUIRE (f);
+ shared_array<uint8_t> ref (new uint8_t[size]);
+ fread (ref.get(), 1, size, f);
+ fclose (f);
+
+ BOOST_REQUIRE (subs2->_fonts["theFontId"].data);
+ BOOST_CHECK_EQUAL (memcmp (subs2->_fonts["theFontId"].data.get(), ref.get(), size), 0);
+}