From cbd4450197a083bf58bda510e626f73ba583cb66 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 21 Jul 2018 15:16:18 +0100 Subject: [PATCH] Basics of multiple captions per content so that DCPContent can hold subs and closed captions. --- src/lib/active_captions.cc | 8 +-- src/lib/active_captions.h | 8 +-- src/lib/analyse_audio_job.cc | 2 +- src/lib/caption_content.cc | 24 +++++-- src/lib/caption_content.h | 12 +++- src/lib/content.cc | 31 ++++++++- src/lib/content.h | 5 +- src/lib/dcp_content.cc | 97 +++++++++++++++++++--------- src/lib/dcp_content.h | 24 ++++--- src/lib/dcp_decoder.cc | 42 ++++++++---- src/lib/dcp_decoder.h | 4 +- src/lib/dcp_encoder.cc | 6 +- src/lib/dcp_examiner.cc | 17 ++++- src/lib/dcp_examiner.h | 6 +- src/lib/dcp_subtitle_content.cc | 14 ++-- src/lib/dcp_subtitle_decoder.cc | 4 +- src/lib/decoder.cc | 21 ++++-- src/lib/decoder.h | 4 +- src/lib/ffmpeg_content.cc | 11 ++-- src/lib/ffmpeg_decoder.cc | 16 ++--- src/lib/film.cc | 43 +++++++----- src/lib/hints.cc | 8 +-- src/lib/overlaps.cc | 2 +- src/lib/overlaps.h | 2 +- src/lib/player.cc | 65 ++++++++++++------- src/lib/player.h | 12 ++-- src/lib/playlist.cc | 16 +++-- src/lib/playlist.h | 2 +- src/lib/text_caption_file_content.cc | 10 +-- src/lib/text_caption_file_decoder.cc | 4 +- src/lib/types.h | 10 +++ src/tools/dcpomatic.cc | 14 ++-- src/tools/dcpomatic_player.cc | 7 +- src/wx/caption_appearance_dialog.cc | 43 ++++++------ src/wx/caption_appearance_dialog.h | 3 +- src/wx/caption_panel.cc | 61 ++++++++--------- src/wx/caption_panel.h | 1 + src/wx/caption_view.cc | 12 +++- src/wx/caption_view.h | 4 +- src/wx/content_panel.cc | 4 +- src/wx/dcp_panel.cc | 2 +- src/wx/fonts_dialog.cc | 13 ++-- src/wx/fonts_dialog.h | 6 +- src/wx/player_information.cc | 2 +- src/wx/timeline.cc | 16 ++--- src/wx/timeline_labels_view.cc | 14 ++-- src/wx/timeline_labels_view.h | 4 +- src/wx/timeline_text_content_view.cc | 5 +- src/wx/timeline_text_content_view.h | 5 +- src/wx/timing_panel.cc | 2 +- test/closed_caption_test.cc | 2 +- test/dcp_subtitle_test.cc | 14 ++-- test/ffmpeg_encoder_test.cc | 24 +++---- test/player_test.cc | 4 +- test/remake_with_subtitle_test.cc | 6 +- test/srt_subtitle_test.cc | 26 ++++---- test/ssa_subtitle_test.cc | 4 +- test/subtitle_reel_number_test.cc | 4 +- test/vf_test.cc | 16 +++-- 59 files changed, 537 insertions(+), 311 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 caption = i->first.lock (); + shared_ptr 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 content, PlayerCaption ps, DCPTime from) +ActiveCaptions::add_from (weak_ptr content, PlayerCaption ps, DCPTime from) { if (_data.find(content) == _data.end()) { _data[content] = list(); @@ -104,7 +104,7 @@ ActiveCaptions::add_from (weak_ptr content, PlayerCaption ps, DC * @return Return the corresponding subtitles and their from time. */ pair -ActiveCaptions::add_to (weak_ptr content, DCPTime to) +ActiveCaptions::add_to (weak_ptr content, DCPTime to) { DCPOMATIC_ASSERT (_data.find(content) != _data.end()); @@ -121,7 +121,7 @@ ActiveCaptions::add_to (weak_ptr content, DCPTime to) * @return true if we have any active subtitles from this content. */ bool -ActiveCaptions::have (weak_ptr content) const +ActiveCaptions::have (weak_ptr 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 get_burnt (DCPTimePeriod period, bool always_burn_captions) const; void clear_before (DCPTime time); void clear (); - void add_from (boost::weak_ptr content, PlayerCaption ps, DCPTime from); - std::pair add_to (boost::weak_ptr content, DCPTime to); - bool have (boost::weak_ptr content) const; + void add_from (boost::weak_ptr content, PlayerCaption ps, DCPTime from); + std::pair add_to (boost::weak_ptr content, DCPTime to); + bool have (boost::weak_ptr content) const; private: class Period @@ -59,7 +59,7 @@ private: boost::optional to; }; - typedef std::map, std::list > Map; + typedef std::map, std::list > 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 (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 +/** @return CaptionContents from node or nodes under node (according to version). + * The list could be empty if no CaptionContents are found. + */ +list > 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 (); + return list >(); } /* 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("SubtitleXOffset") && !node->optional_number_child("SubtitleOffset")) { - return shared_ptr (); + return list >(); } - return shared_ptr (new CaptionContent (parent, node, version)); + list > c; + c.push_back (shared_ptr (new CaptionContent (parent, node, version))); + return c; } if (!node->node_child("Caption")) { - return shared_ptr (); + return list >(); } - return shared_ptr (new CaptionContent (parent, node->node_child("Caption"), version)); + list > c; + BOOST_FOREACH (cxml::ConstNodePtr i, node->node_children("Caption")) { + c.push_back (shared_ptr (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 from_xml (Content* parent, cxml::ConstNodePtr, int version); + CaptionType original_type () const { + boost::mutex::scoped_lock lm (_mutex); + return _original_type; + } + + static std::list > 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 _fade_in; boost::optional _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 c) if (audio && c->audio) { audio->take_settings_from (c->audio); } - if (caption && c->caption) { - caption->take_settings_from (c->caption); + + list >::iterator i = caption.begin (); + list >::const_iterator j = c->caption.begin (); + while (i != caption.end() && j != c->caption.end()) { + (*i)->take_settings_from (*j); + ++i; + ++j; + } +} + +shared_ptr +Content::only_caption () const +{ + DCPOMATIC_ASSERT (caption.size() < 2); + if (caption.empty ()) { + return shared_ptr (); + } + return caption.front (); +} + +shared_ptr +Content::caption_of_original_type (CaptionType type) const +{ + BOOST_FOREACH (shared_ptr i, caption) { + if (i->original_type() == type) { + return i; + } } + + return shared_ptr (); } 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 video; boost::shared_ptr audio; - boost::shared_ptr caption; + std::list > caption; + + boost::shared_ptr only_caption () const; + boost::shared_ptr 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 film, boost::filesystem::path p) : Content (film) @@ -69,11 +69,14 @@ DCPContent::DCPContent (shared_ptr 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 film, cxml::ConstNodePtr node, int version) @@ -83,6 +86,10 @@ DCPContent::DCPContent (shared_ptr 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 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) bool const needed_assets = needs_assets (); bool const needed_kdm = needs_kdm (); string const old_name = name (); - bool had_subtitles = static_cast (caption); + int const old_captions = caption.size (); if (job) { job->set_progress_unknown (); @@ -174,16 +187,14 @@ DCPContent::examine (shared_ptr 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 (new CaptionContent (this))); } - has_subtitles = static_cast (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) _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 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 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 @@ -459,7 +474,7 @@ DCPContent::reel_split_points () const } bool -DCPContent::can_reference (function (shared_ptr)> part, string overlapping, string& why_not) const +DCPContent::can_reference (function)> 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 c) +{ + return static_cast(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 c) +{ + return static_cast(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 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 decoder; try { @@ -576,15 +608,20 @@ DCPContent::can_reference_subtitle (string& why_not) const } BOOST_FOREACH (shared_ptr 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 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 reels () const; bool can_reference ( - boost::function (boost::shared_ptr)>, + boost::function )>, 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 _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 #include #include +#include #include #include #include @@ -62,9 +63,9 @@ DCPDecoder::DCPDecoder (shared_ptr c, shared_ptr log, boo if (c->audio) { audio.reset (new AudioDecoder (this, c->audio, log, fast)); } - if (c->caption) { + BOOST_FOREACH (shared_ptr 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 (new CaptionDecoder (this, i, log, ContentTime()))); } list > 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 asset = (*_reel)->main_picture()->asset (); @@ -190,15 +191,32 @@ DCPDecoder::pass () } void -DCPDecoder::pass_subtitles (ContentTime next) +DCPDecoder::pass_captions (ContentTime next) +{ + list >::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 asset, bool reference, int64_t entry_point, shared_ptr 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 > subs = (*_reel)->main_subtitle()->asset()->subtitles_during ( + if (_decode_referenced || !reference) { + list > 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 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 #include #include +#include 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 asset, bool reference, int64_t entry_point, boost::shared_ptr 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 film, weak_ptr job) _player_caption_connection = _player->Caption.connect (bind (&DCPEncoder::caption, this, _1, _2, _3)); BOOST_FOREACH (shared_ptr c, film->content ()) { - if (c->caption && c->caption->use() && !c->caption->burn()) { - _non_burnt_subtitles = true; + BOOST_FOREACH (shared_ptr 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 #include #include +#include #include #include #include @@ -57,7 +58,7 @@ DCPExaminer::DCPExaminer (shared_ptr 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 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 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 film, boost::filesystem::path path) : Content (film, path) { - caption.reset (new CaptionContent (this)); + caption.push_back (shared_ptr (new CaptionContent (this))); } DCPSubtitleContent::DCPSubtitleContent (shared_ptr film, cxml::ConstNodePtr node, int version) @@ -66,18 +66,18 @@ DCPSubtitleContent::examine (shared_ptr 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 i, sc->load_font_nodes ()) { - caption->add_font (shared_ptr (new Font (i->id))); + only_caption()->add_font (shared_ptr (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 (_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 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 (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 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 i, caption) { + i->seek (); } } + +shared_ptr +Decoder::only_caption () const +{ + DCPOMATIC_ASSERT (caption.size() < 2); + if (caption.empty ()) { + return shared_ptr (); + } + 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 video; boost::shared_ptr audio; - boost::shared_ptr caption; + std::list > caption; + + boost::shared_ptr 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) _subtitle_streams = examiner->subtitle_streams (); if (!_subtitle_streams.empty ()) { - caption.reset (new CaptionContent (this)); + caption.clear (); + caption.push_back (shared_ptr (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 c, shared_ptr 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 (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 (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 > (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 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 i, content()) { + BOOST_FOREACH (shared_ptr 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 i, content ()) { shared_ptr dc = dynamic_pointer_cast (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(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 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 languages; - ContentList cl = content (); - BOOST_FOREACH (shared_ptr& c, cl) { - if (c->caption) { - languages.insert (c->caption->language ()); + BOOST_FOREACH (shared_ptr i, content()) { + BOOST_FOREACH (shared_ptr 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 film) bool big_font_files = false; if (film->interop ()) { BOOST_FOREACH (shared_ptr i, content) { - if (i->caption) { - BOOST_FOREACH (shared_ptr j, i->caption->fonts ()) { - for (int k = 0; k < FontFiles::VARIANTS; ++k) { - optional const p = j->file (static_cast (k)); + BOOST_FOREACH (shared_ptr j, i->caption) { + BOOST_FOREACH (shared_ptr k, j->fonts()) { + for (int l = 0; l < FontFiles::VARIANTS; ++l) { + optional const p = k->file (static_cast(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)> part, DCPTime from, DCPTime to) +ContentList overlaps (ContentList cl, function)> 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)> part, DCPTime from, DCPTime to + ContentList cl, boost::function)> 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 #include #include +#include #include #include #include @@ -88,7 +89,7 @@ Player::Player (shared_ptr film, shared_ptr 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 i, decoder->caption) { + i->set_ignore (true); + } } shared_ptr dcp = dynamic_pointer_cast (decoder); @@ -164,16 +167,20 @@ Player::setup_pieces () decoder->audio->Data.connect (bind (&Player::audio, this, weak_ptr (piece), _1, _2)); } - if (decoder->caption) { - decoder->caption->BitmapStart.connect ( - bind(&Player::bitmap_text_start, this, weak_ptr(piece), weak_ptr(piece->content->caption), _1) + list >::const_iterator j = decoder->caption.begin(); + + while (j != decoder->caption.end()) { + (*j)->BitmapStart.connect ( + bind(&Player::bitmap_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) ); - decoder->caption->PlainStart.connect ( - bind(&Player::plain_text_start, this, weak_ptr(piece), weak_ptr(piece->content->caption), _1) + (*j)->PlainStart.connect ( + bind(&Player::plain_text_start, this, weak_ptr(piece), weak_ptr((*j)->content()), _1) ); - decoder->caption->Stop.connect ( - bind(&Player::subtitle_stop, this, weak_ptr(piece), weak_ptr(piece->content->caption), _1, _2) + (*j)->Stop.connect ( + bind(&Player::subtitle_stop, this, weak_ptr(piece), weak_ptr((*j)->content()), _1, _2) ); + + ++j; } } @@ -411,12 +418,12 @@ Player::get_subtitle_fonts () } list > fonts; - BOOST_FOREACH (shared_ptr& p, _pieces) { - if (p->content->caption) { + BOOST_FOREACH (shared_ptr i, _pieces) { + BOOST_FOREACH (shared_ptr j, i->content->caption) { /* XXX: things may go wrong if there are duplicate font IDs with different font files. */ - list > f = p->content->caption->fonts (); + list > 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 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 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 wp, AudioStreamPtr stream, ContentAudio content_a } void -Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, ContentBitmapCaption subtitle) +Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, ContentBitmapCaption subtitle) { shared_ptr piece = wp.lock (); - shared_ptr caption = wc.lock (); + shared_ptr caption = wc.lock (); if (!piece || !caption) { return; } @@ -879,10 +896,10 @@ Player::bitmap_text_start (weak_ptr wp, weak_ptr wc, Cont } void -Player::plain_text_start (weak_ptr wp, weak_ptr wc, ContentTextCaption subtitle) +Player::plain_text_start (weak_ptr wp, weak_ptr wc, ContentTextCaption subtitle) { shared_ptr piece = wp.lock (); - shared_ptr caption = wc.lock (); + shared_ptr caption = wc.lock (); if (!piece || !caption) { return; } @@ -923,14 +940,14 @@ Player::plain_text_start (weak_ptr wp, weak_ptr wc, Conte } void -Player::subtitle_stop (weak_ptr wp, weak_ptr wc, ContentTime to, CaptionType type) +Player::subtitle_stop (weak_ptr wp, weak_ptr wc, ContentTime to, CaptionType type) { if (!_active_captions[type].have (wc)) { return; } shared_ptr piece = wp.lock (); - shared_ptr caption = wc.lock (); + shared_ptr 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 black_player_video_frame (Eyes eyes) const; void video (boost::weak_ptr, ContentVideo); void audio (boost::weak_ptr, AudioStreamPtr, ContentAudio); - void bitmap_text_start (boost::weak_ptr, boost::weak_ptr, ContentBitmapCaption); - void plain_text_start (boost::weak_ptr, boost::weak_ptr, ContentTextCaption); - void subtitle_stop (boost::weak_ptr, boost::weak_ptr, ContentTime, CaptionType); + void bitmap_text_start (boost::weak_ptr, boost::weak_ptr, ContentBitmapCaption); + void plain_text_start (boost::weak_ptr, boost::weak_ptr, ContentTextCaption); + void subtitle_stop (boost::weak_ptr, boost::weak_ptr, ContentTime, CaptionType); DCPTime one_video_frame () const; void fill_audio (DCPTimePeriod period); std::pair, 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 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 i, _content) { - if (i->video || (i->caption && i->caption->burn())) { + bool burn = false; + BOOST_FOREACH (shared_ptr 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 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 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 film, boost::filesystem::path path) : Content (film, path) { - caption.reset (new CaptionContent (this)); + caption.push_back (shared_ptr (new CaptionContent (this))); } TextCaptionFileContent::TextCaptionFileContent (shared_ptr film, cxml::ConstNodePtr node, int version) @@ -55,11 +55,11 @@ TextCaptionFileContent::examine (boost::shared_ptr 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 (new Font (TEXT_FONT_ID))); + only_caption()->add_font (shared_ptr (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 (_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_ptrcaption, log, first)); + caption.push_back (shared_ptr (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, diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index 9262be1b5..416bdf6c4 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -563,7 +563,7 @@ private: { DCPOMATIC_ASSERT (_clipboard); - PasteDialog* d = new PasteDialog (this, static_cast(_clipboard->video), static_cast(_clipboard->audio), static_cast(_clipboard->caption)); + PasteDialog* d = new PasteDialog (this, static_cast(_clipboard->video), static_cast(_clipboard->audio), !_clipboard->caption.empty()); if (d->ShowModal() == wxID_OK) { BOOST_FOREACH (shared_ptr i, _film_editor->content_panel()->selected()) { if (d->video() && i->video) { @@ -574,9 +574,15 @@ private: DCPOMATIC_ASSERT (_clipboard->audio); i->audio->take_settings_from (_clipboard->audio); } - if (d->caption() && i->caption) { - DCPOMATIC_ASSERT (_clipboard->caption); - i->caption->take_settings_from (_clipboard->caption); + + if (d->caption()) { + list >::iterator j = i->caption.begin (); + list >::const_iterator k = _clipboard->caption.begin (); + while (j != i->caption.end() && k != _clipboard->caption.end()) { + (*j)->take_settings_from (*k); + ++j; + ++k; + } } } } diff --git a/src/tools/dcpomatic_player.cc b/src/tools/dcpomatic_player.cc index 0af44fd05..d357e566b 100644 --- a/src/tools/dcpomatic_player.cc +++ b/src/tools/dcpomatic_player.cc @@ -618,8 +618,11 @@ private: void setup_from_dcp (shared_ptr dcp) { - if (dcp->caption) { - dcp->caption->set_use (true); + BOOST_FOREACH (shared_ptr i, dcp->caption) { + /* XXX: we should offer the option to view closed captions */ + if (i->type() == CAPTION_OPEN) { + i->set_use (true); + } } if (dcp->video) { diff --git a/src/wx/caption_appearance_dialog.cc b/src/wx/caption_appearance_dialog.cc index 9377fa6ef..af01dcbe8 100644 --- a/src/wx/caption_appearance_dialog.cc +++ b/src/wx/caption_appearance_dialog.cc @@ -39,9 +39,10 @@ int const CaptionAppearanceDialog::NONE = 0; int const CaptionAppearanceDialog::OUTLINE = 1; int const CaptionAppearanceDialog::SHADOW = 2; -CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptr content) +CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptr content, shared_ptr caption) : wxDialog (parent, wxID_ANY, _("Caption appearance")) , _content (content) + , _caption (caption) { shared_ptr ff = dynamic_pointer_cast (content); if (ff) { @@ -126,7 +127,7 @@ CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptrAppend (_("Outline")); _effect->Append (_("Shadow"));; - optional colour = _content->caption->colour(); + optional colour = _caption->colour(); _force_colour->SetValue (static_cast(colour)); if (colour) { _colour->SetColour (wxColour (colour->r, colour->g, colour->b)); @@ -134,7 +135,7 @@ CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptrSetColour (wxColour (255, 255, 255)); } - optional effect = _content->caption->effect(); + optional effect = _caption->effect(); _force_effect->SetValue (static_cast(effect)); if (effect) { switch (*effect) { @@ -152,7 +153,7 @@ CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptrSetSelection (NONE); } - optional effect_colour = _content->caption->effect_colour(); + optional effect_colour = _caption->effect_colour(); _force_effect_colour->SetValue (static_cast(effect_colour)); if (effect_colour) { _effect_colour->SetColour (wxColour (effect_colour->r, effect_colour->g, effect_colour->b)); @@ -160,7 +161,7 @@ CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptrSetColour (wxColour (0, 0, 0)); } - optional fade_in = _content->caption->fade_in(); + optional fade_in = _caption->fade_in(); _force_fade_in->SetValue (static_cast(fade_in)); if (fade_in) { _fade_in->set (*fade_in, _content->active_video_frame_rate()); @@ -168,7 +169,7 @@ CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptrset (ContentTime(), _content->active_video_frame_rate()); } - optional fade_out = _content->caption->fade_out(); + optional fade_out = _caption->fade_out(); _force_fade_out->SetValue (static_cast(fade_out)); if (fade_out) { _fade_out->set (*fade_out, _content->active_video_frame_rate ()); @@ -176,7 +177,7 @@ CaptionAppearanceDialog::CaptionAppearanceDialog (wxWindow* parent, shared_ptrset (ContentTime(), _content->active_video_frame_rate ()); } - _outline_width->SetValue (_content->caption->outline_width ()); + _outline_width->SetValue (_caption->outline_width ()); _force_colour->Bind (wxEVT_CHECKBOX, bind (&CaptionAppearanceDialog::setup_sensitivity, this)); _force_effect_colour->Bind (wxEVT_CHECKBOX, bind (&CaptionAppearanceDialog::setup_sensitivity, this)); @@ -206,42 +207,42 @@ CaptionAppearanceDialog::apply () { if (_force_colour->GetValue ()) { wxColour const c = _colour->GetColour (); - _content->caption->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue())); + _caption->set_colour (dcp::Colour (c.Red(), c.Green(), c.Blue())); } else { - _content->caption->unset_colour (); + _caption->unset_colour (); } if (_force_effect->GetValue()) { switch (_effect->GetSelection()) { case NONE: - _content->caption->set_effect (dcp::NONE); + _caption->set_effect (dcp::NONE); break; case OUTLINE: - _content->caption->set_effect (dcp::BORDER); + _caption->set_effect (dcp::BORDER); break; case SHADOW: - _content->caption->set_effect (dcp::SHADOW); + _caption->set_effect (dcp::SHADOW); break; } } else { - _content->caption->unset_effect (); + _caption->unset_effect (); } if (_force_effect_colour->GetValue ()) { wxColour const ec = _effect_colour->GetColour (); - _content->caption->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue())); + _caption->set_effect_colour (dcp::Colour (ec.Red(), ec.Green(), ec.Blue())); } else { - _content->caption->unset_effect_colour (); + _caption->unset_effect_colour (); } if (_force_fade_in->GetValue ()) { - _content->caption->set_fade_in (_fade_in->get (_content->active_video_frame_rate ())); + _caption->set_fade_in (_fade_in->get (_content->active_video_frame_rate ())); } else { - _content->caption->unset_fade_in (); + _caption->unset_fade_in (); } if (_force_fade_out->GetValue ()) { - _content->caption->set_fade_out (_fade_out->get (_content->active_video_frame_rate ())); + _caption->set_fade_out (_fade_out->get (_content->active_video_frame_rate ())); } else { - _content->caption->unset_fade_out (); + _caption->unset_fade_out (); } - _content->caption->set_outline_width (_outline_width->GetValue ()); + _caption->set_outline_width (_outline_width->GetValue ()); if (_stream) { for (map::const_iterator i = _pickers.begin(); i != _pickers.end(); ++i) { @@ -272,7 +273,7 @@ CaptionAppearanceDialog::setup_sensitivity () _fade_in->Enable (_force_fade_in->GetValue ()); _fade_out->Enable (_force_fade_out->GetValue ()); - bool const can_outline_width = _effect->GetSelection() == OUTLINE && _content->caption->burn (); + bool const can_outline_width = _effect->GetSelection() == OUTLINE && _caption->burn (); _outline_width->Enable (can_outline_width); if (can_outline_width) { _outline_width->UnsetToolTip (); diff --git a/src/wx/caption_appearance_dialog.h b/src/wx/caption_appearance_dialog.h index ebb70047e..3fb993099 100644 --- a/src/wx/caption_appearance_dialog.h +++ b/src/wx/caption_appearance_dialog.h @@ -36,7 +36,7 @@ class wxWidget; class CaptionAppearanceDialog : public wxDialog { public: - CaptionAppearanceDialog (wxWindow* parent, boost::shared_ptr content); + CaptionAppearanceDialog (wxWindow* parent, boost::shared_ptr content, boost::shared_ptr caption); void apply (); @@ -60,6 +60,7 @@ private: std::map _pickers; boost::shared_ptr _content; + boost::shared_ptr _caption; boost::shared_ptr _stream; boost::signals2::scoped_connection _content_connection; diff --git a/src/wx/caption_panel.cc b/src/wx/caption_panel.cc index ca341167d..5a3ac39ed 100644 --- a/src/wx/caption_panel.cc +++ b/src/wx/caption_panel.cc @@ -47,6 +47,7 @@ CaptionPanel::CaptionPanel (ContentPanel* p) : ContentSubPanel (p, _("Captions")) , _caption_view (0) , _fonts_dialog (0) + , _original_type (CAPTION_OPEN) { wxBoxSizer* reference_sizer = new wxBoxSizer (wxVERTICAL); @@ -163,7 +164,7 @@ CaptionPanel::CaptionPanel (ContentPanel* p) _reference->Bind (wxEVT_CHECKBOX, boost::bind (&CaptionPanel::reference_clicked, this)); _use->Bind (wxEVT_CHECKBOX, boost::bind (&CaptionPanel::use_toggled, this)); - _type->Bind (wxEVT_CHOICE, boost::bind (&CaptionPanel::type_changed, this)); + _type->Bind (wxEVT_CHOICE, boost::bind (&CaptionPanel::type_changed, this)); _burn->Bind (wxEVT_CHECKBOX, boost::bind (&CaptionPanel::burn_toggled, this)); _x_offset->Bind (wxEVT_SPINCTRL, boost::bind (&CaptionPanel::x_offset_changed, this)); _y_offset->Bind (wxEVT_SPINCTRL, boost::bind (&CaptionPanel::y_offset_changed, this)); @@ -217,11 +218,11 @@ CaptionPanel::film_content_changed (int property) } setup_sensitivity (); } else if (property == CaptionContentProperty::USE) { - checked_set (_use, scs ? scs->caption->use() : false); + checked_set (_use, scs ? scs->caption_of_original_type(_original_type)->use() : false); setup_sensitivity (); } else if (property == CaptionContentProperty::TYPE) { if (scs) { - switch (scs->caption->type()) { + switch (scs->caption_of_original_type(_original_type)->type()) { case CAPTION_OPEN: _type->SetSelection (0); break; @@ -236,29 +237,29 @@ CaptionPanel::film_content_changed (int property) } setup_sensitivity (); } else if (property == CaptionContentProperty::BURN) { - checked_set (_burn, scs ? scs->caption->burn() : false); + checked_set (_burn, scs ? scs->caption_of_original_type(_original_type)->burn() : false); } else if (property == CaptionContentProperty::X_OFFSET) { - checked_set (_x_offset, scs ? lrint (scs->caption->x_offset() * 100) : 0); + checked_set (_x_offset, scs ? lrint (scs->caption_of_original_type(_original_type)->x_offset() * 100) : 0); } else if (property == CaptionContentProperty::Y_OFFSET) { - checked_set (_y_offset, scs ? lrint (scs->caption->y_offset() * 100) : 0); + checked_set (_y_offset, scs ? lrint (scs->caption_of_original_type(_original_type)->y_offset() * 100) : 0); } else if (property == CaptionContentProperty::X_SCALE) { - checked_set (_x_scale, scs ? lrint (scs->caption->x_scale() * 100) : 100); + checked_set (_x_scale, scs ? lrint (scs->caption_of_original_type(_original_type)->x_scale() * 100) : 100); } else if (property == CaptionContentProperty::Y_SCALE) { - checked_set (_y_scale, scs ? lrint (scs->caption->y_scale() * 100) : 100); + checked_set (_y_scale, scs ? lrint (scs->caption_of_original_type(_original_type)->y_scale() * 100) : 100); } else if (property == CaptionContentProperty::LINE_SPACING) { - checked_set (_line_spacing, scs ? lrint (scs->caption->line_spacing() * 100) : 100); + checked_set (_line_spacing, scs ? lrint (scs->caption_of_original_type(_original_type)->line_spacing() * 100) : 100); } else if (property == CaptionContentProperty::LANGUAGE) { - checked_set (_language, scs ? scs->caption->language() : ""); - } else if (property == DCPContentProperty::REFERENCE_SUBTITLE) { + checked_set (_language, scs ? scs->caption_of_original_type(_original_type)->language() : ""); + } else if (property == DCPContentProperty::REFERENCE_CAPTION) { if (scs) { shared_ptr dcp = dynamic_pointer_cast (scs); - checked_set (_reference, dcp ? dcp->reference_subtitle () : false); + checked_set (_reference, dcp ? dcp->reference_caption(_original_type) : false); } else { checked_set (_reference, false); } setup_sensitivity (); - } else if (property == DCPContentProperty::HAS_SUBTITLES) { + } else if (property == DCPContentProperty::CAPTIONS) { setup_sensitivity (); } } @@ -267,7 +268,7 @@ void CaptionPanel::use_toggled () { BOOST_FOREACH (shared_ptr i, _parent->selected_caption()) { - i->caption->set_use (_use->GetValue()); + i->caption_of_original_type(_original_type)->set_use (_use->GetValue()); } } @@ -277,10 +278,10 @@ CaptionPanel::type_changed () BOOST_FOREACH (shared_ptr i, _parent->selected_caption()) { switch (_type->GetSelection()) { case 0: - i->caption->set_type (CAPTION_OPEN); + i->caption_of_original_type(_original_type)->set_type (CAPTION_OPEN); break; case 1: - i->caption->set_type (CAPTION_CLOSED); + i->caption_of_original_type(_original_type)->set_type (CAPTION_CLOSED); break; } } @@ -290,7 +291,7 @@ void CaptionPanel::burn_toggled () { BOOST_FOREACH (shared_ptr i, _parent->selected_caption ()) { - i->caption->set_burn (_burn->GetValue()); + i->caption_of_original_type(_original_type)->set_burn (_burn->GetValue()); } } @@ -307,7 +308,7 @@ CaptionPanel::setup_sensitivity () shared_ptr dc = boost::dynamic_pointer_cast (i); shared_ptr dsc = boost::dynamic_pointer_cast (i); if (fc) { - if (fc->caption) { + if (!fc->caption.empty()) { ++ffmpeg_subs; ++any_subs; } @@ -325,7 +326,7 @@ CaptionPanel::setup_sensitivity () } string why_not; - bool const can_reference = dcp && dcp->can_reference_subtitle (why_not); + bool const can_reference = dcp && dcp->can_reference_caption (_original_type, why_not); setup_refer_button (_reference, _reference_note, dcp, can_reference, why_not); bool const reference = _reference->GetValue (); @@ -373,7 +374,7 @@ void CaptionPanel::x_offset_changed () { BOOST_FOREACH (shared_ptr i, _parent->selected_caption ()) { - i->caption->set_x_offset (_x_offset->GetValue() / 100.0); + i->caption_of_original_type(_original_type)->set_x_offset (_x_offset->GetValue() / 100.0); } } @@ -381,7 +382,7 @@ void CaptionPanel::y_offset_changed () { BOOST_FOREACH (shared_ptr i, _parent->selected_caption ()) { - i->caption->set_y_offset (_y_offset->GetValue() / 100.0); + i->caption_of_original_type(_original_type)->set_y_offset (_y_offset->GetValue() / 100.0); } } @@ -390,7 +391,7 @@ CaptionPanel::x_scale_changed () { ContentList c = _parent->selected_caption (); if (c.size() == 1) { - c.front()->caption->set_x_scale (_x_scale->GetValue() / 100.0); + c.front()->caption_of_original_type(_original_type)->set_x_scale (_x_scale->GetValue() / 100.0); } } @@ -398,7 +399,7 @@ void CaptionPanel::y_scale_changed () { BOOST_FOREACH (shared_ptr i, _parent->selected_caption ()) { - i->caption->set_y_scale (_y_scale->GetValue() / 100.0); + i->caption_of_original_type(_original_type)->set_y_scale (_y_scale->GetValue() / 100.0); } } @@ -406,7 +407,7 @@ void CaptionPanel::line_spacing_changed () { BOOST_FOREACH (shared_ptr i, _parent->selected_caption ()) { - i->caption->set_line_spacing (_line_spacing->GetValue() / 100.0); + i->caption_of_original_type(_original_type)->set_line_spacing (_line_spacing->GetValue() / 100.0); } } @@ -414,7 +415,7 @@ void CaptionPanel::language_changed () { BOOST_FOREACH (shared_ptr i, _parent->selected_caption ()) { - i->caption->set_language (wx_to_std (_language->GetValue())); + i->caption_of_original_type(_original_type)->set_language (wx_to_std (_language->GetValue())); } } @@ -432,7 +433,7 @@ CaptionPanel::content_selection_changed () film_content_changed (CaptionContentProperty::LANGUAGE); film_content_changed (CaptionContentProperty::FONTS); film_content_changed (CaptionContentProperty::TYPE); - film_content_changed (DCPContentProperty::REFERENCE_SUBTITLE); + film_content_changed (DCPContentProperty::REFERENCE_CAPTION); } void @@ -449,7 +450,7 @@ CaptionPanel::caption_view_clicked () shared_ptr decoder = decoder_factory (c.front(), _parent->film()->log(), false); if (decoder) { - _caption_view = new CaptionView (this, _parent->film(), c.front(), decoder, _parent->film_viewer()); + _caption_view = new CaptionView (this, _parent->film(), c.front(), c.front()->caption_of_original_type(_original_type), decoder, _parent->film_viewer()); _caption_view->Show (); } } @@ -465,7 +466,7 @@ CaptionPanel::fonts_dialog_clicked () ContentList c = _parent->selected_caption (); DCPOMATIC_ASSERT (c.size() == 1); - _fonts_dialog = new FontsDialog (this, c.front ()); + _fonts_dialog = new FontsDialog (this, c.front(), c.front()->caption_of_original_type(_original_type)); _fonts_dialog->Show (); } @@ -482,7 +483,7 @@ CaptionPanel::reference_clicked () return; } - d->set_reference_subtitle (_reference->GetValue ()); + d->set_reference_caption (_original_type, _reference->GetValue ()); } void @@ -491,7 +492,7 @@ CaptionPanel::appearance_dialog_clicked () ContentList c = _parent->selected_caption (); DCPOMATIC_ASSERT (c.size() == 1); - CaptionAppearanceDialog* d = new CaptionAppearanceDialog (this, c.front()); + CaptionAppearanceDialog* d = new CaptionAppearanceDialog (this, c.front(), c.front()->caption_of_original_type(_original_type)); if (d->ShowModal () == wxID_OK) { d->apply (); } diff --git a/src/wx/caption_panel.h b/src/wx/caption_panel.h index 38501e4d4..8adb85e15 100644 --- a/src/wx/caption_panel.h +++ b/src/wx/caption_panel.h @@ -69,4 +69,5 @@ private: wxButton* _fonts_dialog_button; FontsDialog* _fonts_dialog; wxButton* _appearance_dialog_button; + CaptionType _original_type; }; diff --git a/src/wx/caption_view.cc b/src/wx/caption_view.cc index c57f9161e..e6e63efff 100644 --- a/src/wx/caption_view.cc +++ b/src/wx/caption_view.cc @@ -35,7 +35,7 @@ using boost::shared_ptr; using boost::bind; using boost::dynamic_pointer_cast; -CaptionView::CaptionView (wxWindow* parent, shared_ptr film, shared_ptr content, shared_ptr decoder, FilmViewer* viewer) +CaptionView::CaptionView (wxWindow* parent, shared_ptr film, shared_ptr content, shared_ptr caption, shared_ptr decoder, FilmViewer* viewer) : wxDialog (parent, wxID_ANY, _("Captions"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) , _content (content) , _film_viewer (viewer) @@ -85,8 +85,14 @@ CaptionView::CaptionView (wxWindow* parent, shared_ptr film, shared_ptractive_frame_rate_change (content->position()); - decoder->caption->PlainStart.connect (bind (&CaptionView::data_start, this, _1)); - decoder->caption->Stop.connect (bind (&CaptionView::data_stop, this, _1)); + + /* Find the decoder that is being used for our CaptionContent and attach to it */ + BOOST_FOREACH (shared_ptr i, decoder->caption) { + if (i->content() == caption) { + i->PlainStart.connect (bind (&CaptionView::data_start, this, _1)); + i->Stop.connect (bind (&CaptionView::data_stop, this, _1)); + } + } while (!decoder->pass ()) {} SetSizerAndFit (sizer); } diff --git a/src/wx/caption_view.h b/src/wx/caption_view.h index 34fec1905..71c492ad6 100644 --- a/src/wx/caption_view.h +++ b/src/wx/caption_view.h @@ -29,7 +29,9 @@ class FilmViewer; class CaptionView : public wxDialog { public: - CaptionView (wxWindow *, boost::shared_ptr, boost::shared_ptr content, boost::shared_ptr, FilmViewer* viewer); + CaptionView ( + wxWindow *, boost::shared_ptr, boost::shared_ptr content, boost::shared_ptr caption, boost::shared_ptr, FilmViewer* viewer + ); private: void data_start (ContentTextCaption cts); diff --git a/src/wx/content_panel.cc b/src/wx/content_panel.cc index 5a4873716..40501f717 100644 --- a/src/wx/content_panel.cc +++ b/src/wx/content_panel.cc @@ -196,7 +196,7 @@ ContentPanel::selected_caption () ContentList sc; BOOST_FOREACH (shared_ptr i, selected ()) { - if (i->caption) { + if (!i->caption.empty()) { sc.push_back (i); } } @@ -447,7 +447,7 @@ ContentPanel::setup_sensitivity () _video_panel->Enable (_generally_sensitive && video_selection.size() > 0); _audio_panel->Enable (_generally_sensitive && audio_selection.size() > 0); - _caption_panel->Enable (_generally_sensitive && selection.size() == 1 && selection.front()->caption); + _caption_panel->Enable (_generally_sensitive && selection.size() == 1 && !selection.front()->caption.empty()); _timing_panel->Enable (_generally_sensitive); } diff --git a/src/wx/dcp_panel.cc b/src/wx/dcp_panel.cc index fcec93a31..51881030f 100644 --- a/src/wx/dcp_panel.cc +++ b/src/wx/dcp_panel.cc @@ -438,7 +438,7 @@ DCPPanel::film_content_changed (int property) property == VideoContentProperty::SCALE || property == DCPContentProperty::REFERENCE_VIDEO || property == DCPContentProperty::REFERENCE_AUDIO || - property == DCPContentProperty::REFERENCE_SUBTITLE) { + property == DCPContentProperty::REFERENCE_CAPTION) { setup_dcp_name (); setup_sensitivity (); } diff --git a/src/wx/fonts_dialog.cc b/src/wx/fonts_dialog.cc index 528b3999b..269dad678 100644 --- a/src/wx/fonts_dialog.cc +++ b/src/wx/fonts_dialog.cc @@ -34,9 +34,10 @@ using std::string; using std::cout; using boost::shared_ptr; -FontsDialog::FontsDialog (wxWindow* parent, shared_ptr content) +FontsDialog::FontsDialog (wxWindow* parent, shared_ptr content, shared_ptr caption) : wxDialog (parent, wxID_ANY, _("Fonts")) , _content (content) + , _caption (caption) { _fonts = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxSize (550, 200), wxLC_REPORT | wxLC_SINGLE_SEL); @@ -99,13 +100,14 @@ void FontsDialog::setup () { shared_ptr content = _content.lock (); - if (!content) { + shared_ptr caption = _caption.lock (); + if (!content || !caption) { return; } _fonts->DeleteAllItems (); size_t n = 0; - BOOST_FOREACH (shared_ptr i, content->caption->fonts ()) { + BOOST_FOREACH (shared_ptr i, caption->fonts ()) { wxListItem item; item.SetId (n); _fonts->InsertItem (item); @@ -138,14 +140,15 @@ void FontsDialog::edit_clicked () { shared_ptr content = _content.lock (); - if (!content) { + shared_ptr caption = _caption.lock (); + if (!content || !caption) { return; } int const item = _fonts->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); string const id = wx_to_std (_fonts->GetItemText (item, 0)); shared_ptr font; - BOOST_FOREACH (shared_ptr i, content->caption->fonts()) { + BOOST_FOREACH (shared_ptr i, caption->fonts()) { if (i->id() == id) { font = i; } diff --git a/src/wx/fonts_dialog.h b/src/wx/fonts_dialog.h index e251ddab1..6c6873ea3 100644 --- a/src/wx/fonts_dialog.h +++ b/src/wx/fonts_dialog.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2014-2016 Carl Hetherington + Copyright (C) 2014-2018 Carl Hetherington This file is part of DCP-o-matic. @@ -25,11 +25,12 @@ #include class Content; +class CaptionContent; class FontsDialog : public wxDialog { public: - FontsDialog (wxWindow* parent, boost::shared_ptr); + FontsDialog (wxWindow* parent, boost::shared_ptr, boost::shared_ptr caption); private: void setup (); @@ -38,6 +39,7 @@ private: void edit_clicked (); boost::weak_ptr _content; + boost::weak_ptr _caption; wxListCtrl* _fonts; wxButton* _edit; }; diff --git a/src/wx/player_information.cc b/src/wx/player_information.cc index aab8c2cd5..c2bf77259 100644 --- a/src/wx/player_information.cc +++ b/src/wx/player_information.cc @@ -131,7 +131,7 @@ PlayerInformation::triggered_update () if (dcp->audio && !dcp->audio->streams().empty()) { checked_set (_dcp[r++], wxString::Format(_("Audio channels: %d"), dcp->audio->streams().front()->channels())); } - if (dcp->caption) { + if (!dcp->caption.empty()) { checked_set (_dcp[r++], _("Subtitles: yes")); } else { checked_set (_dcp[r++], _("Subtitles: no")); diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc index d3b291a9b..f7a31117b 100644 --- a/src/wx/timeline.cc +++ b/src/wx/timeline.cc @@ -228,8 +228,8 @@ Timeline::recreate_views () _views.push_back (shared_ptr (new TimelineAudioContentView (*this, i))); } - if (i->caption) { - _views.push_back (shared_ptr (new TimelineTextContentView (*this, i))); + BOOST_FOREACH (shared_ptr j, i->caption) { + _views.push_back (shared_ptr (new TimelineTextContentView (*this, i, j))); } if (dynamic_pointer_cast (i)) { @@ -334,9 +334,9 @@ Timeline::assign_tracks () /* Tracks are: Video (mono or left-eye) Video (right-eye) - Subtitle 1 - Subtitle 2 - Subtitle N + Caption 1 + Caption 2 + Caption N Atmos Audio 1 Audio 2 @@ -373,9 +373,9 @@ Timeline::assign_tracks () _tracks = max (_tracks, 1); - /* Subtitle */ + /* Captions */ - int const subtitle_tracks = place (_views, _tracks); + int const caption_tracks = place (_views, _tracks); /* Atmos */ @@ -405,7 +405,7 @@ Timeline::assign_tracks () _labels_view->set_3d (have_3d); _labels_view->set_audio_tracks (audio_tracks); - _labels_view->set_subtitle_tracks (subtitle_tracks); + _labels_view->set_caption_tracks (caption_tracks); _labels_view->set_atmos (have_atmos); _time_axis_view->set_y (tracks()); diff --git a/src/wx/timeline_labels_view.cc b/src/wx/timeline_labels_view.cc index 43adfc936..af9cb771e 100644 --- a/src/wx/timeline_labels_view.cc +++ b/src/wx/timeline_labels_view.cc @@ -32,13 +32,13 @@ TimelineLabelsView::TimelineLabelsView (Timeline& tl) : TimelineView (tl) , _threed (true) , _audio_tracks (0) - , _subtitle_tracks (0) + , _caption_tracks (0) , _atmos (true) { wxString labels[] = { _("Video"), _("Audio"), - _("Subtitles"), + _("Captions"), _("Atmos") }; @@ -70,9 +70,9 @@ TimelineLabelsView::do_paint (wxGraphicsContext* gc, list > gc->DrawText (_("Video"), 0, (ty + fy) / 2 - 8); fy = ty; - if (_subtitle_tracks) { - ty = fy + _subtitle_tracks * h; - gc->DrawText (_("Subtitles"), 0, (ty + fy) / 2 - 8); + if (_caption_tracks) { + ty = fy + _caption_tracks * h; + gc->DrawText (_("Captions"), 0, (ty + fy) / 2 - 8); fy = ty; } @@ -101,9 +101,9 @@ TimelineLabelsView::set_audio_tracks (int n) } void -TimelineLabelsView::set_subtitle_tracks (int n) +TimelineLabelsView::set_caption_tracks (int n) { - _subtitle_tracks = n; + _caption_tracks = n; } void diff --git a/src/wx/timeline_labels_view.h b/src/wx/timeline_labels_view.h index 25302cc33..646d93a58 100644 --- a/src/wx/timeline_labels_view.h +++ b/src/wx/timeline_labels_view.h @@ -31,7 +31,7 @@ public: void set_3d (bool s); void set_audio_tracks (int n); - void set_subtitle_tracks (int n); + void set_caption_tracks (int n); void set_atmos (bool s); private: @@ -40,6 +40,6 @@ private: int _width; bool _threed; int _audio_tracks; - int _subtitle_tracks; + int _caption_tracks; bool _atmos; }; diff --git a/src/wx/timeline_text_content_view.cc b/src/wx/timeline_text_content_view.cc index bb874449e..30158941c 100644 --- a/src/wx/timeline_text_content_view.cc +++ b/src/wx/timeline_text_content_view.cc @@ -24,8 +24,9 @@ using boost::shared_ptr; -TimelineTextContentView::TimelineTextContentView (Timeline& tl, shared_ptr c) +TimelineTextContentView::TimelineTextContentView (Timeline& tl, shared_ptr c, shared_ptr caption) : TimelineContentView (tl, c) + , _caption (caption) { } @@ -55,5 +56,5 @@ TimelineTextContentView::active () const { shared_ptr c = _content.lock (); DCPOMATIC_ASSERT (c); - return c->caption && c->caption->use(); + return _caption->use(); } diff --git a/src/wx/timeline_text_content_view.h b/src/wx/timeline_text_content_view.h index 540a90607..4f69f2aef 100644 --- a/src/wx/timeline_text_content_view.h +++ b/src/wx/timeline_text_content_view.h @@ -21,6 +21,7 @@ #include "timeline_content_view.h" class TextContent; +class CaptionContent; /** @class TimelineTextContentView * @brief Timeline view for TextContent. @@ -28,10 +29,12 @@ class TextContent; class TimelineTextContentView : public TimelineContentView { public: - TimelineTextContentView (Timeline& tl, boost::shared_ptr c); + TimelineTextContentView (Timeline& tl, boost::shared_ptr, boost::shared_ptr); private: bool active () const; wxColour background_colour () const; wxColour foreground_colour () const; + + boost::shared_ptr _caption; }; diff --git a/src/wx/timing_panel.cc b/src/wx/timing_panel.cc index 9b516ccfa..9bb608cb2 100644 --- a/src/wx/timing_panel.cc +++ b/src/wx/timing_panel.cc @@ -282,7 +282,7 @@ TimingPanel::film_content_changed (int property) ++count_ac; content = i; } - if (i->caption && i->video_frame_rate()) { + if (!i->caption.empty() && i->video_frame_rate()) { ++count_sc; content = i; } diff --git a/test/closed_caption_test.cc b/test/closed_caption_test.cc index a837c1a07..75768eca8 100644 --- a/test/closed_caption_test.cc +++ b/test/closed_caption_test.cc @@ -37,7 +37,7 @@ BOOST_AUTO_TEST_CASE (closed_caption_test1) film->examine_and_add_content (content); BOOST_REQUIRE (!wait_for_jobs ()); - content->caption->set_type (CAPTION_CLOSED); + content->only_caption()->set_type (CAPTION_CLOSED); film->make_dcp (); BOOST_REQUIRE (!wait_for_jobs ()); diff --git a/test/dcp_subtitle_test.cc b/test/dcp_subtitle_test.cc index 48f88f8dc..3d4b57e3c 100644 --- a/test/dcp_subtitle_test.cc +++ b/test/dcp_subtitle_test.cc @@ -71,8 +71,8 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test) BOOST_CHECK_EQUAL (content->full_length().get(), DCPTime::from_seconds(2).get()); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->make_dcp (); BOOST_REQUIRE (!wait_for_jobs ()); @@ -91,7 +91,7 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_within_dcp_test) BOOST_REQUIRE (!wait_for_jobs ()); shared_ptr decoder (new DCPDecoder (content, film->log(), false)); - decoder->caption->PlainStart.connect (bind (store, _1)); + decoder->only_caption()->PlainStart.connect (bind (store, _1)); stored = optional (); while (!decoder->pass() && !stored) {} @@ -114,7 +114,7 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test2) BOOST_REQUIRE (!wait_for_jobs ()); shared_ptr decoder (new DCPSubtitleDecoder (content, film->log())); - decoder->caption->PlainStart.connect (bind (store, _1)); + decoder->only_caption()->PlainStart.connect (bind (store, _1)); stored = optional (); while (!decoder->pass ()) { @@ -142,7 +142,7 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test3) shared_ptr decoder (new DCPSubtitleDecoder (content, film->log())); stored = optional (); while (!decoder->pass ()) { - decoder->caption->PlainStart.connect (bind (store, _1)); + decoder->only_caption()->PlainStart.connect (bind (store, _1)); if (stored && stored->from() == ContentTime::from_seconds(0.08)) { list s = stored->subs; list::const_iterator i = s.begin (); @@ -171,8 +171,8 @@ BOOST_AUTO_TEST_CASE (dcp_subtitle_test4) film->examine_and_add_content (content2); BOOST_REQUIRE (!wait_for_jobs ()); - content->caption->add_font (shared_ptr (new Font ("font1"))); - content2->caption->add_font (shared_ptr (new Font ("font2"))); + content->only_caption()->add_font (shared_ptr (new Font ("font1"))); + content2->only_caption()->add_font (shared_ptr (new Font ("font2"))); film->make_dcp (); BOOST_REQUIRE (!wait_for_jobs ()); diff --git a/test/ffmpeg_encoder_test.cc b/test/ffmpeg_encoder_test.cc index d9dd0f383..837541f9d 100644 --- a/test/ffmpeg_encoder_test.cc +++ b/test/ffmpeg_encoder_test.cc @@ -124,9 +124,9 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test6) shared_ptr s (new TextCaptionFileContent (film, "test/data/subrip2.srt")); film->examine_and_add_content (s); BOOST_REQUIRE (!wait_for_jobs ()); - s->caption->set_colour (dcp::Colour (255, 255, 0)); - s->caption->set_effect (dcp::SHADOW); - s->caption->set_effect_colour (dcp::Colour (0, 255, 255)); + s->only_caption()->set_colour (dcp::Colour (255, 255, 0)); + s->only_caption()->set_effect (dcp::SHADOW); + s->only_caption()->set_effect_colour (dcp::Colour (0, 255, 255)); film->write_metadata(); shared_ptr job (new TranscodeJob (film)); @@ -149,9 +149,9 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_prores_test7) shared_ptr s (new TextCaptionFileContent (film, "test/data/subrip.srt")); film->examine_and_add_content (s); BOOST_REQUIRE (!wait_for_jobs ()); - s->caption->set_colour (dcp::Colour (255, 255, 0)); - s->caption->set_effect (dcp::SHADOW); - s->caption->set_effect_colour (dcp::Colour (0, 255, 255)); + s->only_caption()->set_colour (dcp::Colour (255, 255, 0)); + s->only_caption()->set_effect (dcp::SHADOW); + s->only_caption()->set_effect_colour (dcp::Colour (0, 255, 255)); shared_ptr job (new TranscodeJob (film)); FFmpegEncoder encoder (film, job, "build/test/ffmpeg_encoder_prores_test7.mov", FFmpegEncoder::FORMAT_PRORES, false); @@ -175,9 +175,9 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test2) shared_ptr s (new TextCaptionFileContent (film, "test/data/subrip2.srt")); film->examine_and_add_content (s); BOOST_REQUIRE (!wait_for_jobs ()); - s->caption->set_colour (dcp::Colour (255, 255, 0)); - s->caption->set_effect (dcp::SHADOW); - s->caption->set_effect_colour (dcp::Colour (0, 255, 255)); + s->only_caption()->set_colour (dcp::Colour (255, 255, 0)); + s->only_caption()->set_effect (dcp::SHADOW); + s->only_caption()->set_effect_colour (dcp::Colour (0, 255, 255)); film->write_metadata(); shared_ptr job (new TranscodeJob (film)); @@ -200,9 +200,9 @@ BOOST_AUTO_TEST_CASE (ffmpeg_encoder_h264_test3) shared_ptr s (new TextCaptionFileContent (film, "test/data/subrip.srt")); film->examine_and_add_content (s); BOOST_REQUIRE (!wait_for_jobs ()); - s->caption->set_colour (dcp::Colour (255, 255, 0)); - s->caption->set_effect (dcp::SHADOW); - s->caption->set_effect_colour (dcp::Colour (0, 255, 255)); + s->only_caption()->set_colour (dcp::Colour (255, 255, 0)); + s->only_caption()->set_effect (dcp::SHADOW); + s->only_caption()->set_effect_colour (dcp::Colour (0, 255, 255)); film->write_metadata(); shared_ptr job (new TranscodeJob (film)); diff --git a/test/player_test.cc b/test/player_test.cc index 510083236..bc4af970e 100644 --- a/test/player_test.cc +++ b/test/player_test.cc @@ -211,7 +211,7 @@ BOOST_AUTO_TEST_CASE (player_seek_test) shared_ptr dcp (new DCPContent (film, private_data / "awkward_subs")); film->examine_and_add_content (dcp, true); BOOST_REQUIRE (!wait_for_jobs ()); - dcp->caption->set_use (true); + dcp->only_caption()->set_use (true); shared_ptr player (new Player (film, film->playlist())); player->set_fast (); @@ -242,7 +242,7 @@ BOOST_AUTO_TEST_CASE (player_seek_test2) shared_ptr dcp (new DCPContent (film, private_data / "awkward_subs2")); film->examine_and_add_content (dcp, true); BOOST_REQUIRE (!wait_for_jobs ()); - dcp->caption->set_use (true); + dcp->only_caption()->set_use (true); shared_ptr player (new Player (film, film->playlist())); player->set_fast (); diff --git a/test/remake_with_subtitle_test.cc b/test/remake_with_subtitle_test.cc index 5c371e855..bef1bc36a 100644 --- a/test/remake_with_subtitle_test.cc +++ b/test/remake_with_subtitle_test.cc @@ -37,14 +37,14 @@ BOOST_AUTO_TEST_CASE (remake_with_subtitle_test) shared_ptr content = dynamic_pointer_cast(content_factory(film, private_data / "prophet_short_clip.mkv").front()); film->examine_and_add_content (content); BOOST_REQUIRE (!wait_for_jobs ()); - content->caption->set_burn (true); - content->caption->set_use (true); + content->only_caption()->set_burn (true); + content->only_caption()->set_use (true); film->make_dcp (); BOOST_REQUIRE (!wait_for_jobs ()); boost::filesystem::remove_all (film->dir (film->dcp_name(), false)); - content->caption->set_use (false); + content->only_caption()->set_use (false); film->make_dcp (); BOOST_REQUIRE (!wait_for_jobs ()); diff --git a/test/srt_subtitle_test.cc b/test/srt_subtitle_test.cc index 28ef9606f..6975403ba 100644 --- a/test/srt_subtitle_test.cc +++ b/test/srt_subtitle_test.cc @@ -51,8 +51,8 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test) film->examine_and_add_content (content); wait_for_jobs (); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->make_dcp (); wait_for_jobs (); @@ -73,10 +73,10 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test2) film->examine_and_add_content (content); wait_for_jobs (); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); /* Use test/data/subrip2.srt as if it were a font file */ - content->caption->fonts().front()->set_file (FontFiles::NORMAL, "test/data/subrip2.srt"); + content->only_caption()->fonts().front()->set_file (FontFiles::NORMAL, "test/data/subrip2.srt"); film->make_dcp (); wait_for_jobs (); @@ -108,8 +108,8 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test3) film->examine_and_add_content (content); wait_for_jobs (); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->make_dcp (); wait_for_jobs (); @@ -126,8 +126,8 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test4) film->set_name ("frobozz"); film->set_interop (false); shared_ptr content (new TextCaptionFileContent (film, "test/data/subrip2.srt")); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->examine_and_add_content (content); wait_for_jobs (); film->make_dcp (); @@ -147,8 +147,8 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test5) film->set_interop (true); film->set_sequence (false); shared_ptr content (new TextCaptionFileContent (film, "test/data/subrip2.srt")); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->examine_and_add_content (content); film->examine_and_add_content (content); wait_for_jobs (); @@ -165,8 +165,8 @@ BOOST_AUTO_TEST_CASE (srt_subtitle_test6) shared_ptr film = new_test_film2 ("srt_subtitle_test6"); film->set_interop (false); shared_ptr content (new TextCaptionFileContent (film, "test/data/frames.srt")); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->examine_and_add_content (content); BOOST_REQUIRE (!wait_for_jobs ()); film->make_dcp (); diff --git a/test/ssa_subtitle_test.cc b/test/ssa_subtitle_test.cc index 6d4eecd92..57813b944 100644 --- a/test/ssa_subtitle_test.cc +++ b/test/ssa_subtitle_test.cc @@ -51,8 +51,8 @@ BOOST_AUTO_TEST_CASE (ssa_subtitle_test1) film->examine_and_add_content (content); wait_for_jobs (); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->make_dcp (); wait_for_jobs (); diff --git a/test/subtitle_reel_number_test.cc b/test/subtitle_reel_number_test.cc index a82094a73..7cb184477 100644 --- a/test/subtitle_reel_number_test.cc +++ b/test/subtitle_reel_number_test.cc @@ -46,8 +46,8 @@ BOOST_AUTO_TEST_CASE (subtitle_reel_number_test) shared_ptr content (new TextCaptionFileContent (film, "test/data/subrip5.srt")); film->examine_and_add_content (content); BOOST_REQUIRE (!wait_for_jobs ()); - content->caption->set_use (true); - content->caption->set_burn (false); + content->only_caption()->set_use (true); + content->only_caption()->set_burn (false); film->set_reel_type (REELTYPE_BY_LENGTH); film->set_interop (true); film->set_reel_length (1024 * 1024 * 512); diff --git a/test/vf_test.cc b/test/vf_test.cc index 3b3077242..5cded6e25 100644 --- a/test/vf_test.cc +++ b/test/vf_test.cc @@ -57,14 +57,16 @@ BOOST_AUTO_TEST_CASE (vf_test1) string why_not; BOOST_CHECK (!dcp->can_reference_video(why_not)); BOOST_CHECK (!dcp->can_reference_audio(why_not)); - BOOST_CHECK (!dcp->can_reference_subtitle(why_not)); + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_OPEN, why_not)); + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_CLOSED, why_not)); /* Multi-reel DCP can be referenced if we are using by-video-content */ film->set_reel_type (REELTYPE_BY_VIDEO_CONTENT); BOOST_CHECK (dcp->can_reference_video(why_not)); BOOST_CHECK (dcp->can_reference_audio(why_not)); - /* (but reels_test2 has no subtitles to reference) */ - BOOST_CHECK (!dcp->can_reference_subtitle(why_not)); + /* (but reels_test2 has no captions to reference) */ + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_OPEN, why_not)); + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_CLOSED, why_not)); shared_ptr other (new FFmpegContent (film, "test/data/test.mp4")); film->examine_and_add_content (other); @@ -74,14 +76,16 @@ BOOST_AUTO_TEST_CASE (vf_test1) other->set_position (DCPTime (0)); BOOST_CHECK (!dcp->can_reference_video(why_not)); BOOST_CHECK (!dcp->can_reference_audio(why_not)); - BOOST_CHECK (!dcp->can_reference_subtitle(why_not)); + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_OPEN, why_not)); + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_CLOSED, why_not)); /* This should not be considered an overlap */ other->set_position (dcp->end ()); BOOST_CHECK (dcp->can_reference_video(why_not)); BOOST_CHECK (dcp->can_reference_audio(why_not)); - /* (reels_test2 has no subtitles to reference) */ - BOOST_CHECK (!dcp->can_reference_subtitle(why_not)); + /* (reels_test2 has no captions to reference) */ + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_OPEN, why_not)); + BOOST_CHECK (!dcp->can_reference_caption(CAPTION_CLOSED, why_not)); } /** Make a OV with video and audio and a VF referencing the OV and adding subs */ -- 2.30.2