summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2015-01-12 17:20:54 +0000
committerCarl Hetherington <cth@carlh.net>2015-01-12 17:20:54 +0000
commitfd1d21f713e9b0abd8afebcbab36d70aac9b6cce (patch)
treec6c8a1907a64550fb2e6b6d7f4757c0a4d85fb17 /src
parent94399f35aea4e37c699918f06eddfa931e4aaefc (diff)
Un-compiling work-in-progress.
Diffstat (limited to 'src')
-rw-r--r--src/dcp_reader.cc134
-rw-r--r--src/dcp_reader.h43
-rw-r--r--src/exceptions.h8
-rw-r--r--src/frame_time.cc55
-rw-r--r--src/frame_time.h22
-rw-r--r--src/raw_subtitle.cc8
-rw-r--r--src/raw_subtitle.h2
-rw-r--r--src/reader_factory.cc26
-rw-r--r--src/subtitle.cc2
-rw-r--r--src/time_pair.cc45
-rw-r--r--src/time_pair.h14
-rw-r--r--src/wscript10
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 = """