Add a hint about >79 length subtitles.
[dcpomatic.git] / src / lib / hints.cc
index 9781f24bb9e01bcd0cf1bc86094bda48248be2dc..ea524df501f13a9576392eda508105a103a24c5e 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2016-2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2016-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #include <dcp/reel.h>
 #include <dcp/reel_closed_caption_asset.h>
 #include <dcp/reel_subtitle_asset.h>
-#include <boost/foreach.hpp>
 #include <boost/algorithm/string.hpp>
 #include <iostream>
 
 #include "i18n.h"
 
-using std::vector;
-using std::string;
-using std::pair;
-using std::min;
-using std::max;
 using std::cout;
-using boost::shared_ptr;
-using boost::weak_ptr;
+using std::make_shared;
+using std::max;
+using std::min;
+using std::pair;
+using std::shared_ptr;
+using std::string;
+using std::vector;
+using std::weak_ptr;
 using boost::optional;
 using boost::bind;
 using namespace dcpomatic;
@@ -68,17 +68,14 @@ using namespace boost::placeholders;
 #define SIZE_SLACK 4096
 
 
+/* When writing hints:
+ * - put quotation marks around the name of a GUI tab that you are referring to (e.g. "DCP" or "DCP→Video" tab)
+ */
+
+
 Hints::Hints (weak_ptr<const Film> film)
        : WeakConstFilm (film)
        , _writer (new Writer(film, weak_ptr<Job>(), true))
