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 "dcp_content_type.h"
27 #include "video_content.h"
28 #include "text_content.h"
29 #include "audio_processor.h"
31 #include "font_data.h"
33 #include "audio_analysis.h"
34 #include "compose.hpp"
40 #include <dcp/raw_convert.h>
42 #include <dcp/reel_closed_caption_asset.h>
43 #include <dcp/reel_subtitle_asset.h>
44 #include <boost/algorithm/string.hpp>
51 using std::make_shared;
55 using std::shared_ptr;
59 using boost::optional;
61 using namespace dcpomatic;
62 #if BOOST_VERSION >= 106100
63 using namespace boost::placeholders;
67 /* When checking to see if things are too big, we'll say they are if they
68 * are more than the target size minus this "slack."
70 #define SIZE_SLACK 4096
73 /* When writing hints:
74 * - put quotation marks around the name of a GUI tab that you are referring to (e.g. "DCP" or "DCP→Video" tab)
78 Hints::Hints (weak_ptr<const Film> weak_film)
79 : WeakConstFilm (weak_film)
80 , _writer (new Writer(weak_film, weak_ptr<Job>(), true))
81 , _analyser (film(), film()->playlist(), true, [](float) {})
91 _thread = boost::thread (bind(&Hints::thread, this));
97 boost::this_thread::disable_interruption dis;
101 _thread.interrupt ();
108 Hints::check_few_audio_channels ()
110 if (film()->audio_channels() < 6) {
111 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."));
117 Hints::check_upmixers ()
119 auto ap = film()->audio_processor();
120 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
121 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."));
127 Hints::check_incorrect_container ()
129 int narrower_than_scope = 0;
131 for (auto i: film()->content()) {
133 auto const r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
134 if (r && r->id() == "239") {
136 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
137 ++narrower_than_scope;
142 string const film_container = film()->container()->id();
144 if (scope && !narrower_than_scope && film_container == "185") {
145 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."));
148 if (!scope && narrower_than_scope && film_container == "239") {
149 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."));
155 Hints::check_unusual_container ()
157 auto const film_container = film()->container()->id();
158 if (film_container != "185" && film_container != "239") {
159 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"));
165 Hints::check_high_j2k_bandwidth ()
167 if (film()->j2k_bandwidth() >= 245000000) {
168 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."));
174 Hints::check_frame_rate ()
177 switch (f->video_frame_rate()) {
183 /* You might want to go to 24 */
184 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);
187 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
193 /* 30fps: we can't really offer any decent solutions */
194 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."));
199 /* You almost certainly want to go to half frame rate */
200 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));
207 Hints::check_4k_3d ()
210 if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
211 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."));
217 Hints::check_speed_up ()
219 optional<double> lowest_speed_up;
220 optional<double> highest_speed_up;
221 for (auto i: film()->content()) {
222 double spu = film()->active_frame_rate_change(i->position()).speed_up;
223 if (!lowest_speed_up || spu < *lowest_speed_up) {
224 lowest_speed_up = spu;
226 if (!highest_speed_up || spu > *highest_speed_up) {
227 highest_speed_up = spu;
231 double worst_speed_up = 1;
232 if (highest_speed_up) {
233 worst_speed_up = *highest_speed_up;
235 if (lowest_speed_up) {
236 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
239 if (worst_speed_up > 25.5/24.0) {
240 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."));
247 Hints::check_interop ()
249 if (film()->interop()) {
250 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."));
256 Hints::check_big_font_files ()
258 bool big_font_files = false;
259 if (film()->interop ()) {
260 for (auto i: film()->content()) {
261 for (auto j: i->text) {
262 for (auto k: j->fonts()) {
263 auto const p = k->file ();
264 if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
265 big_font_files = true;
272 if (big_font_files) {
273 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
282 for (auto i: film()->content()) {
283 if (boost::algorithm::starts_with(i->path(0).filename().string(), "VTS_")) {
289 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));
295 Hints::check_3d_in_2d ()
298 for (auto i: film()->content()) {
299 if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
304 if (three_d > 0 && !film()->three_d()) {
305 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.)"));
310 /** @return true if the loudness could be checked, false if it could not because no analysis was available */
312 Hints::check_loudness ()
314 auto path = film()->audio_analysis_path(film()->playlist());
315 if (!boost::filesystem::exists(path)) {
320 auto an = make_shared<AudioAnalysis>(path);
324 auto sample_peak = an->sample_peak ();
325 auto true_peak = an->true_peak ();
327 for (size_t i = 0; i < sample_peak.size(); ++i) {
328 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
329 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
331 ch += dcp::raw_convert<string>(short_audio_channel_name(i)) + ", ";
335 ch = ch.substr (0, ch.length() - 2);
338 hint(String::compose(
339 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
344 } catch (OldFormatError& e) {
345 /* The audio analysis is too old to load in */
355 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
357 return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
362 Hints::check_out_of_range_markers ()
364 auto const length = film()->length();
365 for (auto const& i: film()->markers()) {
366 if (i.second >= length) {
367 hint (_("At least one marker comes after the end of the project and will be ignored."));
377 start_of_thread ("Hints");
379 auto film = _film.lock ();
384 auto content = film->content ();
387 check_big_font_files ();
388 check_few_audio_channels ();
390 check_incorrect_container ();
391 check_unusual_container ();
392 check_high_j2k_bandwidth ();
398 auto const check_loudness_done = check_loudness ();
399 check_ffec_and_ffmc_in_smpte_feature ();
400 check_out_of_range_markers ();
401 check_text_languages ();
403 if (check_loudness_done) {
404 emit (bind(boost::ref(Progress), _("Examining subtitles and closed captions")));
406 emit (bind(boost::ref(Progress), _("Examining audio, subtitles and closed captions")));
409 auto player = make_shared<Player>(film);
410 player->set_ignore_video ();
411 if (check_loudness_done) {
412 /* We don't need to analyse audio because we already loaded a suitable analysis */
413 player->set_ignore_audio ();
415 player->Audio.connect (bind(&Hints::audio, this, _1, _2));
416 player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
418 struct timeval last_pulse;
419 gettimeofday (&last_pulse, 0);
421 while (!player->pass()) {
424 gettimeofday (&now, 0);
425 if ((seconds(now) - seconds(last_pulse)) > 1) {
429 emit (bind (boost::ref(Pulse)));
434 if (!check_loudness_done) {
436 _analyser.get().write(film->audio_analysis_path(film->playlist()));
440 _writer->write (player->get_subtitle_fonts());
442 if (_long_subtitle && !_very_long_subtitle) {
443 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."));
444 } else if (_very_long_subtitle) {
445 hint (_("At least one of your subtitle lines has more than 79 characters. You should make each line 79 characters at most in length."));
448 bool ccap_xml_too_big = false;
449 bool ccap_mxf_too_big = false;
450 bool subs_mxf_too_big = false;
452 auto dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
453 boost::filesystem::remove_all (dcp_dir);
455 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
457 dcp::DCP dcp (dcp_dir);
459 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
460 for (auto reel: dcp.cpls()[0]->reels()) {
461 for (auto ccap: reel->closed_captions()) {
462 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
464 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
465 ". You should divide the DCP into shorter reels."
467 ccap_xml_too_big = true;
469 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
471 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
472 " in total. You should divide the DCP into shorter reels."
474 ccap_mxf_too_big = true;
477 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
479 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
480 "You should divide the DCP into shorter reels."
482 subs_mxf_too_big = true;
485 boost::filesystem::remove_all (dcp_dir);
487 emit (bind(boost::ref(Finished)));
489 catch (boost::thread_interrupted)
491 /* The Hints object is being destroyed before it has finished, so just give up */
500 Hints::hint (string h)
502 emit(bind(boost::ref(Hint), h));
507 Hints::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
509 _analyser.analyse (audio, time);
514 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
516 _writer->write (text, type, track, period);
519 case TextType::CLOSED_CAPTION:
520 closed_caption (text, period);
522 case TextType::OPEN_SUBTITLE:
523 open_subtitle (text, period);
532 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
534 int lines = text.string.size();
535 for (auto i: text.string) {
536 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
542 "At least one of your closed caption lines has more than %1 characters. "
543 "It is advisable to make each line %1 characters at most in length.",
544 MAX_CLOSED_CAPTION_LENGTH,
545 MAX_CLOSED_CAPTION_LENGTH)
551 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
552 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
553 _too_many_ccap_lines = true;
556 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
557 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
558 _overlap_ccap = true;
559 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
567 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
569 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
570 _early_subtitle = true;
571 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."));
574 int const vfr = film()->video_frame_rate ();
576 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
577 _short_subtitle = true;
578 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
581 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
582 _subtitles_too_close = true;
583 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."));
586 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
587 _too_many_subtitle_lines = true;
588 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
591 size_t longest_line = 0;
592 for (auto const& i: text.string) {
593 longest_line = max (longest_line, i.text().length());
596 if (longest_line > 52) {
597 _long_subtitle = true;
600 if (longest_line > 79) {
601 _very_long_subtitle = true;
604 _last_subtitle = period;
609 Hints::check_ffec_and_ffmc_in_smpte_feature ()
612 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
613 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."));
626 Hints::check_text_languages ()
628 for (auto i: film()->content()) {
629 for (auto j: i->text) {
630 if (j->use() && !j->language()) {
631 hint (_("At least one piece of subtitle or closed caption content has no specified language. "
632 "It is advisable to set the language for each piece of subtitle or closed caption content "
633 "in the \"Content→Timed text\", \"Content→Open subtitles\" or \"Content→Closed captions\" tab."));