diff options
| author | Carl Hetherington <cth@carlh.net> | 2021-03-23 00:50:11 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2021-03-31 22:48:53 +0200 |
| commit | e0a70cd5cfb11fc2de167f3146acdd437a6faa82 (patch) | |
| tree | 022ceef78d7daa12bccf519ebab0db5e23a3feb6 /src/lib | |
| parent | 8640da877450479a85c73b2a921897d83b478c84 (diff) | |
Put subtitle language back into content from the film (#1930).
This also adds the main/additional language flag.
Of all the considerations about how to specify subtitle language,
the most important seems to be that the language specification happens
for the content where the language is; i.e. in the content text tab.
Diffstat (limited to 'src/lib')
| -rw-r--r-- | src/lib/content.h | 2 | ||||
| -rw-r--r-- | src/lib/film.cc | 94 | ||||
| -rw-r--r-- | src/lib/film.h | 9 | ||||
| -rw-r--r-- | src/lib/reel_writer.cc | 10 | ||||
| -rw-r--r-- | src/lib/subtitle_encoder.cc | 10 | ||||
| -rw-r--r-- | src/lib/text_content.cc | 49 | ||||
| -rw-r--r-- | src/lib/text_content.h | 25 | ||||
| -rw-r--r-- | src/lib/writer.cc | 12 |
8 files changed, 113 insertions, 98 deletions
diff --git a/src/lib/content.h b/src/lib/content.h index b8626e212..567cd5c1f 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -196,7 +196,7 @@ public: std::shared_ptr<VideoContent> video; std::shared_ptr<AudioContent> audio; - std::list<std::shared_ptr<TextContent> > text; + std::list<std::shared_ptr<TextContent>> text; std::shared_ptr<AtmosContent> atmos; std::shared_ptr<TextContent> only_text () const; diff --git a/src/lib/film.cc b/src/lib/film.cc index 46074e2ad..763af8f8a 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -496,9 +496,6 @@ Film::metadata (bool with_content_paths) const } root->add_child("UserExplicitContainer")->add_child_text(_user_explicit_container ? "1" : "0"); root->add_child("UserExplicitResolution")->add_child_text(_user_explicit_resolution ? "1" : "0"); - for (auto i: _subtitle_languages) { - root->add_child("SubtitleLanguage")->add_child_text(i.to_string()); - } _playlist->as_xml (root->add_child ("Playlist"), with_content_paths); return doc; @@ -680,10 +677,6 @@ Film::read_metadata (optional<boost::filesystem::path> path) _user_explicit_container = f.optional_bool_child("UserExplicitContainer").get_value_or(true); _user_explicit_resolution = f.optional_bool_child("UserExplicitResolution").get_value_or(true); - for (auto i: f.node_children("SubtitleLanguage")) { - _subtitle_languages.push_back (dcp::LanguageTag(i->content())); - } - list<string> notes; _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes); @@ -692,41 +685,6 @@ Film::read_metadata (optional<boost::filesystem::path> path) set_backtrace_file (file ("backtrace.txt")); } - /* Around 2.15.108 we removed subtitle language state from the text content and the ISDCF - * metadata and put it into the Film instead. If we've loaded an old Film let's try and fish - * out the settings from where they were so that they don't get lost. - */ - - optional<dcp::LanguageTag> found_language; - - for (auto i: f.node_child("Playlist")->node_children("Content")) { - auto text = i->optional_node_child("Text"); - if (text && text->optional_string_child("Language") && !found_language) { - try { - found_language = dcp::LanguageTag(text->string_child("Language")); - } catch (...) {} - } - } - - if (_state_version >= 9) { - auto isdcf_language = f.node_child("ISDCFMetadata")->optional_string_child("SubtitleLanguage"); - if (isdcf_language && !found_language) { - try { - found_language = dcp::LanguageTag(*isdcf_language); - } catch (...) { - try { - found_language = dcp::LanguageTag(boost::algorithm::to_lower_copy(*isdcf_language)); - } catch (...) { - - } - } - } - } - - if (found_language) { - _subtitle_languages.push_back (*found_language); - } - _dirty = false; return notes; } @@ -793,6 +751,30 @@ Film::mapped_audio_channels () const return mapped; } + +pair<optional<dcp::LanguageTag>, vector<dcp::LanguageTag>> +Film::subtitle_languages () const +{ + pair<optional<dcp::LanguageTag>, vector<dcp::LanguageTag>> result; + for (auto i: content()) { + for (auto j: i->text) { + if (j->use() && j->type() == TextType::OPEN_SUBTITLE && j->language()) { + if (j->language_is_additional()) { + result.second.push_back (j->language().get()); + } else { + result.first = j->language().get(); + } + } + } + } + + std::sort (result.second.begin(), result.second.end()); + auto last = std::unique (result.second.begin(), result.second.end()); + result.second.erase (last, result.second.end()); + return result; +} + + /** @return a ISDCF-compliant name for a DCP of this film */ string Film::isdcf_name (bool if_created_now) const @@ -924,8 +906,9 @@ Film::isdcf_name (bool if_created_now) const } } - if (!_subtitle_languages.empty()) { - auto lang = _subtitle_languages.front().language().get_value_or("en").subtag(); + auto sublangs = subtitle_languages(); + if (sublangs.first && sublangs.first->language()) { + auto lang = sublangs.first->language()->subtag(); if (burnt_in) { transform (lang.begin(), lang.end(), lang.begin(), ::tolower); } else { @@ -2038,29 +2021,6 @@ Film::set_luminance (optional<dcp::Luminance> l) void -Film::set_subtitle_language (dcp::LanguageTag language) -{ - set_subtitle_languages ({language}); -} - - -void -Film::unset_subtitle_language () -{ - FilmChangeSignaller ch (this, Property::SUBTITLE_LANGUAGES); - _subtitle_languages.clear(); -} - - -void -Film::set_subtitle_languages (vector<dcp::LanguageTag> languages) -{ - FilmChangeSignaller ch (this, Property::SUBTITLE_LANGUAGES); - _subtitle_languages = languages; -} - - -void Film::set_facility (optional<string> f) { FilmChangeSignaller ch (this, Property::FACILITY); diff --git a/src/lib/film.h b/src/lib/film.h index 9b45fd073..9feb5d0d3 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -184,6 +184,7 @@ public: std::list<dcpomatic::DCPTimePeriod> reels () const; std::list<int> mapped_audio_channels () const; + std::pair<boost::optional<dcp::LanguageTag>, std::vector<dcp::LanguageTag>> subtitle_languages () const; std::string content_summary (dcpomatic::DCPTimePeriod period) const; @@ -376,10 +377,6 @@ public: return _luminance; } - std::vector<dcp::LanguageTag> subtitle_languages () const { - return _subtitle_languages; - } - /* SET */ void set_directory (boost::filesystem::path); @@ -421,9 +418,6 @@ public: void set_facility (boost::optional<std::string> f = boost::none); void set_distributor (boost::optional<std::string> d = boost::none); void set_luminance (boost::optional<dcp::Luminance> l = boost::none); - void set_subtitle_language (dcp::LanguageTag language); - void unset_subtitle_language (); - void set_subtitle_languages (std::vector<dcp::LanguageTag> languages); void add_ffoc_lfoc (Markers& markers) const; @@ -529,7 +523,6 @@ private: boost::optional<std::string> _distributor; boost::optional<std::string> _facility; boost::optional<dcp::Luminance> _luminance; - std::vector<dcp::LanguageTag> _subtitle_languages; int _state_version; diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 366e6edc6..2c55f0f06 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -623,8 +623,8 @@ ReelWriter::create_reel_text ( if (subtitle) { /* We have a subtitle asset that we either made or are referencing */ - if (!film()->subtitle_languages().empty()) { - subtitle->set_language (film()->subtitle_languages().front()); + if (auto main_language = film()->subtitle_languages().first) { + subtitle->set_language (*main_language); } } else if (ensure_subtitles) { /* We had no subtitle asset, but we've been asked to make sure there is one */ @@ -776,7 +776,7 @@ ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track) const auto s = make_shared<dcp::InteropSubtitleAsset>(); s->set_movie_title (film()->name()); if (type == TextType::OPEN_SUBTITLE) { - s->set_language (lang.empty() ? "Unknown" : lang.front().to_string()); + s->set_language (lang.first ? lang.first->to_string() : "Unknown"); } else if (!track->language.empty()) { s->set_language (track->language); } @@ -786,8 +786,8 @@ ReelWriter::empty_text_asset (TextType type, optional<DCPTextTrack> track) const auto s = make_shared<dcp::SMPTESubtitleAsset>(); s->set_content_title_text (film()->name()); s->set_metadata (mxf_metadata()); - if (type == TextType::OPEN_SUBTITLE && !lang.empty()) { - s->set_language (lang.front()); + if (type == TextType::OPEN_SUBTITLE && lang.first) { + s->set_language (*lang.first); } else if (track && !track->language.empty()) { s->set_language (dcp::LanguageTag(track->language)); } diff --git a/src/lib/subtitle_encoder.cc b/src/lib/subtitle_encoder.cc index 3721ee02b..b61876ad6 100644 --- a/src/lib/subtitle_encoder.cc +++ b/src/lib/subtitle_encoder.cc @@ -135,20 +135,20 @@ SubtitleEncoder::text (PlayerText subs, TextType type, optional<DCPTextTrack> tr if (!_assets[_reel_index].first) { shared_ptr<dcp::SubtitleAsset> asset; - vector<dcp::LanguageTag> lang = _film->subtitle_languages (); + auto lang = _film->subtitle_languages (); if (_film->interop ()) { auto s = make_shared<dcp::InteropSubtitleAsset>(); s->set_movie_title (_film->name()); - if (!lang.empty()) { - s->set_language (lang.front().to_string()); + if (lang.first) { + s->set_language (lang.first->to_string()); } s->set_reel_number (raw_convert<string>(_reel_index + 1)); _assets[_reel_index].first = s; } else { auto s = make_shared<dcp::SMPTESubtitleAsset>(); s->set_content_title_text (_film->name()); - if (!lang.empty()) { - s->set_language (lang.front()); + if (lang.first) { + s->set_language (*lang.first); } else if (!track->language.empty()) { s->set_language (dcp::LanguageTag(track->language)); } diff --git a/src/lib/text_content.cc b/src/lib/text_content.cc index 0c25e5696..c86150881 100644 --- a/src/lib/text_content.cc +++ b/src/lib/text_content.cc @@ -18,6 +18,7 @@ */ + #include "text_content.h" #include "util.h" #include "exceptions.h" @@ -30,6 +31,7 @@ #include "i18n.h" + using std::string; using std::vector; using std::cout; @@ -41,6 +43,7 @@ using boost::optional; using dcp::raw_convert; using namespace dcpomatic; + int const TextContentProperty::X_OFFSET = 500; int const TextContentProperty::Y_OFFSET = 501; int const TextContentProperty::X_SCALE = 502; @@ -57,6 +60,9 @@ int const TextContentProperty::FADE_OUT = 512; int const TextContentProperty::OUTLINE_WIDTH = 513; int const TextContentProperty::TYPE = 514; int const TextContentProperty::DCP_TRACK = 515; +int const TextContentProperty::LANGUAGE = 516; +int const TextContentProperty::LANGUAGE_IS_ADDITIONAL = 517; + TextContent::TextContent (Content* parent, TextType type, TextType original_type) : ContentPart (parent) @@ -95,9 +101,7 @@ TextContent::from_xml (Content* parent, cxml::ConstNodePtr node, int version) if (!node->optional_number_child<double>("SubtitleXOffset") && !node->optional_number_child<double>("SubtitleOffset")) { return {}; } - list<shared_ptr<TextContent>> c; - c.push_back (make_shared<TextContent>(parent, node, version)); - return c; + return { make_shared<TextContent>(parent, node, version) }; } if (!node->optional_node_child("Text")) { @@ -124,6 +128,7 @@ TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version) , _outline_width (node->optional_number_child<int>("OutlineWidth").get_value_or(4)) , _type (TextType::OPEN_SUBTITLE) , _original_type (TextType::OPEN_SUBTITLE) + , _language ("en-US") { if (version >= 37) { _use = node->bool_child ("Use"); @@ -231,6 +236,13 @@ TextContent::TextContent (Content* parent, cxml::ConstNodePtr node, int version) if (dt) { _dcp_track = DCPTextTrack (dt); } + + auto lang = node->optional_node_child("Language"); + if (lang) { + _language = dcp::LanguageTag(lang->content()); + auto add = lang->optional_bool_attribute("Additional"); + _language_is_additional = add && *add; + } } TextContent::TextContent (Content* parent, vector<shared_ptr<Content>> c) @@ -290,6 +302,14 @@ TextContent::TextContent (Content* parent, vector<shared_ptr<Content>> c) throw JoinError (_("Content to be joined must use the same DCP track.")); } + if (c[i]->only_text()->language() != ref->language()) { + throw JoinError (_("Content to be joined must use the same text language.")); + } + + if (c[i]->only_text()->language_is_additional() != ref->language_is_additional()) { + throw JoinError (_("Content to be joined must both be main subtitle languages or both additional.")); + } + auto j = ref_fonts.begin (); auto k = fonts.begin (); @@ -316,6 +336,8 @@ TextContent::TextContent (Content* parent, vector<shared_ptr<Content>> c) _type = ref->type (); _original_type = ref->original_type (); _dcp_track = ref->dcp_track (); + _language = ref->language (); + _language_is_additional = ref->language_is_additional (); connect_to_fonts (); } @@ -375,6 +397,11 @@ TextContent::as_xml (xmlpp::Node* root) const if (_dcp_track) { _dcp_track->as_xml(text->add_child("DCPTrack")); } + if (_language) { + auto lang = text->add_child("Language"); + lang->add_child_text (_language->to_string()); + lang->set_attribute ("Additional", _language_is_additional ? "1" : "0"); + } } string @@ -400,7 +427,7 @@ TextContent::identifier () const s += "_" + f->file().get_value_or("Default").string(); } - /* The DCP track is for metadata only, and doesn't affect how this content looks */ + /* The DCP track and language are for metadata only, and don't affect how this content looks */ return s; } @@ -560,6 +587,18 @@ TextContent::unset_dcp_track () } void +TextContent::set_language (optional<dcp::LanguageTag> language) +{ + maybe_set (_language, language, TextContentProperty::LANGUAGE); +} + +void +TextContent::set_language_is_additional (bool additional) +{ + maybe_set (_language_is_additional, additional, TextContentProperty::LANGUAGE_IS_ADDITIONAL); +} + +void TextContent::take_settings_from (shared_ptr<const TextContent> c) { set_use (c->_use); @@ -595,4 +634,6 @@ TextContent::take_settings_from (shared_ptr<const TextContent> c) } else { unset_dcp_track (); } + set_language (c->_language); + set_language_is_additional (c->_language_is_additional); } diff --git a/src/lib/text_content.h b/src/lib/text_content.h index e566d0552..4c6918a42 100644 --- a/src/lib/text_content.h +++ b/src/lib/text_content.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2020 Carl Hetherington <cth@carlh.net> + Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net> This file is part of DCP-o-matic. @@ -18,15 +18,19 @@ */ + #ifndef DCPOMATIC_CAPTION_CONTENT_H #define DCPOMATIC_CAPTION_CONTENT_H + #include "content_part.h" #include "dcp_text_track.h" #include <libcxml/cxml.h> +#include <dcp/language_tag.h> #include <dcp/types.h> #include <boost/signals2.hpp> + namespace dcpomatic { class Font; } @@ -50,8 +54,11 @@ public: static int const OUTLINE_WIDTH; static int const TYPE; static int const DCP_TRACK; + static int const LANGUAGE; + static int const LANGUAGE_IS_ADDITIONAL; }; + /** @class TextContent * @brief Description of how some text content should be presented. * @@ -92,6 +99,8 @@ public: void set_type (TextType type); void set_dcp_track (DCPTextTrack track); void unset_dcp_track (); + void set_language (boost::optional<dcp::LanguageTag> language = boost::none); + void set_language_is_additional (bool additional); bool use () const { boost::mutex::scoped_lock lm (_mutex); @@ -178,7 +187,17 @@ public: return _dcp_track; } - static std::list<std::shared_ptr<TextContent> > from_xml (Content* parent, cxml::ConstNodePtr, int version); + boost::optional<dcp::LanguageTag> language () const { + boost::mutex::scoped_lock lm (_mutex); + return _language; + } + + bool language_is_additional () const { + boost::mutex::scoped_lock lm (_mutex); + return _language_is_additional; + } + + static std::list<std::shared_ptr<TextContent>> from_xml (Content* parent, cxml::ConstNodePtr, int version); private: friend struct ffmpeg_pts_offset_test; @@ -219,6 +238,8 @@ private: TextType _original_type; /** the track of closed captions that this content should be put in, or empty to put in the default (only) track */ boost::optional<DCPTextTrack> _dcp_track; + boost::optional<dcp::LanguageTag> _language; + bool _language_is_additional = false; }; #endif diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 839156d34..3448c5bca 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -663,9 +663,9 @@ Writer::finish (boost::filesystem::path output_dcp) cpl->set_main_picture_active_area (active_area); } - vector<dcp::LanguageTag> sl = film()->subtitle_languages(); - if (sl.size() > 1) { - cpl->set_additional_subtitle_languages(std::vector<dcp::LanguageTag>(sl.begin() + 1, sl.end())); + auto sl = film()->subtitle_languages().second; + if (!sl.empty()) { + cpl->set_additional_subtitle_languages(sl); } auto signer = Config::instance()->signer_chain(); @@ -709,10 +709,10 @@ Writer::write_cover_sheet (boost::filesystem::path output_dcp) boost::algorithm::replace_all (text, "$AUDIO_LANGUAGE", film()->isdcf_metadata().audio_language); auto subtitle_languages = film()->subtitle_languages(); - if (subtitle_languages.empty()) { - boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", "None"); + if (subtitle_languages.first) { + boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_languages.first->description()); } else { - boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", subtitle_languages.front().description()); + boost::algorithm::replace_all (text, "$SUBTITLE_LANGUAGE", "None"); } boost::uintmax_t size = 0; |
