2 Copyright (C) 2016-2021 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"
28 #include "dcp_content_type.h"
31 #include "font_data.h"
35 #include "text_content.h"
38 #include "video_content.h"
41 #include <dcp/raw_convert.h>
43 #include <dcp/reel_closed_caption_asset.h>
44 #include <dcp/reel_subtitle_asset.h>
45 #include <boost/algorithm/string.hpp>
52 using std::make_shared;
56 using std::shared_ptr;
60 using boost::optional;
62 using namespace dcpomatic;
63 #if BOOST_VERSION >= 106100
64 using namespace boost::placeholders;
68 /* When checking to see if things are too big, we'll say they are if they
69 * are more than the target size minus this "slack."
71 #define SIZE_SLACK 4096
74 /* When writing hints:
75 * - put quotation marks around the name of a GUI tab that you are referring to (e.g. "DCP" or "DCP→Video" tab)
79 Hints::Hints (weak_ptr<const Film> weak_film)
80 : WeakConstFilm (weak_film)
81 , _writer (new Writer(weak_film, weak_ptr<Job>(), true))
82 , _analyser (film(), film()->playlist(), true, [](float) {})
92 _thread = boost::thread (bind(&Hints::thread, this));
98 boost::this_thread::disable_interruption dis;
102 _thread.interrupt ();
109 Hints::check_few_audio_channels ()
111 if (film()->audio_channels() < 6) {
112 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."));
118 Hints::check_upmixers ()
120 auto ap = film()->audio_processor();
121 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
122 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."));
128 Hints::check_incorrect_container ()
130 int narrower_than_scope = 0;
132 for (auto i: film()->content()) {
134 auto const r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
135 if (r && r->id() == "239") {
137 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
138 ++narrower_than_scope;
143 string const film_container = film()->container()->id();
145 if (scope && !narrower_than_scope && film_container == "185") {
146 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."));
149 if (!scope && narrower_than_scope && film_container == "239") {
150 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."));
156 Hints::check_unusual_container ()
158 auto const film_container = film()->container()->id();
159 if (film_container != "185" && film_container != "239") {
160 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."));
166 Hints::check_high_j2k_bandwidth ()
168 if (film()->j2k_bandwidth() >= 245000000) {
169 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."));
175 Hints::check_frame_rate ()
178 switch (f->video_frame_rate()) {
184 /* You might want to go to 24 */
185 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);
188 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
194 /* 30fps: we can't really offer any decent solutions */
195 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."));
200 /* You almost certainly want to go to half frame rate */
201 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. You are advised to change the DCP frame rate to %2 fps."), f->video_frame_rate(), f->video_frame_rate() / 2));
208 Hints::check_4k_3d ()
211 if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
212 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."));
218 Hints::check_speed_up ()
220 optional<double> lowest_speed_up;
221 optional<double> highest_speed_up;
222 for (auto i: film()->content()) {
223 double spu = film()->active_frame_rate_change(i->position()).speed_up;
224 if (!lowest_speed_up || spu < *lowest_speed_up) {
225 lowest_speed_up = spu;
227 if (!highest_speed_up || spu > *highest_speed_up) {
228 highest_speed_up = spu;
232 double worst_speed_up = 1;
233 if (highest_speed_up) {
234 worst_speed_up = *highest_speed_up;
236 if (lowest_speed_up) {
237 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
240 if (worst_speed_up > 25.5/24.0) {
241 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. You are advised to set your DCP frame rate to one closer to your content, provided that your target projection systems support your chosen DCP rate."));
248 Hints::check_interop ()
250 if (film()->interop()) {
251 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."));
257 Hints::check_big_font_files ()
259 bool big_font_files = false;
260 if (film()->interop ()) {
261 for (auto i: film()->content()) {
262 for (auto j: i->text) {
263 for (auto k: j->fonts()) {
264 auto const p = k->file ();
265 if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
266 big_font_files = true;
273 if (big_font_files) {
274 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
283 for (auto i: film()->content()) {
284 if (boost::algorithm::starts_with(i->path(0).filename().string(), "VTS_")) {
290 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));
296 Hints::check_3d_in_2d ()
299 for (auto i: film()->content()) {
300 if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
305 if (three_d > 0 && !film()->three_d()) {
306 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.)"));
311 /** @return true if the loudness could be checked, false if it could not because no analysis was available */
313 Hints::check_loudness ()
315 auto path = film()->audio_analysis_path(film()->playlist());
316 if (!boost::filesystem::exists(path)) {
321 auto an = make_shared<AudioAnalysis>(path);
325 auto sample_peak = an->sample_peak ();
326 auto true_peak = an->true_peak ();
328 for (size_t i = 0; i < sample_peak.size(); ++i) {
329 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
330 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
332 ch += dcp::raw_convert<string>(short_audio_channel_name(i)) + ", ";
336 ch = ch.substr (0, ch.length() - 2);
339 hint(String::compose(
340 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
345 } catch (OldFormatError& e) {
346 /* The audio analysis is too old to load in */
356 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
358 return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
363 Hints::check_out_of_range_markers ()
365 auto const length = film()->length();
366 for (auto const& i: film()->markers()) {
367 if (i.second >= length) {
368 hint (_("At least one marker comes after the end of the project and will be ignored."));
378 start_of_thread ("Hints");
380 auto film = _film.lock ();
385 auto content = film->content ();
388 check_big_font_files ();
389 check_few_audio_channels ();
391 check_incorrect_container ();
392 check_unusual_container ();
393 check_high_j2k_bandwidth ();
399 auto const check_loudness_done = check_loudness ();
400 check_ffec_and_ffmc_in_smpte_feature ();
401 check_out_of_range_markers ();
402 check_text_languages ();
404 if (check_loudness_done) {
405 emit (bind(boost::ref(Progress), _("Examining subtitles and closed captions")));
407 emit (bind(boost::ref(Progress), _("Examining audio, subtitles and closed captions")));
410 auto player = make_shared<Player>(film);
411 player->set_ignore_video ();
412 if (check_loudness_done) {
413 /* We don't need to analyse audio because we already loaded a suitable analysis */
414 player->set_ignore_audio ();
416 player->Audio.connect (bind(&Hints::audio, this, _1, _2));
417 player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
419 struct timeval last_pulse;
420 gettimeofday (&last_pulse, 0);
422 while (!player->pass()) {
425 gettimeofday (&now, 0);
426 if ((seconds(now) - seconds(last_pulse)) > 1) {
430 emit (bind (boost::ref(Pulse)));
435 if (!check_loudness_done) {
437 _analyser.get().write(film->audio_analysis_path(film->playlist()));
441 _writer->write (player->get_subtitle_fonts());
443 if (_long_subtitle && !_very_long_subtitle) {
444 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."));
445 } else if (_very_long_subtitle) {
446 hint (_("At least one of your subtitle lines has more than 79 characters. You should make each line 79 characters at most in length."));
449 bool ccap_xml_too_big = false;
450 bool ccap_mxf_too_big = false;
451 bool subs_mxf_too_big = false;
453 auto dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
454 boost::filesystem::remove_all (dcp_dir);
456 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
458 dcp::DCP dcp (dcp_dir);
460 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
461 for (auto reel: dcp.cpls()[0]->reels()) {
462 for (auto ccap: reel->closed_captions()) {
463 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
465 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
466 ". You should divide the DCP into shorter reels."
468 ccap_xml_too_big = true;
470 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
472 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
473 " in total. You should divide the DCP into shorter reels."
475 ccap_mxf_too_big = true;
478 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
480 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
481 "You should divide the DCP into shorter reels."
483 subs_mxf_too_big = true;
486 boost::filesystem::remove_all (dcp_dir);
488 emit (bind(boost::ref(Finished)));
490 catch (boost::thread_interrupted)
492 /* The Hints object is being destroyed before it has finished, so just give up */
501 Hints::hint (string h)
503 emit(bind(boost::ref(Hint), h));
508 Hints::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
510 _analyser.analyse (audio, time);
515 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
517 _writer->write (text, type, track, period);
520 case TextType::CLOSED_CAPTION:
521 closed_caption (text, period);
523 case TextType::OPEN_SUBTITLE:
524 open_subtitle (text, period);
533 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
535 int lines = text.string.size();
536 for (auto i: text.string) {
537 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
543 "At least one of your closed caption lines has more than %1 characters. "
544 "It is advisable to make each line %1 characters at most in length.",
545 MAX_CLOSED_CAPTION_LENGTH,
546 MAX_CLOSED_CAPTION_LENGTH)
552 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
553 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
554 _too_many_ccap_lines = true;
557 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
558 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
559 _overlap_ccap = true;
560 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
568 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
570 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
571 _early_subtitle = true;
572 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."));
575 int const vfr = film()->video_frame_rate ();
577 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
578 _short_subtitle = true;
579 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
582 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
583 _subtitles_too_close = true;
584 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."));
587 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
588 _too_many_subtitle_lines = true;
589 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
592 size_t longest_line = 0;
593 for (auto const& i: text.string) {
594 longest_line = max (longest_line, i.text().length());
597 if (longest_line > 52) {
598 _long_subtitle = true;
601 if (longest_line > 79) {
602 _very_long_subtitle = true;
605 _last_subtitle = period;
610 Hints::check_ffec_and_ffmc_in_smpte_feature ()
613 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
614 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."));
627 Hints::check_text_languages ()
629 for (auto i: film()->content()) {
630 for (auto j: i->text) {
631 if (j->use() && !j->language()) {
632 hint (_("At least one piece of subtitle or closed caption content has no specified language. "
633 "It is advisable to set the language for each piece of subtitle or closed caption content "
634 "in the \"Content→Timed text\", \"Content→Open subtitles\" or \"Content→Closed captions\" tab."));