diff options
| author | Carl Hetherington <cth@carlh.net> | 2015-06-09 14:02:20 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2015-06-09 14:02:20 +0100 |
| commit | e4b8bed37b4fcfb932e2b899003f2a95df908ba0 (patch) | |
| tree | 6af8acd73fdfdaa0025dca758cc29e488c4ce8a4 | |
| parent | 2ac1dbfeaffa5166f104ef4a01fedc41f542cacf (diff) | |
Handle storing/recovery of fonts in SMPTE MXF files.
| -rwxr-xr-x | run/tools/dcpinfo | 2 | ||||
| -rw-r--r-- | src/interop_subtitle_asset.cc | 4 | ||||
| -rw-r--r-- | src/smpte_load_font_node.cc | 10 | ||||
| -rw-r--r-- | src/smpte_load_font_node.h | 1 | ||||
| -rw-r--r-- | src/smpte_subtitle_asset.cc | 84 | ||||
| -rw-r--r-- | src/subtitle_asset.cc | 2 | ||||
| -rw-r--r-- | src/subtitle_asset.h | 14 | ||||
| -rw-r--r-- | test/data/dummy.mxf | bin | 0 -> 17714 bytes | |||
| -rw-r--r-- | test/dcp_font_test.cc | 39 |
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 Binary files differnew file mode 100644 index 00000000..3c604548 --- /dev/null +++ b/test/data/dummy.mxf 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); +} |
