diff options
| author | Carl Hetherington <cth@carlh.net> | 2015-01-08 23:22:05 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2015-01-08 23:22:05 +0000 |
| commit | 7ca029dea19ab0aa1b7e96d363fe6de61350d2e7 (patch) | |
| tree | d520d2f6597fa4ccbe2a78e3aea4d1d2614d9373 /src | |
| parent | bbb3db16cc7e6f2262a89da4bec9fc356d6c3c12 (diff) | |
Change libdcp::Time to allow sub-second units to be anything, so that
we can support SMPTE subtitles which use TimeCodeRate as the base rather
than the arbitrary "ticks" (4ms) of Interop.
Diffstat (limited to 'src')
| -rw-r--r-- | src/dcp_time.cc | 109 | ||||
| -rw-r--r-- | src/dcp_time.h | 43 | ||||
| -rw-r--r-- | src/parse/subtitle.cc | 65 | ||||
| -rw-r--r-- | src/parse/subtitle.h | 8 | ||||
| -rw-r--r-- | src/subtitle_asset.cc | 41 | ||||
| -rw-r--r-- | src/subtitle_asset.h | 2 |
6 files changed, 163 insertions, 105 deletions
diff --git a/src/dcp_time.cc b/src/dcp_time.cc index 428215cb..1f3ff06d 100644 --- a/src/dcp_time.cc +++ b/src/dcp_time.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012 Carl Hetherington <cth@carlh.net> + 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 @@ -33,31 +33,26 @@ using namespace std; using namespace boost; using namespace libdcp; -Time::Time (int frame, int frames_per_second) +Time::Time (int frame, int frames_per_second, int tcr_) : h (0) , m (0) , s (0) - , t (0) + , e (0) + , tcr (tcr_) { - set (double (frame) / frames_per_second); -} - -Time::Time (int64_t ticks) -{ - h = ticks / (60 * 60 * 250); - ticks -= int64_t (h) * 60 * 60 * 250; - m = ticks / (60 * 250); - ticks -= int64_t (m) * 60 * 250; - s = ticks / 250; - ticks -= int64_t (s) * 250; - t = ticks; + set (double (frame) / frames_per_second, tcr); } +/** @param s_ Seconds. + * @param tcr Timecode rate. + */ void -Time::set (double ss) +Time::set (double seconds, int tcr_) { - s = floor (ss); - t = int (round (1000 * (ss - s) / 4)); + s = floor (seconds); + tcr = tcr_; + + e = int (round ((seconds - s) * tcr)); if (s >= 60) { m = s / 60; @@ -70,7 +65,8 @@ Time::set (double ss) } } -Time::Time (string time) +Time::Time (string time, int tcr_) + : tcr (tcr_) { vector<string> b; split (b, time, is_any_of (":")); @@ -81,13 +77,13 @@ Time::Time (string time) h = raw_convert<int> (b[0]); m = raw_convert<int> (b[1]); s = raw_convert<int> (b[2]); - t = raw_convert<int> (b[3]); + e = raw_convert<int> (b[3]); } bool libdcp::operator== (Time const & a, Time const & b) { - return (a.h == b.h && a.m == b.m && a.s == b.s && a.t == b.t); + return (a.h == b.h && a.m == b.m && a.s == b.s && (a.e * b.tcr) == (b.e * a.tcr)); } bool @@ -117,8 +113,8 @@ libdcp::operator< (Time const & a, Time const & b) return a.s < b.s; } - if (a.t != b.t) { - return a.t < b.t; + if ((a.e * b.tcr) != (b.e * a.tcr)) { + return (a.e * b.tcr) < (b.e * a.tcr); } return true; @@ -139,8 +135,8 @@ libdcp::operator> (Time const & a, Time const & b) return a.s > b.s; } - if (a.t != b.t) { - return a.t > b.t; + if ((a.e * b.tcr) != (b.e * a.tcr)) { + return (a.e * b.tcr) > (b.e * a.tcr); } return true; @@ -152,21 +148,23 @@ libdcp::operator>= (Time const & a, Time const & b) return a == b || a > b; } -ostream & -libdcp::operator<< (ostream& s, Time const & t) -{ - s << t.h << ":" << t.m << ":" << t.s << "." << t.t; - return s; -} - libdcp::Time -libdcp::operator+ (Time a, Time const & b) +libdcp::operator+ (Time a, Time b) { Time r; - r.t = a.t + b.t; - if (r.t >= 250) { - r.t -= 250; + /* Make sure we have a common tcr */ + if (a.tcr != b.tcr) { + a.e *= b.tcr; + b.e *= a.tcr; + r.tcr = a.tcr * b.tcr; + } else { + r.tcr = a.tcr; + } + + r.e = a.e + b.e; + if (r.e >= r.tcr) { + r.e -= r.tcr; r.s++; } @@ -188,13 +186,22 @@ libdcp::operator+ (Time a, Time const & b) } libdcp::Time -libdcp::operator- (Time a, Time const & b) +libdcp::operator- (Time a, Time b) { Time r; - r.t = a.t - b.t; - if (r.t < 0) { - r.t += 250; + /* Make sure we have a common tcr */ + if (a.tcr != b.tcr) { + a.e *= b.tcr; + b.e *= a.tcr; + r.tcr = a.tcr * b.tcr; + } else { + r.tcr = a.tcr; + } + + r.e = a.e - b.e; + if (r.e < 0) { + r.e += a.tcr; r.s--; } @@ -218,24 +225,30 @@ libdcp::operator- (Time a, Time const & b) float libdcp::operator/ (Time a, Time const & b) { - int64_t const at = a.h * 3600 * 250 + a.m * 60 * 250 + a.s * 250 + a.t; - int64_t const bt = b.h * 3600 * 250 + b.m * 60 * 250 + b.s * 250 + b.t; - return float (at) / bt; + float const as = a.h * 3600 * 250 + a.m * 60 * 250 + a.s * float (a.e) / a.tcr; + float const bs = b.h * 3600 * 250 + b.m * 60 * 250 + b.s * float (b.e) / b.tcr; + return as / bs; } -/** @return A string of the form h:m:s:t */ +/** @return A string of the form h:m:s:e */ string Time::to_string () const { stringstream str; - str << h << ":" << m << ":" << s << ":" << t; + str << h << ":" << m << ":" << s << ":" << e; return str.str (); } -/** @return This time in ticks */ int64_t -Time::to_ticks () const +Time::to_editable_units (int tcr_) const +{ + return (int64_t(e) * float (tcr_ / tcr)) + int64_t(s) * tcr_ + int64_t(m) * 60 * tcr_ + int64_t(h) * 60 * 60 * tcr_; +} + +ostream & +libdcp::operator<< (ostream& s, Time const & t) { - return int64_t(t) + int64_t(s) * 250 + int64_t(m) * 60 * 250 + int64_t(h) * 60 * 60 * 250; + s << t.to_string (); + return s; } diff --git a/src/dcp_time.h b/src/dcp_time.h index a8e7c90c..58b8a75d 100644 --- a/src/dcp_time.h +++ b/src/dcp_time.h @@ -37,40 +37,41 @@ namespace libdcp { class Time { public: - Time () : h (0), m (0), s (0), t (0) {} + Time () : h (0), m (0), s (0), e (0) {} - Time (int64_t ticks); - - /** Construct a Time from a frame index (starting from 0) - * and a frames per second count. + /** Construct a Time from a frame index (starting from 0), + * a frames per second count and a timecode rate. */ - Time (int frame, int frames_per_second); + Time (int frame, int frames_per_second, int tcr); - /** Construct a Time from hours, minutes, seconds and ticks. + /** Construct a Time from hours, minutes, seconds, editable units and timecode rate. * @param h_ Hours. * @param m_ Minutes. * @param s_ Seconds. - * @param t_ Ticks (where 1 tick is 4 milliseconds). + * @param e_ Editable units + * @param tcr_ Timecode rate */ - Time (int h_, int m_, int s_, int t_) + Time (int h_, int m_, int s_, int e_, int tcr_) : h (h_) , m (m_) , s (s_) - , t (t_) + , e (e_) + , tcr (tcr_) {} - - Time (std::string time); - - int h; ///< hours - int m; ///< minutes - int s; ///< seconds - int t; ///< `ticks', where 1 tick is 4 milliseconds + + Time (std::string time, int tcr_); + + int h; ///< hours + int m; ///< minutes + int s; ///< seconds + int e; ///< `editable units', where 1 editable unit is 1/tcr seconds + int tcr; ///< time code rate; the number of editable units per second std::string to_string () const; - int64_t to_ticks () const; + int64_t to_editable_units (int tcr_) const; private: - void set (double); + void set (double seconds, int tcr); }; extern bool operator== (Time const & a, Time const & b); @@ -80,8 +81,8 @@ extern bool operator< (Time const & a, Time const & b); extern bool operator> (Time const & a, Time const & b); extern bool operator>= (Time const & a, Time const & b); extern std::ostream & operator<< (std::ostream & s, Time const & t); -extern Time operator+ (Time a, Time const & b); -extern Time operator- (Time a, Time const & b); +extern Time operator+ (Time a, Time b); +extern Time operator- (Time a, Time b); extern float operator/ (Time a, Time const & b); } diff --git a/src/parse/subtitle.cc b/src/parse/subtitle.cc index 831fef59..56222c97 100644 --- a/src/parse/subtitle.cc +++ b/src/parse/subtitle.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net> + 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 @@ -30,7 +30,7 @@ using boost::optional; using namespace libdcp; using namespace libdcp::parse; -Font::Font (shared_ptr<const cxml::Node> node) +Font::Font (shared_ptr<const cxml::Node> node, optional<int> tcr) { text = node->content (); @@ -49,9 +49,21 @@ Font::Font (shared_ptr<const cxml::Node> node) if (c) { effect_color = Color (c.get ()); } - subtitle_nodes = type_children<Subtitle> (node, "Subtitle"); - font_nodes = type_children<Font> (node, "Font"); - text_nodes = type_children<Text> (node, "Text"); + + list<cxml::NodePtr> s = node->node_children ("Subtitle"); + for (list<cxml::NodePtr>::iterator i = s.begin(); i != s.end(); ++i) { + subtitle_nodes.push_back (shared_ptr<Subtitle> (new Subtitle (*i, tcr))); + } + + list<cxml::NodePtr> f = node->node_children ("Font"); + for (list<cxml::NodePtr>::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr<Font> (new Font (*i, tcr))); + } + + list<cxml::NodePtr> t = node->node_children ("Text"); + for (list<cxml::NodePtr>::iterator i = t.begin(); i != t.end(); ++i) { + text_nodes.push_back (shared_ptr<Text> (new Text (*i, tcr))); + } } Font::Font (list<shared_ptr<Font> > const & font_nodes) @@ -93,38 +105,48 @@ LoadFont::LoadFont (shared_ptr<const cxml::Node> node) uri = node->optional_string_attribute ("URI"); } -Subtitle::Subtitle (shared_ptr<const cxml::Node> node) +/** @param tcr A timecode rate, if this subtitle is from a SMPTE file, or empty if it is Interop */ +Subtitle::Subtitle (shared_ptr<const cxml::Node> node, optional<int> tcr) { - in = Time (node->string_attribute ("TimeIn")); - out = Time (node->string_attribute ("TimeOut")); - font_nodes = type_children<Font> (node, "Font"); - text_nodes = type_children<Text> (node, "Text"); - fade_up_time = fade_time (node, "FadeUpTime"); - fade_down_time = fade_time (node, "FadeDownTime"); + in = Time (node->string_attribute ("TimeIn"), tcr.get_value_or (250)); + out = Time (node->string_attribute ("TimeOut"), tcr.get_value_or (250)); + + list<cxml::NodePtr> f = node->node_children ("Font"); + for (list<cxml::NodePtr>::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr<Font> (new Font (*i, tcr))); + } + + list<cxml::NodePtr> t = node->node_children ("Text"); + for (list<cxml::NodePtr>::iterator i = t.begin(); i != t.end(); ++i) { + text_nodes.push_back (shared_ptr<Text> (new Text (*i, tcr))); + } + + fade_up_time = fade_time (node, "FadeUpTime", tcr); + fade_down_time = fade_time (node, "FadeDownTime", tcr); } Time -Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name) +Subtitle::fade_time (shared_ptr<const cxml::Node> node, string name, optional<int> tcr) { string const u = node->optional_string_attribute (name).get_value_or (""); Time t; if (u.empty ()) { - t = Time (0, 0, 0, 20); + t = Time (0, 0, 0, 20, 250); } else if (u.find (":") != string::npos) { - t = Time (u); + t = Time (u, tcr.get_value_or(250)); } else { - t = Time (0, 0, 0, raw_convert<int> (u)); + t = Time (0, 0, 0, raw_convert<int> (u), tcr.get_value_or(250)); } - if (t > Time (0, 0, 8, 0)) { - t = Time (0, 0, 8, 0); + if (t > Time (0, 0, 8, 0, 250)) { + t = Time (0, 0, 8, 0, 250); } return t; } -Text::Text (shared_ptr<const cxml::Node> node) +Text::Text (shared_ptr<const cxml::Node> node, optional<int> tcr) : v_align (CENTER) { /* Vertical position */ @@ -144,6 +166,9 @@ Text::Text (shared_ptr<const cxml::Node> node) v_align = string_to_valign (v.get ()); } - font_nodes = type_children<Font> (node, "Font"); + list<cxml::NodePtr> f = node->node_children ("Font"); + for (list<cxml::NodePtr>::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr<Font> (new Font (*i, tcr))); + } } diff --git a/src/parse/subtitle.h b/src/parse/subtitle.h index 3d99d9bc..867b3f0e 100644 --- a/src/parse/subtitle.h +++ b/src/parse/subtitle.h @@ -37,7 +37,7 @@ public: , v_align (TOP) {} - Text (boost::shared_ptr<const cxml::Node> node); + Text (boost::shared_ptr<const cxml::Node> node, boost::optional<int> tcr); float v_position; VAlign v_align; @@ -49,7 +49,7 @@ class Subtitle { public: Subtitle () {} - Subtitle (boost::shared_ptr<const cxml::Node> node); + Subtitle (boost::shared_ptr<const cxml::Node> node, boost::optional<int> tcr); Time in; Time out; @@ -59,7 +59,7 @@ public: std::list<boost::shared_ptr<Text> > text_nodes; private: - Time fade_time (boost::shared_ptr<const cxml::Node>, std::string name); + Time fade_time (boost::shared_ptr<const cxml::Node>, std::string name, boost::optional<int> tcr); }; class Font @@ -69,7 +69,7 @@ public: : size (0) {} - Font (boost::shared_ptr<const cxml::Node> node); + Font (boost::shared_ptr<const cxml::Node> node, boost::optional<int> tcr); Font (std::list<boost::shared_ptr<Font> > const & font_nodes); std::string text; diff --git a/src/subtitle_asset.cc b/src/subtitle_asset.cc index acd40e9b..cc95d1a5 100644 --- a/src/subtitle_asset.cc +++ b/src/subtitle_asset.cc @@ -88,21 +88,27 @@ SubtitleAsset::read_mxf (string mxf_file) stringstream t; t << s; xml->read_stream (t); - read_xml (xml); + read_xml (xml, true); } void SubtitleAsset::read_xml (string xml_file) { - shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle")); - xml->read_file (xml_file); - read_xml (xml); + try { + shared_ptr<cxml::Document> xml (new cxml::Document ("DCSubtitle")); + xml->read_file (xml_file); + read_xml (xml, false); + } catch (cxml::Error& e) { + shared_ptr<cxml::Document> xml (new cxml::Document ("SubtitleReel")); + xml->read_file (xml_file); + read_xml (xml, true); + } } void -SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml) +SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml, bool smpte) { - /* XXX: hacks aplenty in here; need separate parsers for DCSubtitle and SubtitleReel */ + /* XXX: hacks aplenty in here; need separate parsers for DCSubtitle (Interop) and SubtitleReel (SMPTE) */ /* DCSubtitle */ optional<string> x = xml->optional_string_child ("SubtitleID"); @@ -116,9 +122,19 @@ SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml) _reel_number = xml->string_child ("ReelNumber"); _language = xml->string_child ("Language"); + optional<int> tcr; + if (smpte) { + tcr = xml->optional_number_child<int> ("TimeCodeRate"); + } + xml->ignore_child ("LoadFont"); - list<shared_ptr<libdcp::parse::Font> > font_nodes = type_children<libdcp::parse::Font> (xml, "Font"); + list<shared_ptr<parse::Font> > font_nodes; + list<cxml::NodePtr> f = xml->node_children ("Font"); + for (list<cxml::NodePtr>::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr<parse::Font> (new parse::Font (*i, tcr))); + } + _load_font_nodes = type_children<libdcp::parse::LoadFont> (xml, "LoadFont"); /* Now make Subtitle objects to represent the raw XML nodes @@ -127,8 +143,11 @@ SubtitleAsset::read_xml (shared_ptr<cxml::Document> xml) shared_ptr<cxml::Node> subtitle_list = xml->optional_node_child ("SubtitleList"); if (subtitle_list) { - list<shared_ptr<libdcp::parse::Font> > font = type_children<libdcp::parse::Font> (subtitle_list, "Font"); - copy (font.begin(), font.end(), back_inserter (font_nodes)); + list<shared_ptr<parse::Font> > font; + list<cxml::NodePtr> f = subtitle_list->node_children ("Font"); + for (list<cxml::NodePtr>::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr<parse::Font> (new parse::Font (*i, tcr))); + } } ParseState parse_state; @@ -464,8 +483,8 @@ SubtitleAsset::xml_as_string () const subtitle->set_attribute ("SpotNumber", raw_convert<string> (spot_number++)); subtitle->set_attribute ("TimeIn", (*i)->in().to_string()); subtitle->set_attribute ("TimeOut", (*i)->out().to_string()); - subtitle->set_attribute ("FadeUpTime", raw_convert<string> ((*i)->fade_up_time().to_ticks())); - subtitle->set_attribute ("FadeDownTime", raw_convert<string> ((*i)->fade_down_time().to_ticks())); + subtitle->set_attribute ("FadeUpTime", raw_convert<string> ((*i)->fade_up_time().to_editable_units(250))); + subtitle->set_attribute ("FadeDownTime", raw_convert<string> ((*i)->fade_down_time().to_editable_units(250))); last_in = (*i)->in (); last_out = (*i)->out (); diff --git a/src/subtitle_asset.h b/src/subtitle_asset.h index 0fa881a5..d7976bef 100644 --- a/src/subtitle_asset.h +++ b/src/subtitle_asset.h @@ -174,7 +174,7 @@ protected: private: std::string font_id_to_name (std::string id) const; void read_mxf (std::string); - void read_xml (boost::shared_ptr<cxml::Document>); + void read_xml (boost::shared_ptr<cxml::Document>, bool smpte); struct ParseState { std::list<boost::shared_ptr<parse::Font> > font_nodes; |
