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 narrower_than_scope = 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") {
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 is 2.35:1 or narrower 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 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 switch (film->video_frame_rate()) {
164 /* You might want to go to 24 */
165 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);
166 if (film->interop()) {
168 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
174 /* 30fps: we can't really offer any decent solutions */
175 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."));
180 /* You almost certainly want to go to half frame rate */
181 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."), film->video_frame_rate(), film->video_frame_rate() / 2));
185 optional<double> lowest_speed_up;
186 optional<double> highest_speed_up;
187 BOOST_FOREACH (shared_ptr<const Content> i, content) {
188 double spu = film->active_frame_rate_change(i->position()).speed_up;
189 if (!lowest_speed_up || spu < *lowest_speed_up) {
190 lowest_speed_up = spu;
192 if (!highest_speed_up || spu > *highest_speed_up) {
193 highest_speed_up = spu;
197 double worst_speed_up = 1;
198 if (highest_speed_up) {
199 worst_speed_up = *highest_speed_up;
201 if (lowest_speed_up) {
202 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
205 if (worst_speed_up > 25.5/24.0) {
206 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."));
210 BOOST_FOREACH (shared_ptr<const Content> i, content) {
211 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
217 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));
221 BOOST_FOREACH (shared_ptr<const Content> i, content) {
222 if (i->video && i->video->frame_type() != VIDEO_FRAME_TYPE_2D) {
227 if (three_d > 0 && !film->three_d()) {
228 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.)"));
231 boost::filesystem::path path = film->audio_analysis_path (film->playlist ());
232 if (boost::filesystem::exists (path)) {
234 shared_ptr<AudioAnalysis> an (new AudioAnalysis (path));
238 vector<AudioAnalysis::PeakTime> sample_peak = an->sample_peak ();
239 vector<float> true_peak = an->true_peak ();
241 for (size_t i = 0; i < sample_peak.size(); ++i) {
242 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
243 float const peak_dB = 20 * log10 (peak) + an->gain_correction (film->playlist ());
245 ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
249 ch = ch.substr (0, ch.length() - 2);
252 hint (String::compose (
253 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
258 } catch (OldFormatError& e) {
259 /* The audio analysis is too old to load in; just skip this hint as if
260 it had never been run.
265 emit (bind(boost::ref(Progress), _("Examining closed captions")));
267 shared_ptr<Player> player (new Player (film, film->playlist ()));
268 player->set_ignore_video ();
269 player->set_ignore_audio ();
270 player->Text.connect (bind(&Hints::text, this, _1, _2, _4));
272 struct timeval last_pulse;
273 gettimeofday (&last_pulse, 0);
275 while (!player->pass()) {
278 gettimeofday (&now, 0);
279 if ((seconds(now) - seconds(last_pulse)) > 1) {
281 boost::mutex::scoped_lock lm (_mutex);
286 emit (bind (boost::ref(Pulse)));
291 emit (bind(boost::ref(Finished)));
295 Hints::hint (string h)
297 emit(bind(boost::ref(Hint), h));
301 Hints::text (PlayerText text, TextType type, DCPTimePeriod period)
303 if (type != TEXT_CLOSED_CAPTION) {
307 int lines = text.string.size();
308 BOOST_FOREACH (StringText i, text.string) {
309 if (utf8_strlen(i.text()) > CLOSED_CAPTION_LENGTH) {
313 hint (String::compose(_("Some of your closed captions have lines longer than %1 characters, so they will probably be word-wrapped."), CLOSED_CAPTION_LENGTH));
318 if (!_too_many_ccap_lines && lines > CLOSED_CAPTION_LINES) {
319 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), CLOSED_CAPTION_LINES));
320 _too_many_ccap_lines = true;
323 if (!_overlap_ccap && _last && _last->overlap(period)) {
324 _overlap_ccap = true;
325 hint (_("You have overlapping closed captions, which are not allowed."));