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;
54 using std::shared_ptr;
57 using boost::optional;
59 using namespace dcpomatic;
60 #if BOOST_VERSION >= 106100
61 using namespace boost::placeholders;
65 /* When checking to see if things are too big, we'll say they are if they
66 * are more than the target size minus this "slack."
68 #define SIZE_SLACK 4096
71 /* When writing hints:
72 * - put quotation marks around the name of a GUI tab that you are referring to (e.g. "DCP" or "DCP→Video" tab)
76 Hints::Hints (weak_ptr<const Film> weak_film)
77 : WeakConstFilm (weak_film)
78 , _writer (new Writer(weak_film, weak_ptr<Job>(), true))
79 , _analyser (film(), film()->playlist(), true, [](float) {})
89 _thread = boost::thread (bind(&Hints::thread, this));
95 boost::this_thread::disable_interruption dis;
106 Hints::check_few_audio_channels ()
108 if (film()->audio_channels() < 6) {
109 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."));
115 Hints::check_upmixers ()
117 auto ap = film()->audio_processor();
118 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
119 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."));
125 Hints::check_incorrect_container ()
127 int narrower_than_scope = 0;
129 for (auto i: film()->content()) {
131 auto const r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
132 if (r && r->id() == "239") {
134 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
135 ++narrower_than_scope;
140 string const film_container = film()->container()->id();
142 if (scope && !narrower_than_scope && film_container == "185") {
143 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."));
146 if (!scope && narrower_than_scope && film_container == "239") {
147 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."));
153 Hints::check_unusual_container ()
155 auto const film_container = film()->container()->id();
156 if (film_container != "185" && film_container != "239") {
157 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."));
163 Hints::check_high_j2k_bandwidth ()
165 if (film()->j2k_bandwidth() >= 245000000) {
166 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."));
172 Hints::check_frame_rate ()
175 switch (f->video_frame_rate()) {
181 /* You might want to go to 24 */
182 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);
185 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
191 /* 30fps: we can't really offer any decent solutions */
192 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."));
197 /* You almost certainly want to go to half frame rate */
198 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));
205 Hints::check_4k_3d ()
208 if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
209 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."));
215 Hints::check_speed_up ()
217 optional<double> lowest_speed_up;
218 optional<double> highest_speed_up;
219 for (auto i: film()->content()) {
220 double spu = film()->active_frame_rate_change(i->position()).speed_up;
221 if (!lowest_speed_up || spu < *lowest_speed_up) {
222 lowest_speed_up = spu;
224 if (!highest_speed_up || spu > *highest_speed_up) {
225 highest_speed_up = spu;
229 double worst_speed_up = 1;
230 if (highest_speed_up) {
231 worst_speed_up = *highest_speed_up;
233 if (lowest_speed_up) {
234 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
237 if (worst_speed_up > 25.5/24.0) {
238 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."));
245 Hints::check_interop ()
247 if (film()->interop()) {
248 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."));
254 Hints::check_big_font_files ()
256 bool big_font_files = false;
257 if (film()->interop ()) {
258 for (auto i: film()->content()) {
259 for (auto j: i->text) {
260 for (auto k: j->fonts()) {
261 auto const p = k->file ();
262 if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
263 big_font_files = true;
270 if (big_font_files) {
271 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
280 for (auto i: film()->content()) {
281 if (boost::algorithm::starts_with(i->path(0).filename().string(), "VTS_")) {
287 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));
293 Hints::check_3d_in_2d ()
296 for (auto i: film()->content()) {
297 if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
302 if (three_d > 0 && !film()->three_d()) {
303 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.)"));
308 /** @return true if the loudness could be checked, false if it could not because no analysis was available */
310 Hints::check_loudness ()
312 auto path = film()->audio_analysis_path(film()->playlist());
313 if (!boost::filesystem::exists(path)) {
318 auto an = make_shared<AudioAnalysis>(path);
322 auto sample_peak = an->sample_peak ();
323 auto true_peak = an->true_peak ();
325 for (size_t i = 0; i < sample_peak.size(); ++i) {
326 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
327 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
329 ch += dcp::raw_convert<string>(short_audio_channel_name(i)) + ", ";
333 ch = ch.substr (0, ch.length() - 2);
336 hint(String::compose(
337 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
342 } catch (OldFormatError& e) {
343 /* The audio analysis is too old to load in */
353 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
355 return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
360 Hints::check_out_of_range_markers ()
362 auto const length = film()->length();
363 for (auto const& i: film()->markers()) {
364 if (i.second >= length) {
365 hint (_("At least one marker comes after the end of the project and will be ignored."));
375 start_of_thread ("Hints");
377 auto film = _film.lock ();
382 auto content = film->content ();
385 check_big_font_files ();
386 check_few_audio_channels ();
388 check_incorrect_container ();
389 check_unusual_container ();
390 check_high_j2k_bandwidth ();
396 auto const check_loudness_done = check_loudness ();
397 check_ffec_and_ffmc_in_smpte_feature ();
398 check_out_of_range_markers ();
399 check_text_languages ();
400 check_audio_language ();
402 if (check_loudness_done) {
403 emit (bind(boost::ref(Progress), _("Examining subtitles and closed captions")));
405 emit (bind(boost::ref(Progress), _("Examining audio, subtitles and closed captions")));
408 auto player = make_shared<Player>(film, Image::Alignment::COMPACT);
409 player->set_ignore_video ();
410 if (check_loudness_done) {
411 /* We don't need to analyse audio because we already loaded a suitable analysis */
412 player->set_ignore_audio ();
414 player->Audio.connect (bind(&Hints::audio, this, _1, _2));
415 player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
417 struct timeval last_pulse;
418 gettimeofday (&last_pulse, 0);
420 while (!player->pass()) {
423 gettimeofday (&now, 0);
424 if ((seconds(now) - seconds(last_pulse)) > 1) {
428 emit (bind (boost::ref(Pulse)));
433 if (!check_loudness_done) {
435 _analyser.get().write(film->audio_analysis_path(film->playlist()));
439 _writer->write (player->get_subtitle_fonts());
441 if (_long_subtitle && !_very_long_subtitle) {
442 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."));
443 } else if (_very_long_subtitle) {
444 hint (_("At least one of your subtitle lines has more than 79 characters. You should make each line 79 characters at most in length."));
447 bool ccap_xml_too_big = false;
448 bool ccap_mxf_too_big = false;
449 bool subs_mxf_too_big = false;
451 auto dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
452 boost::filesystem::remove_all (dcp_dir);
454 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
456 dcp::DCP dcp (dcp_dir);
458 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
459 for (auto reel: dcp.cpls()[0]->reels()) {
460 for (auto ccap: reel->closed_captions()) {
461 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
463 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
464 ". You should divide the DCP into shorter reels."
466 ccap_xml_too_big = true;
468 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
470 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
471 " in total. You should divide the DCP into shorter reels."
473 ccap_mxf_too_big = true;
476 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
478 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
479 "You should divide the DCP into shorter reels."
481 subs_mxf_too_big = true;
484 boost::filesystem::remove_all (dcp_dir);
486 emit (bind(boost::ref(Finished)));
488 catch (boost::thread_interrupted)
490 /* The Hints object is being destroyed before it has finished, so just give up */
499 Hints::hint (string h)
501 emit(bind(boost::ref(Hint), h));
506 Hints::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
508 _analyser.analyse (audio, time);
513 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
515 _writer->write (text, type, track, period);
518 case TextType::CLOSED_CAPTION:
519 closed_caption (text, period);
521 case TextType::OPEN_SUBTITLE:
522 open_subtitle (text, period);
531 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
533 int lines = text.string.size();
534 for (auto i: text.string) {
535 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
541 "At least one of your closed caption lines has more than %1 characters. "
542 "It is advisable to make each line %1 characters at most in length.",
543 MAX_CLOSED_CAPTION_LENGTH,
544 MAX_CLOSED_CAPTION_LENGTH)
550 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
551 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
552 _too_many_ccap_lines = true;
555 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
556 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
557 _overlap_ccap = true;
558 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
566 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
568 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
569 _early_subtitle = true;
570 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."));
573 int const vfr = film()->video_frame_rate ();
575 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
576 _short_subtitle = true;
577 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
580 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
581 _subtitles_too_close = true;
582 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."));
585 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
586 _too_many_subtitle_lines = true;
587 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
590 size_t longest_line = 0;
591 for (auto const& i: text.string) {
592 longest_line = max (longest_line, i.text().length());
595 if (longest_line > 52) {
596 _long_subtitle = true;
599 if (longest_line > 79) {
600 _very_long_subtitle = true;
603 _last_subtitle = period;
608 Hints::check_ffec_and_ffmc_in_smpte_feature ()
611 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
612 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."));
625 Hints::check_text_languages ()
627 for (auto i: film()->content()) {
628 for (auto j: i->text) {
629 if (j->use() && !j->language()) {
630 hint (_("At least one piece of subtitle or closed caption content has no specified language. "
631 "It is advisable to set the language for each piece of subtitle or closed caption content "
632 "in the \"Content→Timed text\", \"Content→Open subtitles\" or \"Content→Closed captions\" tab."));
641 Hints::check_audio_language ()
643 auto content = film()->content();
645 std::find_if(content.begin(), content.end(), [](shared_ptr<const Content> c) {
646 return c->audio && !c->audio->mapping().mapped_output_channels().empty();
649 if (mapped_audio != content.end() && !film()->audio_language()) {
650 hint (_("Some of your content has audio but you have not set the audio language. It is advisable to set the audio language "
651 "in the \"DCP\" tab unless your audio has no spoken parts."));