diff options
| author | Carl Hetherington <cth@carlh.net> | 2015-01-12 17:20:54 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2015-01-12 17:20:54 +0000 |
| commit | fd1d21f713e9b0abd8afebcbab36d70aac9b6cce (patch) | |
| tree | c6c8a1907a64550fb2e6b6d7f4757c0a4d85fb17 /src | |
| parent | 94399f35aea4e37c699918f06eddfa931e4aaefc (diff) | |
Un-compiling work-in-progress.
Diffstat (limited to 'src')
| -rw-r--r-- | src/dcp_reader.cc | 134 | ||||
| -rw-r--r-- | src/dcp_reader.h | 43 | ||||
| -rw-r--r-- | src/exceptions.h | 8 | ||||
| -rw-r--r-- | src/frame_time.cc | 55 | ||||
| -rw-r--r-- | src/frame_time.h | 22 | ||||
| -rw-r--r-- | src/raw_subtitle.cc | 8 | ||||
| -rw-r--r-- | src/raw_subtitle.h | 2 | ||||
| -rw-r--r-- | src/reader_factory.cc | 26 | ||||
| -rw-r--r-- | src/subtitle.cc | 2 | ||||
| -rw-r--r-- | src/time_pair.cc | 45 | ||||
| -rw-r--r-- | src/time_pair.h | 14 | ||||
| -rw-r--r-- | src/wscript | 10 |
12 files changed, 240 insertions, 129 deletions
diff --git a/src/dcp_reader.cc b/src/dcp_reader.cc index 73a2276..4145542 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,72 +20,100 @@ #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 <libdcp/subtitle_asset.h> using std::list; using std::cout; +using std::string; using boost::shared_ptr; using namespace sub; -static MetricTime -dcp_to_metric (libdcp::Time t) +void +DCPReader::parse_common (shared_ptr<cxml::Document> xml, list<shared_ptr<dcp::Font> > font_nodes) { - return MetricTime (t.h, t.m, t.s, t.e * 1000 / t.tcr); -} + _reel_number = xml->string_child ("ReelNumber"); + _language = xml->string_child ("Language"); -static Colour -dcp_to_colour (libdcp::Color c) -{ - return Colour (float (c.r) / 255, float (c.g) / 255, float (c.b) / 255); + ParseState parse_state; + examine_font_nodes (xml, font_nodes, parse_state); } -/** @class DCPReader - * @brief A class to read DCP subtitles. - */ -DCPReader::DCPReader (boost::filesystem::path file) +void +DCPReader::examine_font_nodes ( + shared_ptr<const cxml::Node> xml, + list<shared_ptr<dcp::Font> > const & font_nodes, + ParseState& parse_state + ) { - libdcp::SubtitleAsset asset (file.parent_path().string(), file.leaf().string()); - list<shared_ptr<libdcp::Subtitle> > subs = asset.subtitles (); - for (list<shared_ptr<libdcp::Subtitle> >::const_iterator i = subs.begin(); i != subs.end(); ++i) { - RawSubtitle sub; - - sub.vertical_position.proportional = float ((*i)->v_position ()) / 100; - switch ((*i)->v_align ()) { - case libdcp::TOP: - sub.vertical_position.reference = TOP_OF_SCREEN; - break; - case libdcp::CENTER: - sub.vertical_position.reference = CENTRE_OF_SCREEN; - break; - case libdcp::BOTTOM: - sub.vertical_position.reference = BOTTOM_OF_SCREEN; - break; + for (list<shared_ptr<dcp::Font> >::const_iterator i = font_nodes.begin(); i != font_nodes.end(); ++i) { + + parse_state.font_nodes.push_back (*i); + maybe_add_subtitle ((*i)->text, parse_state); + + for (list<shared_ptr<dcp::Subtitle> >::iterator j = (*i)->subtitle_nodes.begin(); j != (*i)->subtitle_nodes.end(); ++j) { + parse_state.subtitle_nodes.push_back (*j); + examine_text_nodes (xml, (*j)->text_nodes, parse_state); + examine_font_nodes (xml, (*j)->font_nodes, parse_state); + parse_state.subtitle_nodes.pop_back (); } - - 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 ()); + + examine_font_nodes (xml, (*i)->font_nodes, parse_state); + examine_text_nodes (xml, (*i)->text_nodes, parse_state); - sub.text = (*i)->text (); - sub.font = (*i)->font (); - sub.font_size.set_proportional (float ((*i)->size ()) / (72 * 11)); - switch ((*i)->effect ()) { - case libdcp::NONE: - break; - case libdcp::BORDER: - sub.effect = BORDER; - break; - case libdcp::SHADOW: - sub.effect = SHADOW; - break; - } + parse_state.font_nodes.pop_back (); + } +} - sub.effect_colour = dcp_to_colour ((*i)->effect_color ()); - sub.colour = dcp_to_colour ((*i)->color ()); - sub.italic = (*i)->italic (); - - _subs.push_back (sub); +void +DCPReader::examine_text_nodes ( + shared_ptr<const cxml::Node> xml, + list<shared_ptr<dcp::Text> > const & text_nodes, + ParseState& parse_state + ) +{ + for (list<shared_ptr<dcp::Text> >::const_iterator i = text_nodes.begin(); i != text_nodes.end(); ++i) { + parse_state.text_nodes.push_back (*i); + maybe_add_subtitle ((*i)->text, parse_state); + examine_font_nodes (xml, (*i)->font_nodes, parse_state); + parse_state.text_nodes.pop_back (); } } + +void +DCPReader::maybe_add_subtitle (string text, ParseState const & parse_state) +{ + if (empty_or_white_space (text)) { + return; + } + + if (parse_state.text_nodes.empty() || parse_state.subtitle_nodes.empty ()) { + return; + } + + 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 ()); + + RawSubtitle rs; + + rs.text = text; + rs.font = effective_font.id.get (); + 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.from = effective_subtitle.in; + rs.to = effective_subtitle.out; + rs.fade_up = effective_subtitle.fade_up_time.metric (timecode_rate ()); + rs.fade_down = effective_subtitle.fade_down_time.metric (timecode_rate ()); + + _subs.push_back (rs); +} diff --git a/src/dcp_reader.h b/src/dcp_reader.h index 6d4fa71..5f65a94 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 @@ -26,17 +26,54 @@ namespace cxml { class Node; + class Document; } 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); +protected: + + virtual int timecode_rate () const = 0; + + 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 (boost::shared_ptr<cxml::Document> xml, std::list<boost::shared_ptr<dcp::Font> > font_nodes); + + void maybe_add_subtitle (std::string text, ParseState const & parse_state); + + void examine_font_nodes ( + boost::shared_ptr<const cxml::Node> xml, + std::list<boost::shared_ptr<dcp::Font> > const & font_nodes, + ParseState& parse_state + ); + + void examine_text_nodes ( + boost::shared_ptr<const cxml::Node> xml, + std::list<boost::shared_ptr<dcp::Text> > const & text_nodes, + ParseState& parse_state + ); + +protected: + std::string _id; + +private: + std::string _reel_number; + std::string _language; }; } diff --git a/src/exceptions.h b/src/exceptions.h index a0ca1b9..fa0b607 100644 --- a/src/exceptions.h +++ b/src/exceptions.h @@ -75,6 +75,14 @@ public: {} }; +class MXFError : public MessageError +{ +public: + MXFError (std::string const & message) + : MessageError (message) + {} +}; + } #endif diff --git a/src/frame_time.cc b/src/frame_time.cc index 65996c0..c39f4a2 100644 --- a/src/frame_time.cc +++ b/src/frame_time.cc @@ -20,18 +20,13 @@ #include "frame_time.h" #include "compose.hpp" #include <iostream> +#include <cmath> 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()) { @@ -46,7 +41,7 @@ sub::operator< (FrameTime const & a, FrameTime const & b) return a.seconds() < b.seconds(); } - return a.frames() < b.frames(); + return (a.frames() * b.frame_rate()) < (b.frames() * a.frame_rate ()); } ostream& @@ -68,46 +63,56 @@ FrameTime::FrameTime (int64_t f, float fps) } void -FrameTime::set_from_frames (int64_t f, float fps) +FrameTime::set_from_frames (int64_t f, float r) { - _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); + _frame_rate = r; + + _hours = f / (60 * 60 * _frame_rate); + f -= _hours * 60 * 60 * _frame_rate; + _minutes = f / (60 * _frame_rate); + f -= _minutes * 60 * _frame_rate; + _seconds = f / _frame_rate; + f -= _seconds * _frame_rate; + _frames = rint (f); } void -FrameTime::add (FrameTime t, float fps) +FrameTime::addX (FrameTime t, float frame_rate_epsilon) { - _frames += t.frames (); - if (_frames > fps) { - _frames -= fps; + /* Make sure we have a common frame rate */ + + if (fabs (_frame_rate - t._frame_rate) > frame_rate_epsilon) { + _frames *= t._frame_rate; + t._frames *= _frame_rate; + _frame_rate *= t._frame_rate; + } + + _frames += t._frames; + if (_frames > _frame_rate) { + _frames -= _frame_rate; _seconds++; } - _seconds += t.seconds (); + _seconds += t._seconds; if (_seconds >= 60) { _seconds -= 60; ++_minutes; } - _minutes += t.minutes (); + _minutes += t._minutes; if (_minutes >= 60) { _minutes -= 60; ++_hours; } - _hours += t.hours (); + _hours += t._hours; } void -FrameTime::scale (float f, float frames_per_second) +FrameTime::scale (float f) { set_from_frames ( - (((_hours * 3600 + _minutes * 60 + _seconds) * frames_per_second) + _frames) * f, - frames_per_second + (((_hours * 3600 + _minutes * 60 + _seconds) * _frame_rate) + _frames) * f, + _frame_rate ); } diff --git a/src/frame_time.h b/src/frame_time.h index ed5fc81..61b76ad 100644 --- a/src/frame_time.h +++ b/src/frame_time.h @@ -26,7 +26,7 @@ namespace sub { /** @class FrameTime - * @brief A time stored in hours, minutes, seconds and frames. + * @brief A time stored in hours, minutes, seconds and frames along with the frame rate. */ class FrameTime { @@ -36,18 +36,20 @@ public: , _minutes (0) , _seconds (0) , _frames (0) + , _frame_rate (24) {} /** @param f Number of frames. - * @param fps Frames per second. + * @param r Frame rate. */ - FrameTime (int64_t f, float fps); + FrameTime (int64_t f, float r); - FrameTime (int h, int m, int s, int f) + FrameTime (int h, int m, int s, int f, float r) : _hours (h) , _minutes (m) , _seconds (s) , _frames (f) + , _frame_rate (r) {} int hours () const { @@ -66,21 +68,25 @@ public: return _frames; } + float frame_rate () const { + return _frame_rate; + } + std::string timecode () const; - void add (FrameTime t, float fps); - void scale (float f, float fps); + void addX (FrameTime t, float frame_rate_epsilon); + void scale (float f); private: - void set_from_frames (int64_t f, float fps); + void set_from_frames (int64_t f, float r); int _hours; int _minutes; int _seconds; int _frames; + float _frame_rate; }; -bool operator== (FrameTime const & a, FrameTime const & b); bool operator< (FrameTime const & a, FrameTime const & b); std::ostream& operator<< (std::ostream&, FrameTime const & t); diff --git a/src/raw_subtitle.cc b/src/raw_subtitle.cc index 24f1a8e..f80d6e7 100644 --- a/src/raw_subtitle.cc +++ b/src/raw_subtitle.cc @@ -24,12 +24,12 @@ 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.optional_frame() && b.from.optional_frame()) { + return a.from.optional_frame().get() < b.from.optional_frame().get(); } - if (a.from.metric() && b.from.metric()) { - return a.from.metric().get() < b.from.metric().get(); + if (a.from.optional_metric() && b.from.optional_metric()) { + return a.from.optional_metric().get() < b.from.optional_metric().get(); } assert (false); diff --git a/src/raw_subtitle.h b/src/raw_subtitle.h index 8a8ac7f..cece161 100644 --- a/src/raw_subtitle.h +++ b/src/raw_subtitle.h @@ -49,6 +49,8 @@ public: /** Subtitle text in UTF-8 */ std::string text; + + /* XXX: this probably needs to be a cleverer type */ std::string font; /** font size */ diff --git a/src/reader_factory.cc b/src/reader_factory.cc index 31a205b..2652763 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,9 +37,23 @@ 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") { + /* 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 == ".xml" || ext == ".mxf") { - return shared_ptr<Reader> (new DCPReader (file_name)); + if (ext == ".mxf") { + /* Assume this is some MXF-wrapped SMPTE subtitles */ + return shared_ptr<Reader> (new SMPTEDCPReader (file_name, true)); } if (ext == ".stl") { diff --git a/src/subtitle.cc b/src/subtitle.cc index d828628..ffa24ad 100644 --- a/src/subtitle.cc +++ b/src/subtitle.cc @@ -33,7 +33,7 @@ Subtitle::Subtitle (RawSubtitle s) bool Subtitle::same_metadata (RawSubtitle s) const { - return from == s.from && to == s.to && fade_up == s.fade_up && fade_down == s.fade_down; + return from.close_to (s.from) && to.close_to (s.to) && fade_up == s.fade_up && fade_down == s.fade_down; } Line::Line (RawSubtitle s) diff --git a/src/time_pair.cc b/src/time_pair.cc index b625aca..1475d78 100644 --- a/src/time_pair.cc +++ b/src/time_pair.cc @@ -35,60 +35,61 @@ TimePair::frame (float fps) const } MetricTime const m = _metric.get (); - return FrameTime (m.hours(), m.minutes(), m.seconds(), rint (m.milliseconds() * fps / 1000)); + return FrameTime (m.hours(), m.minutes(), m.seconds(), rint (m.milliseconds() * fps / 1000), fps); } MetricTime -TimePair::metric (float fps) const +TimePair::metric () const { if (_metric) { return _metric.get (); } FrameTime const f = _frame.get (); - return MetricTime (f.hours(), f.minutes(), f.seconds(), rint (f.frames() * 1000 / fps)); + return MetricTime (f.hours(), f.minutes(), f.seconds(), rint (f.frames() * 1000 / f.frame_rate())); } void -TimePair::add (FrameTime t, float fps) +TimePair::add (FrameTime t) { if (_frame) { - _frame.get().add (t, fps); + _frame.get().addX (t, 1e-6); } else { - _metric.get().add (MetricTime (t.hours(), t.minutes(), t.seconds(), rint (t.frames() * 1000 / fps))); + _metric.get().add (MetricTime (t.hours(), t.minutes(), t.seconds(), rint (t.frames() * 1000 / t.frame_rate()))); } } void -TimePair::scale (float f, float fps) +TimePair::scale (float f) { if (_frame) { - _frame.get().scale (f, fps); + _frame.get().scale (f); } else { _metric.get().scale (f); } } -bool -TimePair::operator== (TimePair const & other) const +ostream & +sub::operator<< (ostream& s, TimePair const & t) { - if (_metric && other._metric) { - return _metric.get() == other._metric.get(); - } else if (_frame && other._frame) { - return _frame.get() == other._frame.get(); + if (t.optional_frame ()) { + s << "[FRAME] " << t.optional_frame().get(); + } else { + s << "[METRIC]" << t.optional_metric().get(); } - return false; + return s; } -ostream & -sub::operator<< (ostream& s, TimePair const & t) +/** @param epsilon Allowable difference in seconds */ +bool +TimePair::close_to (TimePair const & other, float epsilon) const { - if (t.frame ()) { - s << "[FRAME] " << t.frame().get(); - } else { - s << "[METRIC]" << t.metric().get(); + if (_metric && other._metric) { + return fabs (_metric.all_as_seconds(), other._metric.all_as_seconds()) < epsilon; + } else if (_frame && other._frame) { + return fabs (_frame.all_as_seconds(), other._frame.all_as_seconds()) < epsilon; } - return s; + return false; } diff --git a/src/time_pair.h b/src/time_pair.h index 6265480..a39b265 100644 --- a/src/time_pair.h +++ b/src/time_pair.h @@ -56,22 +56,22 @@ public: _frame = boost::optional<FrameTime> (); } - boost::optional<FrameTime> frame () const { + boost::optional<FrameTime> optional_frame () const { return _frame; } - boost::optional<MetricTime> metric () const { + boost::optional<MetricTime> optional_metric () const { return _metric; } FrameTime frame (float fps) const; - MetricTime metric (float fps) const; + MetricTime metric () const; - void add (FrameTime t, float fps); - void scale (float f, float fps); + void add (FrameTime t); + void scale (float f); + + TimePair close_to (TimePair const & other, float epsilon); - bool operator== (TimePair const & other) const; - private: boost::optional<FrameTime> _frame; boost::optional<MetricTime> _metric; diff --git a/src/wscript b/src/wscript index 599e2af..aa09fe6 100644 --- a/src/wscript +++ b/src/wscript @@ -8,13 +8,15 @@ def build(bld): obj.name = 'libsub' obj.target = 'sub' - obj.uselib = 'CXML DCP BOOST_FILESYSTEM BOOST_LOCALE' + obj.uselib = 'CXML DCP BOOST_FILESYSTEM BOOST_LOCALE OPENSSL' + obj.use = 'libkumu-libsub libasdcp-libsub' obj.export_includes = ['.'] obj.source = """ colour.cc dcp_reader.cc effect.cc frame_time.cc + interop_dcp_reader.cc iso6937.cc iso6937_tables.cc metric_time.cc @@ -27,10 +29,16 @@ def build(bld): stl_text_reader.cc stl_util.cc time_pair.cc + smpte_dcp_reader.cc subrip_reader.cc subtitle.cc + util.cc vertical_reference.cc vertical_position.cc + dcp/interop_load_font.cc + dcp/font.cc + dcp/smpte_load_font.cc + dcp/subtitle.cc """ headers = """ |
