diff options
| author | Carl Hetherington <cth@carlh.net> | 2015-01-14 17:39:32 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2015-01-20 11:20:25 +0000 |
| commit | 3f630fb8334238ab8a58fbe1a0f513ae2c00de80 (patch) | |
| tree | 4b773b91029d6374bfd4f2194053d3e249d597cd /src | |
| parent | 49cafda01b3e07c47e3b20dd5ee91e1426446aea (diff) | |
Simplify time representation; better in-tree DCP subtitle parser.
Diffstat (limited to 'src')
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 <Text> 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 """ |
