2 Copyright (C) 2016-2021 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"
30 #include "font_data.h"
32 #include "audio_analysis.h"
33 #include "compose.hpp"
39 #include <dcp/raw_convert.h>
41 #include <dcp/reel_closed_caption_asset.h>
42 #include <dcp/reel_subtitle_asset.h>
43 #include <boost/algorithm/string.hpp>
49 using std::make_shared;
53 using std::shared_ptr;
57 using boost::optional;
59 using namespace dcpomatic;
60 #if BOOST_VERSION >= 106100
61 using namespace boost::placeholders;
65 /* When checking to see if things are too big, we'll say they are if they
66 * are more than the target size minus this "slack."
68 #define SIZE_SLACK 4096
71 /* When writing hints:
72 * - put quotation marks around the name of a GUI tab that you are referring to (e.g. "DCP" or "DCP→Video" tab)
76 Hints::Hints (weak_ptr<const Film> film)
77 : WeakConstFilm (film)
78 , _writer (new Writer(film, weak_ptr<Job>(), true))
80 , _overlap_ccap (false)
81 , _too_many_ccap_lines (false)
82 , _early_subtitle (false)
83 , _short_subtitle (false)
84 , _subtitles_too_close (false)
85 , _too_many_subtitle_lines (false)
86 , _long_subtitle (false)
95 _thread = boost::thread (bind(&Hints::thread, this));
100 boost::this_thread::disable_interruption dis;
104 _thread.interrupt ();
111 Hints::check_few_audio_channels ()
113 if (film()->audio_channels() < 6) {
114 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."));
120 Hints::check_upmixers ()
122 auto 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."));
130 Hints::check_incorrect_container ()
132 int narrower_than_scope = 0;
134 for (auto i: film()->content()) {
136 Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio());
137 if (r && r->id() == "239") {
139 } else if (r && r->id() != "239" && r->id() != "235" && r->id() != "190") {
140 ++narrower_than_scope;
145 string const film_container = film()->container()->id();
147 if (scope && !narrower_than_scope && film_container == "185") {
148 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."));
151 if (!scope && narrower_than_scope && film_container == "239") {
152 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."));
158 Hints::check_unusual_container ()
160 auto const film_container = film()->container()->id();
161 if (film_container != "185" && film_container != "239") {
162 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"));
168 Hints::check_high_j2k_bandwidth ()
170 if (film()->j2k_bandwidth() >= 245000000) {
171 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."));
177 Hints::check_frame_rate ()
180 switch (f->video_frame_rate()) {
186 /* You might want to go to 24 */
187 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);
190 base += _("If you do use 25fps you should change your DCP standard to SMPTE.");
196 /* 30fps: we can't really offer any decent solutions */
197 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."));
202 /* You almost certainly want to go to half frame rate */
203 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));
210 Hints::check_4k_3d ()
213 if (f->resolution() == Resolution::FOUR_K && f->three_d()) {
214 hint (_("4K 3D is only supported by a very limited number of projectors. Unless you know that you will play this DCP back on a capable projector, it is advisable to set the DCP to be 2K in the \"DCP→Video\" tab."));
220 Hints::check_speed_up ()
222 optional<double> lowest_speed_up;
223 optional<double> highest_speed_up;
224 for (auto i: film()->content()) {
225 double spu = film()->active_frame_rate_change(i->position()).speed_up;
226 if (!lowest_speed_up || spu < *lowest_speed_up) {
227 lowest_speed_up = spu;
229 if (!highest_speed_up || spu > *highest_speed_up) {
230 highest_speed_up = spu;
234 double worst_speed_up = 1;
235 if (highest_speed_up) {
236 worst_speed_up = *highest_speed_up;
238 if (lowest_speed_up) {
239 worst_speed_up = max (worst_speed_up, 1 / *lowest_speed_up);
242 if (worst_speed_up > 25.5/24.0) {
243 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."));
250 Hints::check_interop ()
252 if (film()->interop()) {
253 hint (_("In general it is now advisable to make SMPTE DCPs unless you have a particular reason to use Interop. You are advised to set your DCP to use the SMPTE standard in the \"DCP\" tab."));
259 Hints::check_big_font_files ()
261 bool big_font_files = false;
262 if (film()->interop ()) {
263 for (auto i: film()->content()) {
264 for (auto j: i->text) {
265 for (auto k: j->fonts()) {
266 optional<boost::filesystem::path> const p = k->file ();
267 if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) {
268 big_font_files = true;
275 if (big_font_files) {
276 hint (_("You have specified a font file which is larger than 640kB. This is very likely to cause problems on playback."));
285 for (auto i: film()->content()) {
286 if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
292 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));
298 Hints::check_3d_in_2d ()
301 for (auto i: film()->content()) {
302 if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) {
307 if (three_d > 0 && !film()->three_d()) {
308 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.)"));
314 Hints::check_loudness ()
316 auto path = film()->audio_analysis_path(film()->playlist());
317 if (boost::filesystem::exists (path)) {
319 auto an = make_shared<AudioAnalysis>(path);
323 auto sample_peak = an->sample_peak ();
324 auto true_peak = an->true_peak ();
326 for (size_t i = 0; i < sample_peak.size(); ++i) {
327 float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]);
328 float const peak_dB = linear_to_db(peak) + an->gain_correction(film()->playlist());
330 ch += dcp::raw_convert<string> (short_audio_channel_name (i)) + ", ";
334 ch = ch.substr (0, ch.length() - 2);
337 hint (String::compose (
338 _("Your audio level is very high (on %1). You should reduce the gain of your audio content."),
343 } catch (OldFormatError& e) {
344 /* The audio analysis is too old to load in; just skip this hint as if
345 it had never been run.
354 subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
356 return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
361 Hints::check_out_of_range_markers ()
363 auto const length = film()->length();
364 for (auto const& i: film()->markers()) {
365 if (i.second >= length) {
366 hint (_("At least one marker comes after the end of the project and will be ignored."));
375 auto film = _film.lock ();
380 auto content = film->content ();
383 check_big_font_files ();
384 check_few_audio_channels ();
386 check_incorrect_container ();
387 check_unusual_container ();
388 check_high_j2k_bandwidth ();
395 check_ffec_and_ffmc_in_smpte_feature ();
396 check_out_of_range_markers ();
397 check_text_languages ();
399 emit (bind(boost::ref(Progress), _("Examining closed captions")));
401 auto player = make_shared<Player>(film);
402 player->set_ignore_video ();
403 player->set_ignore_audio ();
404 player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4));
406 struct timeval last_pulse;
407 gettimeofday (&last_pulse, 0);
410 while (!player->pass()) {
413 gettimeofday (&now, 0);
414 if ((seconds(now) - seconds(last_pulse)) > 1) {
418 emit (bind (boost::ref(Pulse)));
426 _writer->write (player->get_subtitle_fonts());
428 bool ccap_xml_too_big = false;
429 bool ccap_mxf_too_big = false;
430 bool subs_mxf_too_big = false;
432 boost::filesystem::path dcp_dir = film->dir("hints") / dcpomatic::get_process_id();
433 boost::filesystem::remove_all (dcp_dir);
436 _writer->finish (film->dir("hints") / dcpomatic::get_process_id());
439 emit (bind(boost::ref(Finished)));
443 dcp::DCP dcp (dcp_dir);
445 DCPOMATIC_ASSERT (dcp.cpls().size() == 1);
446 for (auto reel: dcp.cpls()[0]->reels()) {
447 for (auto ccap: reel->closed_captions()) {
448 if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast<size_t>(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) {
450 "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT
451 ". You should divide the DCP into shorter reels."
453 ccap_xml_too_big = true;
455 if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) {
457 "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT
458 " in total. You should divide the DCP into shorter reels."
460 ccap_mxf_too_big = true;
463 if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) {
465 "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. "
466 "You should divide the DCP into shorter reels."
468 subs_mxf_too_big = true;
471 boost::filesystem::remove_all (dcp_dir);
473 emit (bind(boost::ref(Finished)));
477 Hints::hint (string h)
479 emit(bind(boost::ref(Hint), h));
483 Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
485 _writer->write (text, type, track, period);
488 case TextType::CLOSED_CAPTION:
489 closed_caption (text, period);
491 case TextType::OPEN_SUBTITLE:
492 open_subtitle (text, period);
501 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
503 int lines = text.string.size();
504 for (auto i: text.string) {
505 if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
511 "At least one of your closed caption lines has more than %1 characters. "
512 "It is advisable to make each line %1 characters at most in length.",
513 MAX_CLOSED_CAPTION_LENGTH,
514 MAX_CLOSED_CAPTION_LENGTH)
520 if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) {
521 hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES));
522 _too_many_ccap_lines = true;
525 /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */
526 if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) {
527 _overlap_ccap = true;
528 hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE."));
536 Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
538 if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) {
539 _early_subtitle = true;
540 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."));
543 int const vfr = film()->video_frame_rate ();
545 if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) {
546 _short_subtitle = true;
547 hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long."));
550 if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) {
551 _subtitles_too_close = true;
552 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."));
555 if (text.string.size() > 3 && !_too_many_subtitle_lines) {
556 _too_many_subtitle_lines = true;
557 hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines."));
560 size_t longest_line = 0;
561 for (auto const& i: text.string) {
562 longest_line = max (longest_line, i.text().length());
565 if (longest_line > 52 && !_long_subtitle) {
566 _long_subtitle = true;
567 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."));
570 _last_subtitle = period;
575 Hints::check_ffec_and_ffmc_in_smpte_feature ()
578 if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) {
579 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."));
592 Hints::check_text_languages ()
594 for (auto i: film()->content()) {
595 for (auto j: i->text) {
596 if (j->use() && !j->language()) {
597 hint (_("At least one piece of subtitle or closed caption content has no specified language. "
598 "It is advisable to set the language for each piece of subtitle or closed caption content in the \"Content→Timed Text\" tab"));