2 Copyright (C) 2016-2020 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"
31 #include "audio_analysis.h"
32 #include "compose.hpp"
36 #include <dcp/raw_convert.h>
37 #include <boost/foreach.hpp>
38 #include <boost/algorithm/string.hpp>
49 using boost::shared_ptr;
50 using boost::weak_ptr;
51 using boost::optional;
53 using namespace dcpomatic;
54 #if BOOST_VERSION >= 106100
55 using namespace boost::placeholders;
58 Hints::Hints (weak_ptr<const Film> film)
61 , _overlap_ccap (false)
62 , _too_many_ccap_lines (false)
63 , _early_subtitle (false)
64 , _short_subtitle (false)
65 , _subtitles_too_close (false)
66 , _too_many_subtitle_lines (false)
67 , _long_subtitle (false)
76 _thread = boost::thread (bind(&Hints::thread, this));
81 boost::this_thread::disable_interruption dis;
92 Hints::check_few_audio_channels ()
94 if (film()->audio_channels() < 6) {
95 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."));
101 Hints::check_upmixers ()
103 AudioProcessor const * ap = film()->audio_processor();
104 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
105 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."));
111 Hints::check_incorrect_container ()
113 int narrower_than_scope = 0;
115 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
117 Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
118 if (r && r->id() == "239") {
120 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
121 ++narrower_than_scope;
126 string const film_container = film()->container()->id();
128 if (scope && !narrower_than_scope && film_container == "185") {
129 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."));
132 if (!scope && narrower_than_scope && film_container == "239") {
133 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."));
139 Hints::check_unusual_container ()
141 string const film_container = film()->container()->id();
142 if (film_container != "185" && film_container != "239" && film_container != "190") {
143 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"));
149 Hints::check_high_j2k_bandwidth ()
151 if (film()->j2k_bandwidth() >= 245000000) {
152 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."));
158 Hints::check_frame_rate ()
160 shared_ptr<const Film> f = film ();
161 switch (f->video_frame_rate()) {
167 /* You might want to go to 24 */
168 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);
171 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
177 /* 30fps: we can't really offer any decent solutions */
178 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."));
183 /* You almost certainly want to go to half frame rate */
184 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));
191 Hints::check_speed_up ()
193 optional<double> lowest_speed_up;
194 optional<double> highest_speed_up;
195 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
196 double spu = film()->active_frame_rate_change(i->position()).speed_up;
197 if (!lowest_speed_up || spu < *lowest_speed_up) {
198 lowest_speed_up = spu;
200 if (!highest_speed_up || spu > *highest_speed_up) {
201 highest_speed_up = spu;
205 double worst_speed_up = 1;
206 if (highest_speed_up) {
207 worst_speed_up = *highest_speed_up;
209 if (lowest_speed_up) {
210 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
213 if (worst_speed_up > 25.5/24.0) {
214 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."));
221 Hints::check_big_font_files ()
223 bool big_font_files = false;
224 if (film()->interop ()) {
225 BOOST_FOREACH (shared_ptr<Content> i, film()->content()) {
226 BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
227 BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
228 optional<boost::filesystem::path> const p = k->file ();
229 if (p && boost::filesystem::file_size(p.get()) >= (640 * 1024)) {
230 big_font_files = true;
237 if (big_font_files) {
238 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
247 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
248 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
254 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));
260 Hints::check_3d_in_2d ()
263 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
264 if (i->video && i->video->frame_type() != VIDEO_FRAME_TYPE_2D) {
269 if (three_d > 0 && !film()->three_d()) {
270 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.)"));
276 Hints::check_loudness ()
278 boost::filesystem::path path = film()->audio_analysis_path(film()->playlist());
279 if (boost::filesystem::exists (path)) {
281 shared_ptr<AudioAnalysis> an (new AudioAnalysis (path));
285 vector<AudioAnalysis::PeakTime> sample_peak = an->sample_peak ();
286 vector<float> true_peak = an->true_peak ();
288 for (size_t i = 0; i < sample_peak.size(); ++i) {
289 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
290 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
292 ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
296 ch = ch.substr (0, ch.length() - 2);
299 hint (String::compose (
300 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
305 } catch (OldFormatError& e) {
306 /* The audio analysis is too old to load in; just skip this hint as if
307 it had never been run.
317 shared_ptr<const Film> film = _film.lock ();
322 ContentList content = film->content ();
324 check_big_font_files ();
325 check_few_audio_channels ();
327 check_incorrect_container ();
328 check_unusual_container ();
329 check_high_j2k_bandwidth ();
335 check_ffec_and_ffmc_in_smpte_feature ();
337 emit (bind(boost::ref(Progress), _("Examining closed captions")));
339 shared_ptr<Player> player (new Player(film));
340 player->set_ignore_video ();
341 player->set_ignore_audio ();
342 player->Text.connect (bind(&Hints::text, this, _1, _2, _4));
344 struct timeval last_pulse;
345 gettimeofday (&last_pulse, 0);
348 while (!player->pass()) {
351 gettimeofday (&now, 0);
352 if ((seconds(now) - seconds(last_pulse)) > 1) {
356 emit (bind (boost::ref(Pulse)));
364 emit (bind(boost::ref(Finished)));
368 Hints::hint (string h)
370 emit(bind(boost::ref(Hint), h));
374 Hints::text (PlayerText text, TextType type, DCPTimePeriod period)
377 case TEXT_CLOSED_CAPTION:
378 closed_caption (text, period);
380 case TEXT_OPEN_SUBTITLE:
381 open_subtitle (text, period);
390 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
392 int lines = text.string.size();
393 BOOST_FOREACH (StringText i, text.string) {
394 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
400 "At least one of your closed caption lines has more than %1 characters. "
401 "It is advisable to make each line %1 characters at most in length.",
402 MAX_CLOSED_CAPTION_LENGTH,
403 MAX_CLOSED_CAPTION_LENGTH)
409 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
410 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
411 _too_many_ccap_lines = true;
414 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
415 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
416 _overlap_ccap = true;
417 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
425 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
427 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
428 _early_subtitle = true;
429 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."));
432 int const vfr = film()->video_frame_rate ();
434 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
435 _short_subtitle = true;
436 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
439 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
440 _subtitles_too_close = true;
441 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."));
444 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
445 _too_many_subtitle_lines = true;
446 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
449 size_t longest_line = 0;
450 BOOST_FOREACH (StringText const& i, text.string) {
451 longest_line = max (longest_line, i.text().length());
454 if (longest_line > 52 && !_long_subtitle) {
455 _long_subtitle = true;
456 hint (_("At least one of your subtitle lines has more than 52 characters. It is advisable to make each line 52 characters at most in length."));
459 _last_subtitle = period;
463 shared_ptr<const Film>
466 shared_ptr<const Film> film = _film.lock ();
467 DCPOMATIC_ASSERT (film);
473 Hints::check_ffec_and_ffmc_in_smpte_feature ()
475 shared_ptr<const Film> f = film();
476 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::FEATURE && (!f->marker(dcp::FFEC) || !f->marker(dcp::FFMC))) {
477 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."));