Tidy up/fix Hints thread in a couple of ways:
[dcpomatic.git] / src / lib / hints.cc
index c9f901c1829b1f9346c7dcf5b52d5558513d358f..b23f32238182e1f603655ada06a2e24d51bc1807 100644 (file)
@@ -73,17 +73,10 @@ using namespace boost::placeholders;
  */
 
 
-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)
+Hints::Hints (weak_ptr<const Film> weak_film)
+       : WeakConstFilm (weak_film)
+       , _writer (new Writer(weak_film, weak_ptr<Job>(), true))
+       , _analyser (film(), film()->playlist(), true, [](float) {})
        , _stop (false)
 {
 
@@ -133,7 +126,7 @@ Hints::check_incorrect_container ()
        int scope = 0;
        for (auto i: film()->content()) {
                if (i->video) {
-                       Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
+                       auto const r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
                        if (r && r->id() == "239") {
                                ++scope;
                        } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
@@ -310,42 +303,46 @@ Hints::check_3d_in_2d ()
 }
 
 
-void
+/** @return true if the loudness could be checked, false if it could not because no analysis was available */
+bool
 Hints::check_loudness ()
 {
        auto path = film()->audio_analysis_path(film()->playlist());
-       if (boost::filesystem::exists (path)) {
-               try {
-                       auto an = make_shared<AudioAnalysis>(path);
+       if (!boost::filesystem::exists(path)) {
+               return false;
+       }
 
-                       string ch;
+       try {
+               auto an = make_shared<AudioAnalysis>(path);
 
-                       auto sample_peak = an->sample_peak ();
-                       auto true_peak = an->true_peak ();
+               string ch;
 
-                       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]);
-                               float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
-                               if (peak_dB > -3) {
-                                       ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
-                               }
+               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]);
+                       float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
+                       if (peak_dB > -3) {
+                               ch += dcp::raw_convert<string>(short_audio_channel_name(i)) + ", ";
                        }
+               }
 
-                       ch = ch.substr (0, ch.length() - 2);
+               ch = ch.substr (0, ch.length() - 2);
 
-                       if (!ch.empty ()) {
-                               hint (String::compose (
-                                             _("Your audio level is very high (on %1).  You should reduce the gain of your audio content."),
-                                             ch
-                                             )
-                                       );
-                       }
-               } catch (OldFormatError& e) {
-                       /* The audio analysis is too old to load in; just skip this hint as if
-                          it had never been run.
-                       */
+               if (!ch.empty ()) {
+                       hint (String::compose (
+                                       _("Your audio level is very high (on %1).  You should reduce the gain of your audio content."),
+                                       ch
+                                       )
+                            );
                }
+       } catch (OldFormatError& e) {
+               /* The audio analysis is too old to load in */
+               return false;
        }
+
+       return true;
 }
 
 
@@ -371,6 +368,7 @@ Hints::check_out_of_range_markers ()
 
 void
 Hints::thread ()
+try
 {
        auto film = _film.lock ();
        if (!film) {
@@ -391,40 +389,56 @@ Hints::thread ()
        check_speed_up ();
        check_vob ();
        check_3d_in_2d ();
-       check_loudness ();
+       auto const check_loudness_done = 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")));
+       if (check_loudness_done) {
+               emit (bind(boost::ref(Progress), _("Examining subtitles and closed captions")));
+       } else {
+               emit (bind(boost::ref(Progress), _("Examining audio, subtitles and closed captions")));
+       }
 
        auto player = make_shared<Player>(film);
        player->set_ignore_video ();
-       player->set_ignore_audio ();
+       if (check_loudness_done) {
+               /* We don't need to analyse audio because we already loaded a suitable analysis */
+               player->set_ignore_audio ();
+       }
+       player->Audio.connect (bind(&Hints::audio, this, _1, _2));
        player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
 
        struct timeval last_pulse;
        gettimeofday (&last_pulse, 0);
 
-       try {
-               while (!player->pass()) {
+       while (!player->pass()) {
 
-                       struct timeval now;
-                       gettimeofday (&now, 0);
-                       if ((seconds(now) - seconds(last_pulse)) > 1) {
-                               if (_stop) {
-                                       break;
-                               }
-                               emit (bind (boost::ref(Pulse)));
-                               last_pulse = now;
+               struct timeval now;
+               gettimeofday (&now, 0);
+               if ((seconds(now) - seconds(last_pulse)) > 1) {
+                       if (_stop) {
+                               return;
                        }
+                       emit (bind (boost::ref(Pulse)));
+                       last_pulse = now;
                }
-       } catch (...) {
-               store_current ();
+       }
+
+       if (!check_loudness_done) {
+               _analyser.finish ();
+               _analyser.get().write(film->audio_analysis_path(film->playlist()));
+               check_loudness ();
        }
 
        _writer->write (player->get_subtitle_fonts());
 
+       if (_long_subtitle && !_very_long_subtitle) {
+               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."));
+       } else if (_very_long_subtitle) {
+               hint (_("At least one of your subtitle lines has more than 79 characters.  You should make each line 79 characters at most in length."));
+       }
+
        bool ccap_xml_too_big = false;
        bool ccap_mxf_too_big = false;
        bool subs_mxf_too_big = false;
@@ -432,13 +446,7 @@ Hints::thread ()
        boost::filesystem::path dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
        boost::filesystem::remove_all (dcp_dir);
 
-       try {
-               _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
-       } catch (...) {
-               store_current ();
-               emit (bind(boost::ref(Finished)));
-               return;
-       }
+       _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
 
        dcp::DCP dcp (dcp_dir);
        dcp.read ();
@@ -472,6 +480,14 @@ Hints::thread ()
 
        emit (bind(boost::ref(Finished)));
 }
+catch (boost::thread_interrupted)
+{
+       /* The Hints object is being destroyed before it has finished, so just give up */
+}
+catch (...)
+{
+       store_current ();
+}
 
 void
 Hints::hint (string h)
@@ -479,6 +495,14 @@ Hints::hint (string h)
        emit(bind(boost::ref(Hint), h));
 }
 
+
+void
+Hints::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
+{
+       _analyser.analyse (audio, time);
+}
+
+
 void
 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
 {
@@ -562,9 +586,12 @@ Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
                longest_line = max (longest_line, i.text().length());
        }
 
-       if (longest_line > 52 && !_long_subtitle) {
+       if (longest_line > 52) {
                _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."));
+       }
+
+       if (longest_line > 79) {
+               _very_long_subtitle = true;
        }
 
        _last_subtitle = period;
@@ -595,7 +622,8 @@ Hints::check_text_languages ()
                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\" tab"));
+                                       "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;
                        }
                }