summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2015-01-14 17:39:32 +0000
committerCarl Hetherington <cth@carlh.net>2015-01-20 11:20:25 +0000
commit3f630fb8334238ab8a58fbe1a0f513ae2c00de80 (patch)
tree4b773b91029d6374bfd4f2194053d3e249d597cd /src
parent49cafda01b3e07c47e3b20dd5ee91e1426446aea (diff)
Simplify time representation; better in-tree DCP subtitle parser.
Diffstat (limited to 'src')
-rw-r--r--src/dcp/font.cc76
-rw-r--r--src/dcp/font.h58
-rw-r--r--src/dcp/interop_load_font.cc56
-rw-r--r--src/dcp/interop_load_font.h43
-rw-r--r--src/dcp/load_font.h38
-rw-r--r--src/dcp/smpte_load_font.cc31
-rw-r--r--src/dcp/smpte_load_font.h41
-rw-r--r--src/dcp/subtitle.cc119
-rw-r--r--src/dcp/subtitle.h58
-rw-r--r--src/dcp/text.cc65
-rw-r--r--src/dcp/text.h57
-rw-r--r--src/dcp/wscript1
-rw-r--r--src/dcp_reader.cc135
-rw-r--r--src/dcp_reader.h33
-rw-r--r--src/exceptions.h24
-rw-r--r--src/frame_time.cc113
-rw-r--r--src/frame_time.h89
-rw-r--r--src/interop_dcp_reader.cc42
-rw-r--r--src/interop_dcp_reader.h38
-rw-r--r--src/metric_time.cc120
-rw-r--r--src/metric_time.h74
-rw-r--r--src/raw_convert.h46
-rw-r--r--src/raw_subtitle.cc10
-rw-r--r--src/raw_subtitle.h14
-rw-r--r--src/reader_factory.cc32
-rw-r--r--src/smpte_dcp_reader.cc67
-rw-r--r--src/smpte_dcp_reader.h37
-rw-r--r--src/stl_binary_reader.cc8
-rw-r--r--src/stl_binary_reader.h2
-rw-r--r--src/stl_binary_writer.cc16
-rw-r--r--src/stl_text_reader.cc14
-rw-r--r--src/stl_text_reader.h2
-rw-r--r--src/sub_time.cc154
-rw-r--r--src/sub_time.h86
-rw-r--r--src/subrip_reader.cc20
-rw-r--r--src/subrip_reader.h5
-rw-r--r--src/subtitle.h13
-rw-r--r--src/time_pair.cc94
-rw-r--r--src/time_pair.h84
-rw-r--r--src/util.cc37
-rw-r--r--src/util.h28
-rw-r--r--src/wscript16
42 files changed, 1378 insertions, 718 deletions
diff --git a/src/dcp/font.cc b/src/dcp/font.cc
new file mode 100644
index 0000000..b2fd128
--- /dev/null
+++ b/src/dcp/font.cc
@@ -0,0 +1,76 @@
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "font.h"
+#include "text.h"
+#include <libcxml/cxml.h>
+#include <boost/foreach.hpp>
+
+using std::string;
+using std::list;
+using boost::shared_ptr;
+using boost::optional;
+using namespace sub;
+
+dcp::Font::Font (cxml::ConstNodePtr node)
+{
+ id = node->optional_string_attribute ("Id");
+ size = node->optional_number_attribute<int64_t> ("Size").get_value_or (0);
+ italic = node->optional_bool_attribute ("Italic");
+ optional<string> c = node->optional_string_attribute ("Color");
+ if (c) {
+ colour = Colour (c.get ());
+ }
+ optional<string> const e = node->optional_string_attribute ("Effect");
+ if (e) {
+ effect = string_to_effect (e.get ());
+ }
+ c = node->optional_string_attribute ("EffectColor");
+ if (c) {
+ effect_colour = Colour (c.get ());
+ }
+}
+
+dcp::Font::Font (std::list<boost::shared_ptr<Font> > const & font_nodes)
+ : size (0)
+ , italic (false)
+ , colour ("FFFFFFFF")
+ , effect_colour ("FFFFFFFF")
+{
+ for (list<shared_ptr<Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) {
+ if ((*i)->id) {
+ id = (*i)->id;
+ }
+ if ((*i)->size != 0) {
+ size = (*i)->size;
+ }
+ if ((*i)->italic) {
+ italic = (*i)->italic.get ();
+ }
+ if ((*i)->colour) {
+ colour = (*i)->colour.get ();
+ }
+ if ((*i)->effect) {
+ effect = (*i)->effect.get ();
+ }
+ if ((*i)->effect_colour) {
+ effect_colour = (*i)->effect_colour.get ();
+ }
+ }
+}
diff --git a/src/dcp/font.h b/src/dcp/font.h
new file mode 100644
index 0000000..a1d8223
--- /dev/null
+++ b/src/dcp/font.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file src/dcp/font.h
+ * @brief Font class
+ */
+
+#include "../colour.h"
+#include "../effect.h"
+#include "subtitle.h"
+#include <libcxml/cxml.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <list>
+
+namespace sub {
+namespace dcp {
+
+/** @class Font
+ * @brief Helper class for parsing subtitle XML.
+ */
+class Font
+{
+public:
+ Font ()
+ : size (0)
+ {}
+
+ Font (cxml::ConstNodePtr node);
+ Font (std::list<boost::shared_ptr<Font> > const & font_nodes);
+
+ boost::optional<std::string> id;
+ int size;
+ boost::optional<bool> italic;
+ boost::optional<Colour> colour;
+ boost::optional<Effect> effect;
+ boost::optional<Colour> effect_colour;
+};
+
+}
+
+}
diff --git a/src/dcp/interop_load_font.cc b/src/dcp/interop_load_font.cc
new file mode 100644
index 0000000..2ee3ee9
--- /dev/null
+++ b/src/dcp/interop_load_font.cc
@@ -0,0 +1,56 @@
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "interop_load_font.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using boost::shared_ptr;
+using boost::optional;
+using namespace sub;
+
+dcp::InteropLoadFont::InteropLoadFont (string id_, string uri_)
+ : LoadFont (id_)
+ , uri (uri_)
+{
+
+}
+
+dcp::InteropLoadFont::InteropLoadFont (cxml::ConstNodePtr node)
+{
+ optional<string> x = node->optional_string_attribute ("Id");
+ if (!x) {
+ x = node->optional_string_attribute ("ID");
+ }
+ id = x.get_value_or ("");
+
+ uri = node->string_attribute ("URI");
+}
+
+bool
+dcp::operator== (InteropLoadFont const & a, InteropLoadFont const & b)
+{
+ return a.id == b.id && a.uri == b.uri;
+}
+
+bool
+dcp::operator!= (InteropLoadFont const & a, InteropLoadFont const & b)
+{
+ return !(a == b);
+}
diff --git a/src/dcp/interop_load_font.h b/src/dcp/interop_load_font.h
new file mode 100644
index 0000000..373b26c
--- /dev/null
+++ b/src/dcp/interop_load_font.h
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "load_font.h"
+#include <libcxml/cxml.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+namespace sub {
+namespace dcp {
+
+class InteropLoadFont : public LoadFont
+{
+public:
+ InteropLoadFont () {}
+ InteropLoadFont (std::string id, std::string uri);
+ InteropLoadFont (cxml::ConstNodePtr node);
+
+ std::string uri;
+};
+
+bool operator== (InteropLoadFont const & a, InteropLoadFont const & b);
+bool operator!= (InteropLoadFont const & a, InteropLoadFont const & b);
+
+}
+
+}
diff --git a/src/dcp/load_font.h b/src/dcp/load_font.h
new file mode 100644
index 0000000..f269c87
--- /dev/null
+++ b/src/dcp/load_font.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+
+namespace sub {
+namespace dcp {
+
+class LoadFont
+{
+public:
+ LoadFont () {}
+ LoadFont (std::string id_)
+ : id (id_)
+ {}
+
+ std::string id;
+};
+
+}
+
+}
diff --git a/src/dcp/smpte_load_font.cc b/src/dcp/smpte_load_font.cc
new file mode 100644
index 0000000..d433cbf
--- /dev/null
+++ b/src/dcp/smpte_load_font.cc
@@ -0,0 +1,31 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "smpte_load_font.h"
+#include <libcxml/cxml.h>
+
+using std::string;
+using boost::shared_ptr;
+using namespace sub;
+
+dcp::SMPTELoadFont::SMPTELoadFont (shared_ptr<const cxml::Node> node)
+ : LoadFont (node->string_attribute ("ID"))
+{
+ urn = node->content().substr (9);
+}
diff --git a/src/dcp/smpte_load_font.h b/src/dcp/smpte_load_font.h
new file mode 100644
index 0000000..aba05c8
--- /dev/null
+++ b/src/dcp/smpte_load_font.h
@@ -0,0 +1,41 @@
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "load_font.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+
+namespace cxml {
+ class Node;
+}
+
+namespace sub {
+namespace dcp {
+
+class SMPTELoadFont : public LoadFont
+{
+public:
+ SMPTELoadFont (boost::shared_ptr<const cxml::Node> node);
+
+ std::string urn;
+};
+
+}
+
+}
diff --git a/src/dcp/subtitle.cc b/src/dcp/subtitle.cc
new file mode 100644
index 0000000..68ca559
--- /dev/null
+++ b/src/dcp/subtitle.cc
@@ -0,0 +1,119 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "../exceptions.h"
+#include "../raw_convert.h"
+#include "subtitle.h"
+#include <libcxml/cxml.h>
+#include <boost/lexical_cast.hpp>
+#include <boost/algorithm/string.hpp>
+
+using std::string;
+using std::vector;
+using std::list;
+using boost::optional;
+using boost::shared_ptr;
+using boost::lexical_cast;
+using boost::is_any_of;
+using namespace sub;
+
+dcp::Subtitle::Subtitle (boost::shared_ptr<const cxml::Node> node, optional<int> tcr)
+{
+ if (tcr) {
+ in = smpte_time (node, "TimeIn", tcr.get ()).get ();
+ out = smpte_time (node, "TimeOut", tcr.get ()).get ();
+ } else {
+ in = interop_time (node, "TimeIn").get ();
+ out = interop_time (node, "TimeOut").get ();
+ }
+
+ if (tcr) {
+ fade_up_time = smpte_time (node, "FadeUpTime", tcr.get ()).get_value_or (Time::from_hmsf (0, 0, 0, 2, Rational (tcr.get(), 1)));
+ fade_down_time = smpte_time (node, "FadeDownTime", tcr.get ()).get_value_or (Time::from_hmsf (0, 0, 0, 2, Rational (tcr.get (), 1)));
+ } else {
+ fade_up_time = interop_time (node, "FadeUpTime").get_value_or (Time::from_hms (0, 0, 0, 80));
+ if (fade_up_time > Time::from_hms (0, 0, 8, 0)) {
+ fade_up_time = Time::from_hms (0, 0, 8, 0);
+ }
+ fade_down_time = interop_time (node, "FadeDownTime").get_value_or (Time::from_hms (0, 0, 0, 80));
+ if (fade_down_time > Time::from_hms (0, 0, 8, 0)) {
+ fade_down_time = Time::from_hms (0, 0, 8, 0);
+ }
+ }
+}
+
+optional<Time>
+dcp::Subtitle::smpte_time (shared_ptr<const cxml::Node> node, string name, int tcr)
+{
+ optional<string> u = node->optional_string_attribute (name);
+ if (!u) {
+ return optional<Time> ();
+ }
+
+ vector<string> b;
+ split (b, u.get (), is_any_of (":"));
+ if (b.size() != 4) {
+ boost::throw_exception (DCPError ("unrecognised time specification " + u.get ()));
+ }
+
+ return Time::from_hmsf (
+ raw_convert<int> (b[0]),
+ raw_convert<int> (b[1]),
+ raw_convert<int> (b[2]),
+ raw_convert<int> (b[3]),
+ Rational (tcr, 1)
+ );
+}
+
+optional<Time>
+dcp::Subtitle::interop_time (shared_ptr<const cxml::Node> node, string name)
+{
+ optional<string> u = node->optional_string_attribute (name);
+ if (!u) {
+ return optional<Time> ();
+ }
+
+ if (u.get().find (":") != string::npos) {
+ /* HH:MM:SS:TTT or HH:MM:SS.sss */
+ vector<string> b;
+ split (b, u.get(), is_any_of (":."));
+ if (b.size() != 4) {
+ boost::throw_exception (DCPError ("unrecognised time specification " + u.get ()));
+ }
+
+ if (u.get().find (".") != string::npos) {
+ return Time::from_hms (
+ raw_convert<int> (b[0]),
+ raw_convert<int> (b[1]),
+ raw_convert<int> (b[2]),
+ rint (raw_convert<double> ("." + b[3]) * 1000)
+ );
+ } else {
+ return Time::from_hms (
+ raw_convert<int> (b[0]),
+ raw_convert<int> (b[1]),
+ raw_convert<int> (b[2]),
+ raw_convert<int> (b[3]) * 4
+ );
+ }
+ } else {
+ return Time::from_hms (0, 0, 0, raw_convert<int> (u.get ()) * 4);
+ }
+}
+
diff --git a/src/dcp/subtitle.h b/src/dcp/subtitle.h
new file mode 100644
index 0000000..672dc9a
--- /dev/null
+++ b/src/dcp/subtitle.h
@@ -0,0 +1,58 @@
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBSUB_DCP_SUBTITLE_H
+#define LIBSUB_DCP_SUBTITLE_H
+
+#include "../sub_time.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/optional.hpp>
+#include <list>
+
+namespace cxml {
+ class Node;
+}
+
+namespace sub {
+namespace dcp {
+
+class Font;
+class Text;
+
+class Subtitle
+{
+public:
+ Subtitle () {}
+ Subtitle (boost::shared_ptr<const cxml::Node> node, boost::optional<int> tcr);
+
+ Time in;
+ Time out;
+ Time fade_up_time;
+ Time fade_down_time;
+
+private:
+ boost::optional<Time> smpte_time (boost::shared_ptr<const cxml::Node> node, std::string name, int tcr);
+ boost::optional<Time> interop_time (boost::shared_ptr<const cxml::Node> node, std::string name);
+};
+
+}
+
+}
+
+#endif
diff --git a/src/dcp/text.cc b/src/dcp/text.cc
new file mode 100644
index 0000000..313ebdd
--- /dev/null
+++ b/src/dcp/text.cc
@@ -0,0 +1,65 @@
+/*
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file src/text.cc
+ * @brief Text class for parsing DCP subtitle XML.
+ */
+
+#include "../xml.h"
+#include "text.h"
+#include "font.h"
+#include <libcxml/cxml.h>
+#include <boost/foreach.hpp>
+
+using std::string;
+using std::list;
+using boost::shared_ptr;
+using boost::optional;
+using namespace sub;
+
+/** Read a &lt;Text&gt; node from a subtitle XML file, noting its contents
+ * in this object's member variables.
+ * @param node Node to read.
+ */
+dcp::Text::Text (boost::shared_ptr<const cxml::Node> node)
+ : v_align (CENTRE_OF_SCREEN)
+{
+ optional<float> x = node->optional_number_attribute<float> ("VPosition");
+ if (!x) {
+ x = node->number_attribute<float> ("Vposition");
+ }
+ v_position = x.get ();
+
+ optional<string> v = node->optional_string_attribute ("VAlign");
+ if (!v) {
+ v = node->optional_string_attribute ("Valign");
+ }
+
+ if (v) {
+ if (v.get() == "top") {
+ v_align = TOP_OF_SCREEN;
+ } else if (v.get() == "center") {
+ v_align = CENTRE_OF_SCREEN;
+ } else if (v.get() == "bottom") {
+ v_align = BOTTOM_OF_SCREEN;
+ } else {
+ boost::throw_exception (DCPError ("unknown subtitle valign type"));
+ }
+ }
+}
diff --git a/src/dcp/text.h b/src/dcp/text.h
new file mode 100644
index 0000000..f2c5059
--- /dev/null
+++ b/src/dcp/text.h
@@ -0,0 +1,57 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+/** @file src/text.h
+ * @brief Text class for parsing DCP subtitle XML.
+ */
+
+#include "../vertical_reference.h"
+#include <boost/shared_ptr.hpp>
+#include <list>
+
+namespace cxml {
+ class Node;
+}
+
+namespace sub {
+namespace dcp {
+
+class Font;
+
+/** @class Text
+ * @brief Parser for Text nodes from subtitle XML.
+ */
+class Text
+{
+public:
+ /** Construct a default text node */
+ Text ()
+ : v_position (0)
+ , v_align (TOP_OF_SCREEN)
+ {}
+
+ Text (boost::shared_ptr<const cxml::Node> node);
+
+ float v_position;
+ VerticalReference v_align;
+};
+
+}
+
+}
diff --git a/src/dcp/wscript b/src/dcp/wscript
new file mode 100644
index 0000000..a70ef26
--- /dev/null
+++ b/src/dcp/wscript
@@ -0,0 +1 @@
+# This is a dummy just so my waft script works
diff --git a/src/dcp_reader.cc b/src/dcp_reader.cc
index f63a893..4dcbe48 100644
--- a/src/dcp_reader.cc
+++ b/src/dcp_reader.cc
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -20,81 +20,94 @@
#include "dcp_reader.h"
#include "vertical_reference.h"
#include "xml.h"
+#include "util.h"
+#include "dcp/font.h"
+#include "dcp/text.h"
+#include "dcp/subtitle.h"
#include <libcxml/cxml.h>
-#include <dcp/interop_subtitle_content.h>
-#include <dcp/smpte_subtitle_content.h>
-#include <dcp/subtitle.h>
+#include <libxml++/libxml++.h>
using std::list;
using std::cout;
+using std::string;
using boost::shared_ptr;
+using boost::optional;
using namespace sub;
-static MetricTime
-dcp_to_metric (dcp::Time t)
+void
+DCPReader::parse_common (cxml::NodePtr root, optional<int> tcr)
{
- return MetricTime (t.h, t.m, t.s, t.e * 1000 / t.tcr);
+ _reel_number = root->string_child ("ReelNumber");
+ _language = root->string_child ("Language");
+
+ ParseState parse_state;
+ parse_node (root->node(), parse_state, tcr);
}
-static Colour
-dcp_to_colour (dcp::Colour c)
+void
+DCPReader::parse_node (xmlpp::Node const * node, ParseState& parse_state, optional<int> tcr)
{
- return Colour (float (c.r) / 255, float (c.g) / 255, float (c.b) / 255);
-}
+ xmlpp::Node::NodeList children = node->get_children ();
+ for (xmlpp::Node::NodeList::iterator i = children.begin(); i != children.end(); ++i) {
+ xmlpp::ContentNode const * c = dynamic_cast<xmlpp::ContentNode const *> (*i);
+ if (c) {
+ maybe_add_subtitle (c->get_content (), parse_state);
+ }
-/** @class DCPReader
- * @brief A class to read DCP subtitles.
- */
-DCPReader::DCPReader (boost::filesystem::path file, bool interop)
+ xmlpp::Element* e = dynamic_cast<xmlpp::Element *> (*i);
+ if (e) {
+ cxml::NodePtr n (new cxml::Node (e));
+ if (n->name() == "Font") {
+ parse_state.font_nodes.push_back (shared_ptr<dcp::Font> (new dcp::Font (n)));
+ parse_node (e, parse_state, tcr);
+ parse_state.font_nodes.pop_back ();
+ } else if (n->name() == "Text") {
+ parse_state.text_nodes.push_back (shared_ptr<dcp::Text> (new dcp::Text (n)));
+ parse_node (e, parse_state, tcr);
+ parse_state.text_nodes.pop_back ();
+ } else if (n->name() == "Subtitle") {
+ parse_state.subtitle_nodes.push_back (shared_ptr<dcp::Subtitle> (new dcp::Subtitle (n, tcr)));
+ parse_node (e, parse_state, tcr);
+ parse_state.subtitle_nodes.pop_back ();
+ } else if (n->name() == "SubtitleList") {
+ parse_node (e, parse_state, tcr);
+ }
+ }
+ }
+}
+
+void
+DCPReader::maybe_add_subtitle (string text, ParseState const & parse_state)
{
- shared_ptr<dcp::SubtitleContent> content;
- if (interop) {
- content.reset (new dcp::InteropSubtitleContent (file));
- } else {
- content.reset (new dcp::SMPTESubtitleContent (file));
+ if (empty_or_white_space (text)) {
+ return;
}
- list<dcp::SubtitleString> subs = content->subtitles ();
- for (list<dcp::SubtitleString>::const_iterator i = subs.begin(); i != subs.end(); ++i) {
- RawSubtitle sub;
+ if (parse_state.text_nodes.empty() || parse_state.subtitle_nodes.empty ()) {
+ return;
+ }
- sub.vertical_position.proportional = float (i->v_position ()) / 100;
- switch (i->v_align ()) {
- case dcp::TOP:
- sub.vertical_position.reference = TOP_OF_SCREEN;
- break;
- case dcp::CENTER:
- sub.vertical_position.reference = CENTRE_OF_SCREEN;
- break;
- case dcp::BOTTOM:
- sub.vertical_position.reference = BOTTOM_OF_SCREEN;
- break;
- }
-
- sub.from.set_metric (dcp_to_metric (i->in ()));
- sub.to.set_metric (dcp_to_metric (i->out ()));
- sub.fade_up = dcp_to_metric (i->fade_up_time ());
- sub.fade_down = dcp_to_metric (i->fade_down_time ());
-
- sub.text = i->text ();
- /* XXX: should sub.font be optional? */
- sub.font = i->font().get_value_or ("");
- sub.font_size.set_proportional (float (i->size ()) / (72 * 11));
- switch (i->effect ()) {
- case dcp::NONE:
- break;
- case dcp::BORDER:
- sub.effect = BORDER;
- break;
- case dcp::SHADOW:
- sub.effect = SHADOW;
- break;
- }
+ dcp::Font effective_font (parse_state.font_nodes);
+ dcp::Text effective_text (*parse_state.text_nodes.back ());
+ dcp::Subtitle effective_subtitle (*parse_state.subtitle_nodes.back ());
- sub.effect_colour = dcp_to_colour (i->effect_colour ());
- sub.colour = dcp_to_colour (i->colour ());
- sub.italic = i->italic ();
-
- _subs.push_back (sub);
- }
+ RawSubtitle rs;
+
+ rs.text = text;
+ rs.font = effective_font.id;
+ rs.font_size.set_proportional (float (effective_font.size) / (72 * 11));
+ rs.effect = effective_font.effect;
+ rs.effect_colour = effective_font.effect_colour;
+ rs.colour = effective_font.colour.get();
+ rs.bold = false;
+ rs.italic = effective_font.italic.get();
+ rs.underline = false;
+ rs.vertical_position.proportional = float (effective_text.v_position) / 100;
+ rs.vertical_position.reference = effective_text.v_align;
+ rs.from = effective_subtitle.in;
+ rs.to = effective_subtitle.out;
+ rs.fade_up = effective_subtitle.fade_up_time;
+ rs.fade_down = effective_subtitle.fade_down_time;
+
+ _subs.push_back (rs);
}
diff --git a/src/dcp_reader.h b/src/dcp_reader.h
index 09d9f6a..f749f82 100644
--- a/src/dcp_reader.h
+++ b/src/dcp_reader.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -21,22 +21,41 @@
#define LIBSUB_DCP_READER_H
#include "reader.h"
+#include <libcxml/cxml.h>
#include <boost/shared_ptr.hpp>
#include <boost/filesystem.hpp>
-namespace cxml {
- class Node;
-}
-
namespace sub {
+namespace dcp {
+ class Font;
+ class Text;
+ class Subtitle;
+}
+
/** @class DCPReader
* @brief A class which reads DCP subtitles.
*/
class DCPReader : public Reader
{
-public:
- DCPReader (boost::filesystem::path file, bool interop);
+protected:
+
+ struct ParseState {
+ std::list<boost::shared_ptr<dcp::Font> > font_nodes;
+ std::list<boost::shared_ptr<dcp::Text> > text_nodes;
+ std::list<boost::shared_ptr<dcp::Subtitle> > subtitle_nodes;
+ };
+
+ void parse_common (cxml::NodePtr root, boost::optional<int> tcr);
+
+ std::string _id;
+
+private:
+ void parse_node (xmlpp::Node const * node, ParseState& parse_state, boost::optional<int> tcr);
+ void maybe_add_subtitle (std::string text, ParseState const & parse_state);
+
+ std::string _reel_number;
+ std::string _language;
};
}
diff --git a/src/exceptions.h b/src/exceptions.h
index a0ca1b9..62be0d5 100644
--- a/src/exceptions.h
+++ b/src/exceptions.h
@@ -75,6 +75,30 @@ public:
{}
};
+class MXFError : public MessageError
+{
+public:
+ MXFError (std::string const & message)
+ : MessageError (message)
+ {}
+};
+
+class UnknownFrameRateError : public MessageError
+{
+public:
+ UnknownFrameRateError ()
+ : MessageError ("unknown frame rate")
+ {}
+};
+
+class DCPError : public MessageError
+{
+public:
+ DCPError (std::string const & message)
+ : MessageError (message)
+ {}
+};
+
}
#endif
diff --git a/src/frame_time.cc b/src/frame_time.cc
deleted file mode 100644
index 65996c0..0000000
--- a/src/frame_time.cc
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "frame_time.h"
-#include "compose.hpp"
-#include <iostream>
-
-using std::ostream;
-using std::string;
-using namespace sub;
-
-bool
-sub::operator== (FrameTime const & a, FrameTime const & b)
-{
- return a.hours() == b.hours() && a.minutes() == b.minutes() && a.seconds() == b.seconds() && a.frames() == b.frames();
-}
-
-bool
-sub::operator< (FrameTime const & a, FrameTime const & b)
-{
- if (a.hours() != b.hours()) {
- return a.hours() < b.hours();
- }
-
- if (a.minutes() != b.minutes()) {
- return a.minutes() < b.minutes();
- }
-
- if (a.seconds() != b.seconds()) {
- return a.seconds() < b.seconds();
- }
-
- return a.frames() < b.frames();
-}
-
-ostream&
-sub::operator<< (ostream& s, FrameTime const & t)
-{
- s << t.hours() << ":" << t.minutes() << ":" << t.seconds() << ":" << t.frames();
- return s;
-}
-
-string
-FrameTime::timecode () const
-{
- return String::compose ("%1:%2:%3:%4", _hours, _minutes, _seconds, _frames);
-}
-
-FrameTime::FrameTime (int64_t f, float fps)
-{
- set_from_frames (f, fps);
-}
-
-void
-FrameTime::set_from_frames (int64_t f, float fps)
-{
- _hours = f / (60 * 60 * fps);
- f -= _hours * 60 * 60 * fps;
- _minutes = f / (60 * fps);
- f -= _minutes * 60 * fps;
- _seconds = f / fps;
- f -= _seconds * fps;
- _frames = int (f);
-}
-
-void
-FrameTime::add (FrameTime t, float fps)
-{
- _frames += t.frames ();
- if (_frames > fps) {
- _frames -= fps;
- _seconds++;
- }
-
- _seconds += t.seconds ();
- if (_seconds >= 60) {
- _seconds -= 60;
- ++_minutes;
- }
-
- _minutes += t.minutes ();
- if (_minutes >= 60) {
- _minutes -= 60;
- ++_hours;
- }
-
- _hours += t.hours ();
-}
-
-void
-FrameTime::scale (float f, float frames_per_second)
-{
- set_from_frames (
- (((_hours * 3600 + _minutes * 60 + _seconds) * frames_per_second) + _frames) * f,
- frames_per_second
- );
-}
diff --git a/src/frame_time.h b/src/frame_time.h
deleted file mode 100644
index ed5fc81..0000000
--- a/src/frame_time.h
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBSUB_FRAME_TIME_H
-#define LIBSUB_FRAME_TIME_H
-
-#include <iostream>
-#include <stdint.h>
-
-namespace sub {
-
-/** @class FrameTime
- * @brief A time stored in hours, minutes, seconds and frames.
- */
-class FrameTime
-{
-public:
- FrameTime ()
- : _hours (0)
- , _minutes (0)
- , _seconds (0)
- , _frames (0)
- {}
-
- /** @param f Number of frames.
- * @param fps Frames per second.
- */
- FrameTime (int64_t f, float fps);
-
- FrameTime (int h, int m, int s, int f)
- : _hours (h)
- , _minutes (m)
- , _seconds (s)
- , _frames (f)
- {}
-
- int hours () const {
- return _hours;
- }
-
- int minutes () const {
- return _minutes;
- }
-
- int seconds () const {
- return _seconds;
- }
-
- int frames () const {
- return _frames;
- }
-
- std::string timecode () const;
-
- void add (FrameTime t, float fps);
- void scale (float f, float fps);
-
-private:
- void set_from_frames (int64_t f, float fps);
-
- int _hours;
- int _minutes;
- int _seconds;
- int _frames;
-};
-
-bool operator== (FrameTime const & a, FrameTime const & b);
-bool operator< (FrameTime const & a, FrameTime const & b);
-std::ostream& operator<< (std::ostream&, FrameTime const & t);
-
-}
-
-#endif
diff --git a/src/interop_dcp_reader.cc b/src/interop_dcp_reader.cc
new file mode 100644
index 0000000..5751dcd
--- /dev/null
+++ b/src/interop_dcp_reader.cc
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "interop_dcp_reader.h"
+#include "dcp/interop_load_font.h"
+#include "xml.h"
+#include "dcp/font.h"
+#include <libcxml/cxml.h>
+#include <boost/foreach.hpp>
+
+using std::list;
+using boost::shared_ptr;
+using boost::optional;
+using namespace sub;
+
+InteropDCPReader::InteropDCPReader (boost::filesystem::path file)
+{
+ shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle"));
+ xml->read_file (file);
+ _id = xml->string_child ("SubtitleID");
+
+ _movie_title = xml->string_child ("MovieTitle");
+ _load_font_nodes = type_children<dcp::InteropLoadFont> (xml, "LoadFont");
+
+ parse_common (xml, optional<int> ());
+}
diff --git a/src/interop_dcp_reader.h b/src/interop_dcp_reader.h
new file mode 100644
index 0000000..1b8b447
--- /dev/null
+++ b/src/interop_dcp_reader.h
@@ -0,0 +1,38 @@
+/*
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "dcp_reader.h"
+
+namespace sub {
+
+namespace dcp {
+ class InteropLoadFont;
+}
+
+class InteropDCPReader : public DCPReader
+{
+public:
+ InteropDCPReader (boost::filesystem::path file);
+
+private:
+ std::string _movie_title;
+ std::list<boost::shared_ptr<dcp::InteropLoadFont> > _load_font_nodes;
+};
+
+}
diff --git a/src/metric_time.cc b/src/metric_time.cc
deleted file mode 100644
index 4fad390..0000000
--- a/src/metric_time.cc
+++ /dev/null
@@ -1,120 +0,0 @@
-/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#include "metric_time.h"
-#include "compose.hpp"
-#include <iostream>
-#include <cmath>
-
-using std::ostream;
-using std::string;
-using std::cout;
-using namespace sub;
-
-MetricTime::MetricTime (int h, int m, int s, int ms)
- /* cast up to int64_t to force a 64-bit calculation */
- : _ms ((int64_t (h) * 3600 + m * 60 + s) * 1000 + ms)
-{
-
-}
-
-void
-MetricTime::split (int& h, int &m, int& s, int& ms) const
-{
- int64_t w = _ms;
- h = floor (w / (3600 * 1000));
- /* this multiply could overflow 32 bits so cast to make sure it is done as 64-bit */
- w -= int64_t (h) * (3600 * 1000);
- m = floor (w / (60 * 1000));
- w -= m * (60 * 1000);
- s = floor (w / 1000);
- w -= s * 1000;
- ms = w;
-}
-
-int
-MetricTime::hours () const
-{
- int h, m, s, ms;
- split (h, m, s, ms);
- return h;
-}
-
-int
-MetricTime::minutes () const
-{
- int h, m, s, ms;
- split (h, m, s, ms);
- return m;
-}
-
-int
-MetricTime::seconds () const
-{
- int h, m, s, ms;
- split (h, m, s, ms);
- return s;
-}
-
-int
-MetricTime::milliseconds () const
-{
- int h, m, s, ms;
- split (h, m, s, ms);
- return ms;
-}
-
-void
-MetricTime::add (MetricTime t)
-{
- _ms += t._ms;
-}
-
-void
-MetricTime::scale (float f)
-{
- _ms *= f;
-}
-
-bool
-sub::operator== (MetricTime const & a, MetricTime const & b)
-{
- return a._ms == b._ms;
-}
-
-bool
-sub::operator> (MetricTime const & a, MetricTime const & b)
-{
- return a._ms > b._ms;
-}
-
-bool
-sub::operator< (MetricTime const & a, MetricTime const & b)
-{
- return a._ms < b._ms;
-}
-
-ostream&
-sub::operator<< (ostream& st, MetricTime const & t)
-{
- int h, m, s, ms;
- t.split (h, m, s, ms);
- st << h << ":" << m << ":" << s << ":" << ms;
- return st;
-}
diff --git a/src/metric_time.h b/src/metric_time.h
deleted file mode 100644
index 404464e..0000000
--- a/src/metric_time.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-#ifndef LIBSUB_METRIC_TIME_H
-#define LIBSUB_METRIC_TIME_H
-
-#include <stdint.h>
-#include <iostream>
-
-namespace sub {
-
-/** @class MetricTime
- * @brief A time stored in milliseconds.
- */
-class MetricTime
-{
-public:
- MetricTime ()
- : _ms (0)
- {}
-
- MetricTime (int h, int m, int s, int ms);
-
- int hours () const;
- int minutes () const;
- int seconds () const;
- int milliseconds () const;
-
- double all_as_seconds () const {
- return all_as_milliseconds() / 1000.0;
- }
-
- int64_t all_as_milliseconds () const {
- return _ms;
- }
-
- void add (MetricTime t);
- void scale (float f);
-
-private:
- void split (int& h, int& m, int& s, int& ms) const;
-
- friend bool operator== (MetricTime const & a, MetricTime const & b);
- friend bool operator> (MetricTime const & a, MetricTime const & b);
- friend bool operator< (MetricTime const & a, MetricTime const & b);
- friend std::ostream& operator<< (std::ostream&, MetricTime const & t);
-
- int64_t _ms;
-};
-
-bool operator== (MetricTime const & a, MetricTime const & b);
-bool operator> (MetricTime const & a, MetricTime const & b);
-bool operator< (MetricTime const & a, MetricTime const & b);
-std::ostream& operator<< (std::ostream&, MetricTime const & t);
-
-}
-
-#endif
diff --git a/src/raw_convert.h b/src/raw_convert.h
new file mode 100644
index 0000000..6b9b68f
--- /dev/null
+++ b/src/raw_convert.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBSUB_RAW_CONVERT_H
+#define LIBSUB_RAW_CONVERT_H
+
+#include <sstream>
+#include <iomanip>
+
+namespace sub {
+
+/** A sort-of version of boost::lexical_cast that does uses the "C"
+ * locale (i.e. no thousands separators and a . for the decimal separator).
+ */
+template <typename P, typename Q>
+P
+raw_convert (Q v, int precision = 16)
+{
+ std::stringstream s;
+ s.imbue (std::locale::classic ());
+ s << std::setprecision (precision);
+ s << v;
+ P r;
+ s >> r;
+ return r;
+}
+
+};
+
+#endif
diff --git a/src/raw_subtitle.cc b/src/raw_subtitle.cc
index 24f1a8e..818bb15 100644
--- a/src/raw_subtitle.cc
+++ b/src/raw_subtitle.cc
@@ -24,13 +24,5 @@ using namespace sub;
bool
sub::operator< (RawSubtitle const & a, RawSubtitle const & b)
{
- if (a.from.frame() && b.from.frame()) {
- return a.from.frame().get() < b.from.frame().get();
- }
-
- if (a.from.metric() && b.from.metric()) {
- return a.from.metric().get() < b.from.metric().get();
- }
-
- assert (false);
+ return a.from < b.from;
}
diff --git a/src/raw_subtitle.h b/src/raw_subtitle.h
index 8a8ac7f..a27329a 100644
--- a/src/raw_subtitle.h
+++ b/src/raw_subtitle.h
@@ -20,12 +20,10 @@
#ifndef LIBSUB_RAW_SUBTITLE_H
#define LIBSUB_RAW_SUBTITLE_H
-#include "frame_time.h"
-#include "metric_time.h"
+#include "sub_time.h"
#include "colour.h"
#include "vertical_reference.h"
#include "effect.h"
-#include "time_pair.h"
#include "font_size.h"
#include "vertical_position.h"
#include <boost/optional.hpp>
@@ -49,7 +47,7 @@ public:
/** Subtitle text in UTF-8 */
std::string text;
- std::string font;
+ boost::optional<std::string> font;
/** font size */
FontSize font_size;
@@ -66,12 +64,12 @@ public:
VerticalPosition vertical_position;
/** from time */
- TimePair from;
+ Time from;
/** to time */
- TimePair to;
+ Time to;
- boost::optional<MetricTime> fade_up;
- boost::optional<MetricTime> fade_down;
+ boost::optional<Time> fade_up;
+ boost::optional<Time> fade_down;
};
bool operator< (RawSubtitle const &, RawSubtitle const &);
diff --git a/src/reader_factory.cc b/src/reader_factory.cc
index 18783da..05634c0 100644
--- a/src/reader_factory.cc
+++ b/src/reader_factory.cc
@@ -17,12 +17,14 @@
*/
-#include <fstream>
-#include <boost/algorithm/string.hpp>
#include "reader_factory.h"
-#include "dcp_reader.h"
+#include "interop_dcp_reader.h"
+#include "smpte_dcp_reader.h"
#include "stl_binary_reader.h"
#include "stl_text_reader.h"
+#include <libxml++/libxml++.h>
+#include <boost/algorithm/string.hpp>
+#include <fstream>
using std::string;
using std::ifstream;
@@ -35,14 +37,24 @@ sub::reader_factory (boost::filesystem::path file_name)
{
string ext = file_name.extension().string();
transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
-
- if (ext == ".xml") {
- return shared_ptr<Reader> (new DCPReader (file_name, true));
- }
- if (ext == ".mxf") {
- return shared_ptr<Reader> (new DCPReader (file_name, false));
- }
+ if (ext == ".xml") {
+ /* XXX: unfortunate API weakness in libcxml; we can't find out what a
+ file's root node name is.
+ */
+ xmlpp::DomParser parser (file_name.string ());
+ string const root = parser.get_document()->get_root_node()->get_name();
+ if (root == "DCSubtitle") {
+ return shared_ptr<Reader> (new InteropDCPReader (file_name));
+ } else if (root == "SubtitleReel") {
+ return shared_ptr<Reader> (new SMPTEDCPReader (file_name, false));
+ }
+ }
+
+ if (ext == ".mxf") {
+ /* Assume this is some MXF-wrapped SMPTE subtitles */
+ return shared_ptr<Reader> (new SMPTEDCPReader (file_name, true));
+ }
if (ext == ".stl") {
/* Check the start of the DFC */
diff --git a/src/smpte_dcp_reader.cc b/src/smpte_dcp_reader.cc
new file mode 100644
index 0000000..6014196
--- /dev/null
+++ b/src/smpte_dcp_reader.cc
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "smpte_dcp_reader.h"
+#include "exceptions.h"
+#include "xml.h"
+#include "AS_DCP.h"
+#include "KM_util.h"
+#include "dcp/font.h"
+#include "dcp/smpte_load_font.h"
+#include <libcxml/cxml.h>
+#include <boost/foreach.hpp>
+
+using std::string;
+using std::list;
+using std::stringstream;
+using boost::shared_ptr;
+using namespace sub;
+
+SMPTEDCPReader::SMPTEDCPReader (boost::filesystem::path file, bool mxf)
+{
+ shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel"));
+
+ if (mxf) {
+ ASDCP::TimedText::MXFReader reader;
+ Kumu::Result_t r = reader.OpenRead (file.string().c_str ());
+ if (ASDCP_FAILURE (r)) {
+ boost::throw_exception (MXFError ("could not open MXF file for reading"));
+ }
+
+ string s;
+ reader.ReadTimedTextResource (s, 0, 0);
+ stringstream t;
+ t << s;
+ xml->read_stream (t);
+
+ ASDCP::WriterInfo info;
+ reader.FillWriterInfo (info);
+
+ char buffer[64];
+ Kumu::bin2UUIDhex (info.AssetUUID, ASDCP::UUIDlen, buffer, sizeof (buffer));
+ _id = buffer;
+ } else {
+ xml->read_file (file);
+ _id = xml->string_child("Id").substr (9);
+ }
+
+ _load_font_nodes = type_children<dcp::SMPTELoadFont> (xml, "LoadFont");
+
+ parse_common (xml, xml->number_child<int> ("TimeCodeRate"));
+}
diff --git a/src/smpte_dcp_reader.h b/src/smpte_dcp_reader.h
new file mode 100644
index 0000000..e3257d3
--- /dev/null
+++ b/src/smpte_dcp_reader.h
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "dcp_reader.h"
+
+namespace sub {
+
+namespace dcp {
+ class SMPTELoadFont;
+}
+
+class SMPTEDCPReader : public DCPReader
+{
+public:
+ SMPTEDCPReader (boost::filesystem::path file, bool mxf);
+
+private:
+ std::list<boost::shared_ptr<dcp::SMPTELoadFont> > _load_font_nodes;
+};
+
+}
diff --git a/src/stl_binary_reader.cc b/src/stl_binary_reader.cc
index 2246a01..59cb92b 100644
--- a/src/stl_binary_reader.cc
+++ b/src/stl_binary_reader.cc
@@ -95,8 +95,8 @@ STLBinaryReader::STLBinaryReader (istream& in)
for (size_t i = 0; i < lines.size(); ++i) {
RawSubtitle sub;
- sub.from.set_frame (get_timecode (5));
- sub.to.set_frame (get_timecode (9));
+ sub.from = get_timecode (5);
+ sub.to = get_timecode (9);
sub.vertical_position.line = get_int (13, 1) + i;
sub.vertical_position.lines = maximum_rows;
sub.vertical_position.reference = TOP_OF_SCREEN;
@@ -174,10 +174,10 @@ STLBinaryReader::get_int (int offset, int length) const
return v;
}
-FrameTime
+Time
STLBinaryReader::get_timecode (int offset) const
{
- return FrameTime (_buffer[offset], _buffer[offset + 1], _buffer[offset + 2], _buffer[offset + 3]);
+ return Time::from_hmsf (_buffer[offset], _buffer[offset + 1], _buffer[offset + 2], _buffer[offset + 3], Rational (frame_rate, 1));
}
map<string, string>
diff --git a/src/stl_binary_reader.h b/src/stl_binary_reader.h
index a637e91..240b9e7 100644
--- a/src/stl_binary_reader.h
+++ b/src/stl_binary_reader.h
@@ -70,7 +70,7 @@ public:
private:
std::string get_string (int, int) const;
int get_int (int, int) const;
- FrameTime get_timecode (int) const;
+ Time get_timecode (int) const;
STLBinaryTables _tables;
unsigned char* _buffer;
diff --git a/src/stl_binary_writer.cc b/src/stl_binary_writer.cc
index b9ad86f..43b0adf 100644
--- a/src/stl_binary_writer.cc
+++ b/src/stl_binary_writer.cc
@@ -209,15 +209,15 @@ sub::write_stl_binary (
/* Cumulative status */
put_int_as_int (buffer + 4, tables.cumulative_status_enum_to_file (CUMULATIVE_STATUS_NOT_CUMULATIVE), 1);
/* Time code in */
- put_int_as_int (buffer + 5, i->from.frame(frames_per_second).hours (), 1);
- put_int_as_int (buffer + 6, i->from.frame(frames_per_second).minutes (), 1);
- put_int_as_int (buffer + 7, i->from.frame(frames_per_second).seconds (), 1);
- put_int_as_int (buffer + 8, i->from.frame(frames_per_second).frames (), 1);
+ put_int_as_int (buffer + 5, i->from.hours (), 1);
+ put_int_as_int (buffer + 6, i->from.minutes (), 1);
+ put_int_as_int (buffer + 7, i->from.seconds (), 1);
+ put_int_as_int (buffer + 8, i->from.frames_at (sub::Rational (frames_per_second * 1000, 1000)), 1);
/* Time code out */
- put_int_as_int (buffer + 9, i->to.frame(frames_per_second).hours (), 1);
- put_int_as_int (buffer + 10, i->to.frame(frames_per_second).minutes (), 1);
- put_int_as_int (buffer + 11, i->to.frame(frames_per_second).seconds (), 1);
- put_int_as_int (buffer + 12, i->to.frame(frames_per_second).frames (), 1);
+ put_int_as_int (buffer + 9, i->to.hours (), 1);
+ put_int_as_int (buffer + 10, i->to.minutes (), 1);
+ put_int_as_int (buffer + 11, i->to.seconds (), 1);
+ put_int_as_int (buffer + 12, i->to.frames_at (sub::Rational (frames_per_second * 1000, 1000)), 1);
/* Vertical position */
int vp = 0;
if (j->vertical_position.proportional) {
diff --git a/src/stl_text_reader.cc b/src/stl_text_reader.cc
index b86a656..ad155c9 100644
--- a/src/stl_text_reader.cc
+++ b/src/stl_text_reader.cc
@@ -88,16 +88,16 @@ STLTextReader::STLTextReader (istream& in)
string to_string = line.substr (divider[0] + 1, divider[1] - divider[0] - 1);
trim (to_string);
- optional<FrameTime> from = time (from_string);
- optional<FrameTime> to = time (to_string);
+ optional<Time> from = time (from_string);
+ optional<Time> to = time (to_string);
if (!from || !to) {
warn (String::compose ("Unrecognised line %1", line));
continue;
}
- _subtitle.from.set_frame (from.get ());
- _subtitle.to.set_frame (to.get ());
+ _subtitle.from = from.get ();
+ _subtitle.to = to.get ();
/* Parse ^B/^I/^U */
string text = line.substr (divider[1] + 1);
@@ -131,17 +131,17 @@ STLTextReader::STLTextReader (istream& in)
}
}
-optional<FrameTime>
+optional<Time>
STLTextReader::time (string t) const
{
vector<string> b;
split (b, t, is_any_of (":"));
if (b.size() != 4) {
warn (String::compose ("Unrecognised time %1", t));
- return optional<FrameTime> ();
+ return optional<Time> ();
}
- return FrameTime (lexical_cast<int> (b[0]), lexical_cast<int> (b[1]), lexical_cast<int> (b[2]), lexical_cast<int> (b[3]));
+ return sub::Time::from_hmsf (lexical_cast<int> (b[0]), lexical_cast<int> (b[1]), lexical_cast<int> (b[2]), lexical_cast<int> (b[3]));
}
void
diff --git a/src/stl_text_reader.h b/src/stl_text_reader.h
index 77e9b73..5309040 100644
--- a/src/stl_text_reader.h
+++ b/src/stl_text_reader.h
@@ -36,7 +36,7 @@ public:
private:
void set (std::string name, std::string value);
void maybe_push ();
- boost::optional<FrameTime> time (std::string t) const;
+ boost::optional<Time> time (std::string t) const;
RawSubtitle _subtitle;
};
diff --git a/src/sub_time.cc b/src/sub_time.cc
new file mode 100644
index 0000000..e304310
--- /dev/null
+++ b/src/sub_time.cc
@@ -0,0 +1,154 @@
+/*
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "sub_time.h"
+#include "exceptions.h"
+#include <cmath>
+#include <iomanip>
+
+using std::ostream;
+using std::cout;
+using std::setw;
+using std::setfill;
+using boost::optional;
+using namespace sub;
+
+bool
+sub::operator< (sub::Time const & a, sub::Time const & b)
+{
+ if (a._seconds != b._seconds) {
+ return a._seconds < b._seconds;
+ }
+
+ if (!a._rate && !b._rate) {
+ /* Can compare if neither has a specified frame rate */
+ return a._frames < b._frames;
+ }
+
+ if ((a._rate && !b._rate) || (!a._rate && b._rate)) {
+ throw UnknownFrameRateError ();
+ }
+
+ return (a._frames * a._rate.get().numerator * b._rate.get().denominator) < (b._frames * b._rate.get().numerator * a._rate.get().denominator);
+}
+
+bool
+sub::operator> (sub::Time const & a, sub::Time const & b)
+{
+ if (a._seconds != b._seconds) {
+ return a._seconds > b._seconds;
+ }
+
+ if (!a._rate && !b._rate) {
+ /* Can compare if neither has a specified frame rate */
+ return a._frames > b._frames;
+ }
+
+ if ((a._rate && !b._rate) || (!a._rate && b._rate)) {
+ throw UnknownFrameRateError ();
+ }
+
+ return (a._frames * a._rate.get().numerator * b._rate.get().denominator) > (b._frames * b._rate.get().numerator * a._rate.get().denominator);
+}
+
+bool
+sub::operator== (sub::Time const & a, sub::Time const & b)
+{
+ if (!a._rate && !b._rate) {
+ /* Can compare if neither has a specified frame rate */
+ return (a._seconds == b._seconds && a._frames == b._frames);
+ }
+
+ if ((a._rate && !b._rate) || (!a._rate && b._rate)) {
+ throw UnknownFrameRateError ();
+ }
+
+ if (a._seconds != b._seconds) {
+ return false;
+ }
+
+ return (a._frames * a._rate.get().numerator * b._rate.get().denominator) == (b._frames * b._rate.get().numerator * a._rate.get().denominator);
+}
+
+bool
+sub::operator!= (sub::Time const & a, sub::Time const & b)
+{
+ return !(a == b);
+}
+
+ostream&
+sub::operator<< (ostream& s, Time const & t)
+{
+ s << setw (2) << setfill('0') << t.hours() << ":"
+ << setw (2) << setfill('0') << t.minutes() << ":"
+ << setw (2) << setfill('0') << t.seconds() << ":"
+ << t._frames;
+
+ if (t._rate) {
+ s << " @ " << t._rate.get().numerator << "/" << t._rate.get().denominator;
+ }
+
+ return s;
+}
+
+int
+Time::hours () const
+{
+ return _seconds / 3600;
+}
+
+int
+Time::minutes () const
+{
+ return (_seconds - hours() * 3600) / 60;
+}
+
+int
+Time::seconds () const
+{
+ return (_seconds - hours() * 3600 - minutes() * 60);
+}
+
+int
+Time::frames_at (Rational rate) const
+{
+ if (!_rate) {
+ throw UnknownFrameRateError ();
+ }
+
+ return rint (double (_frames) * _rate.get().denominator * rate.numerator / (_rate.get().numerator * rate.denominator));
+}
+
+int
+Time::milliseconds () const
+{
+ return frames_at (Rational (1000, 1));
+}
+
+Time
+Time::from_hmsf (int h, int m, int s, int f, optional<Rational> rate)
+{
+ return Time (h * 3600 + m * 60 + s, f, rate);
+}
+
+Time
+Time::from_hms (int h, int m, int s, int ms)
+{
+ return Time (h * 3600 + m * 60 + s, ms, Rational (1000, 1));
+}
diff --git a/src/sub_time.h b/src/sub_time.h
new file mode 100644
index 0000000..7e1d193
--- /dev/null
+++ b/src/sub_time.h
@@ -0,0 +1,86 @@
+/*
+ Copyright (C) 2014-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#ifndef LIBSUB_SUB_TIME_H
+#define LIBSUB_SUB_TIME_H
+
+#include <boost/optional.hpp>
+
+namespace sub {
+
+class Rational
+{
+public:
+ Rational (int numerator_, int denominator_)
+ : numerator (numerator_)
+ , denominator (denominator_)
+ {}
+
+ int numerator;
+ int denominator;
+
+ double fraction () const {
+ return double (numerator) / denominator;
+ }
+};
+
+class Time
+{
+public:
+ Time ()
+ : _seconds (0)
+ , _frames (0)
+ {}
+
+ int hours () const;
+ int minutes () const;
+ int seconds () const;
+
+ int frames_at (Rational rate) const;
+ int milliseconds () const;
+
+ static Time from_hmsf (int h, int m, int s, int f, boost::optional<Rational> rate = boost::optional<Rational> ());
+ static Time from_hms (int h, int m, int s, int ms);
+
+private:
+ friend bool operator< (Time const & a, Time const & b);
+ friend bool operator> (Time const & a, Time const & b);
+ friend bool operator== (Time const & a, Time const & b);
+ friend std::ostream& operator<< (std::ostream& s, Time const & t);
+
+ Time (int seconds, int frames, boost::optional<Rational> rate)
+ : _seconds (seconds)
+ , _frames (frames)
+ , _rate (rate)
+ {}
+
+ int _seconds;
+ int _frames;
+ boost::optional<Rational> _rate;
+};
+
+bool operator< (Time const & a, Time const & b);
+bool operator> (Time const & a, Time const & b);
+bool operator== (Time const & a, Time const & b);
+bool operator!= (Time const & a, Time const & b);
+std::ostream& operator<< (std::ostream& s, Time const & t);
+
+}
+
+#endif
diff --git a/src/subrip_reader.cc b/src/subrip_reader.cc
index 0aba120..5e9e010 100644
--- a/src/subrip_reader.cc
+++ b/src/subrip_reader.cc
@@ -39,8 +39,8 @@ SubripReader::SubripReader (FILE* f)
char buffer[256];
- TimePair from;
- TimePair to;
+ Time from;
+ Time to;
string line;
int line_number = 0;
@@ -94,7 +94,7 @@ SubripReader::SubripReader (FILE* f)
}
}
-TimePair
+Time
SubripReader::convert_time (string t)
{
vector<string> a;
@@ -106,18 +106,16 @@ SubripReader::convert_time (string t)
vector<string> b;
boost::algorithm::split (b, a[2], boost::is_any_of (","));
- return TimePair (
- MetricTime (
- lexical_cast<int> (a[0]),
- lexical_cast<int> (a[1]),
- lexical_cast<int> (b[0]),
- lexical_cast<int> (b[1])
- )
+ return Time::from_hms (
+ lexical_cast<int> (a[0]),
+ lexical_cast<int> (a[1]),
+ lexical_cast<int> (b[0]),
+ lexical_cast<int> (b[1])
);
}
void
-SubripReader::convert_line (string t, int line_number, TimePair from, TimePair to)
+SubripReader::convert_line (string t, int line_number, Time from, Time to)
{
enum {
TEXT,
diff --git a/src/subrip_reader.h b/src/subrip_reader.h
index 9b9ff92..a010f4a 100644
--- a/src/subrip_reader.h
+++ b/src/subrip_reader.h
@@ -21,7 +21,6 @@
#define LIBSUB_SUBRIP_READER_H
#include "reader.h"
-#include "time_pair.h"
struct subrip_reader_convert_line_test;
struct subrip_reader_convert_time_test;
@@ -39,8 +38,8 @@ private:
friend struct ::subrip_reader_convert_time_test;
SubripReader () {}
- static TimePair convert_time (std::string t);
- void convert_line (std::string t, int line_number, TimePair from, TimePair to);
+ static Time convert_time (std::string t);
+ void convert_line (std::string t, int line_number, Time from, Time to);
void maybe_content (RawSubtitle& p);
};
diff --git a/src/subtitle.h b/src/subtitle.h
index 6afdc51..3906314 100644
--- a/src/subtitle.h
+++ b/src/subtitle.h
@@ -20,12 +20,9 @@
#ifndef LIBSUB_SUBTITLE_H
#define LIBSUB_SUBTITLE_H
-#include "frame_time.h"
-#include "metric_time.h"
#include "colour.h"
#include "vertical_reference.h"
#include "effect.h"
-#include "time_pair.h"
#include "font_size.h"
#include "vertical_position.h"
#include "raw_subtitle.h"
@@ -55,7 +52,7 @@ public:
/** Subtitle text in UTF-8 */
std::string text;
- std::string font;
+ boost::optional<std::string> font;
/** font size */
FontSize font_size;
@@ -106,12 +103,12 @@ public:
Subtitle (RawSubtitle s);
/** from time */
- TimePair from;
+ Time from;
/** to time */
- TimePair to;
+ Time to;
- boost::optional<MetricTime> fade_up;
- boost::optional<MetricTime> fade_down;
+ boost::optional<Time> fade_up;
+ boost::optional<Time> fade_down;
std::list<Line> lines;
diff --git a/src/time_pair.cc b/src/time_pair.cc
deleted file mode 100644
index b625aca..0000000
--- a/src/time_pair.cc
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/time_pair.cc
- * @brief TimePair class.
- */
-
-#include "time_pair.h"
-#include <cmath>
-
-using std::ostream;
-using namespace sub;
-
-FrameTime
-TimePair::frame (float fps) const
-{
- if (_frame) {
- return _frame.get ();
- }
-
- MetricTime const m = _metric.get ();
- return FrameTime (m.hours(), m.minutes(), m.seconds(), rint (m.milliseconds() * fps / 1000));
-}
-
-MetricTime
-TimePair::metric (float fps) const
-{
- if (_metric) {
- return _metric.get ();
- }
-
- FrameTime const f = _frame.get ();
- return MetricTime (f.hours(), f.minutes(), f.seconds(), rint (f.frames() * 1000 / fps));
-}
-
-void
-TimePair::add (FrameTime t, float fps)
-{
- if (_frame) {
- _frame.get().add (t, fps);
- } else {
- _metric.get().add (MetricTime (t.hours(), t.minutes(), t.seconds(), rint (t.frames() * 1000 / fps)));
- }
-}
-
-void
-TimePair::scale (float f, float fps)
-{
- if (_frame) {
- _frame.get().scale (f, fps);
- } else {
- _metric.get().scale (f);
- }
-}
-
-bool
-TimePair::operator== (TimePair const & other) const
-{
- if (_metric && other._metric) {
- return _metric.get() == other._metric.get();
- } else if (_frame && other._frame) {
- return _frame.get() == other._frame.get();
- }
-
- return false;
-}
-
-ostream &
-sub::operator<< (ostream& s, TimePair const & t)
-{
- if (t.frame ()) {
- s << "[FRAME] " << t.frame().get();
- } else {
- s << "[METRIC]" << t.metric().get();
- }
-
- return s;
-}
diff --git a/src/time_pair.h b/src/time_pair.h
deleted file mode 100644
index 6265480..0000000
--- a/src/time_pair.h
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- Copyright (C) 2014 Carl Hetherington <cth@carlh.net>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
-
-*/
-
-/** @file src/time_pair.h
- * @brief TimePair class.
- */
-
-#ifndef LIBSUB_TIME_PAIR_H
-#define LIBSUB_TIME_PAIR_H
-
-#include "frame_time.h"
-#include "metric_time.h"
-#include <boost/optional.hpp>
-
-namespace sub {
-
-/** @class TimePair
- * @brief A time, expressed either in metric (h:m:s:ms) or frames (h:m:s:f).
- */
-class TimePair
-{
-public:
- TimePair () {}
-
- TimePair (FrameTime t)
- : _frame (t)
- {}
-
- TimePair (MetricTime t)
- : _metric (t)
- {}
-
- void set_frame (FrameTime t) {
- _frame = t;
- _metric = boost::optional<MetricTime> ();
- }
-
- void set_metric (MetricTime t) {
- _metric = t;
- _frame = boost::optional<FrameTime> ();
- }
-
- boost::optional<FrameTime> frame () const {
- return _frame;
- }
-
- boost::optional<MetricTime> metric () const {
- return _metric;
- }
-
- FrameTime frame (float fps) const;
- MetricTime metric (float fps) const;
-
- void add (FrameTime t, float fps);
- void scale (float f, float fps);
-
- bool operator== (TimePair const & other) const;
-
-private:
- boost::optional<FrameTime> _frame;
- boost::optional<MetricTime> _metric;
-};
-
-std::ostream& operator<< (std::ostream & s, TimePair const &);
-
-}
-
-#endif
diff --git a/src/util.cc b/src/util.cc
new file mode 100644
index 0000000..5f4cd39
--- /dev/null
+++ b/src/util.cc
@@ -0,0 +1,37 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include "util.h"
+
+using std::string;
+
+/** @param s A string.
+ * @return true if the string contains only space, newline or tab characters, or is empty.
+ */
+bool
+sub::empty_or_white_space (string s)
+{
+ for (size_t i = 0; i < s.length(); ++i) {
+ if (s[i] != ' ' && s[i] != '\n' && s[i] != '\t') {
+ return false;
+ }
+ }
+
+ return true;
+}
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..e77d62e
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,28 @@
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <string>
+
+namespace sub {
+
+extern bool empty_or_white_space (std::string s);
+
+}
+
+
diff --git a/src/wscript b/src/wscript
index a1b9a11..a3f69dd 100644
--- a/src/wscript
+++ b/src/wscript
@@ -9,29 +9,36 @@ def build(bld):
obj.name = 'libsub%s' % bld.env.API_VERSION
obj.target = 'sub%s' % bld.env.API_VERSION
obj.uselib = 'CXML DCP BOOST_FILESYSTEM BOOST_LOCALE'
+ obj.use = 'libkumu-libsub libasdcp-libsub'
obj.export_includes = ['.']
obj.source = """
colour.cc
dcp_reader.cc
effect.cc
font_size.cc
- frame_time.cc
+ interop_dcp_reader.cc
iso6937.cc
iso6937_tables.cc
- metric_time.cc
raw_subtitle.cc
reader.cc
reader_factory.cc
+ smpte_dcp_reader.cc
stl_binary_reader.cc
stl_binary_tables.cc
stl_binary_writer.cc
stl_text_reader.cc
stl_util.cc
- time_pair.cc
+ sub_time.cc
subrip_reader.cc
subtitle.cc
+ util.cc
vertical_reference.cc
vertical_position.cc
+ dcp/font.cc
+ dcp/interop_load_font.cc
+ dcp/smpte_load_font.cc
+ dcp/subtitle.cc
+ dcp/text.cc
"""
headers = """
@@ -40,8 +47,6 @@ def build(bld):
dcp_reader.h
effect.h
font_size.h
- frame_time.h
- metric_time.h
raw_subtitle.h
reader.h
stl_binary_tables.h
@@ -49,7 +54,6 @@ def build(bld):
stl_text_reader.h
subrip_reader.h
subtitle.h
- time_pair.h
vertical_position.h
vertical_reference.h
"""