-       , _long_ccap (false)
-       , _overlap_ccap (false)
-       , _too_many_ccap_lines (false)
-       , _early_subtitle (false)
-       , _short_subtitle (false)
-       , _subtitles_too_close (false)
-       , _too_many_subtitle_lines (false)
-       , _long_subtitle (false)
        , _stop (false)
 {
 
@@ -114,7 +111,7 @@ Hints::check_few_audio_channels ()
 void
 Hints::check_upmixers ()
 {
-       AudioProcessor const * ap = film()->audio_processor();
+       auto ap = film()->audio_processor();
        if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
                hint (_("You are using DCP-o-matic's stereo-to-5.1 upmixer.  This is experimental and may result in poor-quality audio.  If you continue, you should listen to the resulting DCP in a cinema to make sure that it sounds good."));
        }
@@ -126,7 +123,7 @@ Hints::check_incorrect_container ()
 {
        int narrower_than_scope = 0;
        int scope = 0;
-       BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
+       for (auto i: film()->content()) {
                if (i->video) {
                        Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
                        if (r && r->id() == "239") {
@@ -152,8 +149,8 @@ Hints::check_incorrect_container ()
 void
 Hints::check_unusual_container ()
 {
-       string const film_container = film()->container()->id();
-       if (film_container != "185" && film_container != "239" && film_container != "190") {
+       auto const film_container = film()->container()->id();
+       if (film_container != "185" && film_container != "239") {
                hint (_("Your DCP uses an unusual container ratio.  This may cause problems on some projectors.  If possible, use Flat or Scope for the DCP container ratio"));
        }
 }
@@ -171,7 +168,7 @@ Hints::check_high_j2k_bandwidth ()
 void
 Hints::check_frame_rate ()
 {
-       shared_ptr<const Film> f = film ();
+       auto f = film ();
        switch (f->video_frame_rate()) {
        case 24:
                /* Fine */
@@ -201,12 +198,22 @@ Hints::check_frame_rate ()
 }
 
 
+void
+Hints::check_4k_3d ()
+{
+       auto f = film();
+       if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
+               hint (_("4K 3D is only supported by a very limited number of projectors.  Unless you know that you will play this DCP back on a capable projector, it is advisable to set the DCP to be 2K in the \"DCP→Video\" tab."));
+       }
+}
+
+
 void
 Hints::check_speed_up ()
 {
        optional<double> lowest_speed_up;
        optional<double> highest_speed_up;
-       BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
+       for (auto i: film()->content()) {
                double spu = film()->active_frame_rate_change(i->position()).speed_up;
                if (!lowest_speed_up || spu < *lowest_speed_up) {
                        lowest_speed_up = spu;
@@ -231,14 +238,23 @@ Hints::check_speed_up ()
 }
 
 
+void
+Hints::check_interop ()
+{
+       if (film()->interop()) {
+               hint (_("In general it is now advisable to make SMPTE DCPs unless you have a particular reason to use Interop.  You are advised to set your DCP to use the SMPTE standard in the \"DCP\" tab."));
+       }
+}
+
+
 void
 Hints::check_big_font_files ()
 {
        bool big_font_files = false;
        if (film()->interop ()) {
-               BOOST_FOREACH (shared_ptr<Content> i, film()->content()) {
-                       BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
-                               BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
+               for (auto i: film()->content()) {
+                       for (auto j: i->text) {
+                               for (auto k: j->fonts()) {
                                        optional<boost::filesystem::path> const p = k->file ();
                                        if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
                                                big_font_files = true;
@@ -258,7 +274,7 @@ void
 Hints::check_vob ()
 {
        int vob = 0;
-       BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
+       for (auto i: film()->content()) {
                if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
                        ++vob;
                }
@@ -274,8 +290,8 @@ void
 Hints::check_3d_in_2d ()
 {
        int three_d = 0;
-       BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
-               if (i->video && i->video->frame_type() != VIDEO_FRAME_TYPE_2D) {
+       for (auto i: film()->content()) {
+               if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
                        ++three_d;
                }
        }
@@ -289,15 +305,15 @@ Hints::check_3d_in_2d ()
 void
 Hints::check_loudness ()
 {
-       boost::filesystem::path path = film()->audio_analysis_path(film()->playlist());
+       auto path = film()->audio_analysis_path(film()->playlist());
        if (boost::filesystem::exists (path)) {
                try {
-                       shared_ptr<AudioAnalysis> an (new AudioAnalysis (path));
+                       auto an = make_shared<AudioAnalysis>(path);
 
                        string ch;
 
-                       vector<AudioAnalysis::PeakTime> sample_peak = an->sample_peak ();
-                       vector<float> true_peak = an->true_peak ();
+                       auto sample_peak = an->sample_peak ();
+                       auto true_peak = an->true_peak ();
 
                        for (size_t i = 0; i < sample_peak.size(); ++i) {
                                float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
@@ -333,16 +349,29 @@ subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
 }
 
 
+void
+Hints::check_out_of_range_markers ()
+{
+       auto const length = film()->length();
+       for (auto const& i: film()->markers()) {
+               if (i.second >= length) {
+                       hint (_("At least one marker comes after the end of the project and will be ignored."));
+               }
+       }
+}
+
+
 void
 Hints::thread ()
 {
-       shared_ptr<const Film> film = _film.lock ();
+       auto film = _film.lock ();
        if (!film) {
                return;
        }
 
-       ContentList content = film->content ();
+       auto content = film->content ();
 
+       check_interop ();
        check_big_font_files ();
        check_few_audio_channels ();
        check_upmixers ();
@@ -350,15 +379,18 @@ Hints::thread ()
        check_unusual_container ();
        check_high_j2k_bandwidth ();
        check_frame_rate ();
+       check_4k_3d ();
        check_speed_up ();
        check_vob ();
        check_3d_in_2d ();
        check_loudness ();
        check_ffec_and_ffmc_in_smpte_feature ();
+       check_out_of_range_markers ();
+       check_text_languages ();
 
        emit (bind(boost::ref(Progress), _("Examining closed captions")));
 
-       shared_ptr<Player> player (new Player(film));
+       auto player = make_shared<Player>(film);
        player->set_ignore_video ();
        player->set_ignore_audio ();
        player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
@@ -403,8 +435,8 @@ Hints::thread ()
        dcp::DCP dcp (dcp_dir);
        dcp.read ();
        DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
-       BOOST_FOREACH (shared_ptr<dcp::Reel> reel, dcp.cpls().front()->reels()) {
-               BOOST_FOREACH (shared_ptr<dcp::ReelClosedCaptionAsset> ccap, reel->closed_captions()) {
+       for (auto reel: dcp.cpls()[0]->reels()) {
+               for (auto ccap: reel->closed_captions()) {
                        if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
                                hint (_(
                                                "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
@@ -445,10 +477,10 @@ Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTi
        _writer->write (text, type, track, period);
 
        switch (type) {
-       case TEXT_CLOSED_CAPTION:
+       case TextType::CLOSED_CAPTION:
                closed_caption (text, period);
                break;
-       case TEXT_OPEN_SUBTITLE:
+       case TextType::OPEN_SUBTITLE:
                open_subtitle (text, period);
                break;
        default:
@@ -461,7 +493,7 @@ void
 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
 {
        int lines = text.string.size();
-       BOOST_FOREACH (StringText i, text.string) {
+       for (auto i: text.string) {
                if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
                        ++lines;
                        if (!_long_ccap) {
@@ -518,13 +550,18 @@ Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
        }
 
        size_t longest_line = 0;
-       BOOST_FOREACH (StringText const& i, text.string) {
+       for (auto const& i: text.string) {
                longest_line = max (longest_line, i.text().length());
        }
 
        if (longest_line > 52 && !_long_subtitle) {
                _long_subtitle = true;
-               hint (_("At least one of your subtitle lines has more than 52 characters.  It is advisable to make each line 52 characters at most in length."));
+               hint (_("At least one of your subtitle lines has more than 52 characters.  It is recommended to make each line 52 characters at most in length."));
+       }
+
+       if (longest_line > 79 && !_very_long_subtitle) {
+               _very_long_subtitle = true;
+               hint (_("At least one of your subtitle lines has more than 79 characters.  You should make each line 79 characters at most in length."));
        }
 
        _last_subtitle = period;
@@ -534,9 +571,9 @@ Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
 void
 Hints::check_ffec_and_ffmc_in_smpte_feature ()
 {
-       shared_ptr<const Film> f = film();
-       if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::FEATURE && (!f->marker(dcp::FFEC) || !f->marker(dcp::FFMC))) {
-               hint (_("SMPTE DCPs with the type FTR (feature) should have markers for the first frame of end credits (FFEC) and the first frame of moving credits (FFMC).  You should add these markers using the 'Markers' button in the DCP tab."));
+       auto f = film();
+       if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
+               hint (_("SMPTE DCPs with the type FTR (feature) should have markers for the first frame of end credits (FFEC) and the first frame of moving credits (FFMC).  You should add these markers using the 'Markers' button in the \"DCP\" tab."));
        }
 }
 
@@ -546,3 +583,19 @@ Hints::join ()
 {
        _thread.join ();
 }
+
+
+void
+Hints::check_text_languages ()
+{
+       for (auto i: film()->content()) {
+               for (auto j: i->text) {
+                       if (j->use() && !j->language()) {
+                               hint (_("At least one piece of subtitle or closed caption content has no specified language.  "
+                                       "It is advisable to set the language for each piece of subtitle or closed caption content "
+                                       "in the \"Content→Timed text\", \"Content→Open subtitles\" or \"Content→Closed captions\" tab"));
+                               return;
+                       }
+               }
+       }
+}