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"
38 #include <dcp/raw_convert.h>
40 #include <dcp/reel_closed_caption_asset.h>
41 #include <dcp/reel_subtitle_asset.h>
42 #include <boost/foreach.hpp>
43 #include <boost/algorithm/string.hpp>
54 using boost::shared_ptr;
55 using boost::weak_ptr;
56 using boost::optional;
58 using namespace dcpomatic;
59 #if BOOST_VERSION >= 106100
60 using namespace boost::placeholders;
64 /* When checking to see if things are too big, we'll say they are if they
65 * are more than the target size minus this "slack."
67 #define SIZE_SLACK 4096
70 Hints::Hints (weak_ptr<const Film> film)
71 : WeakConstFilm (film)
72 , _writer (new Writer(film, weak_ptr<Job>(), true))
74 , _overlap_ccap (false)
75 , _too_many_ccap_lines (false)
76 , _early_subtitle (false)
77 , _short_subtitle (false)
78 , _subtitles_too_close (false)
79 , _too_many_subtitle_lines (false)
80 , _long_subtitle (false)
89 _thread = boost::thread (bind(&Hints::thread, this));
94 boost::this_thread::disable_interruption dis;
105 Hints::check_few_audio_channels ()
107 if (film()->audio_channels() < 6) {
108 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."));
114 Hints::check_upmixers ()
116 AudioProcessor const * ap = film()->audio_processor();
117 if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) {
118 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."));
124 Hints::check_incorrect_container ()
126 int narrower_than_scope = 0;
128 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
130 Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
131 if (r && r->id() == "239") {
133 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
134 ++narrower_than_scope;
139 string const film_container = film()->container()->id();
141 if (scope && !narrower_than_scope && film_container == "185") {
142 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."));
145 if (!scope && narrower_than_scope && film_container == "239") {
146 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."));
152 Hints::check_unusual_container ()
154 string const film_container = film()->container()->id();
155 if (film_container != "185" && film_container != "239" && film_container != "190") {
156 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"));
162 Hints::check_high_j2k_bandwidth ()
164 if (film()->j2k_bandwidth() >= 245000000) {
165 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."));
171 Hints::check_frame_rate ()
173 shared_ptr<const Film> f = film ();
174 switch (f->video_frame_rate()) {
180 /* You might want to go to 24 */
181 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);
184 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
190 /* 30fps: we can't really offer any decent solutions */
191 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."));
196 /* You almost certainly want to go to half frame rate */
197 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));
204 Hints::check_speed_up ()
206 optional<double> lowest_speed_up;
207 optional<double> highest_speed_up;
208 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
209 double spu = film()->active_frame_rate_change(i->position()).speed_up;
210 if (!lowest_speed_up || spu < *lowest_speed_up) {
211 lowest_speed_up = spu;
213 if (!highest_speed_up || spu > *highest_speed_up) {
214 highest_speed_up = spu;
218 double worst_speed_up = 1;
219 if (highest_speed_up) {
220 worst_speed_up = *highest_speed_up;
222 if (lowest_speed_up) {
223 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
226 if (worst_speed_up > 25.5/24.0) {
227 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."));
234 Hints::check_big_font_files ()
236 bool big_font_files = false;
237 if (film()->interop ()) {
238 BOOST_FOREACH (shared_ptr<Content> i, film()->content()) {
239 BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
240 BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
241 optional<boost::filesystem::path> const p = k->file ();
242 if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
243 big_font_files = true;
250 if (big_font_files) {
251 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
260 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
261 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
267 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));
273 Hints::check_3d_in_2d ()
276 BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
277 if (i->video && i->video->frame_type() != VIDEO_FRAME_TYPE_2D) {
282 if (three_d > 0 && !film()->three_d()) {
283 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.)"));
289 Hints::check_loudness ()
291 boost::filesystem::path path = film()->audio_analysis_path(film()->playlist());
292 if (boost::filesystem::exists (path)) {
294 shared_ptr<AudioAnalysis> an (new AudioAnalysis (path));
298 vector<AudioAnalysis::PeakTime> sample_peak = an->sample_peak ();
299 vector<float> true_peak = an->true_peak ();
301 for (size_t i = 0; i < sample_peak.size(); ++i) {
302 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
303 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
305 ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
309 ch = ch.substr (0, ch.length() - 2);
312 hint (String::compose (
313 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
318 } catch (OldFormatError& e) {
319 /* The audio analysis is too old to load in; just skip this hint as if
320 it had never been run.
329 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
331 return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
338 shared_ptr<const Film> film = _film.lock ();
343 ContentList content = film->content ();
345 check_big_font_files ();
346 check_few_audio_channels ();
348 check_incorrect_container ();
349 check_unusual_container ();
350 check_high_j2k_bandwidth ();
356 check_ffec_and_ffmc_in_smpte_feature ();
358 emit (bind(boost::ref(Progress), _("Examining closed captions")));
360 shared_ptr<Player> player (new Player(film));
361 player->set_ignore_video ();
362 player->set_ignore_audio ();
363 player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
365 struct timeval last_pulse;
366 gettimeofday (&last_pulse, 0);
369 while (!player->pass()) {
372 gettimeofday (&now, 0);
373 if ((seconds(now) - seconds(last_pulse)) > 1) {
377 emit (bind (boost::ref(Pulse)));
385 _writer->write (player->get_subtitle_fonts());
387 bool ccap_xml_too_big = false;
388 bool ccap_mxf_too_big = false;
389 bool subs_mxf_too_big = false;
391 boost::filesystem::path dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
392 boost::filesystem::remove_all (dcp_dir);
395 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
398 emit (bind(boost::ref(Finished)));
402 dcp::DCP dcp (dcp_dir);
404 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
405 BOOST_FOREACH (shared_ptr<dcp::Reel> reel, dcp.cpls().front()->reels()) {
406 BOOST_FOREACH (shared_ptr<dcp::ReelClosedCaptionAsset> ccap, reel->closed_captions()) {
407 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
409 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
410 ". You should divide the DCP into shorter reels."
412 ccap_xml_too_big = true;
414 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
416 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
417 " in total. You should divide the DCP into shorter reels."
419 ccap_mxf_too_big = true;
422 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
424 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
425 "You should divide the DCP into shorter reels."
427 subs_mxf_too_big = true;
430 boost::filesystem::remove_all (dcp_dir);
432 emit (bind(boost::ref(Finished)));
436 Hints::hint (string h)
438 emit(bind(boost::ref(Hint), h));
442 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
444 _writer->write (text, type, track, period);
447 case TEXT_CLOSED_CAPTION:
448 closed_caption (text, period);
450 case TEXT_OPEN_SUBTITLE:
451 open_subtitle (text, period);
460 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
462 int lines = text.string.size();
463 BOOST_FOREACH (StringText i, text.string) {
464 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
470 "At least one of your closed caption lines has more than %1 characters. "
471 "It is advisable to make each line %1 characters at most in length.",
472 MAX_CLOSED_CAPTION_LENGTH,
473 MAX_CLOSED_CAPTION_LENGTH)
479 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
480 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
481 _too_many_ccap_lines = true;
484 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
485 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
486 _overlap_ccap = true;
487 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
495 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
497 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
498 _early_subtitle = true;
499 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."));
502 int const vfr = film()->video_frame_rate ();
504 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
505 _short_subtitle = true;
506 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
509 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
510 _subtitles_too_close = true;
511 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."));
514 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
515 _too_many_subtitle_lines = true;
516 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
519 size_t longest_line = 0;
520 BOOST_FOREACH (StringText const& i, text.string) {
521 longest_line = max (longest_line, i.text().length());
524 if (longest_line > 52 && !_long_subtitle) {
525 _long_subtitle = true;
526 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."));
529 _last_subtitle = period;
534 Hints::check_ffec_and_ffmc_in_smpte_feature ()
536 shared_ptr<const Film> f = film();
537 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::FEATURE && (!f->marker(dcp::FFEC) || !f->marker(dcp::FFMC))) {
538 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."));