+
+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 ()
+try
+{
+ start_of_thread ("Hints");
+
+ auto film = _film.lock ();
+ if (!film) {
+ return;
+ }
+
+ auto content = film->content ();
+
+ check_certificates ();
+ check_interop ();
+ check_big_font_files ();
+ check_few_audio_channels ();
+ check_upmixers ();
+ check_incorrect_container ();
+ check_unusual_container ();
+ check_high_j2k_bandwidth ();
+ check_frame_rate ();
+ check_4k_3d ();
+ check_speed_up ();
+ check_vob ();
+ check_3d_in_2d ();
+ auto const check_loudness_done = check_loudness ();
+ check_ffec_and_ffmc_in_smpte_feature ();
+ check_out_of_range_markers ();
+ check_text_languages ();
+ check_audio_language ();
+
+ 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, Image::Alignment::COMPACT);
+ player->set_ignore_video ();
+ if (check_loudness_done || _disable_audio_analysis) {
+ /* 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);
+
+ _writer->write (player->get_subtitle_fonts());
+
+ while (!player->pass()) {
+
+ struct timeval now;
+ gettimeofday (&now, 0);
+ if ((seconds(now) - seconds(last_pulse)) > 1) {
+ if (_stop) {
+ return;
+ }
+ emit (bind (boost::ref(Pulse)));
+ last_pulse = now;
+ }
+ }
+
+ if (!check_loudness_done) {
+ _analyser.finish ();
+ _analyser.get().write(film->audio_analysis_path(film->playlist()));
+ check_loudness ();
+ }
+
+
+ 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;
+
+ auto dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
+ boost::filesystem::remove_all (dcp_dir);
+
+ _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
+
+ dcp::DCP dcp (dcp_dir);
+ dcp.read ();
+ DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
+ 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
+ ". You should divide the DCP into shorter reels."
+ ));
+ ccap_xml_too_big = true;
+ }
+ if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
+ hint (_(
+ "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
+ " in total. You should divide the DCP into shorter reels."
+ ));
+ ccap_mxf_too_big = true;
+ }
+ }
+ if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
+ hint (_(
+ "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
+ "You should divide the DCP into shorter reels."
+ ));
+ subs_mxf_too_big = true;
+ }
+ }
+ boost::filesystem::remove_all (dcp_dir);
+
+ 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)
+{
+ 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)
+{
+ _writer->write (text, type, track, period);
+
+ switch (type) {
+ case TextType::CLOSED_CAPTION:
+ closed_caption (text, period);
+ break;
+ case TextType::OPEN_SUBTITLE:
+ open_subtitle (text, period);
+ break;
+ default:
+ break;
+ }
+}
+
+
+void
+Hints::closed_caption (PlayerText text, DCPTimePeriod period)
+{
+ int lines = text.string.size();
+ for (auto i: text.string) {
+ if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
+ ++lines;
+ if (!_long_ccap) {
+ _long_ccap = true;
+ hint (
+ String::compose(
+ "At least one of your closed caption lines has more than %1 characters. "
+ "It is advisable to make each line %1 characters at most in length.",
+ MAX_CLOSED_CAPTION_LENGTH,
+ MAX_CLOSED_CAPTION_LENGTH)
+ );