From 7ca029dea19ab0aa1b7e96d363fe6de61350d2e7 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 8 Jan 2015 23:22:05 +0000 Subject: [PATCH] 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. --- src/dcp_time.cc | 109 ++++++++++--------- src/dcp_time.h | 43 ++++---- src/parse/subtitle.cc | 65 +++++++---- src/parse/subtitle.h | 8 +- src/subtitle_asset.cc | 41 +++++-- src/subtitle_asset.h | 2 +- test/data/subs3.xml | 19 ++++ test/dcp_time_test.cc | 60 ++++++----- test/subtitle_tests.cc | 237 +++++++++++++++++++++++------------------ 9 files changed, 346 insertions(+), 238 deletions(-) create mode 100644 test/data/subs3.xml 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 + Copyright (C) 2012-2015 Carl Hetherington 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 b; split (b, time, is_any_of (":")); @@ -81,13 +77,13 @@ Time::Time (string time) h = raw_convert (b[0]); m = raw_convert (b[1]); s = raw_convert (b[2]); - t = raw_convert (b[3]); + e = raw_convert (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 + Copyright (C) 2012-2015 Carl Hetherington 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 node) +Font::Font (shared_ptr node, optional tcr) { text = node->content (); @@ -49,9 +49,21 @@ Font::Font (shared_ptr node) if (c) { effect_color = Color (c.get ()); } - subtitle_nodes = type_children (node, "Subtitle"); - font_nodes = type_children (node, "Font"); - text_nodes = type_children (node, "Text"); + + list s = node->node_children ("Subtitle"); + for (list::iterator i = s.begin(); i != s.end(); ++i) { + subtitle_nodes.push_back (shared_ptr (new Subtitle (*i, tcr))); + } + + list f = node->node_children ("Font"); + for (list::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr (new Font (*i, tcr))); + } + + list t = node->node_children ("Text"); + for (list::iterator i = t.begin(); i != t.end(); ++i) { + text_nodes.push_back (shared_ptr (new Text (*i, tcr))); + } } Font::Font (list > const & font_nodes) @@ -93,38 +105,48 @@ LoadFont::LoadFont (shared_ptr node) uri = node->optional_string_attribute ("URI"); } -Subtitle::Subtitle (shared_ptr node) +/** @param tcr A timecode rate, if this subtitle is from a SMPTE file, or empty if it is Interop */ +Subtitle::Subtitle (shared_ptr node, optional tcr) { - in = Time (node->string_attribute ("TimeIn")); - out = Time (node->string_attribute ("TimeOut")); - font_nodes = type_children (node, "Font"); - text_nodes = type_children (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 f = node->node_children ("Font"); + for (list::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr (new Font (*i, tcr))); + } + + list t = node->node_children ("Text"); + for (list::iterator i = t.begin(); i != t.end(); ++i) { + text_nodes.push_back (shared_ptr (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 node, string name) +Subtitle::fade_time (shared_ptr node, string name, optional 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 (u)); + t = Time (0, 0, 0, raw_convert (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 node) +Text::Text (shared_ptr node, optional tcr) : v_align (CENTER) { /* Vertical position */ @@ -144,6 +166,9 @@ Text::Text (shared_ptr node) v_align = string_to_valign (v.get ()); } - font_nodes = type_children (node, "Font"); + list f = node->node_children ("Font"); + for (list::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr (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 node); + Text (boost::shared_ptr node, boost::optional tcr); float v_position; VAlign v_align; @@ -49,7 +49,7 @@ class Subtitle { public: Subtitle () {} - Subtitle (boost::shared_ptr node); + Subtitle (boost::shared_ptr node, boost::optional tcr); Time in; Time out; @@ -59,7 +59,7 @@ public: std::list > text_nodes; private: - Time fade_time (boost::shared_ptr, std::string name); + Time fade_time (boost::shared_ptr, std::string name, boost::optional tcr); }; class Font @@ -69,7 +69,7 @@ public: : size (0) {} - Font (boost::shared_ptr node); + Font (boost::shared_ptr node, boost::optional tcr); Font (std::list > 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 xml (new cxml::Document ("DCSubtitle")); - xml->read_file (xml_file); - read_xml (xml); + try { + shared_ptr xml (new cxml::Document ("DCSubtitle")); + xml->read_file (xml_file); + read_xml (xml, false); + } catch (cxml::Error& e) { + shared_ptr xml (new cxml::Document ("SubtitleReel")); + xml->read_file (xml_file); + read_xml (xml, true); + } } void -SubtitleAsset::read_xml (shared_ptr xml) +SubtitleAsset::read_xml (shared_ptr 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 x = xml->optional_string_child ("SubtitleID"); @@ -116,9 +122,19 @@ SubtitleAsset::read_xml (shared_ptr xml) _reel_number = xml->string_child ("ReelNumber"); _language = xml->string_child ("Language"); + optional tcr; + if (smpte) { + tcr = xml->optional_number_child ("TimeCodeRate"); + } + xml->ignore_child ("LoadFont"); - list > font_nodes = type_children (xml, "Font"); + list > font_nodes; + list f = xml->node_children ("Font"); + for (list::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr (new parse::Font (*i, tcr))); + } + _load_font_nodes = type_children (xml, "LoadFont"); /* Now make Subtitle objects to represent the raw XML nodes @@ -127,8 +143,11 @@ SubtitleAsset::read_xml (shared_ptr xml) shared_ptr subtitle_list = xml->optional_node_child ("SubtitleList"); if (subtitle_list) { - list > font = type_children (subtitle_list, "Font"); - copy (font.begin(), font.end(), back_inserter (font_nodes)); + list > font; + list f = subtitle_list->node_children ("Font"); + for (list::iterator i = f.begin(); i != f.end(); ++i) { + font_nodes.push_back (shared_ptr (new parse::Font (*i, tcr))); + } } ParseState parse_state; @@ -464,8 +483,8 @@ SubtitleAsset::xml_as_string () const subtitle->set_attribute ("SpotNumber", raw_convert (spot_number++)); subtitle->set_attribute ("TimeIn", (*i)->in().to_string()); subtitle->set_attribute ("TimeOut", (*i)->out().to_string()); - subtitle->set_attribute ("FadeUpTime", raw_convert ((*i)->fade_up_time().to_ticks())); - subtitle->set_attribute ("FadeDownTime", raw_convert ((*i)->fade_down_time().to_ticks())); + subtitle->set_attribute ("FadeUpTime", raw_convert ((*i)->fade_up_time().to_editable_units(250))); + subtitle->set_attribute ("FadeDownTime", raw_convert ((*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); + void read_xml (boost::shared_ptr, bool smpte); struct ParseState { std::list > font_nodes; diff --git a/test/data/subs3.xml b/test/data/subs3.xml new file mode 100644 index 00000000..4e331f9a --- /dev/null +++ b/test/data/subs3.xml @@ -0,0 +1,19 @@ + + +urn:uuid:d351291b-a133-88f7-caca-e7ed2ee6cc69 +Test +Test +2014-11-21T18:10:50+01:00 +1 +en +25 1 +25 +00:00:00:00 + + + +Hello world + + + + diff --git a/test/dcp_time_test.cc b/test/dcp_time_test.cc index 52ff43c6..0ebddb29 100644 --- a/test/dcp_time_test.cc +++ b/test/dcp_time_test.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013 Carl Hetherington + Copyright (C) 2013-2015 Carl Hetherington 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 @@ -23,65 +23,69 @@ /** Check that libdcp::Time works */ BOOST_AUTO_TEST_CASE (dcp_time) { - libdcp::Time t (977143, 24); + /* tcr of 250 makes the editable event length the same as an Interop `tick' */ + libdcp::Time t (977143, 24, 250); - BOOST_CHECK_EQUAL (t.t, 73); + BOOST_CHECK_EQUAL (t.e, 73); BOOST_CHECK_EQUAL (t.s, 34); BOOST_CHECK_EQUAL (t.m, 18); BOOST_CHECK_EQUAL (t.h, 11); BOOST_CHECK_EQUAL (t.to_string(), "11:18:34:73"); - BOOST_CHECK_EQUAL (t.to_ticks(), 10178573); - libdcp::Time a (3, 2, 3, 4); - libdcp::Time b (2, 3, 4, 5); + /* Use a tcr of 24 so that the editable event is a frame */ + libdcp::Time a (3, 2, 3, 4, 24); + libdcp::Time b (2, 3, 4, 5, 24); libdcp::Time r = a - b; BOOST_CHECK_EQUAL (r.h, 0); BOOST_CHECK_EQUAL (r.m, 58); BOOST_CHECK_EQUAL (r.s, 58); - BOOST_CHECK_EQUAL (r.t, 249); - BOOST_CHECK_EQUAL (r.to_string(), "0:58:58:249"); - BOOST_CHECK_EQUAL (r.to_ticks(), 884749); + BOOST_CHECK_EQUAL (r.e, 23); + BOOST_CHECK_EQUAL (r.to_string(), "0:58:58:23"); - a = libdcp::Time (1, 58, 56, 240); - b = libdcp::Time (1, 7, 12, 120); + /* Different tcr (25) */ + a = libdcp::Time (1, 58, 56, 2, 25); + b = libdcp::Time (1, 7, 12, 1, 25); r = a + b; BOOST_CHECK_EQUAL (r.h, 3); BOOST_CHECK_EQUAL (r.m, 6); - BOOST_CHECK_EQUAL (r.s, 9); - BOOST_CHECK_EQUAL (r.t, 110); - BOOST_CHECK_EQUAL (r.to_string(), "3:6:9:110"); - BOOST_CHECK_EQUAL (r.to_ticks(), 2792360); + BOOST_CHECK_EQUAL (r.s, 8); + BOOST_CHECK_EQUAL (r.e, 3); + BOOST_CHECK_EQUAL (r.to_string(), "3:6:8:3"); - a = libdcp::Time (24, 12, 6, 3); - b = libdcp::Time (16, 8, 4, 2); + /* Another arbitrary tcr (30) */ + a = libdcp::Time (24, 12, 6, 3, 30); + b = libdcp::Time (16, 8, 4, 2, 30); BOOST_CHECK_CLOSE (a / b, 1.5, 1e-5); - BOOST_CHECK_EQUAL (libdcp::Time (4128391203LL).to_ticks(), 4128391203LL); - BOOST_CHECK_EQUAL (libdcp::Time (60000).to_ticks(), 60000); - - a = libdcp::Time (3600 * 24, 24); + a = libdcp::Time (3600 * 24, 24, 250); BOOST_CHECK_EQUAL (a.h, 1); BOOST_CHECK_EQUAL (a.m, 0); BOOST_CHECK_EQUAL (a.s, 0); - BOOST_CHECK_EQUAL (a.t, 0); + BOOST_CHECK_EQUAL (a.e, 0); - a = libdcp::Time (60 * 24, 24); + a = libdcp::Time (60 * 24, 24, 250); BOOST_CHECK_EQUAL (a.h, 0); BOOST_CHECK_EQUAL (a.m, 1); BOOST_CHECK_EQUAL (a.s, 0); - BOOST_CHECK_EQUAL (a.t, 0); + BOOST_CHECK_EQUAL (a.e, 0); /* Check rounding; 3424 is 142.666666666... seconds or 0.166666666... ticks */ - a = libdcp::Time (3424, 24); + a = libdcp::Time (3424, 24, 250); BOOST_CHECK_EQUAL (a.h, 0); BOOST_CHECK_EQUAL (a.m, 2); BOOST_CHECK_EQUAL (a.s, 22); - BOOST_CHECK_EQUAL (a.t, 167); + BOOST_CHECK_EQUAL (a.e, 167); - a = libdcp::Time (3425, 24); + a = libdcp::Time (3425, 24, 250); BOOST_CHECK_EQUAL (a.h, 0); BOOST_CHECK_EQUAL (a.m, 2); BOOST_CHECK_EQUAL (a.s, 22); - BOOST_CHECK_EQUAL (a.t, 177); + BOOST_CHECK_EQUAL (a.e, 177); + + /* Check addition of times with different tcrs */ + a = libdcp::Time (0, 0, 0, 3, 24); + b = libdcp::Time (0, 0, 0, 4, 48); + r = a + b; + BOOST_CHECK_EQUAL (r, libdcp::Time (0, 0, 0, 240, 1152)); } diff --git a/test/subtitle_tests.cc b/test/subtitle_tests.cc index 0994f329..835ae960 100644 --- a/test/subtitle_tests.cc +++ b/test/subtitle_tests.cc @@ -30,91 +30,91 @@ BOOST_AUTO_TEST_CASE (subtitles1) BOOST_CHECK_EQUAL (subs.language(), "French"); - list > s = subs.subtitles_during (libdcp::Time (0, 0, 6, 1), libdcp::Time (0, 0, 6, 2)); + list > s = subs.subtitles_during (libdcp::Time (0, 0, 6, 1, 250), libdcp::Time (0, 0, 6, 2, 250)); BOOST_CHECK_EQUAL (s.size(), 1); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 39, - libdcp::Time (0, 0, 5, 198), - libdcp::Time (0, 0, 7, 115), + libdcp::Time (0, 0, 5, 198, 250), + libdcp::Time (0, 0, 7, 115, 250), 15, libdcp::BOTTOM, "My jacket was Idi Amin's", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 1), - libdcp::Time (0, 0, 0, 1) + libdcp::Time (0, 0, 0, 1, 250), + libdcp::Time (0, 0, 0, 1, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 0, 7, 190), libdcp::Time (0, 0, 7, 191)); + s = subs.subtitles_during (libdcp::Time (0, 0, 7, 190, 250), libdcp::Time (0, 0, 7, 191, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 39, - libdcp::Time (0, 0, 7, 177), - libdcp::Time (0, 0, 11, 31), + libdcp::Time (0, 0, 7, 177, 250), + libdcp::Time (0, 0, 11, 31, 250), 21, libdcp::BOTTOM, "My corset was H.M. The Queen's", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 1), - libdcp::Time (0, 0, 0, 1) + libdcp::Time (0, 0, 0, 1, 250), + libdcp::Time (0, 0, 0, 1, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 39, - libdcp::Time (0, 0, 7, 177), - libdcp::Time (0, 0, 11, 31), + libdcp::Time (0, 0, 7, 177, 250), + libdcp::Time (0, 0, 11, 31, 250), 15, libdcp::BOTTOM, "My large wonderbra", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 1), - libdcp::Time (0, 0, 0, 1) + libdcp::Time (0, 0, 0, 1, 250), + libdcp::Time (0, 0, 0, 1, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 0, 11, 95), libdcp::Time (0, 0, 11, 96)); + s = subs.subtitles_during (libdcp::Time (0, 0, 11, 95, 250), libdcp::Time (0, 0, 11, 96, 250)); BOOST_CHECK_EQUAL (s.size(), 1); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 39, - libdcp::Time (0, 0, 11, 94), - libdcp::Time (0, 0, 13, 63), + libdcp::Time (0, 0, 11, 94, 250), + libdcp::Time (0, 0, 13, 63, 250), 15, libdcp::BOTTOM, "Once belonged to the Shah", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 1), - libdcp::Time (0, 0, 0, 1) + libdcp::Time (0, 0, 0, 1, 250), + libdcp::Time (0, 0, 0, 1, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 0, 14, 42), libdcp::Time (0, 0, 14, 43)); + s = subs.subtitles_during (libdcp::Time (0, 0, 14, 42, 250), libdcp::Time (0, 0, 14, 43, 250)); BOOST_CHECK_EQUAL (s.size(), 1); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 39, - libdcp::Time (0, 0, 13, 104), - libdcp::Time (0, 0, 15, 177), + libdcp::Time (0, 0, 13, 104, 250), + libdcp::Time (0, 0, 15, 177, 250), 15, libdcp::BOTTOM, "And these are Roy Hattersley's jeans", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 1), - libdcp::Time (0, 0, 0, 1) + libdcp::Time (0, 0, 0, 1, 250), + libdcp::Time (0, 0, 0, 1, 250) )); } @@ -123,300 +123,327 @@ BOOST_AUTO_TEST_CASE (subtitles2) { libdcp::SubtitleAsset subs ("test/data", "subs2.xml"); - list > s = subs.subtitles_during (libdcp::Time (0, 0, 42, 100), libdcp::Time (0, 0, 42, 101)); + list > s = subs.subtitles_during (libdcp::Time (0, 0, 42, 100, 250), libdcp::Time (0, 0, 42, 101, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 0, 41, 62), - libdcp::Time (0, 0, 43, 52), + libdcp::Time (0, 0, 41, 62, 250), + libdcp::Time (0, 0, 43, 52, 250), 89, libdcp::TOP, "At afternoon tea with John Peel", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 0, 41, 62), - libdcp::Time (0, 0, 43, 52), + libdcp::Time (0, 0, 41, 62, 250), + libdcp::Time (0, 0, 43, 52, 250), 95, libdcp::TOP, "I enquired if his accent was real", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 0, 50, 50), libdcp::Time (0, 0, 50, 51)); + s = subs.subtitles_during (libdcp::Time (0, 0, 50, 50, 250), libdcp::Time (0, 0, 50, 51, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 0, 50, 42), - libdcp::Time (0, 0, 52, 21), + libdcp::Time (0, 0, 50, 42, 250), + libdcp::Time (0, 0, 52, 21, 250), 89, libdcp::TOP, "He said \"out of the house", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 0, 50, 42), - libdcp::Time (0, 0, 52, 21), + libdcp::Time (0, 0, 50, 42, 250), + libdcp::Time (0, 0, 52, 21, 250), 95, libdcp::TOP, "I'm incredibly scouse", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 1, 2, 300), libdcp::Time (0, 1, 2, 301)); + s = subs.subtitles_during (libdcp::Time (0, 1, 2, 300, 250), libdcp::Time (0, 1, 2, 301, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 2, 208), - libdcp::Time (0, 1, 4, 10), + libdcp::Time (0, 1, 2, 208, 250), + libdcp::Time (0, 1, 4, 10, 250), 89, libdcp::TOP, "At home it depends how I feel.\"", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 2, 208), - libdcp::Time (0, 1, 4, 10), + libdcp::Time (0, 1, 2, 208, 250), + libdcp::Time (0, 1, 4, 10, 250), 95, libdcp::TOP, "I spent a long weekend in Brighton", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 1, 15, 50), libdcp::Time (0, 1, 15, 51)); + s = subs.subtitles_during (libdcp::Time (0, 1, 15, 50, 250), libdcp::Time (0, 1, 15, 51, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 15, 42), - libdcp::Time (0, 1, 16, 42), + libdcp::Time (0, 1, 15, 42, 250), + libdcp::Time (0, 1, 16, 42, 250), 89, libdcp::TOP, "With the legendary Miss Enid Blyton", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 15, 42), - libdcp::Time (0, 1, 16, 42), + libdcp::Time (0, 1, 15, 42, 250), + libdcp::Time (0, 1, 16, 42, 250), 95, libdcp::TOP, "She said \"you be Noddy", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 1, 27, 200), libdcp::Time (0, 1, 27, 201)); + s = subs.subtitles_during (libdcp::Time (0, 1, 27, 200, 250), libdcp::Time (0, 1, 27, 201, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 27, 115), - libdcp::Time (0, 1, 28, 208), + libdcp::Time (0, 1, 27, 115, 250), + libdcp::Time (0, 1, 28, 208, 250), 89, libdcp::TOP, "That curious creature the Sphinx", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 27, 115), - libdcp::Time (0, 1, 28, 208), + libdcp::Time (0, 1, 27, 115, 250), + libdcp::Time (0, 1, 28, 208, 250), 95, libdcp::TOP, "Is smarter than anyone thinks", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 1, 42, 300), libdcp::Time (0, 1, 42, 301)); + s = subs.subtitles_during (libdcp::Time (0, 1, 42, 300, 250), libdcp::Time (0, 1, 42, 301, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 42, 229), - libdcp::Time (0, 1, 45, 62), + libdcp::Time (0, 1, 42, 229, 250), + libdcp::Time (0, 1, 45, 62, 250), 89, libdcp::TOP, "It sits there and smirks", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 42, 229), - libdcp::Time (0, 1, 45, 62), + libdcp::Time (0, 1, 42, 229, 250), + libdcp::Time (0, 1, 45, 62, 250), 95, libdcp::TOP, "And you don't think it works", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 1, 45, 200), libdcp::Time (0, 1, 45, 201)); + s = subs.subtitles_during (libdcp::Time (0, 1, 45, 200, 250), libdcp::Time (0, 1, 45, 201, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 45, 146), - libdcp::Time (0, 1, 47, 94), + libdcp::Time (0, 1, 45, 146, 250), + libdcp::Time (0, 1, 47, 94, 250), 89, libdcp::TOP, "Then when you're not looking, it winks.", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 45, 146), - libdcp::Time (0, 1, 47, 94), + libdcp::Time (0, 1, 45, 146, 250), + libdcp::Time (0, 1, 47, 94, 250), 95, libdcp::TOP, "When it snows you will find Sister Sledge", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 1, 47, 249), libdcp::Time (0, 1, 47, 250)); + s = subs.subtitles_during (libdcp::Time (0, 1, 47, 249, 250), libdcp::Time (0, 1, 47, 250, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 47, 146), - libdcp::Time (0, 1, 48, 167), + libdcp::Time (0, 1, 47, 146, 250), + libdcp::Time (0, 1, 48, 167, 250), 89, libdcp::TOP, "Out mooning, at night, on the ledge", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", false, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 1, 47, 146), - libdcp::Time (0, 1, 48, 167), + libdcp::Time (0, 1, 47, 146, 250), + libdcp::Time (0, 1, 48, 167, 250), 95, libdcp::TOP, "One storey down", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); - s = subs.subtitles_during (libdcp::Time (0, 2, 6, 210), libdcp::Time (0, 2, 6, 211)); + s = subs.subtitles_during (libdcp::Time (0, 2, 6, 210, 250), libdcp::Time (0, 2, 6, 211, 250)); BOOST_CHECK_EQUAL (s.size(), 2); BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 2, 5, 208), - libdcp::Time (0, 2, 7, 31), + libdcp::Time (0, 2, 5, 208, 250), + libdcp::Time (0, 2, 7, 31, 250), 89, libdcp::TOP, "HELLO", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); BOOST_CHECK_EQUAL (*(s.back().get()), libdcp::Subtitle ( "Arial", true, libdcp::Color (255, 255, 255), 42, - libdcp::Time (0, 2, 5, 208), - libdcp::Time (0, 2, 7, 31), + libdcp::Time (0, 2, 5, 208, 250), + libdcp::Time (0, 2, 7, 31, 250), 95, libdcp::TOP, "WORLD", libdcp::BORDER, libdcp::Color (0, 0, 0), - libdcp::Time (0, 0, 0, 0), - libdcp::Time (0, 0, 0, 0) + libdcp::Time (0, 0, 0, 0, 250), + libdcp::Time (0, 0, 0, 0, 250) )); } + +/* A very simple SMPTE one */ +BOOST_AUTO_TEST_CASE (subtitles3) +{ + libdcp::SubtitleAsset subs ("test/data", "subs3.xml"); + + list > s = subs.subtitles_during (libdcp::Time (0, 0, 0, 0, 25), libdcp::Time (0, 0, 7, 0, 25)); + + BOOST_REQUIRE_EQUAL (s.size(), 1); + BOOST_CHECK_EQUAL (*(s.front().get()), libdcp::Subtitle ( + "", + false, + libdcp::Color (255, 255, 255), + 42, + libdcp::Time (0, 0, 4, 21, 25), + libdcp::Time (0, 0, 6, 5, 25), + 8, + libdcp::BOTTOM, + "Hello world", + libdcp::BORDER, + libdcp::Color (0, 0, 0), + libdcp::Time (0, 0, 0, 0, 25), + libdcp::Time (0, 0, 0, 0, 25) + )); + + +} -- 2.30.2