2 Copyright (C) 2016-2019 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/>.
25 #include "video_content.h"
26 #include "text_content.h"
27 #include "audio_processor.h"
30 #include "audio_analysis.h"
31 #include "compose.hpp"
35 #include <dcp/raw_convert.h>
36 #include <boost/foreach.hpp>
37 #include <boost/algorithm/string.hpp>
48 using boost::shared_ptr;
49 using boost::weak_ptr;
50 using boost::optional;
53 Hints::Hints (weak_ptr<const Film> film)
57 , _overlap_ccap (false)
58 , _too_many_ccap_lines (false)
67 _thread = new boost::thread (bind(&Hints::thread, this));
78 boost::mutex::scoped_lock lm (_mutex);
81 _thread->interrupt ();
93 shared_ptr<const Film> film = _film.lock ();
98 ContentList content = film->content ();
100 bool big_font_files = false;
101 if (film->interop ()) {
102 BOOST_FOREACH (shared_ptr<Content> i, content) {
103 BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
104 BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
105 optional<boost::filesystem::path> const p = k->file ();
106 if (p && boost::filesystem::file_size(p.get()) >= (640 * 1024)) {
107 big_font_files = true;
114 if (big_font_files) {
115 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
118 if (film->audio_channels() < 6) {
119 hint (_("Your DCP has fewer than 6 audio channels. This may cause problems on some projectors."));
122 AudioProcessor const * ap = film->audio_processor();
123 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
124 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 int flat_or_narrower = 0;
129 BOOST_FOREACH (shared_ptr<const Content> i, content) {
131 Ratio const * r = i->video->scale().ratio ();
132 if (r && r->id() == "239") {
134 } else if (r && r->id() != "239" && r->id() != "190") {
140 string const film_container = film->container()->id();
142 if (scope && !flat_or_narrower && 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 && flat_or_narrower && film_container == "239") {
147 hint (_("All of your content is at 1.85:1 or narrower but your DCP's container is Scope (2.39:1). This will pillar-box your content inside a Flat (1.85:1) frame. You may prefer to set your DCP's container to Flat (1.85:1) in the \"DCP\" tab."));
150 if (film_container != "185" && film_container != "239" && film_container != "190") {
151 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"));
154 if (film->j2k_bandwidth() >= 245000000) {
155 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 if (film->interop() && film->video_frame_rate() != 24 && film->video_frame_rate() != 48) {
159 string base = _("You are set up for an Interop DCP at a frame rate which is not officially supported. You are advised either to change the frame rate of your DCP or to make a SMPTE DCP instead.");
161 pair<double, double> range24 = film->speed_up_range (24);
162 pair<double, double> range48 = film->speed_up_range (48);
163 pair<double, double> range (max (range24.first, range48.first), min (range24.second, range48.second));
165 if (range.second > (29.0/24)) {
167 h += _("However, setting your DCP frame rate to 24 or 48 will cause a significant speed-up of your content, and SMPTE DCPs are not supported by all projectors.");
168 } else if (range.first < (24.0/29)) {
170 h += _("However, setting your DCP frame rate to 24 or 48 will cause a significant slowdown of your content, and SMPTE DCPs are not supported by all projectors.");
172 h = _("You are set up for an Interop DCP at a frame rate which is not officially supported. You are advised either to change the frame rate of your DCP or to make a SMPTE DCP instead (although SMPTE DCPs are not supported by all projectors).");
178 if (film->video_frame_rate() > 30) {
179 hint (String::compose(_("You are set up for a DCP at a frame rate of %1. This frame rate is not supported by all projectors. You are advised to change the DCP frame rate to %2."), film->video_frame_rate(), film->video_frame_rate() / 2));
182 optional<double> lowest_speed_up;
183 optional<double> highest_speed_up;
184 BOOST_FOREACH (shared_ptr<const Content> i, content) {
185 double spu = film->active_frame_rate_change(i->position()).speed_up;
186 if (!lowest_speed_up || spu < *lowest_speed_up) {
187 lowest_speed_up = spu;
189 if (!highest_speed_up || spu > *highest_speed_up) {
190 highest_speed_up = spu;
194 double worst_speed_up = 1;
195 if (highest_speed_up) {
196 worst_speed_up = *highest_speed_up;
198 if (lowest_speed_up) {
199 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
202 if (worst_speed_up > 25.5/24.0) {
203 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."));
207 BOOST_FOREACH (shared_ptr<const Content> i, content) {
208 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
214 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));
218 BOOST_FOREACH (shared_ptr<const Content> i, content) {
219 if (i->video && i->video->frame_type() != VIDEO_FRAME_TYPE_2D) {
224 if (three_d > 0 && !film->three_d()) {
225 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.)"));
228 boost::filesystem::path path = film->audio_analysis_path (film->playlist ());
229 if (boost::filesystem::exists (path)) {
231 shared_ptr<AudioAnalysis> an (new AudioAnalysis (path));
235 vector<AudioAnalysis::PeakTime> sample_peak = an->sample_peak ();
236 vector<float> true_peak = an->true_peak ();
238 for (size_t i = 0; i < sample_peak.size(); ++i) {
239 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
240 float const peak_dB = 20 * log10 (peak) + an->gain_correction (film->playlist ());
242 ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
246 ch = ch.substr (0, ch.length() - 2);
249 hint (String::compose (
250 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
255 } catch (OldFormatError& e) {
256 /* The audio analysis is too old to load in; just skip this hint as if
257 it had never been run.
262 emit (bind(boost::ref(Progress), _("Examining closed captions")));
264 shared_ptr<Player> player (new Player (film, film->playlist ()));
265 player->set_ignore_video ();
266 player->set_ignore_audio ();
267 player->Text.connect (bind(&Hints::text, this, _1, _2, _4));
269 struct timeval last_pulse;
270 gettimeofday (&last_pulse, 0);
272 while (!player->pass()) {
275 gettimeofday (&now, 0);
276 if ((seconds(now) - seconds(last_pulse)) > 1) {
278 boost::mutex::scoped_lock lm (_mutex);
283 emit (bind (boost::ref(Pulse)));
288 emit (bind(boost::ref(Finished)));
292 Hints::hint (string h)
294 emit(bind(boost::ref(Hint), h));
298 Hints::text (PlayerText text, TextType type, DCPTimePeriod period)
300 if (type != TEXT_CLOSED_CAPTION) {
304 int lines = text.string.size();
305 BOOST_FOREACH (StringText i, text.string) {
306 if (utf8_strlen(i.text()) > CLOSED_CAPTION_LENGTH) {
310 hint (String::compose(_("Some of your closed captions have lines longer than %1 characters, so they will probably be word-wrapped."), CLOSED_CAPTION_LENGTH));
315 if (!_too_many_ccap_lines && lines > CLOSED_CAPTION_LINES) {
316 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), CLOSED_CAPTION_LINES));
317 _too_many_ccap_lines = true;
320 if (!_overlap_ccap && _last && _last->overlap(period)) {
321 _overlap_ccap = true;
322 hint (_("You have overlapping closed captions, which are not allowed."));