33c2faba5938a371a10888d290621a3408bdca0a
[dcpomatic.git] / src / lib / hints.cc
1 /*
2     Copyright (C) 2016-2018 Carl Hetherington <cth@carlh.net>
3
4     This file is part of DCP-o-matic.
5
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.
10
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.
15
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/>.
18
19 */
20
21 #include "hints.h"
22 #include "types.h"
23 #include "film.h"
24 #include "content.h"
25 #include "video_content.h"
26 #include "caption_content.h"
27 #include "audio_processor.h"
28 #include "font.h"
29 #include "ratio.h"
30 #include "audio_analysis.h"
31 #include "compose.hpp"
32 #include "util.h"
33 #include <dcp/raw_convert.h>
34 #include <boost/foreach.hpp>
35 #include <boost/algorithm/string.hpp>
36 #include <iostream>
37
38 #include "i18n.h"
39
40 using std::vector;
41 using std::string;
42 using std::pair;
43 using std::min;
44 using std::max;
45 using std::cout;
46 using boost::shared_ptr;
47 using boost::optional;
48
49 vector<string>
50 get_hints (shared_ptr<const Film> film)
51 {
52         vector<string> hints;
53
54         ContentList content = film->content ();
55
56         bool big_font_files = false;
57         if (film->interop ()) {
58                 BOOST_FOREACH (shared_ptr<Content> i, content) {
59                         BOOST_FOREACH (shared_ptr<CaptionContent> j, i->caption) {
60                                 BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
61                                         for (int l = 0; l < FontFiles::VARIANTS; ++l) {
62                                                 optional<boost::filesystem::path> const p = k->file (static_cast<FontFiles::Variant>(l));
63                                                 if (p && boost::filesystem::file_size (p.get()) >= (640 * 1024)) {
64                                                         big_font_files = true;
65                                                 }
66                                         }
67                                 }
68                         }
69                 }
70         }
71
72         if (big_font_files) {
73                 hints.push_back (_("You have specified a font file which is larger than 640kB.  This is very likely to cause problems on playback."));
74         }
75
76         if (film->audio_channels() < 6) {
77                 hints.push_back (_("Your DCP has fewer than 6 audio channels.  This may cause problems on some projectors."));
78         }
79
80         AudioProcessor const * ap = film->audio_processor();
81         if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
82                 hints.push_back (_("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."));
83         }
84
85         int flat_or_narrower = 0;
86         int scope = 0;
87         BOOST_FOREACH (shared_ptr<const Content> i, content) {
88                 if (i->video) {
89                         Ratio const * r = i->video->scale().ratio ();
90                         if (r && r->id() == "239") {
91                                 ++scope;
92                         } else if (r && r->id() != "239" && r->id() != "190") {
93                                 ++flat_or_narrower;
94                         }
95                 }
96         }
97
98         string const film_container = film->container()->id();
99
100         if (scope && !flat_or_narrower && film_container == "185") {
101                 hints.push_back (_("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."));
102         }
103
104         if (!scope && flat_or_narrower && film_container == "239") {
105                 hints.push_back (_("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."));
106         }
107
108         if (film_container != "185" && film_container != "239" && film_container != "190") {
109                 hints.push_back (_("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"));
110         }
111
112         if (film->j2k_bandwidth() >= 245000000) {
113                 hints.push_back (_("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."));
114         }
115
116         if (film->interop() && film->video_frame_rate() != 24 && film->video_frame_rate() != 48) {
117                 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.");
118                 base += "  ";
119                 pair<double, double> range24 = film->speed_up_range (24);
120                 pair<double, double> range48 = film->speed_up_range (48);
121                 pair<double, double> range (max (range24.first, range48.first), min (range24.second, range48.second));
122                 string h;
123                 if (range.second > (29.0/24)) {
124                         h = base;
125                         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.");
126                 } else if (range.first < (24.0/29)) {
127                         h = base;
128                         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.");
129                 } else {
130                         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).");
131                 }
132
133                 hints.push_back (h);
134         }
135
136         optional<double> lowest_speed_up;
137         optional<double> highest_speed_up;
138         BOOST_FOREACH (shared_ptr<const Content> i, content) {
139                 double spu = film->active_frame_rate_change(i->position()).speed_up;
140                 if (!lowest_speed_up || spu < *lowest_speed_up) {
141                         lowest_speed_up = spu;
142                 }
143                 if (!highest_speed_up || spu > *highest_speed_up) {
144                         highest_speed_up = spu;
145                 }
146         }
147
148         double worst_speed_up = 1;
149         if (highest_speed_up) {
150                 worst_speed_up = *highest_speed_up;
151         }
152         if (lowest_speed_up) {
153                 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
154         }
155
156         if (worst_speed_up > 25.5/24.0) {
157                 hints.push_back (_("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."));
158         }
159
160         int vob = 0;
161         BOOST_FOREACH (shared_ptr<const Content> i, content) {
162                 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
163                         ++vob;
164                 }
165         }
166
167         if (vob > 1) {
168                 hints.push_back (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));
169         }
170
171         int three_d = 0;
172         BOOST_FOREACH (shared_ptr<const Content> i, content) {
173                 if (i->video && i->video->frame_type() != VIDEO_FRAME_TYPE_2D) {
174                         ++three_d;
175                 }
176         }
177
178         if (three_d > 0 && !film->three_d()) {
179                 hints.push_back (_("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.)"));
180         }
181
182         boost::filesystem::path path = film->audio_analysis_path (film->playlist ());
183         if (boost::filesystem::exists (path)) {
184                 try {
185                         shared_ptr<AudioAnalysis> an (new AudioAnalysis (path));
186
187                         string ch;
188
189                         vector<AudioAnalysis::PeakTime> sample_peak = an->sample_peak ();
190                         vector<float> true_peak = an->true_peak ();
191
192                         for (size_t i = 0; i < sample_peak.size(); ++i) {
193                                 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
194                                 float const peak_dB = 20 * log10 (peak) + an->gain_correction (film->playlist ());
195                                 if (peak_dB > -3) {
196                                         ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
197                                 }
198                         }
199
200                         ch = ch.substr (0, ch.length() - 2);
201
202                         if (!ch.empty ()) {
203                                 hints.push_back (
204                                         String::compose (
205                                                 _("Your audio level is very high (on %1).  You should reduce the gain of your audio content."),
206                                                 ch
207                                                 )
208                                         );
209                         }
210                 } catch (OldFormatError& e) {
211                         /* The audio analysis is too old to load in; just skip this hint as if
212                            it had never been run.
213                         */
214                 }
215         }
216
217         return hints;
218 }