X-Git-Url: https://main.carlh.net/gitweb/?p=dcpomatic.git;a=blobdiff_plain;f=src%2Flib%2Fhints.cc;h=2dd11899303bab56013dfcaaeaf81786e5c411c5;hp=6228f0a48a7df0e6e2ebce0d5988b7b8a3bf061e;hb=3035031bf80a1c1b6b2800cb9a3f6806f6a85aab;hpb=b78068f34be9adfa7a9018643d46e3917f132070 diff --git a/src/lib/hints.cc b/src/lib/hints.cc index 6228f0a48..2dd118993 100644 --- a/src/lib/hints.cc +++ b/src/lib/hints.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2016-2019 Carl Hetherington + Copyright (C) 2016-2021 Carl Hetherington This file is part of DCP-o-matic. @@ -18,6 +18,7 @@ */ +#include "dcp_content_type.h" #include "hints.h" #include "types.h" #include "film.h" @@ -26,27 +27,33 @@ #include "text_content.h" #include "audio_processor.h" #include "font.h" +#include "font_data.h" #include "ratio.h" #include "audio_analysis.h" #include "compose.hpp" #include "util.h" #include "cross.h" #include "player.h" +#include "writer.h" +#include #include -#include +#include +#include +#include #include #include #include "i18n.h" -using std::vector; -using std::string; -using std::pair; -using std::min; -using std::max; using std::cout; -using boost::shared_ptr; -using boost::weak_ptr; +using std::make_shared; +using std::max; +using std::min; +using std::pair; +using std::shared_ptr; +using std::string; +using std::vector; +using std::weak_ptr; using boost::optional; using boost::bind; using namespace dcpomatic; @@ -54,11 +61,29 @@ using namespace dcpomatic; using namespace boost::placeholders; #endif + +/* When checking to see if things are too big, we'll say they are if they + * are more than the target size minus this "slack." + */ +#define SIZE_SLACK 4096 + + +/* When writing hints: + * - put quotation marks around the name of a GUI tab that you are referring to (e.g. "DCP" or "DCP→Video" tab) + */ + + Hints::Hints (weak_ptr film) - : _film (film) + : WeakConstFilm (film) + , _writer (new Writer(film, weak_ptr(), true)) , _long_ccap (false) , _overlap_ccap (false) , _too_many_ccap_lines (false) + , _early_subtitle (false) + , _short_subtitle (false) + , _subtitles_too_close (false) + , _too_many_subtitle_lines (false) + , _long_subtitle (false) , _stop (false) { @@ -94,7 +119,7 @@ Hints::check_few_audio_channels () void Hints::check_upmixers () { - AudioProcessor const * ap = film()->audio_processor(); + auto ap = film()->audio_processor(); if (ap && (ap->id() == "stereo-5.1-upmix-a" || ap->id() == "stereo-5.1-upmix-b")) { 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.")); } @@ -106,7 +131,7 @@ Hints::check_incorrect_container () { int narrower_than_scope = 0; int scope = 0; - BOOST_FOREACH (shared_ptr i, film()->content()) { + for (auto i: film()->content()) { if (i->video) { Ratio const * r = Ratio::nearest_from_ratio(i->video->scaled_size(film()->frame_size()).ratio()); if (r && r->id() == "239") { @@ -132,8 +157,8 @@ Hints::check_incorrect_container () void Hints::check_unusual_container () { - string const film_container = film()->container()->id(); - if (film_container != "185" && film_container != "239" && film_container != "190") { + auto const film_container = film()->container()->id(); + if (film_container != "185" && film_container != "239") { 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")); } } @@ -151,7 +176,7 @@ Hints::check_high_j2k_bandwidth () void Hints::check_frame_rate () { - shared_ptr f = film (); + auto f = film (); switch (f->video_frame_rate()) { case 24: /* Fine */ @@ -181,12 +206,22 @@ Hints::check_frame_rate () } +void +Hints::check_4k_3d () +{ + auto f = film(); + if (f->resolution() == Resolution::FOUR_K && f->three_d()) { + 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.")); + } +} + + void Hints::check_speed_up () { optional lowest_speed_up; optional highest_speed_up; - BOOST_FOREACH (shared_ptr i, film()->content()) { + for (auto i: film()->content()) { double spu = film()->active_frame_rate_change(i->position()).speed_up; if (!lowest_speed_up || spu < *lowest_speed_up) { lowest_speed_up = spu; @@ -211,16 +246,25 @@ Hints::check_speed_up () } +void +Hints::check_interop () +{ + if (film()->interop()) { + 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.")); + } +} + + void Hints::check_big_font_files () { bool big_font_files = false; if (film()->interop ()) { - BOOST_FOREACH (shared_ptr i, film()->content()) { - BOOST_FOREACH (shared_ptr j, i->text) { - BOOST_FOREACH (shared_ptr k, j->fonts()) { + for (auto i: film()->content()) { + for (auto j: i->text) { + for (auto k: j->fonts()) { optional const p = k->file (); - if (p && boost::filesystem::file_size(p.get()) >= (640 * 1024)) { + if (p && boost::filesystem::file_size(p.get()) >= (MAX_FONT_FILE_SIZE - SIZE_SLACK)) { big_font_files = true; } } @@ -238,7 +282,7 @@ void Hints::check_vob () { int vob = 0; - BOOST_FOREACH (shared_ptr i, film()->content()) { + for (auto i: film()->content()) { if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) { ++vob; } @@ -254,8 +298,8 @@ void Hints::check_3d_in_2d () { int three_d = 0; - BOOST_FOREACH (shared_ptr i, film()->content()) { - if (i->video && i->video->frame_type() != VIDEO_FRAME_TYPE_2D) { + for (auto i: film()->content()) { + if (i->video && i->video->frame_type() != VideoFrameType::TWO_D) { ++three_d; } } @@ -269,15 +313,15 @@ Hints::check_3d_in_2d () void Hints::check_loudness () { - boost::filesystem::path path = film()->audio_analysis_path(film()->playlist()); + auto path = film()->audio_analysis_path(film()->playlist()); if (boost::filesystem::exists (path)) { try { - shared_ptr an (new AudioAnalysis (path)); + auto an = make_shared(path); string ch; - vector sample_peak = an->sample_peak (); - vector true_peak = an->true_peak (); + auto sample_peak = an->sample_peak (); + auto true_peak = an->true_peak (); for (size_t i = 0; i < sample_peak.size(); ++i) { float const peak = max (sample_peak[i].peak, true_peak.empty() ? 0 : true_peak[i]); @@ -305,16 +349,37 @@ Hints::check_loudness () } +static +bool +subtitle_mxf_too_big (shared_ptr asset) +{ + return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK); +} + + +void +Hints::check_out_of_range_markers () +{ + auto const length = film()->length(); + for (auto const& i: film()->markers()) { + if (i.second >= length) { + hint (_("At least one marker comes after the end of the project and will be ignored.")); + } + } +} + + void Hints::thread () { - shared_ptr film = _film.lock (); + auto film = _film.lock (); if (!film) { return; } - ContentList content = film->content (); + auto content = film->content (); + check_interop (); check_big_font_files (); check_few_audio_channels (); check_upmixers (); @@ -322,17 +387,21 @@ Hints::thread () check_unusual_container (); check_high_j2k_bandwidth (); check_frame_rate (); + check_4k_3d (); check_speed_up (); check_vob (); check_3d_in_2d (); check_loudness (); + check_ffec_and_ffmc_in_smpte_feature (); + check_out_of_range_markers (); + check_text_languages (); emit (bind(boost::ref(Progress), _("Examining closed captions"))); - shared_ptr player (new Player(film)); + auto player = make_shared(film); player->set_ignore_video (); player->set_ignore_audio (); - player->Text.connect (bind(&Hints::text, this, _1, _2, _4)); + player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4)); struct timeval last_pulse; gettimeofday (&last_pulse, 0); @@ -354,6 +423,53 @@ Hints::thread () store_current (); } + _writer->write (player->get_subtitle_fonts()); + + bool ccap_xml_too_big = false; + bool ccap_mxf_too_big = false; + bool subs_mxf_too_big = false; + + boost::filesystem::path dcp_dir = film->dir("hints") / dcpomatic::get_process_id(); + boost::filesystem::remove_all (dcp_dir); + + try { + _writer->finish (film->dir("hints") / dcpomatic::get_process_id()); + } catch (...) { + store_current (); + emit (bind(boost::ref(Finished))); + return; + } + + dcp::DCP dcp (dcp_dir); + dcp.read (); + DCPOMATIC_ASSERT (dcp.cpls().size() == 1); + for (auto reel: dcp.cpls()[0]->reels()) { + for (auto ccap: reel->closed_captions()) { + if (ccap->asset() && ccap->asset()->xml_as_string().length() > static_cast(MAX_CLOSED_CAPTION_XML_SIZE - SIZE_SLACK) && !ccap_xml_too_big) { + hint (_( + "At least one of your closed caption files' XML part is larger than " MAX_CLOSED_CAPTION_XML_SIZE_TEXT + ". You should divide the DCP into shorter reels." + )); + ccap_xml_too_big = true; + } + if (subtitle_mxf_too_big(ccap->asset()) && !ccap_mxf_too_big) { + hint (_( + "At least one of your closed caption files is larger than " MAX_TEXT_MXF_SIZE_TEXT + " in total. You should divide the DCP into shorter reels." + )); + ccap_mxf_too_big = true; + } + } + if (reel->main_subtitle() && subtitle_mxf_too_big(reel->main_subtitle()->asset()) && !subs_mxf_too_big) { + hint (_( + "At least one of your subtitle files is larger than " MAX_TEXT_MXF_SIZE_TEXT " in total. " + "You should divide the DCP into shorter reels." + )); + subs_mxf_too_big = true; + } + } + boost::filesystem::remove_all (dcp_dir); + emit (bind(boost::ref(Finished))); } @@ -364,42 +480,124 @@ Hints::hint (string h) } void -Hints::text (PlayerText text, TextType type, DCPTimePeriod period) +Hints::text (PlayerText text, TextType type, optional track, DCPTimePeriod period) { - if (type != TEXT_CLOSED_CAPTION) { - return; + _writer->write (text, type, track, period); + + switch (type) { + case TextType::CLOSED_CAPTION: + closed_caption (text, period); + break; + case TextType::OPEN_SUBTITLE: + open_subtitle (text, period); + break; + default: + break; } +} + +void +Hints::closed_caption (PlayerText text, DCPTimePeriod period) +{ int lines = text.string.size(); - BOOST_FOREACH (StringText i, text.string) { - if (utf8_strlen(i.text()) > CLOSED_CAPTION_LENGTH) { + for (auto i: text.string) { + if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) { ++lines; if (!_long_ccap) { _long_ccap = true; - hint (String::compose(_("Some of your closed captions have lines longer than %1 characters, so they will probably be word-wrapped."), CLOSED_CAPTION_LENGTH)); + hint ( + String::compose( + "At least one of your closed caption lines has more than %1 characters. " + "It is advisable to make each line %1 characters at most in length.", + MAX_CLOSED_CAPTION_LENGTH, + MAX_CLOSED_CAPTION_LENGTH) + ); } } } - if (!_too_many_ccap_lines && lines > CLOSED_CAPTION_LINES) { - hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), CLOSED_CAPTION_LINES)); + if (!_too_many_ccap_lines && lines > MAX_CLOSED_CAPTION_LINES) { + hint (String::compose(_("Some of your closed captions span more than %1 lines, so they will be truncated."), MAX_CLOSED_CAPTION_LINES)); _too_many_ccap_lines = true; } /* XXX: maybe overlapping closed captions (i.e. different languages) are OK with Interop? */ - if (film()->interop() && !_overlap_ccap && _last && _last->overlap(period)) { + if (film()->interop() && !_overlap_ccap && _last_ccap && _last_ccap->overlap(period)) { _overlap_ccap = true; hint (_("You have overlapping closed captions, which are not allowed in Interop DCPs. Change your DCP standard to SMPTE.")); } - _last = period; + _last_ccap = period; +} + + +void +Hints::open_subtitle (PlayerText text, DCPTimePeriod period) +{ + if (period.from < DCPTime::from_seconds(4) && !_early_subtitle) { + _early_subtitle = true; + 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.")); + } + + int const vfr = film()->video_frame_rate (); + + if (period.duration().frames_round(vfr) < 15 && !_short_subtitle) { + _short_subtitle = true; + hint (_("At least one of your subtitles lasts less than 15 frames. It is advisable to make each subtitle at least 15 frames long.")); + } + + if (_last_subtitle && DCPTime(period.from - _last_subtitle->to).frames_round(vfr) < 2 && !_subtitles_too_close) { + _subtitles_too_close = true; + 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.")); + } + + if (text.string.size() > 3 && !_too_many_subtitle_lines) { + _too_many_subtitle_lines = true; + hint (_("At least one of your subtitles has more than 3 lines. It is advisable to use no more than 3 lines.")); + } + + size_t longest_line = 0; + for (auto const& i: text.string) { + longest_line = max (longest_line, i.text().length()); + } + + if (longest_line > 52 && !_long_subtitle) { + _long_subtitle = true; + 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.")); + } + + _last_subtitle = period; +} + + +void +Hints::check_ffec_and_ffmc_in_smpte_feature () +{ + auto f = film(); + if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::ContentKind::FEATURE && (!f->marker(dcp::Marker::FFEC) || !f->marker(dcp::Marker::FFMC))) { + 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.")); + } +} + + +void +Hints::join () +{ + _thread.join (); } -shared_ptr -Hints::film () const +void +Hints::check_text_languages () { - shared_ptr film = _film.lock (); - DCPOMATIC_ASSERT (film); - return film; + for (auto i: film()->content()) { + for (auto j: i->text) { + if (j->use() && !j->language()) { + hint (_("At least one piece of subtitle or closed caption content has no specified language. " + "It is advisable to set the language for each piece of subtitle or closed caption content in the \"Content→Timed Text\" tab.")); + return; + } + } + } }