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> weak_film)
77 : WeakConstFilm (weak_film)
78 , _writer (new Writer(weak_film, weak_ptr<Job>(), true))
79 , _analyser (film(), film()->playlist(), true, [](float) {})
88 _thread = boost::thread (bind(&Hints::thread, this));
93 boost::this_thread::disable_interruption dis;
104 Hints::check_few_audio_channels ()
106 if (film()->audio_channels() < 6) {
107 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."));
113 Hints::check_upmixers ()
115 auto ap = film()->audio_processor();
116 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
117 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."));
123 Hints::check_incorrect_container ()
125 int narrower_than_scope = 0;
127 for (auto i: film()->content()) {
129 auto const r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
130 if (r && r->id() == "239") {
132 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
133 ++narrower_than_scope;
138 string const film_container = film()->container()->id();
140 if (scope && !narrower_than_scope && film_container == "185") {
141 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."));
144 if (!scope && narrower_than_scope && film_container == "239") {
145 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."));
151 Hints::check_unusual_container ()
153 auto const film_container = film()->container()->id();
154 if (film_container != "185" && film_container != "239") {
155 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"));
161 Hints::check_high_j2k_bandwidth ()
163 if (film()->j2k_bandwidth() >= 245000000) {
164 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."));
170 Hints::check_frame_rate ()
173 switch (f->video_frame_rate()) {
179 /* You might want to go to 24 */
180 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);
183 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
189 /* 30fps: we can't really offer any decent solutions */
190 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."));
195 /* You almost certainly want to go to half frame rate */
196 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));
203 Hints::check_4k_3d ()
206 if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
207 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."));
213 Hints::check_speed_up ()
215 optional<double> lowest_speed_up;
216 optional<double> highest_speed_up;
217 for (auto i: film()->content()) {
218 double spu = film()->active_frame_rate_change(i->position()).speed_up;
219 if (!lowest_speed_up || spu < *lowest_speed_up) {
220 lowest_speed_up = spu;
222 if (!highest_speed_up || spu > *highest_speed_up) {
223 highest_speed_up = spu;
227 double worst_speed_up = 1;
228 if (highest_speed_up) {
229 worst_speed_up = *highest_speed_up;
231 if (lowest_speed_up) {
232 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
235 if (worst_speed_up > 25.5/24.0) {
236 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."));
243 Hints::check_interop ()
245 if (film()->interop()) {
246 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."));
252 Hints::check_big_font_files ()
254 bool big_font_files = false;
255 if (film()->interop ()) {
256 for (auto i: film()->content()) {
257 for (auto j: i->text) {
258 for (auto k: j->fonts()) {
259 optional<boost::filesystem::path> const p = k->file ();
260 if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
261 big_font_files = true;
268 if (big_font_files) {
269 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
278 for (auto i: film()->content()) {
279 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
285 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));
291 Hints::check_3d_in_2d ()
294 for (auto i: film()->content()) {
295 if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
300 if (three_d > 0 && !film()->three_d()) {
301 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 /** @return true if the loudness could be checked, false if it could not because no analysis was available */
308 Hints::check_loudness ()
310 auto path = film()->audio_analysis_path(film()->playlist());
311 if (!boost::filesystem::exists(path)) {
316 auto an = make_shared<AudioAnalysis>(path);
320 auto sample_peak = an->sample_peak ();
321 auto true_peak = an->true_peak ();
323 for (size_t i = 0; i < sample_peak.size(); ++i) {
324 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
325 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
327 ch += dcp::raw_convert<string>(short_audio_channel_name(i)) + ", ";
331 ch = ch.substr (0, ch.length() - 2);
334 hint (String::compose (
335 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
340 } catch (OldFormatError& e) {
341 /* The audio analysis is too old to load in */
351 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
353 return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
358 Hints::check_out_of_range_markers ()
360 auto const length = film()->length();
361 for (auto const& i: film()->markers()) {
362 if (i.second >= length) {
363 hint (_("At least one marker comes after the end of the project and will be ignored."));
373 start_of_thread ("Hints");
375 auto film = _film.lock ();
380 auto content = film->content ();
383 check_big_font_files ();
384 check_few_audio_channels ();
386 check_incorrect_container ();
387 check_unusual_container ();
388 check_high_j2k_bandwidth ();
394 auto const check_loudness_done = check_loudness ();
395 check_ffec_and_ffmc_in_smpte_feature ();
396 check_out_of_range_markers ();
397 check_text_languages ();
399 if (check_loudness_done) {
400 emit (bind(boost::ref(Progress), _("Examining subtitles and closed captions")));
402 emit (bind(boost::ref(Progress), _("Examining audio, subtitles and closed captions")));
405 auto player = make_shared<Player>(film);
406 player->set_ignore_video ();
407 if (check_loudness_done) {
408 /* We don't need to analyse audio because we already loaded a suitable analysis */
409 player->set_ignore_audio ();
411 player->Audio.connect (bind(&Hints::audio, this, _1, _2));
412 player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
414 struct timeval last_pulse;
415 gettimeofday (&last_pulse, 0);
417 while (!player->pass()) {
420 gettimeofday (&now, 0);
421 if ((seconds(now) - seconds(last_pulse)) > 1) {
425 emit (bind (boost::ref(Pulse)));
430 if (!check_loudness_done) {
432 _analyser.get().write(film->audio_analysis_path(film->playlist()));
436 _writer->write (player->get_subtitle_fonts());
438 if (_long_subtitle && !_very_long_subtitle) {
439 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."));
440 } else if (_very_long_subtitle) {
441 hint (_("At least one of your subtitle lines has more than 79 characters. You should make each line 79 characters at most in length."));
444 bool ccap_xml_too_big = false;
445 bool ccap_mxf_too_big = false;
446 bool subs_mxf_too_big = false;
448 boost::filesystem::path dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
449 boost::filesystem::remove_all (dcp_dir);
451 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
453 dcp::DCP dcp (dcp_dir);
455 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
456 for (auto reel: dcp.cpls()[0]->reels()) {
457 for (auto ccap: reel->closed_captions()) {
458 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
460 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
461 ". You should divide the DCP into shorter reels."
463 ccap_xml_too_big = true;
465 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
467 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
468 " in total. You should divide the DCP into shorter reels."
470 ccap_mxf_too_big = true;
473 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
475 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
476 "You should divide the DCP into shorter reels."
478 subs_mxf_too_big = true;
481 boost::filesystem::remove_all (dcp_dir);
483 emit (bind(boost::ref(Finished)));
485 catch (boost::thread_interrupted)
487 /* The Hints object is being destroyed before it has finished, so just give up */
495 Hints::hint (string h)
497 emit(bind(boost::ref(Hint), h));
502 Hints::audio (shared_ptr<AudioBuffers> audio, DCPTime time)
504 _analyser.analyse (audio, time);
509 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
511 _writer->write (text, type, track, period);
514 case TextType::CLOSED_CAPTION:
515 closed_caption (text, period);
517 case TextType::OPEN_SUBTITLE:
518 open_subtitle (text, period);
527 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
529 int lines = text.string.size();
530 for (auto i: text.string) {
531 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
537 "At least one of your closed caption lines has more than %1 characters. "
538 "It is advisable to make each line %1 characters at most in length.",
539 MAX_CLOSED_CAPTION_LENGTH,
540 MAX_CLOSED_CAPTION_LENGTH)
546 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
547 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
548 _too_many_ccap_lines = true;
551 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
552 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
553 _overlap_ccap = true;
554 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
562 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
564 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
565 _early_subtitle = true;
566 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."));
569 int const vfr = film()->video_frame_rate ();
571 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
572 _short_subtitle = true;
573 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
576 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
577 _subtitles_too_close = true;
578 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."));
581 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
582 _too_many_subtitle_lines = true;
583 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
586 size_t longest_line = 0;
587 for (auto const& i: text.string) {
588 longest_line = max (longest_line, i.text().length());
591 if (longest_line > 52) {
592 _long_subtitle = true;
595 if (longest_line > 79) {
596 _very_long_subtitle = true;
599 _last_subtitle = period;
604 Hints::check_ffec_and_ffmc_in_smpte_feature ()
607 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
608 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."));
621 Hints::check_text_languages ()
623 for (auto i: film()->content()) {
624 for (auto j: i->text) {
625 if (j->use() && !j->language()) {
626 hint (_("At least one piece of subtitle or closed caption content has no specified language. "
627 "It is advisable to set the language for each piece of subtitle or closed caption content "
628 "in the \"Content→Timed text\", \"Content→Open subtitles\" or \"Content→Closed captions\" tab."));