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/>.
21 #include "dcp_content_type.h"
26 #include "video_content.h"
27 #include "text_content.h"
28 #include "audio_processor.h"
30 #include "font_data.h"
32 #include "audio_analysis.h"
33 #include "compose.hpp"
39 #include <dcp/raw_convert.h>
41 #include <dcp/reel_closed_caption_asset.h>
42 #include <dcp/reel_subtitle_asset.h>
43 #include <boost/algorithm/string.hpp>
49 using std::make_shared;
53 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> film)
77 : WeakConstFilm (film)
78 , _writer (new Writer(film, weak_ptr<Job>(), true))
87 _thread = boost::thread (bind(&Hints::thread, this));
92 boost::this_thread::disable_interruption dis;
103 Hints::check_few_audio_channels ()
105 if (film()->audio_channels() < 6) {
106 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."));
112 Hints::check_upmixers ()
114 auto ap = film()->audio_processor();
115 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
116 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."));
122 Hints::check_incorrect_container ()
124 int narrower_than_scope = 0;
126 for (auto i: film()->content()) {
128 Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
129 if (r && r->id() == "239") {
131 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
132 ++narrower_than_scope;
137 string const film_container = film()->container()->id();
139 if (scope && !narrower_than_scope && film_container == "185") {
140 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."));
143 if (!scope && narrower_than_scope && film_container == "239") {
144 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."));
150 Hints::check_unusual_container ()
152 auto const film_container = film()->container()->id();
153 if (film_container != "185" && film_container != "239") {
154 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"));
160 Hints::check_high_j2k_bandwidth ()
162 if (film()->j2k_bandwidth() >= 245000000) {
163 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."));
169 Hints::check_frame_rate ()
172 switch (f->video_frame_rate()) {
178 /* You might want to go to 24 */
179 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);
182 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
188 /* 30fps: we can't really offer any decent solutions */
189 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."));
194 /* You almost certainly want to go to half frame rate */
195 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));
202 Hints::check_4k_3d ()
205 if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
206 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."));
212 Hints::check_speed_up ()
214 optional<double> lowest_speed_up;
215 optional<double> highest_speed_up;
216 for (auto i: film()->content()) {
217 double spu = film()->active_frame_rate_change(i->position()).speed_up;
218 if (!lowest_speed_up || spu < *lowest_speed_up) {
219 lowest_speed_up = spu;
221 if (!highest_speed_up || spu > *highest_speed_up) {
222 highest_speed_up = spu;
226 double worst_speed_up = 1;
227 if (highest_speed_up) {
228 worst_speed_up = *highest_speed_up;
230 if (lowest_speed_up) {
231 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
234 if (worst_speed_up > 25.5/24.0) {
235 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."));
242 Hints::check_interop ()
244 if (film()->interop()) {
245 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."));
251 Hints::check_big_font_files ()
253 bool big_font_files = false;
254 if (film()->interop ()) {
255 for (auto i: film()->content()) {
256 for (auto j: i->text) {
257 for (auto k: j->fonts()) {
258 optional<boost::filesystem::path> const p = k->file ();
259 if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
260 big_font_files = true;
267 if (big_font_files) {
268 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
277 for (auto i: film()->content()) {
278 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
284 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));
290 Hints::check_3d_in_2d ()
293 for (auto i: film()->content()) {
294 if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
299 if (three_d > 0 && !film()->three_d()) {
300 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.)"));
306 Hints::check_loudness ()
308 auto path = film()->audio_analysis_path(film()->playlist());
309 if (boost::filesystem::exists (path)) {
311 auto an = make_shared<AudioAnalysis>(path);
315 auto sample_peak = an->sample_peak ();
316 auto true_peak = an->true_peak ();
318 for (size_t i = 0; i < sample_peak.size(); ++i) {
319 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
320 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
322 ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
326 ch = ch.substr (0, ch.length() - 2);
329 hint (String::compose (
330 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
335 } catch (OldFormatError& e) {
336 /* The audio analysis is too old to load in; just skip this hint as if
337 it had never been run.
346 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
348 return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
353 Hints::check_out_of_range_markers ()
355 auto const length = film()->length();
356 for (auto const& i: film()->markers()) {
357 if (i.second >= length) {
358 hint (_("At least one marker comes after the end of the project and will be ignored."));
367 auto film = _film.lock ();
372 auto content = film->content ();
375 check_big_font_files ();
376 check_few_audio_channels ();
378 check_incorrect_container ();
379 check_unusual_container ();
380 check_high_j2k_bandwidth ();
387 check_ffec_and_ffmc_in_smpte_feature ();
388 check_out_of_range_markers ();
389 check_text_languages ();
391 emit (bind(boost::ref(Progress), _("Examining closed captions")));
393 auto player = make_shared<Player>(film);
394 player->set_ignore_video ();
395 player->set_ignore_audio ();
396 player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
398 struct timeval last_pulse;
399 gettimeofday (&last_pulse, 0);
402 while (!player->pass()) {
405 gettimeofday (&now, 0);
406 if ((seconds(now) - seconds(last_pulse)) > 1) {
410 emit (bind (boost::ref(Pulse)));
418 _writer->write (player->get_subtitle_fonts());
420 if (_long_subtitle && !_very_long_subtitle) {
421 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."));
422 } else if (_very_long_subtitle) {
423 hint (_("At least one of your subtitle lines has more than 79 characters. You should make each line 79 characters at most in length."));
426 bool ccap_xml_too_big = false;
427 bool ccap_mxf_too_big = false;
428 bool subs_mxf_too_big = false;
430 boost::filesystem::path dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
431 boost::filesystem::remove_all (dcp_dir);
434 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
437 emit (bind(boost::ref(Finished)));
441 dcp::DCP dcp (dcp_dir);
443 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
444 for (auto reel: dcp.cpls()[0]->reels()) {
445 for (auto ccap: reel->closed_captions()) {
446 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
448 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
449 ". You should divide the DCP into shorter reels."
451 ccap_xml_too_big = true;
453 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
455 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
456 " in total. You should divide the DCP into shorter reels."
458 ccap_mxf_too_big = true;
461 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
463 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
464 "You should divide the DCP into shorter reels."
466 subs_mxf_too_big = true;
469 boost::filesystem::remove_all (dcp_dir);
471 emit (bind(boost::ref(Finished)));
475 Hints::hint (string h)
477 emit(bind(boost::ref(Hint), h));
481 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
483 _writer->write (text, type, track, period);
486 case TextType::CLOSED_CAPTION:
487 closed_caption (text, period);
489 case TextType::OPEN_SUBTITLE:
490 open_subtitle (text, period);
499 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
501 int lines = text.string.size();
502 for (auto i: text.string) {
503 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
509 "At least one of your closed caption lines has more than %1 characters. "
510 "It is advisable to make each line %1 characters at most in length.",
511 MAX_CLOSED_CAPTION_LENGTH,
512 MAX_CLOSED_CAPTION_LENGTH)
518 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
519 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
520 _too_many_ccap_lines = true;
523 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
524 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
525 _overlap_ccap = true;
526 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
534 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
536 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
537 _early_subtitle = true;
538 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."));
541 int const vfr = film()->video_frame_rate ();
543 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
544 _short_subtitle = true;
545 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
548 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
549 _subtitles_too_close = true;
550 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."));
553 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
554 _too_many_subtitle_lines = true;
555 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
558 size_t longest_line = 0;
559 for (auto const& i: text.string) {
560 longest_line = max (longest_line, i.text().length());
563 if (longest_line > 52) {
564 _long_subtitle = true;
567 if (longest_line > 79) {
568 _very_long_subtitle = true;
571 _last_subtitle = period;
576 Hints::check_ffec_and_ffmc_in_smpte_feature ()
579 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
580 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."));
593 Hints::check_text_languages ()
595 for (auto i: film()->content()) {
596 for (auto j: i->text) {
597 if (j->use() && !j->language()) {
598 hint (_("At least one piece of subtitle or closed caption content has no specified language. "
599 "It is advisable to set the language for each piece of subtitle or closed caption content "
600 "in the \"Content→Timed text\", \"Content→Open subtitles\" or \"Content→Closed captions\" tab"));