diff options
| author | Carl Hetherington <cth@carlh.net> | 2018-07-21 15:16:18 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2018-07-21 15:16:18 +0100 |
| commit | cbd4450197a083bf58bda510e626f73ba583cb66 (patch) | |
| tree | 2be308772512539570beab36beab02bde72d6d4b /src/lib | |
| parent | 1013175d5f6adfa0e6a7442e4c9aebb893787748 (diff) | |
Basics of multiple captions per content so that DCPContent can
hold subs and closed captions.
Diffstat (limited to 'src/lib')
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, |
