2 Copyright (C) 2016-2022 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 #include "audio_analysis.h"
23 #include "audio_content.h"
24 #include "audio_processor.h"
25 #include "compose.hpp"
27 #include "constants.h"
30 #include "dcp_content_type.h"
34 #include "maths_util.h"
37 #include "text_content.h"
38 #include "video_content.h"
41 #include <dcp/filesystem.h>
42 #include <dcp/raw_convert.h>
44 #include <dcp/reel_closed_caption_asset.h>
45 #include <dcp/reel_subtitle_asset.h>
46 #include <boost/algorithm/string.hpp>
53 using std::make_shared;
55 using std::shared_ptr;
58 using boost::optional;
60 using namespace dcpomatic;
61 #if BOOST_VERSION >= 106100
62 using namespace boost::placeholders;
66 /* When checking to see if things are too big, we'll say they are if they
67 * are more than the target size minus this "slack."
69 #define SIZE_SLACK 4096
72 /* When writing hints:
73 * - put quotation marks around the name of a GUI tab that you are referring to (e.g. "DCP" or "DCP→Video" tab)
77 Hints::Hints (weak_ptr<const Film> weak_film)
78 : WeakConstFilm (weak_film)
79 , _writer (new Writer(weak_film, weak_ptr<Job>(), true))
80 , _analyser (film(), film()->playlist(), true, [](float) {})
90 _thread = boost::thread (bind(&Hints::thread, this));
96 boost::this_thread::disable_interruption dis;
100 _thread.interrupt ();
107 Hints::check_few_audio_channels ()
109 if (film()->audio_channels() < 6) {
110 hint (_("Your DCP has fewer than 6 audio channels. This may cause problems on some projectors. You may want to set the DCP to have 6 channels. It does not matter if your content has fewer channels, as DCP-o-matic will fill the extras with silence."));
116 Hints::check_upmixers ()
118 auto ap = film()->audio_processor();
119 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
120 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 Hints::check_incorrect_container ()
128 int narrower_than_scope = 0;
130 for (auto i: film()->content()) {
132 auto const r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
133 if (r && r->id() == "239") {
135 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
136 ++narrower_than_scope;
141 string const film_container = film()->container()->id();
143 if (scope && !narrower_than_scope && film_container == "185") {
144 hint (_("All of your content is in Scope (2.39:1) but your DCP's container is Flat (1.85:1). This will letter-box your content inside a Flat (1.85:1) frame. You may prefer to set your DCP's container to Scope (2.39:1) in the \"DCP\" tab."));
147 if (!scope && narrower_than_scope && film_container == "239") {
148 hint (_("All of your content narrower than 1.90:1 but your DCP's container is Scope (2.39:1). This will pillar-box your content. You may prefer to set your DCP's container to have the same ratio as your content."));
154 Hints::check_unusual_container ()
156 auto const film_container = film()->container()->id();
157 if (film_container != "185" && film_container != "239") {
158 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."));
164 Hints::check_high_j2k_bandwidth ()
166 if (film()->j2k_bandwidth() >= 245000000) {
167 hint (_("A few projectors have problems playing back very high bit-rate DCPs. It is a good idea to drop the JPEG2000 bandwidth down to about 200Mbit/s; this is unlikely to have any visible effect on the image."));
173 Hints::check_frame_rate ()
176 switch (f->video_frame_rate()) {
182 /* You might want to go to 24 */
183 string base = String::compose(_("You are set up for a DCP at a frame rate of %1 fps. This frame rate is not supported by all projectors. You may want to consider changing your frame rate to %2 fps."), 25, 24);
186 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
192 /* 30fps: we can't really offer any decent solutions */
193 hint (_("You are set up for a DCP frame rate of 30fps, which is not supported by all projectors. Be aware that you may have compatibility problems."));
198 /* You almost certainly want to go to half frame rate */
199 hint (String::compose(_("You are set up for a DCP at a frame rate of %1 fps. This frame rate is not supported by all projectors. It is advisable to change the DCP frame rate to %2 fps."), f->video_frame_rate(), f->video_frame_rate() / 2));
206 Hints::check_4k_3d ()
209 if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
210 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."));
216 Hints::check_speed_up ()
218 optional<double> lowest_speed_up;
219 optional<double> highest_speed_up;
220 for (auto i: film()->content()) {
221 double spu = film()->active_frame_rate_change(i->position()).speed_up;
222 if (!lowest_speed_up || spu < *lowest_speed_up) {
223 lowest_speed_up = spu;
225 if (!highest_speed_up || spu > *highest_speed_up) {
226 highest_speed_up = spu;
230 double worst_speed_up = 1;
231 if (highest_speed_up) {
232 worst_speed_up = *highest_speed_up;
234 if (lowest_speed_up) {
235 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
238 if (worst_speed_up > 25.5/24.0) {
239 hint (_("There is a large difference between the frame rate of your DCP and that of some of your content. This will cause your audio to play back at a much lower or higher pitch than it should. It is advisable to set your DCP frame rate to one closer to your content, provided that your target projection systems support your chosen DCP rate."));
246 Hints::check_interop ()
248 if (film()->interop()) {
249 hint (_("In general it is now advisable to make SMPTE DCPs unless you have a particular reason to use Interop. It is advisable to set your DCP to use the SMPTE standard in the \"DCP\" tab."));
255 Hints::check_big_font_files ()
257 bool big_font_files = false;
258 if (film()->interop ()) {
259 for (auto i: film()->content()) {
260 for (auto j: i->text) {
261 for (auto k: j->fonts()) {
262 auto const p = k->file ();
263 if (p && dcp::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
264 big_font_files = true;
271 if (big_font_files) {
272 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
281 for (auto i: film()->content()) {
282 if (boost::algorithm::starts_with(i->path(0).filename().string(), "VTS_")) {
288 hint (String::compose (_("You have %1 files that look like they are VOB files from DVD. You should join them to ensure smooth joins between the files."), vob));
294 Hints::check_3d_in_2d ()
297 for (auto i: film()->content()) {
298 if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
303 if (three_d > 0 && !film()->three_d()) {
304 hint (_("You are using 3D content but your DCP is set to 2D. Set the DCP to 3D if you want to play it back on a 3D system (e.g. Real-D, MasterImage etc.)"));
309 /** @return true if the loudness could be checked, false if it could not because no analysis was available */
311 Hints::check_loudness ()
313 auto path = film()->audio_analysis_path(film()->playlist());
314 if (!dcp::filesystem::exists(path)) {
319 auto an = make_shared<AudioAnalysis>(path);
323 auto sample_peak = an->sample_peak ();
324 auto true_peak = an->true_peak ();
326 for (size_t i = 0; i < sample_peak.size(); ++i) {
327 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
328 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
330 ch += dcp::raw_convert<string>(short_audio_channel_name(i)) + ", ";
334 ch = ch.substr (0, ch.length() - 2);
337 hint(String::compose(
338 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
343 } catch (OldFormatError& e) {
344 /* The audio analysis is too old to load in */
354 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
356 return asset && asset->file() && dcp::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
361 Hints::check_out_of_range_markers ()
363 auto const length = film()->length();
364 for (auto const& i: film()->markers()) {
365 if (i.second >= length) {
366 hint (_("At least one marker comes after the end of the project and will be ignored."));
373 Hints::scan_content(shared_ptr<const Film> film)
375 auto const check_loudness_done = check_loudness();
377 auto content = film->playlist()->content();
378 auto iter = std::find_if(content.begin(), content.end(), [](shared_ptr<const Content> content) {
379 auto text_iter = std::find_if(content->text.begin(), content->text.end(), [](shared_ptr<const TextContent> text) {
382 return text_iter != content->text.end();
385 auto const have_text = iter != content.end();
387 if (check_loudness_done && !have_text) {
388 /* We don't need to check loudness, and we don't have any active text to check,
389 * so a scan of the content is pointless.
394 if (check_loudness_done && have_text) {
395 emit (bind(boost::ref(Progress), _("Examining subtitles and closed captions")));
396 } else if (!check_loudness_done && !have_text) {
397 emit (bind(boost::ref(Progress), _("Examining audio")));
399 emit (bind(boost::ref(Progress), _("Examining audio, subtitles and closed captions")));
402 auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
403 player->set_ignore_video();
404 if (check_loudness_done || _disable_audio_analysis) {
405 /* We don't need to analyse audio because we already loaded a suitable analysis */
406 player->set_ignore_audio();
408 /* Send auto to the analyser to check loudness */
409 player->Audio.connect(bind(&Hints::audio, this, _1, _2));
411 player->Text.connect(bind(&Hints::text, this, _1, _2, _3, _4));
413 struct timeval last_pulse;
414 gettimeofday(&last_pulse, 0);
416 _writer->write(player->get_subtitle_fonts());
418 while (!player->pass()) {
421 gettimeofday(&now, 0);
422 if ((seconds(now) - seconds(last_pulse)) > 1) {
426 emit(bind(boost::ref(Pulse)));
431 if (!check_loudness_done) {
433 _analyser.get().write(film->audio_analysis_path(film->playlist()));
443 start_of_thread ("Hints");
445 auto film = _film.lock ();
450 auto content = film->content ();
452 check_certificates ();
454 check_big_font_files ();
455 check_few_audio_channels ();
457 check_incorrect_container ();
458 check_unusual_container ();
459 check_high_j2k_bandwidth ();
465 check_ffec_and_ffmc_in_smpte_feature ();
466 check_out_of_range_markers ();
467 check_subtitle_languages();
468 check_audio_language ();
469 check_8_or_16_audio_channels();
473 if (_long_subtitle && !_very_long_subtitle) {
474 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."));
475 } else if (_very_long_subtitle) {
476 hint (_("At least one of your subtitle lines has more than 79 characters. You should make each line 79 characters at most in length."));
479 bool ccap_xml_too_big = false;
480 bool ccap_mxf_too_big = false;
481 bool subs_mxf_too_big = false;
483 auto dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
484 dcp::filesystem::remove_all(dcp_dir);
486 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
488 dcp::DCP dcp (dcp_dir);
490 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
491 for (auto reel: dcp.cpls()[0]->reels()) {
492 for (auto ccap: reel->closed_captions()) {
493 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
495 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
496 ". You should divide the DCP into shorter reels."
498 ccap_xml_too_big = true;
500 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
502 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
503 " in total. You should divide the DCP into shorter reels."
505 ccap_mxf_too_big = true;
508 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
510 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
511 "You should divide the DCP into shorter reels."
513 subs_mxf_too_big = true;
516 dcp::filesystem::remove_all(dcp_dir);
518 emit (bind(boost::ref(Finished)));
520 catch (boost::thread_interrupted)
522 /* The Hints object is being destroyed before it has finished, so just give up */
531 Hints::hint (string h)
533 emit(bind(boost::ref(Hint), h));
538 Hints::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
540 _analyser.analyse (audio, time);
545 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
547 _writer->write (text, type, track, period);
550 case TextType::CLOSED_CAPTION:
551 closed_caption (text, period);
553 case TextType::OPEN_SUBTITLE:
554 open_subtitle (text, period);
563 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
565 int lines = text.string.size();
566 for (auto i: text.string) {
567 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
573 "At least one of your closed caption lines has more than %1 characters. "
574 "It is advisable to make each line %1 characters at most in length.",
575 MAX_CLOSED_CAPTION_LENGTH,
576 MAX_CLOSED_CAPTION_LENGTH)
582 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
583 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
584 _too_many_ccap_lines = true;
587 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
588 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
589 _overlap_ccap = true;
590 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
598 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
600 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
601 _early_subtitle = true;
602 hint (_("It is advisable to put your first subtitle at least 4 seconds after the start of the DCP to make sure it is seen."));
605 int const vfr = film()->video_frame_rate ();
607 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
608 _short_subtitle = true;
609 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
612 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
613 _subtitles_too_close = true;
614 hint (_("At least one of your subtitles starts less than 2 frames after the previous one. It is advisable to make the gap between subtitles at least 2 frames."));
623 bool operator<(VPos const& other) const {
624 if (static_cast<int>(align) != static_cast<int>(other.align)) {
625 return static_cast<int>(align) < static_cast<int>(other.align);
627 return position < other.position;
631 /* This is rather an approximate way to count distinct lines, but I guess it will do;
632 * to make it better we need to take into account font metrics, and the SMPTE alignment
633 * debacle, and so on.
635 std::set<VPos> lines;
636 for (auto const& line: text.string) {
637 lines.insert({ line.v_align(), line.v_position() });
640 if (lines.size() > 3 && !_too_many_subtitle_lines) {
641 _too_many_subtitle_lines = true;
642 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
645 size_t longest_line = 0;
646 for (auto const& i: text.string) {
647 longest_line = max (longest_line, i.text().length());
650 if (longest_line > 52) {
651 _long_subtitle = true;
654 if (longest_line > 79) {
655 _very_long_subtitle = true;
658 _last_subtitle = period;
663 Hints::check_ffec_and_ffmc_in_smpte_feature ()
666 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
667 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."));
680 Hints::check_subtitle_languages()
682 for (auto i: film()->content()) {
683 for (auto j: i->text) {
684 if (j->use() && j->type() == TextType::OPEN_SUBTITLE && !j->language()) {
685 hint (_("At least one piece of subtitle content has no specified language. "
686 "It is advisable to set the language for each piece of subtitle content "
687 "in the \"Content→Timed text\" or \"Content→Open subtitles\" tab."));
696 Hints::check_audio_language ()
698 auto content = film()->content();
700 std::find_if(content.begin(), content.end(), [](shared_ptr<const Content> c) {
701 return c->audio && !c->audio->mapping().mapped_output_channels().empty();
704 if (mapped_audio != content.end() && !film()->audio_language()) {
705 hint (_("Some of your content has audio but you have not set the audio language. It is advisable to set the audio language "
706 "in the \"DCP\" tab unless your audio has no spoken parts."));
712 Hints::check_certificates ()
714 auto bad = Config::instance()->check_certificates();
720 case Config::BAD_SIGNER_UTF8_STRINGS:
721 hint(_("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs contains a small error "
722 "which will prevent DCPs from being validated correctly on some systems. It is advisable to "
723 "re-create the signing certificate chain by clicking the \"Re-make certificates and key...\" "
724 "button in the Keys page of Preferences."));
726 case Config::BAD_SIGNER_VALIDITY_TOO_LONG:
727 hint(_("The certificate chain that DCP-o-matic uses for signing DCPs and KDMs has a validity period "
728 "that is too long. This will cause problems playing back DCPs on some systems. "
729 "It is advisable to re-create the signing certificate chain by clicking the "
730 "\"Re-make certificates and key...\" button in the Keys page of Preferences."));
733 /* Some bad situations can't happen here as DCP-o-matic would have refused to start until they are fixed */
740 Hints::check_8_or_16_audio_channels()
742 auto const channels = film()->audio_channels();
743 if (channels != 8 && channels != 16) {
744 hint(String::compose(_("Your DCP has %1 audio channels, rather than 8 or 16. This may cause some distributors to raise QC errors when they check your DCP. To avoid this, set the DCP audio channels to 8 or 16."), channels));