summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2018-07-21 15:16:18 +0100
committerCarl Hetherington <cth@carlh.net>2018-07-21 15:16:18 +0100
commitcbd4450197a083bf58bda510e626f73ba583cb66 (patch)
tree2be308772512539570beab36beab02bde72d6d4b /src/lib
parent1013175d5f6adfa0e6a7442e4c9aebb893787748 (diff)
Basics of multiple captions per content so that DCPContent can
hold subs and closed captions.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/active_captions.cc8
-rw-r--r--src/lib/active_captions.h8
-rw-r--r--src/lib/analyse_audio_job.cc2
-rw-r--r--src/lib/caption_content.cc24
-rw-r--r--src/lib/caption_content.h12
-rw-r--r--src/lib/content.cc31
-rw-r--r--src/lib/content.h5
-rw-r--r--src/lib/dcp_content.cc97
-rw-r--r--src/lib/dcp_content.h24
-rw-r--r--src/lib/dcp_decoder.cc42
-rw-r--r--src/lib/dcp_decoder.h4
-rw-r--r--src/lib/dcp_encoder.cc6
-rw-r--r--src/lib/dcp_examiner.cc17
-rw-r--r--src/lib/dcp_examiner.h6
-rw-r--r--src/lib/dcp_subtitle_content.cc14
-rw-r--r--src/lib/dcp_subtitle_decoder.cc4
-rw-r--r--src/lib/decoder.cc21
-rw-r--r--src/lib/decoder.h4
-rw-r--r--src/lib/ffmpeg_content.cc11
-rw-r--r--src/lib/ffmpeg_decoder.cc16
-rw-r--r--src/lib/film.cc43
-rw-r--r--src/lib/hints.cc8
-rw-r--r--src/lib/overlaps.cc2
-rw-r--r--src/lib/overlaps.h2
-rw-r--r--src/lib/player.cc65
-rw-r--r--src/lib/player.h12
-rw-r--r--src/lib/playlist.cc16
-rw-r--r--src/lib/playlist.h2
-rw-r--r--src/lib/text_caption_file_content.cc10
-rw-r--r--src/lib/text_caption_file_decoder.cc4
-rw-r--r--src/lib/types.h10
31 files changed, 361 insertions, 169 deletions
diff --git a/src/lib/active_captions.cc b/src/lib/active_captions.cc
index b84c75a7c..b4252e0c3 100644
--- a/src/lib/active_captions.cc
+++ b/src/lib/active_captions.cc
@@ -41,7 +41,7 @@ ActiveCaptions::get_burnt (DCPTimePeriod period, bool always_burn_captions) cons
for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
- shared_ptr<CaptionContent> caption = i->first.lock ();
+ shared_ptr<const CaptionContent> caption = i->first.lock ();
if (!caption) {
continue;
}
@@ -90,7 +90,7 @@ ActiveCaptions::clear_before (DCPTime time)
* @param from From time for these subtitles.
*/
void
-ActiveCaptions::add_from (weak_ptr<CaptionContent> content, PlayerCaption ps, DCPTime from)
+ActiveCaptions::add_from (weak_ptr<const CaptionContent> content, PlayerCaption ps, DCPTime from)
{
if (_data.find(content) == _data.end()) {
_data[content] = list<Period>();
@@ -104,7 +104,7 @@ ActiveCaptions::add_from (weak_ptr<CaptionContent> content, PlayerCaption ps, DC
* @return Return the corresponding subtitles and their from time.
*/
pair<PlayerCaption, DCPTime>
-ActiveCaptions::add_to (weak_ptr<CaptionContent> content, DCPTime to)
+ActiveCaptions::add_to (weak_ptr<const CaptionContent> content, DCPTime to)
{
DCPOMATIC_ASSERT (_data.find(content) != _data.end());
@@ -121,7 +121,7 @@ ActiveCaptions::add_to (weak_ptr<CaptionContent> content, DCPTime to)
* @return true if we have any active subtitles from this content.
*/
bool
-ActiveCaptions::have (weak_ptr<CaptionContent> content) const
+ActiveCaptions::have (weak_ptr<const CaptionContent> content) const
{
Map::const_iterator i = _data.find(content);
if (i == _data.end()) {
diff --git a/src/lib/active_captions.h b/src/lib/active_captions.h
index 28ddcad52..10b0b5da9 100644
--- a/src/lib/active_captions.h
+++ b/src/lib/active_captions.h
@@ -39,9 +39,9 @@ public:
std::list<PlayerCaption> get_burnt (DCPTimePeriod period, bool always_burn_captions) const;
void clear_before (DCPTime time);
void clear ();
- void add_from (boost::weak_ptr<CaptionContent> content, PlayerCaption ps, DCPTime from);
- std::pair<PlayerCaption, DCPTime> add_to (boost::weak_ptr<CaptionContent> content, DCPTime to);
- bool have (boost::weak_ptr<CaptionContent> content) const;
+ void add_from (boost::weak_ptr<const CaptionContent> content, PlayerCaption ps, DCPTime from);
+ std::pair<PlayerCaption, DCPTime> add_to (boost::weak_ptr<const CaptionContent> content, DCPTime to);
+ bool have (boost::weak_ptr<const CaptionContent> content) const;
private:
class Period
@@ -59,7 +59,7 @@ private:
boost::optional<DCPTime> to;
};
- typedef std::map<boost::weak_ptr<CaptionContent>, std::list<Period> > Map;
+ typedef std::map<boost::weak_ptr<const CaptionContent>, std::list<Period> > Map;
Map _data;
};
diff --git a/src/lib/analyse_audio_job.cc b/src/lib/analyse_audio_job.cc
index db917301a..03497b91e 100644
--- a/src/lib/analyse_audio_job.cc
+++ b/src/lib/analyse_audio_job.cc
@@ -106,7 +106,7 @@ AnalyseAudioJob::run ()
{
shared_ptr<Player> player (new Player (_film, _playlist));
player->set_ignore_video ();
- player->set_ignore_subtitle ();
+ player->set_ignore_caption ();
player->set_fast ();
player->set_play_referenced ();
player->Audio.connect (bind (&AnalyseAudioJob::analyse, this, _1, _2));
diff --git a/src/lib/caption_content.cc b/src/lib/caption_content.cc
index af534980a..d44fb55c5 100644
--- a/src/lib/caption_content.cc
+++ b/src/lib/caption_content.cc
@@ -68,11 +68,15 @@ CaptionContent::CaptionContent (Content* parent)
, _line_spacing (1)
, _outline_width (2)
, _type (CAPTION_OPEN)
+ , _original_type (CAPTION_OPEN)
{
}
-shared_ptr<CaptionContent>
+/** @return CaptionContents from node or <Caption> nodes under node (according to version).
+ * The list could be empty if no CaptionContents are found.
+ */
+list<shared_ptr<CaptionContent> >
CaptionContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
{
if (version < 34) {
@@ -80,7 +84,7 @@ CaptionContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
subtitle streams, so check for that.
*/
if (node->string_child("Type") == "FFmpeg" && node->node_children("SubtitleStream").empty()) {
- return shared_ptr<CaptionContent> ();
+ return list<shared_ptr<CaptionContent> >();
}
/* Otherwise we can drop through to the newer logic */
@@ -88,16 +92,22 @@ CaptionContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version)
if (version < 37) {
if (!node->optional_number_child<double>("SubtitleXOffset") && !node->optional_number_child<double>("SubtitleOffset")) {
- return shared_ptr<CaptionContent> ();
+ return list<shared_ptr<CaptionContent> >();
}
- return shared_ptr<CaptionContent> (new CaptionContent (parent, node, version));
+ list<shared_ptr<CaptionContent> > c;
+ c.push_back (shared_ptr<CaptionContent> (new CaptionContent (parent, node, version)));
+ return c;
}
if (!node->node_child("Caption")) {
- return shared_ptr<CaptionContent> ();
+ return list<shared_ptr<CaptionContent> >();
}
- return shared_ptr<CaptionContent> (new CaptionContent (parent, node->node_child("Caption"), version));
+ list<shared_ptr<CaptionContent> > c;
+ BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Caption")) {
+ c.push_back (shared_ptr<CaptionContent> (new CaptionContent (parent, i, version)));
+ }
+ return c;
}
CaptionContent::CaptionContent (Content* parent, cxml::ConstNodePtr node, int version)
@@ -215,6 +225,7 @@ CaptionContent::CaptionContent (Content* parent, cxml::ConstNodePtr node, int ve
connect_to_fonts ();
_type = string_to_caption_type (node->optional_string_child("Type").get_value_or("open"));
+ _original_type = string_to_caption_type (node->optional_string_child("Type").get_value_or("open"));
}
@@ -270,6 +281,7 @@ CaptionContent::as_xml (xmlpp::Node* root) const
}
caption->add_child("Type")->add_child_text (caption_type_to_string(_type));
+ caption->add_child("OriginalType")->add_child_text (caption_type_to_string(_original_type));
}
string
diff --git a/src/lib/caption_content.h b/src/lib/caption_content.h
index 297289f18..4152dc533 100644
--- a/src/lib/caption_content.h
+++ b/src/lib/caption_content.h
@@ -167,7 +167,12 @@ public:
return _type;
}
- static boost::shared_ptr<CaptionContent> from_xml (Content* parent, cxml::ConstNodePtr, int version);
+ CaptionType original_type () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _original_type;
+ }
+
+ static std::list<boost::shared_ptr<CaptionContent> > from_xml (Content* parent, cxml::ConstNodePtr, int version);
protected:
/** subtitle language (e.g. "German") or empty if it is not known */
@@ -205,7 +210,12 @@ private:
boost::optional<ContentTime> _fade_in;
boost::optional<ContentTime> _fade_out;
int _outline_width;
+ /** what these captions will be used for in the output DCP (not necessarily what
+ * they were originally).
+ */
CaptionType _type;
+ /** the original type of these captions in their content */
+ CaptionType _original_type;
};
#endif
diff --git a/src/lib/content.cc b/src/lib/content.cc
index d232f6e49..13c5794fe 100644
--- a/src/lib/content.cc
+++ b/src/lib/content.cc
@@ -402,7 +402,34 @@ Content::take_settings_from (shared_ptr<const Content> c)
if (audio && c->audio) {
audio->take_settings_from (c->audio);
}
- if (caption && c->caption) {
- caption->take_settings_from (c->caption);
+
+ list<shared_ptr<CaptionContent> >::iterator i = caption.begin ();
+ list<shared_ptr<CaptionContent> >::const_iterator j = c->caption.begin ();
+ while (i != caption.end() && j != c->caption.end()) {
+ (*i)->take_settings_from (*j);
+ ++i;
+ ++j;
+ }
+}
+
+shared_ptr<CaptionContent>
+Content::only_caption () const
+{
+ DCPOMATIC_ASSERT (caption.size() < 2);
+ if (caption.empty ()) {
+ return shared_ptr<CaptionContent> ();
+ }
+ return caption.front ();
+}
+
+shared_ptr<CaptionContent>
+Content::caption_of_original_type (CaptionType type) const
+{
+ BOOST_FOREACH (shared_ptr<CaptionContent> i, caption) {
+ if (i->original_type() == type) {
+ return i;
+ }
}
+
+ return shared_ptr<CaptionContent> ();
}
diff --git a/src/lib/content.h b/src/lib/content.h
index 1e594e136..2a249011a 100644
--- a/src/lib/content.h
+++ b/src/lib/content.h
@@ -181,7 +181,10 @@ public:
boost::shared_ptr<VideoContent> video;
boost::shared_ptr<AudioContent> audio;
- boost::shared_ptr<CaptionContent> caption;
+ std::list<boost::shared_ptr<CaptionContent> > caption;
+
+ boost::shared_ptr<CaptionContent> only_caption () const;
+ boost::shared_ptr<CaptionContent> caption_of_original_type (CaptionType type) const;
void signal_changed (int);
diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc
index e02eb7bd9..e56ad9e21 100644
--- a/src/lib/dcp_content.cc
+++ b/src/lib/dcp_content.cc
@@ -58,9 +58,9 @@ int const DCPContentProperty::NEEDS_ASSETS = 600;
int const DCPContentProperty::NEEDS_KDM = 601;
int const DCPContentProperty::REFERENCE_VIDEO = 602;
int const DCPContentProperty::REFERENCE_AUDIO = 603;
-int const DCPContentProperty::REFERENCE_SUBTITLE = 604;
+int const DCPContentProperty::REFERENCE_CAPTION = 604;
int const DCPContentProperty::NAME = 605;
-int const DCPContentProperty::HAS_SUBTITLES = 606;
+int const DCPContentProperty::CAPTIONS = 606;
DCPContent::DCPContent (shared_ptr<const Film> film, boost::filesystem::path p)
: Content (film)
@@ -69,11 +69,14 @@ DCPContent::DCPContent (shared_ptr<const Film> film, boost::filesystem::path p)
, _kdm_valid (false)
, _reference_video (false)
, _reference_audio (false)
- , _reference_subtitle (false)
, _three_d (false)
{
read_directory (p);
set_default_colour_conversion ();
+
+ for (int i = 0; i < CAPTION_COUNT; ++i) {
+ _reference_caption[i] = false;
+ }
}
DCPContent::DCPContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
@@ -83,6 +86,10 @@ DCPContent::DCPContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, in
audio = AudioContent::from_xml (this, node, version);
caption = CaptionContent::from_xml (this, node, version);
+ for (int i = 0; i < CAPTION_COUNT; ++i) {
+ _reference_caption[i] = false;
+ }
+
if (video && audio) {
audio->set_stream (
AudioStreamPtr (
@@ -107,7 +114,13 @@ DCPContent::DCPContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, in
_kdm_valid = node->bool_child ("KDMValid");
_reference_video = node->optional_bool_child ("ReferenceVideo").get_value_or (false);
_reference_audio = node->optional_bool_child ("ReferenceAudio").get_value_or (false);
- _reference_subtitle = node->optional_bool_child ("ReferenceSubtitle").get_value_or (false);
+ if (version >= 37) {
+ _reference_caption[CAPTION_OPEN] = node->optional_bool_child("ReferenceOpenCaption").get_value_or(false);
+ _reference_caption[CAPTION_CLOSED] = node->optional_bool_child("ReferenceClosedCaption").get_value_or(false);
+ } else {
+ _reference_caption[CAPTION_OPEN] = node->optional_bool_child("ReferenceSubtitle").get_value_or(false);
+ _reference_caption[CAPTION_CLOSED] = false;
+ }
if (node->optional_string_child("Standard")) {
string const s = node->optional_string_child("Standard").get();
if (s == "Interop") {
@@ -143,7 +156,7 @@ DCPContent::examine (shared_ptr<Job> job)
bool const needed_assets = needs_assets ();
bool const needed_kdm = needs_kdm ();
string const old_name = name ();
- bool had_subtitles = static_cast<bool> (caption);
+ int const old_captions = caption.size ();
if (job) {
job->set_progress_unknown ();
@@ -174,16 +187,14 @@ DCPContent::examine (shared_ptr<Job> job)
signal_changed (AudioContentProperty::STREAMS);
}
- bool has_subtitles = false;
+ int captions = 0;
{
boost::mutex::scoped_lock lm (_mutex);
_name = examiner->name ();
- if (examiner->has_subtitles ()) {
- caption.reset (new CaptionContent (this));
- } else {
- caption.reset ();
+ for (int i = 0; i < examiner->captions(); ++i) {
+ caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this)));
}
- has_subtitles = static_cast<bool> (caption);
+ captions = caption.size ();
_encrypted = examiner->encrypted ();
_needs_assets = examiner->needs_assets ();
_kdm_valid = examiner->kdm_valid ();
@@ -193,8 +204,8 @@ DCPContent::examine (shared_ptr<Job> job)
_reel_lengths = examiner->reel_lengths ();
}
- if (had_subtitles != has_subtitles) {
- signal_changed (DCPContentProperty::HAS_SUBTITLES);
+ if (old_captions != captions) {
+ signal_changed (DCPContentProperty::CAPTIONS);
}
if (needed_assets != needs_assets ()) {
@@ -254,8 +265,8 @@ DCPContent::as_xml (xmlpp::Node* node, bool with_paths) const
audio->stream()->mapping().as_xml (node->add_child("AudioMapping"));
}
- if (caption) {
- caption->as_xml (node);
+ BOOST_FOREACH (shared_ptr<CaptionContent> i, caption) {
+ i->as_xml (node);
}
boost::mutex::scoped_lock lm (_mutex);
@@ -268,7 +279,8 @@ DCPContent::as_xml (xmlpp::Node* node, bool with_paths) const
node->add_child("KDMValid")->add_child_text (_kdm_valid ? "1" : "0");
node->add_child("ReferenceVideo")->add_child_text (_reference_video ? "1" : "0");
node->add_child("ReferenceAudio")->add_child_text (_reference_audio ? "1" : "0");
- node->add_child("ReferenceSubtitle")->add_child_text (_reference_subtitle ? "1" : "0");
+ node->add_child("ReferenceOpenCaption")->add_child_text(_reference_caption[CAPTION_OPEN] ? "1" : "0");
+ node->add_child("ReferenceClosedCaption")->add_child_text(_reference_caption[CAPTION_CLOSED] ? "1" : "0");
if (_standard) {
switch (_standard.get ()) {
case dcp::INTEROP:
@@ -309,11 +321,14 @@ DCPContent::identifier () const
s += video->identifier() + "_";
}
- if (caption) {
- s += caption->identifier () + " ";
+ BOOST_FOREACH (shared_ptr<CaptionContent> i, caption) {
+ s += i->identifier () + " ";
}
- s += string (_reference_video ? "1" : "0") + string (_reference_subtitle ? "1" : "0");
+ s += string (_reference_video ? "1" : "0");
+ for (int i = 0; i < CAPTION_COUNT; ++i) {
+ s += string (_reference_caption[i] ? "1" : "0");
+ }
return s;
}
@@ -399,14 +414,14 @@ DCPContent::set_reference_audio (bool r)
}
void
-DCPContent::set_reference_subtitle (bool r)
+DCPContent::set_reference_caption (CaptionType type, bool r)
{
{
boost::mutex::scoped_lock lm (_mutex);
- _reference_subtitle = r;
+ _reference_caption[type] = r;
}
- signal_changed (DCPContentProperty::REFERENCE_SUBTITLE);
+ signal_changed (DCPContentProperty::REFERENCE_CAPTION);
}
list<DCPTimePeriod>
@@ -459,7 +474,7 @@ DCPContent::reel_split_points () const
}
bool
-DCPContent::can_reference (function<shared_ptr<ContentPart> (shared_ptr<const Content>)> part, string overlapping, string& why_not) const
+DCPContent::can_reference (function<bool (shared_ptr<const Content>)> part, string overlapping, string& why_not) const
{
/* We must be using the same standard as the film */
if (_standard) {
@@ -514,6 +529,12 @@ DCPContent::can_reference (function<shared_ptr<ContentPart> (shared_ptr<const Co
return true;
}
+static
+bool check_video (shared_ptr<const Content> c)
+{
+ return static_cast<bool>(c->video);
+}
+
bool
DCPContent::can_reference_video (string& why_not) const
{
@@ -529,7 +550,13 @@ DCPContent::can_reference_video (string& why_not) const
}
/// TRANSLATORS: this string will follow "Cannot reference this DCP: "
- return can_reference (bind (&Content::video, _1), _("it overlaps other video content; remove the other content."), why_not);
+ return can_reference (bind (&check_video, _1), _("it overlaps other video content; remove the other content."), why_not);
+}
+
+static
+bool check_audio (shared_ptr<const Content> c)
+{
+ return static_cast<bool>(c->audio);
}
bool
@@ -558,11 +585,16 @@ DCPContent::can_reference_audio (string& why_not) const
}
/// TRANSLATORS: this string will follow "Cannot reference this DCP: "
- return can_reference (bind (&Content::audio, _1), _("it overlaps other audio content; remove the other content."), why_not);
+ return can_reference (bind (&check_audio, _1), _("it overlaps other audio content; remove the other content."), why_not);
}
+static
+bool check_caption (shared_ptr<const Content> c)
+{
+ return !c->caption.empty();
+}
bool
-DCPContent::can_reference_subtitle (string& why_not) const
+DCPContent::can_reference_caption (CaptionType type, string& why_not) const
{
shared_ptr<DCPDecoder> decoder;
try {
@@ -576,15 +608,20 @@ DCPContent::can_reference_subtitle (string& why_not) const
}
BOOST_FOREACH (shared_ptr<dcp::Reel> i, decoder->reels()) {
- if (!i->main_subtitle()) {
+ if (type == CAPTION_OPEN && !i->main_subtitle()) {
/// TRANSLATORS: this string will follow "Cannot reference this DCP: "
why_not = _("it does not have subtitles in all its reels.");
return false;
}
+ if (type == CAPTION_CLOSED && !i->closed_caption()) {
+ /// TRANSLATORS: this string will follow "Cannot reference this DCP: "
+ why_not = _("it does not have closed captions in all its reels.");
+ return false;
+ }
}
/// TRANSLATORS: this string will follow "Cannot reference this DCP: "
- return can_reference (bind (&Content::caption, _1), _("it overlaps other caption content; remove the other content."), why_not);
+ return can_reference (bind (&check_caption, _1), _("it overlaps other caption content; remove the other content."), why_not);
}
void
@@ -597,7 +634,9 @@ DCPContent::take_settings_from (shared_ptr<const Content> c)
_reference_video = dc->_reference_video;
_reference_audio = dc->_reference_audio;
- _reference_subtitle = dc->_reference_subtitle;
+ for (int i = 0; i < CAPTION_COUNT; ++i) {
+ _reference_caption[i] = dc->_reference_caption[i];
+ }
}
void
diff --git a/src/lib/dcp_content.h b/src/lib/dcp_content.h
index 64642623f..f01790b89 100644
--- a/src/lib/dcp_content.h
+++ b/src/lib/dcp_content.h
@@ -36,9 +36,9 @@ public:
static int const NEEDS_ASSETS;
static int const REFERENCE_VIDEO;
static int const REFERENCE_AUDIO;
- static int const REFERENCE_SUBTITLE;
+ static int const REFERENCE_CAPTION;
static int const NAME;
- static int const HAS_SUBTITLES;
+ static int const CAPTIONS;
};
class ContentPart;
@@ -108,14 +108,17 @@ public:
bool can_reference_audio (std::string &) const;
- void set_reference_subtitle (bool r);
+ void set_reference_caption (CaptionType type, bool r);
- bool reference_subtitle () const {
+ /** @param type Original type of captions in the DCP.
+ * @return true if these captions are to be referenced.
+ */
+ bool reference_caption (CaptionType type) const {
boost::mutex::scoped_lock lm (_mutex);
- return _reference_subtitle;
+ return _reference_caption[type];
}
- bool can_reference_subtitle (std::string &) const;
+ bool can_reference_caption (CaptionType type, std::string &) const;
void set_cpl (std::string id);
@@ -142,7 +145,7 @@ private:
void read_directory (boost::filesystem::path);
std::list<DCPTimePeriod> reels () const;
bool can_reference (
- boost::function <boost::shared_ptr<ContentPart> (boost::shared_ptr<const Content>)>,
+ boost::function <bool (boost::shared_ptr<const Content>)>,
std::string overlapping,
std::string& why_not
) const;
@@ -163,10 +166,11 @@ private:
* rather than by rewrapping.
*/
bool _reference_audio;
- /** true if the subtitle in this DCP should be included in the output by reference
- * rather than by rewrapping.
+ /** true if the captions in this DCP should be included in the output by reference
+ * rather than by rewrapping. The types here are the original caption types,
+ * not what they are being used for.
*/
- bool _reference_subtitle;
+ bool _reference_caption[CAPTION_COUNT];
boost::optional<dcp::Standard> _standard;
bool _three_d;
diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc
index cc415629b..ab655ebf8 100644
--- a/src/lib/dcp_decoder.cc
+++ b/src/lib/dcp_decoder.cc
@@ -37,6 +37,7 @@
#include <dcp/reel_picture_asset.h>
#include <dcp/reel_sound_asset.h>
#include <dcp/reel_subtitle_asset.h>
+#include <dcp/reel_closed_caption_asset.h>
#include <dcp/mono_picture_frame.h>
#include <dcp/stereo_picture_frame.h>
#include <dcp/sound_frame.h>
@@ -62,9 +63,9 @@ DCPDecoder::DCPDecoder (shared_ptr<const DCPContent> c, shared_ptr<Log> log, boo
if (c->audio) {
audio.reset (new AudioDecoder (this, c->audio, log, fast));
}
- if (c->caption) {
+ BOOST_FOREACH (shared_ptr<CaptionContent> i, c->caption) {
/* XXX: this time here should be the time of the first subtitle, not 0 */
- caption.reset (new CaptionDecoder (this, c->caption, log, ContentTime()));
+ caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, i, log, ContentTime())));
}
list<shared_ptr<dcp::CPL> > cpl_list = cpls ();
@@ -109,10 +110,10 @@ DCPDecoder::pass ()
/* Frame within the (played part of the) reel that is coming up next */
int64_t const frame = _next.frames_round (vfr);
- /* We must emit subtitles first as when we emit the video for this frame
- it will expect already to have the subs.
+ /* We must emit captions first as when we emit the video for this frame
+ it will expect already to have the captions.
*/
- pass_subtitles (_next);
+ pass_captions (_next);
if ((_mono_reader || _stereo_reader) && (_decode_referenced || !_dcp_content->reference_video())) {
shared_ptr<dcp::PictureAsset> asset = (*_reel)->main_picture()->asset ();
@@ -190,15 +191,32 @@ DCPDecoder::pass ()
}
void
-DCPDecoder::pass_subtitles (ContentTime next)
+DCPDecoder::pass_captions (ContentTime next)
+{
+ list<shared_ptr<CaptionDecoder> >::const_iterator decoder = caption.begin ();
+ if ((*_reel)->main_subtitle()) {
+ pass_captions (
+ next, (*_reel)->main_subtitle()->asset(), _dcp_content->reference_caption(CAPTION_OPEN), (*_reel)->main_subtitle()->entry_point(), *decoder
+ );
+ ++decoder;
+ }
+ if ((*_reel)->closed_caption()) {
+ pass_captions (
+ next, (*_reel)->closed_caption()->asset(), _dcp_content->reference_caption(CAPTION_CLOSED), (*_reel)->closed_caption()->entry_point(), *decoder
+ );
+ ++decoder;
+ }
+}
+
+void
+DCPDecoder::pass_captions (ContentTime next, shared_ptr<dcp::SubtitleAsset> asset, bool reference, int64_t entry_point, shared_ptr<CaptionDecoder> decoder)
{
double const vfr = _dcp_content->active_video_frame_rate ();
/* Frame within the (played part of the) reel that is coming up next */
int64_t const frame = next.frames_round (vfr);
- if ((*_reel)->main_subtitle() && (_decode_referenced || !_dcp_content->reference_subtitle())) {
- int64_t const entry_point = (*_reel)->main_subtitle()->entry_point ();
- list<shared_ptr<dcp::Subtitle> > subs = (*_reel)->main_subtitle()->asset()->subtitles_during (
+ if (_decode_referenced || !reference) {
+ list<shared_ptr<dcp::Subtitle> > subs = asset->subtitles_during (
dcp::Time (entry_point + frame, vfr, vfr),
dcp::Time (entry_point + frame + 1, vfr, vfr),
true
@@ -209,7 +227,7 @@ DCPDecoder::pass_subtitles (ContentTime next)
if (is) {
list<dcp::SubtitleString> s;
s.push_back (*is);
- caption->emit_plain (
+ decoder->emit_plain (
ContentTimePeriod (
ContentTime::from_frames (_offset - entry_point, vfr) + ContentTime::from_seconds (i->in().as_seconds ()),
ContentTime::from_frames (_offset - entry_point, vfr) + ContentTime::from_seconds (i->out().as_seconds ())
@@ -296,11 +314,11 @@ DCPDecoder::seek (ContentTime t, bool accurate)
next_reel ();
}
- /* Pass subtitles in the pre-roll */
+ /* Pass captions in the pre-roll */
double const vfr = _dcp_content->active_video_frame_rate ();
for (int i = 0; i < pre_roll_seconds * vfr; ++i) {
- pass_subtitles (pre);
+ pass_captions (pre);
pre += ContentTime::from_frames (1, vfr);
}
diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h
index 483c057ac..898b84e5d 100644
--- a/src/lib/dcp_decoder.h
+++ b/src/lib/dcp_decoder.h
@@ -27,6 +27,7 @@
#include <dcp/mono_picture_asset_reader.h>
#include <dcp/stereo_picture_asset_reader.h>
#include <dcp/sound_asset_reader.h>
+#include <dcp/subtitle_asset.h>
namespace dcp {
class Reel;
@@ -56,7 +57,8 @@ private:
void next_reel ();
void get_readers ();
- void pass_subtitles (ContentTime next);
+ void pass_captions (ContentTime next);
+ void pass_captions (ContentTime next, boost::shared_ptr<dcp::SubtitleAsset> asset, bool reference, int64_t entry_point, boost::shared_ptr<CaptionDecoder> decoder);
/** Time of next thing to return from pass relative to the start of _reel */
ContentTime _next;
diff --git a/src/lib/dcp_encoder.cc b/src/lib/dcp_encoder.cc
index 541c23b6c..f518aefef 100644
--- a/src/lib/dcp_encoder.cc
+++ b/src/lib/dcp_encoder.cc
@@ -64,8 +64,10 @@ DCPEncoder::DCPEncoder (shared_ptr<const Film> film, weak_ptr<Job> job)
_player_caption_connection = _player->Caption.connect (bind (&DCPEncoder::caption, this, _1, _2, _3));
BOOST_FOREACH (shared_ptr<const Content> c, film->content ()) {
- if (c->caption && c->caption->use() && !c->caption->burn()) {
- _non_burnt_subtitles = true;
+ BOOST_FOREACH (shared_ptr<CaptionContent> i, c->caption) {
+ if (i->use() && !i->burn()) {
+ _non_burnt_subtitles = true;
+ }
}
}
}
diff --git a/src/lib/dcp_examiner.cc b/src/lib/dcp_examiner.cc
index c097877a3..8ce4aee00 100644
--- a/src/lib/dcp_examiner.cc
+++ b/src/lib/dcp_examiner.cc
@@ -39,6 +39,7 @@
#include <dcp/sound_asset_reader.h>
#include <dcp/subtitle_asset.h>
#include <dcp/reel_subtitle_asset.h>
+#include <dcp/reel_closed_caption_asset.h>
#include <dcp/sound_asset.h>
#include <boost/foreach.hpp>
#include <iostream>
@@ -57,7 +58,7 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content)
, _audio_length (0)
, _has_video (false)
, _has_audio (false)
- , _has_subtitles (false)
+ , _captions (0)
, _encrypted (false)
, _needs_assets (false)
, _kdm_valid (false)
@@ -165,7 +166,17 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content)
return;
}
- _has_subtitles = true;
+ ++_captions;
+ }
+
+ if (i->closed_caption ()) {
+ if (!i->closed_caption()->asset_ref().resolved()) {
+ /* We are missing this asset so we can't continue; examination will be repeated later */
+ _needs_assets = true;
+ return;
+ }
+
+ ++_captions;
}
if (i->main_picture()) {
@@ -174,6 +185,8 @@ DCPExaminer::DCPExaminer (shared_ptr<const DCPContent> content)
_reel_lengths.push_back (i->main_sound()->duration());
} else if (i->main_subtitle()) {
_reel_lengths.push_back (i->main_subtitle()->duration());
+ } else if (i->closed_caption()) {
+ _reel_lengths.push_back (i->closed_caption()->duration());
}
}
diff --git a/src/lib/dcp_examiner.h b/src/lib/dcp_examiner.h
index 0e0b3ef83..0d55d4643 100644
--- a/src/lib/dcp_examiner.h
+++ b/src/lib/dcp_examiner.h
@@ -59,8 +59,8 @@ public:
return _name;
}
- bool has_subtitles () const {
- return _has_subtitles;
+ int captions () const {
+ return _captions;
}
bool encrypted () const {
@@ -119,7 +119,7 @@ private:
bool _has_video;
/** true if this DCP has audio content (but false if it has unresolved references to audio content) */
bool _has_audio;
- bool _has_subtitles;
+ int _captions;
bool _encrypted;
bool _needs_assets;
bool _kdm_valid;
diff --git a/src/lib/dcp_subtitle_content.cc b/src/lib/dcp_subtitle_content.cc
index 17477cfb8..78e81328a 100644
--- a/src/lib/dcp_subtitle_content.cc
+++ b/src/lib/dcp_subtitle_content.cc
@@ -40,7 +40,7 @@ using dcp::raw_convert;
DCPSubtitleContent::DCPSubtitleContent (shared_ptr<const Film> film, boost::filesystem::path path)
: Content (film, path)
{
- caption.reset (new CaptionContent (this));
+ caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this)));
}
DCPSubtitleContent::DCPSubtitleContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
@@ -66,18 +66,18 @@ DCPSubtitleContent::examine (shared_ptr<Job> job)
boost::mutex::scoped_lock lm (_mutex);
/* Default to turning these subtitles on */
- caption->set_use (true);
+ only_caption()->set_use (true);
if (iop) {
- caption->set_language (iop->language ());
+ only_caption()->set_language (iop->language ());
} else if (smpte) {
- caption->set_language (smpte->language().get_value_or (""));
+ only_caption()->set_language (smpte->language().get_value_or (""));
}
_length = ContentTime::from_seconds (sc->latest_subtitle_out().as_seconds ());
BOOST_FOREACH (shared_ptr<dcp::LoadFontNode> i, sc->load_font_nodes ()) {
- caption->add_font (shared_ptr<Font> (new Font (i->id)));
+ only_caption()->add_font (shared_ptr<Font> (new Font (i->id)));
}
}
@@ -106,8 +106,8 @@ DCPSubtitleContent::as_xml (xmlpp::Node* node, bool with_paths) const
node->add_child("Type")->add_child_text ("DCPSubtitle");
Content::as_xml (node, with_paths);
- if (caption) {
- caption->as_xml (node);
+ if (only_caption()) {
+ only_caption()->as_xml (node);
}
node->add_child("Length")->add_child_text (raw_convert<string> (_length.get ()));
diff --git a/src/lib/dcp_subtitle_decoder.cc b/src/lib/dcp_subtitle_decoder.cc
index c7a9a863f..3ed4a6827 100644
--- a/src/lib/dcp_subtitle_decoder.cc
+++ b/src/lib/dcp_subtitle_decoder.cc
@@ -39,7 +39,7 @@ DCPSubtitleDecoder::DCPSubtitleDecoder (shared_ptr<const DCPSubtitleContent> con
if (_next != _subtitles.end()) {
first = content_time_period(*_next).from;
}
- caption.reset (new CaptionDecoder (this, content->caption, log, first));
+ caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, content->only_caption(), log, first)));
}
void
@@ -81,7 +81,7 @@ DCPSubtitleDecoder::pass ()
/* XXX: image subtitles */
}
- caption->emit_plain (p, s);
+ only_caption()->emit_plain (p, s);
return false;
}
diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc
index 70eb5b61a..2fddddc91 100644
--- a/src/lib/decoder.cc
+++ b/src/lib/decoder.cc
@@ -27,6 +27,7 @@
using std::cout;
using boost::optional;
+using boost::shared_ptr;
/** @return Earliest time of content that the next pass() will emit */
ContentTime
@@ -42,8 +43,10 @@ Decoder::position () const
pos = audio->position();
}
- if (caption && !caption->ignore() && (!pos || caption->position() < *pos)) {
- pos = caption->position();
+ BOOST_FOREACH (shared_ptr<CaptionDecoder> i, caption) {
+ if (!i->ignore() && (!pos || i->position() < *pos)) {
+ pos = i->position();
+ }
}
return pos.get_value_or(ContentTime());
@@ -58,7 +61,17 @@ Decoder::seek (ContentTime, bool)
if (audio) {
audio->seek ();
}
- if (caption) {
- caption->seek ();
+ BOOST_FOREACH (shared_ptr<CaptionDecoder> i, caption) {
+ i->seek ();
}
}
+
+shared_ptr<CaptionDecoder>
+Decoder::only_caption () const
+{
+ DCPOMATIC_ASSERT (caption.size() < 2);
+ if (caption.empty ()) {
+ return shared_ptr<CaptionDecoder> ();
+ }
+ return caption.front ();
+}
diff --git a/src/lib/decoder.h b/src/lib/decoder.h
index c3b330cfb..d48df7517 100644
--- a/src/lib/decoder.h
+++ b/src/lib/decoder.h
@@ -45,7 +45,9 @@ public:
boost::shared_ptr<VideoDecoder> video;
boost::shared_ptr<AudioDecoder> audio;
- boost::shared_ptr<CaptionDecoder> caption;
+ std::list<boost::shared_ptr<CaptionDecoder> > caption;
+
+ boost::shared_ptr<CaptionDecoder> only_caption () const;
/** Do some decoding and perhaps emit video, audio or subtitle data.
* @return true if this decoder will emit no more data unless a seek() happens.
diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc
index 7a821a04e..ddf4548b4 100644
--- a/src/lib/ffmpeg_content.cc
+++ b/src/lib/ffmpeg_content.cc
@@ -148,8 +148,8 @@ FFmpegContent::as_xml (xmlpp::Node* node, bool with_paths) const
}
}
- if (caption) {
- caption->as_xml (node);
+ if (only_caption()) {
+ only_caption()->as_xml (node);
}
boost::mutex::scoped_lock lm (_mutex);
@@ -242,7 +242,8 @@ FFmpegContent::examine (shared_ptr<Job> job)
_subtitle_streams = examiner->subtitle_streams ();
if (!_subtitle_streams.empty ()) {
- caption.reset (new CaptionContent (this));
+ caption.clear ();
+ caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this)));
_subtitle_stream = _subtitle_streams.front ();
}
@@ -365,8 +366,8 @@ FFmpegContent::identifier () const
s += "_" + video->identifier();
}
- if (caption && caption->use() && caption->burn()) {
- s += "_" + caption->identifier();
+ if (only_caption() && only_caption()->use() && only_caption()->burn()) {
+ s += "_" + only_caption()->identifier();
}
boost::mutex::scoped_lock lm (_mutex);
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index 9478d8816..1f2fcfef8 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -97,9 +97,9 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const FFmpegContent> c, shared_ptr<Log>
audio.reset (new AudioDecoder (this, c->audio, log, fast));
}
- if (c->caption) {
+ if (c->only_caption()) {
/* XXX: this time here should be the time of the first subtitle, not 0 */
- caption.reset (new CaptionDecoder (this, c->caption, log, ContentTime()));
+ caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, c->only_caption(), log, ContentTime())));
}
_next_time.resize (_format_context->nb_streams);
@@ -184,7 +184,7 @@ FFmpegDecoder::pass ()
if (_video_stream && si == _video_stream.get() && !video->ignore()) {
decode_video_packet ();
- } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index(_format_context, si) && !caption->ignore()) {
+ } else if (fc->subtitle_stream() && fc->subtitle_stream()->uses_index(_format_context, si) && !only_caption()->ignore()) {
decode_subtitle_packet ();
} else {
decode_audio_packet ();
@@ -549,9 +549,9 @@ FFmpegDecoder::decode_subtitle_packet ()
/* Stop any current subtitle, either at the time it was supposed to stop, or now if now is sooner */
if (_have_current_subtitle) {
if (_current_subtitle_to) {
- caption->emit_stop (min(*_current_subtitle_to, subtitle_period(sub).from + _pts_offset));
+ only_caption()->emit_stop (min(*_current_subtitle_to, subtitle_period(sub).from + _pts_offset));
} else {
- caption->emit_stop (subtitle_period(sub).from + _pts_offset);
+ only_caption()->emit_stop (subtitle_period(sub).from + _pts_offset);
}
_have_current_subtitle = false;
}
@@ -593,7 +593,7 @@ FFmpegDecoder::decode_subtitle_packet ()
}
if (_current_subtitle_to) {
- caption->emit_stop (*_current_subtitle_to);
+ only_caption()->emit_stop (*_current_subtitle_to);
}
avsubtitle_free (&sub);
@@ -669,7 +669,7 @@ FFmpegDecoder::decode_bitmap_subtitle (AVSubtitleRect const * rect, ContentTime
static_cast<double> (rect->h) / target_height
);
- caption->emit_bitmap_start (from, image, scaled_rect);
+ only_caption()->emit_bitmap_start (from, image, scaled_rect);
}
void
@@ -702,6 +702,6 @@ FFmpegDecoder::decode_ass_subtitle (string ass, ContentTime from)
);
BOOST_FOREACH (sub::Subtitle const & i, sub::collect<list<sub::Subtitle> > (raw)) {
- caption->emit_plain_start (from, i);
+ only_caption()->emit_plain_start (from, i);
}
}
diff --git a/src/lib/film.cc b/src/lib/film.cc
index 08dd8bdc2..fe7fcbfae 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -697,14 +697,19 @@ Film::isdcf_name (bool if_created_now) const
d += "_" + dm.audio_language;
if (!dm.subtitle_language.empty()) {
- bool burnt_in = true;
- BOOST_FOREACH (shared_ptr<Content> i, content ()) {
- if (!i->caption) {
- continue;
- }
+ /* I'm not clear on the precise details of the convention for CCAP labelling;
+ for now I'm just appending -CCAP if we have any closed captions.
+ */
- if (i->caption->use() && !i->caption->burn()) {
- burnt_in = false;
+ bool burnt_in = true;
+ bool ccap = false;
+ BOOST_FOREACH (shared_ptr<Content> i, content()) {
+ BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+ if (j->type() == CAPTION_OPEN && j->use() && !j->burn()) {
+ burnt_in = false;
+ } else if (j->type() == CAPTION_CLOSED) {
+ ccap = true;
+ }
}
}
@@ -716,6 +721,9 @@ Film::isdcf_name (bool if_created_now) const
}
d += "-" + language;
+ if (ccap) {
+ d += "-CCAP";
+ }
} else {
d += "-XX";
}
@@ -770,7 +778,13 @@ Film::isdcf_name (bool if_created_now) const
bool vf = false;
BOOST_FOREACH (shared_ptr<Content> i, content ()) {
shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
- if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
+ bool any_caption = false;
+ for (int i = 0; i < CAPTION_COUNT; ++i) {
+ if (dc->reference_caption(static_cast<CaptionType>(i))) {
+ any_caption = true;
+ }
+ }
+ if (dc && (dc->reference_video() || dc->reference_audio() || any_caption)) {
vf = true;
}
}
@@ -1083,9 +1097,9 @@ Film::add_content (shared_ptr<Content> c)
{
/* Add {video,subtitle} content after any existing {video,subtitle} content */
if (c->video) {
- c->set_position (_playlist->video_end ());
- } else if (c->caption) {
- c->set_position (_playlist->subtitle_end ());
+ c->set_position (_playlist->video_end());
+ } else if (!c->caption.empty()) {
+ c->set_position (_playlist->caption_end());
}
if (_template_film) {
@@ -1372,10 +1386,9 @@ Film::subtitle_language () const
{
set<string> languages;
- ContentList cl = content ();
- BOOST_FOREACH (shared_ptr<Content>& c, cl) {
- if (c->caption) {
- languages.insert (c->caption->language ());
+ BOOST_FOREACH (shared_ptr<Content> i, content()) {
+ BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+ languages.insert (j->language ());
}
}
diff --git a/src/lib/hints.cc b/src/lib/hints.cc
index eb3ea1d02..33c2faba5 100644
--- a/src/lib/hints.cc
+++ b/src/lib/hints.cc
@@ -56,10 +56,10 @@ get_hints (shared_ptr<const Film> film)
bool big_font_files = false;
if (film->interop ()) {
BOOST_FOREACH (shared_ptr<Content> i, content) {
- if (i->caption) {
- BOOST_FOREACH (shared_ptr<Font> j, i->caption->fonts ()) {
- for (int k = 0; k < FontFiles::VARIANTS; ++k) {
- optional<boost::filesystem::path> const p = j->file (static_cast<FontFiles::Variant> (k));
+ BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+ BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
+ for (int l = 0; l < FontFiles::VARIANTS; ++l) {
+ optional<boost::filesystem::path> const p = k->file (static_cast<FontFiles::Variant>(l));
if (p && boost::filesystem::file_size (p.get()) >= (640 * 1024)) {
big_font_files = true;
}
diff --git a/src/lib/overlaps.cc b/src/lib/overlaps.cc
index ccef4cef8..54077d96b 100644
--- a/src/lib/overlaps.cc
+++ b/src/lib/overlaps.cc
@@ -26,7 +26,7 @@
using boost::shared_ptr;
using boost::function;
-ContentList overlaps (ContentList cl, function<shared_ptr<ContentPart> (shared_ptr<const Content>)> part, DCPTime from, DCPTime to)
+ContentList overlaps (ContentList cl, function<bool (shared_ptr<const Content>)> part, DCPTime from, DCPTime to)
{
ContentList overlaps;
DCPTimePeriod period (from, to);
diff --git a/src/lib/overlaps.h b/src/lib/overlaps.h
index e5b3fc38e..7dd9802c3 100644
--- a/src/lib/overlaps.h
+++ b/src/lib/overlaps.h
@@ -28,5 +28,5 @@ class ContentPart;
* ContentList
*/
ContentList overlaps (
- ContentList cl, boost::function<boost::shared_ptr<ContentPart> (boost::shared_ptr<const Content>)> part, DCPTime from, DCPTime to
+ ContentList cl, boost::function<bool (boost::shared_ptr<const Content>)> part, DCPTime from, DCPTime to
);
diff --git a/src/lib/player.cc b/src/lib/player.cc
index 78928af26..580c3e6d4 100644
--- a/src/lib/player.cc
+++ b/src/lib/player.cc
@@ -52,6 +52,7 @@
#include <dcp/reel_sound_asset.h>
#include <dcp/reel_subtitle_asset.h>
#include <dcp/reel_picture_asset.h>
+#include <dcp/reel_closed_caption_asset.h>
#include <boost/foreach.hpp>
#include <stdint.h>
#include <algorithm>
@@ -88,7 +89,7 @@ Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist
, _playlist (playlist)
, _have_valid_pieces (false)
, _ignore_video (false)
- , _ignore_subtitle (false)
+ , _ignore_caption (false)
, _fast (false)
, _play_referenced (false)
, _audio_merger (_film->audio_frame_rate())
@@ -136,8 +137,10 @@ Player::setup_pieces ()
decoder->video->set_ignore (true);
}
- if (decoder->caption && _ignore_subtitle) {
- decoder->caption->set_ignore (true);
+ if (_ignore_caption) {
+ BOOST_FOREACH (shared_ptr<CaptionDecoder> i, decoder->caption) {
+ i->set_ignore (true);
+ }
}
shared_ptr<DCPDecoder> dcp = dynamic_pointer_cast<DCPDecoder> (decoder);
@@ -164,16 +167,20 @@ Player::setup_pieces ()
decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr<Piece> (piece), _1, _2));
}
- if (decoder->caption) {
- decoder->caption->BitmapStart.connect (
- bind(&Player::bitmap_text_start, this, weak_ptr<Piece>(piece), weak_ptr<CaptionContent>(piece->content->caption), _1)
+ list<shared_ptr<CaptionDecoder> >::const_iterator j = decoder->caption.begin();
+
+ while (j != decoder->caption.end()) {
+ (*j)->BitmapStart.connect (
+ bind(&Player::bitmap_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const CaptionContent>((*j)->content()), _1)
);
- decoder->caption->PlainStart.connect (
- bind(&Player::plain_text_start, this, weak_ptr<Piece>(piece), weak_ptr<CaptionContent>(piece->content->caption), _1)
+ (*j)->PlainStart.connect (
+ bind(&Player::plain_text_start, this, weak_ptr<Piece>(piece), weak_ptr<const CaptionContent>((*j)->content()), _1)
);
- decoder->caption->Stop.connect (
- bind(&Player::subtitle_stop, this, weak_ptr<Piece>(piece), weak_ptr<CaptionContent>(piece->content->caption), _1, _2)
+ (*j)->Stop.connect (
+ bind(&Player::subtitle_stop, this, weak_ptr<Piece>(piece), weak_ptr<const CaptionContent>((*j)->content()), _1, _2)
);
+
+ ++j;
}
}
@@ -411,12 +418,12 @@ Player::get_subtitle_fonts ()
}
list<shared_ptr<Font> > fonts;
- BOOST_FOREACH (shared_ptr<Piece>& p, _pieces) {
- if (p->content->caption) {
+ BOOST_FOREACH (shared_ptr<Piece> i, _pieces) {
+ BOOST_FOREACH (shared_ptr<CaptionContent> j, i->content->caption) {
/* XXX: things may go wrong if there are duplicate font IDs
with different font files.
*/
- list<shared_ptr<Font> > f = p->content->caption->fonts ();
+ list<shared_ptr<Font> > f = j->fonts ();
copy (f.begin(), f.end(), back_inserter (fonts));
}
}
@@ -432,9 +439,9 @@ Player::set_ignore_video ()
}
void
-Player::set_ignore_subtitle ()
+Player::set_ignore_caption ()
{
- _ignore_subtitle = true;
+ _ignore_caption = true;
}
/** Set a type of caption that this player should always burn into the image,
@@ -510,7 +517,7 @@ Player::get_reel_assets ()
);
}
- if (j->reference_subtitle ()) {
+ if (j->reference_caption (CAPTION_OPEN)) {
shared_ptr<dcp::ReelAsset> ra = k->main_subtitle ();
DCPOMATIC_ASSERT (ra);
ra->set_entry_point (ra->entry_point() + trim_start);
@@ -520,6 +527,16 @@ Player::get_reel_assets ()
);
}
+ if (j->reference_caption (CAPTION_CLOSED)) {
+ shared_ptr<dcp::ReelAsset> ra = k->closed_caption ();
+ DCPOMATIC_ASSERT (ra);
+ ra->set_entry_point (ra->entry_point() + trim_start);
+ ra->set_duration (ra->duration() - trim_start - trim_end);
+ a.push_back (
+ ReferencedReelAsset (ra, DCPTimePeriod (from, from + DCPTime::from_frames (ra->duration(), ffr)))
+ );
+ }
+
/* Assume that main picture duration is the length of the reel */
offset += k->main_picture()->duration ();
}
@@ -556,10 +573,10 @@ Player::pass ()
i->done = true;
} else {
- /* Given two choices at the same time, pick the one with a subtitle so we see it before
+ /* Given two choices at the same time, pick the one with captions so we see it before
the video.
*/
- if (!earliest_time || t < *earliest_time || (t == *earliest_time && i->decoder->caption)) {
+ if (!earliest_time || t < *earliest_time || (t == *earliest_time && !i->decoder->caption.empty())) {
earliest_time = t;
earliest_content = i;
}
@@ -851,10 +868,10 @@ Player::audio (weak_ptr<Piece> wp, AudioStreamPtr stream, ContentAudio content_a
}
void
-Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<CaptionContent> wc, ContentBitmapCaption subtitle)
+Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc, ContentBitmapCaption subtitle)
{
shared_ptr<Piece> piece = wp.lock ();
- shared_ptr<CaptionContent> caption = wc.lock ();
+ shared_ptr<const CaptionContent> caption = wc.lock ();
if (!piece || !caption) {
return;
}
@@ -879,10 +896,10 @@ Player::bitmap_text_start (weak_ptr<Piece> wp, weak_ptr<CaptionContent> wc, Cont
}
void
-Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<CaptionContent> wc, ContentTextCaption subtitle)
+Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc, ContentTextCaption subtitle)
{
shared_ptr<Piece> piece = wp.lock ();
- shared_ptr<CaptionContent> caption = wc.lock ();
+ shared_ptr<const CaptionContent> caption = wc.lock ();
if (!piece || !caption) {
return;
}
@@ -923,14 +940,14 @@ Player::plain_text_start (weak_ptr<Piece> wp, weak_ptr<CaptionContent> wc, Conte
}
void
-Player::subtitle_stop (weak_ptr<Piece> wp, weak_ptr<CaptionContent> wc, ContentTime to, CaptionType type)
+Player::subtitle_stop (weak_ptr<Piece> wp, weak_ptr<const CaptionContent> wc, ContentTime to, CaptionType type)
{
if (!_active_captions[type].have (wc)) {
return;
}
shared_ptr<Piece> piece = wp.lock ();
- shared_ptr<CaptionContent> caption = wc.lock ();
+ shared_ptr<const CaptionContent> caption = wc.lock ();
if (!piece || !caption) {
return;
}
diff --git a/src/lib/player.h b/src/lib/player.h
index d0f1eec6d..eda2d7eb0 100644
--- a/src/lib/player.h
+++ b/src/lib/player.h
@@ -78,7 +78,7 @@ public:
void set_video_container_size (dcp::Size);
void set_ignore_video ();
- void set_ignore_subtitle ();
+ void set_ignore_caption ();
void set_always_burn_captions (CaptionType type);
void set_fast ();
void set_play_referenced ();
@@ -126,9 +126,9 @@ private:
boost::shared_ptr<PlayerVideo> black_player_video_frame (Eyes eyes) const;
void video (boost::weak_ptr<Piece>, ContentVideo);
void audio (boost::weak_ptr<Piece>, AudioStreamPtr, ContentAudio);
- void bitmap_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<CaptionContent>, ContentBitmapCaption);
- void plain_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<CaptionContent>, ContentTextCaption);
- void subtitle_stop (boost::weak_ptr<Piece>, boost::weak_ptr<CaptionContent>, ContentTime, CaptionType);
+ void bitmap_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const CaptionContent>, ContentBitmapCaption);
+ void plain_text_start (boost::weak_ptr<Piece>, boost::weak_ptr<const CaptionContent>, ContentTextCaption);
+ void subtitle_stop (boost::weak_ptr<Piece>, boost::weak_ptr<const CaptionContent>, ContentTime, CaptionType);
DCPTime one_video_frame () const;
void fill_audio (DCPTimePeriod period);
std::pair<boost::shared_ptr<AudioBuffers>, DCPTime> discard_audio (
@@ -152,8 +152,8 @@ private:
/** true if the player should ignore all video; i.e. never produce any */
bool _ignore_video;
- /** true if the player should ignore all audio; i.e. never produce any */
- bool _ignore_subtitle;
+ /** true if the player should ignore all captions; i.e. never produce any */
+ bool _ignore_caption;
/** Type of captions that the player should always burn into the video regardless
of content settings.
*/
diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc
index a5451bafa..6c7fd7f4e 100644
--- a/src/lib/playlist.cc
+++ b/src/lib/playlist.cc
@@ -131,11 +131,11 @@ Playlist::maybe_sequence ()
placed.push_back (i);
}
- /* Subtitles */
+ /* Captions */
DCPTime next;
BOOST_FOREACH (shared_ptr<Content> i, _content) {
- if (!i->caption || find (placed.begin(), placed.end(), i) != placed.end()) {
+ if (i->caption.empty() || find (placed.begin(), placed.end(), i) != placed.end()) {
continue;
}
@@ -155,7 +155,13 @@ Playlist::video_identifier () const
string t;
BOOST_FOREACH (shared_ptr<const Content> i, _content) {
- if (i->video || (i->caption && i->caption->burn())) {
+ bool burn = false;
+ BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
+ if (j->burn()) {
+ burn = true;
+ }
+ }
+ if (i->video || burn) {
t += i->identifier ();
}
}
@@ -362,11 +368,11 @@ Playlist::video_end () const
}
DCPTime
-Playlist::subtitle_end () const
+Playlist::caption_end () const
{
DCPTime end;
BOOST_FOREACH (shared_ptr<Content> i, _content) {
- if (i->caption) {
+ if (!i->caption.empty ()) {
end = max (end, i->end ());
}
}
diff --git a/src/lib/playlist.h b/src/lib/playlist.h
index fe19c93c9..073e53de7 100644
--- a/src/lib/playlist.h
+++ b/src/lib/playlist.h
@@ -64,7 +64,7 @@ public:
int best_video_frame_rate () const;
DCPTime video_end () const;
- DCPTime subtitle_end () const;
+ DCPTime caption_end () const;
FrameRateChange active_frame_rate_change (DCPTime, int dcp_frame_rate) const;
std::string content_summary (DCPTimePeriod period) const;
std::pair<double, double> speed_up_range (int dcp_video_frame_rate) const;
diff --git a/src/lib/text_caption_file_content.cc b/src/lib/text_caption_file_content.cc
index cb7d1511b..c8eb2390a 100644
--- a/src/lib/text_caption_file_content.cc
+++ b/src/lib/text_caption_file_content.cc
@@ -38,7 +38,7 @@ using dcp::raw_convert;
TextCaptionFileContent::TextCaptionFileContent (shared_ptr<const Film> film, boost::filesystem::path path)
: Content (film, path)
{
- caption.reset (new CaptionContent (this));
+ caption.push_back (shared_ptr<CaptionContent> (new CaptionContent (this)));
}
TextCaptionFileContent::TextCaptionFileContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, int version)
@@ -55,11 +55,11 @@ TextCaptionFileContent::examine (boost::shared_ptr<Job> job)
TextCaptionFile s (shared_from_this ());
/* Default to turning these subtitles on */
- caption->set_use (true);
+ only_caption()->set_use (true);
boost::mutex::scoped_lock lm (_mutex);
_length = s.length ();
- caption->add_font (shared_ptr<Font> (new Font (TEXT_FONT_ID)));
+ only_caption()->add_font (shared_ptr<Font> (new Font (TEXT_FONT_ID)));
}
string
@@ -80,8 +80,8 @@ TextCaptionFileContent::as_xml (xmlpp::Node* node, bool with_paths) const
node->add_child("Type")->add_child_text ("TextSubtitle");
Content::as_xml (node, with_paths);
- if (caption) {
- caption->as_xml (node);
+ if (only_caption()) {
+ only_caption()->as_xml (node);
}
node->add_child("Length")->add_child_text (raw_convert<string> (_length.get ()));
diff --git a/src/lib/text_caption_file_decoder.cc b/src/lib/text_caption_file_decoder.cc
index 46217e49b..65de6a562 100644
--- a/src/lib/text_caption_file_decoder.cc
+++ b/src/lib/text_caption_file_decoder.cc
@@ -43,7 +43,7 @@ TextCaptionFileDecoder::TextCaptionFileDecoder (shared_ptr<const TextCaptionFile
if (!_subtitles.empty()) {
first = content_time_period(_subtitles[0]).from;
}
- caption.reset (new CaptionDecoder (this, content->caption, log, first));
+ caption.push_back (shared_ptr<CaptionDecoder> (new CaptionDecoder (this, content->only_caption(), log, first)));
}
void
@@ -73,7 +73,7 @@ TextCaptionFileDecoder::pass ()
}
ContentTimePeriod const p = content_time_period (_subtitles[_next]);
- caption->emit_plain (p, _subtitles[_next]);
+ only_caption()->emit_plain (p, _subtitles[_next]);
++_next;
return false;
diff --git a/src/lib/types.h b/src/lib/types.h
index 3337087eb..b2bff78fa 100644
--- a/src/lib/types.h
+++ b/src/lib/types.h
@@ -129,6 +129,16 @@ enum ReelType
REELTYPE_BY_LENGTH
};
+/** Type of captions.
+ * For better or worse DoM has uses two names for text that appears
+ * with the DCP:
+ *
+ * open captions: text that is shown to everybody on-screen (aka subtitles).
+ * closed captions: text that is shown to some viewers using some other method.
+ *
+ * There is also still use of the word `subtitle' in the code; these are the
+ * same as open captions in DoM.
+ */
enum CaptionType
{
CAPTION_OPEN,