More enum class additions.
[dcpomatic.git] / src / lib / hints.cc
index 23d5a15fb128caaa086b4fc32523dd5b8ce2ac3b..32accdd02c611ea5d46a8291ff9745151114520d 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2016-2020 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2016-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #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 <dcp/cpl.h>
 #include <dcp/raw_convert.h>
-#include <boost/foreach.hpp>
+#include <dcp/reel.h>
+#include <dcp/reel_closed_caption_asset.h>
+#include <dcp/reel_subtitle_asset.h>
 #include <boost/algorithm/string.hpp>
 #include <iostream>
 
 #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;
@@ -55,8 +61,16 @@ 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
+
+
 Hints::Hints (weak_ptr<const Film> film)
-       : _film (film)
+       : WeakConstFilm (film)
+       , _writer (new Writer(film, weak_ptr<Job>(), true))
        , _long_ccap (false)
        , _overlap_ccap (false)
        , _too_many_ccap_lines (false)
@@ -100,7 +114,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."));
        }
@@ -112,7 +126,7 @@ Hints::check_incorrect_container ()
 {
        int narrower_than_scope = 0;
        int scope = 0;
-       BOOST_FOREACH (shared_ptr<const Content> 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") {
@@ -138,7 +152,7 @@ Hints::check_incorrect_container ()
 void
 Hints::check_unusual_container ()
 {
-       string const film_container = film()->container()->id();
+       auto const film_container = film()->container()->id();
        if (film_container != "185" && film_container != "239" && film_container != "190") {
                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"));
        }
@@ -157,7 +171,7 @@ Hints::check_high_j2k_bandwidth ()
 void
 Hints::check_frame_rate ()
 {
-       shared_ptr<const Film> f = film ();
+       auto f = film ();
        switch (f->video_frame_rate()) {
        case 24:
                /* Fine */
@@ -192,7 +206,7 @@ Hints::check_speed_up ()
 {
        optional<double> lowest_speed_up;
        optional<double> highest_speed_up;
-       BOOST_FOREACH (shared_ptr<const Content> 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;
@@ -222,11 +236,11 @@ Hints::check_big_font_files ()
 {
        bool big_font_files = false;
        if (film()->interop ()) {
-               BOOST_FOREACH (shared_ptr<Content> i, film()->content()) {
-                       BOOST_FOREACH (shared_ptr<TextContent> j, i->text) {
-                               BOOST_FOREACH (shared_ptr<Font> k, j->fonts()) {
+               for (auto i: film()->content()) {
+                       for (auto j: i->text) {
+                               for (auto k: j->fonts()) {
                                        optional<boost::filesystem::path> 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;
                                        }
                                }
@@ -244,7 +258,7 @@ void
 Hints::check_vob ()
 {
        int vob = 0;
-       BOOST_FOREACH (shared_ptr<const Content> i, film()->content()) {
+       for (auto i: film()->content()) {
                if (boost::algorithm::starts_with (i->path(0).filename().string(), "VTS_")) {
                        ++vob;
                }
@@ -260,8 +274,8 @@ void
 Hints::check_3d_in_2d ()
 {
        int three_d = 0;
-       BOOST_FOREACH (shared_ptr<const Content> 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;
                }
        }
@@ -275,15 +289,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<AudioAnalysis> an (new AudioAnalysis (path));
+                       auto an = make_shared<AudioAnalysis>(path);
 
                        string ch;
 
-                       vector<AudioAnalysis::PeakTime> sample_peak = an->sample_peak ();
-                       vector<float> 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]);
@@ -311,15 +325,23 @@ Hints::check_loudness ()
 }
 
 
+static
+bool
+subtitle_mxf_too_big (shared_ptr<dcp::SubtitleAsset> asset)
+{
+       return asset && asset->file() && boost::filesystem::file_size(*asset->file()) >= (MAX_TEXT_MXF_SIZE - SIZE_SLACK);
+}
+
+
 void
 Hints::thread ()
 {
-       shared_ptr<const Film> film = _film.lock ();
+       auto film = _film.lock ();
        if (!film) {
                return;
        }
 
-       ContentList content = film->content ();
+       auto content = film->content ();
 
        check_big_font_files ();
        check_few_audio_channels ();
@@ -336,10 +358,10 @@ Hints::thread ()
 
        emit (bind(boost::ref(Progress), _("Examining closed captions")));
 
-       shared_ptr<Player> player (new Player(film));
+       auto player = make_shared<Player>(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);
@@ -361,6 +383,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<size_t>(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)));
 }
 
@@ -371,13 +440,15 @@ Hints::hint (string h)
 }
 
 void
-Hints::text (PlayerText text, TextType type, DCPTimePeriod period)
+Hints::text (PlayerText text, TextType type, optional<DCPTextTrack> track, DCPTimePeriod period)
 {
+       _writer->write (text, type, track, period);
+
        switch (type) {
-       case TEXT_CLOSED_CAPTION:
+       case TextType::CLOSED_CAPTION:
                closed_caption (text, period);
                break;
-       case TEXT_OPEN_SUBTITLE:
+       case TextType::OPEN_SUBTITLE:
                open_subtitle (text, period);
                break;
        default:
@@ -390,7 +461,7 @@ void
 Hints::closed_caption (PlayerText text, DCPTimePeriod period)
 {
        int lines = text.string.size();
-       BOOST_FOREACH (StringText i, text.string) {
+       for (auto i: text.string) {
                if (utf8_strlen(i.text()) > MAX_CLOSED_CAPTION_LENGTH) {
                        ++lines;
                        if (!_long_ccap) {
@@ -447,7 +518,7 @@ Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
        }
 
        size_t longest_line = 0;
-       BOOST_FOREACH (StringText const& i, text.string) {
+       for (auto const& i: text.string) {
                longest_line = max (longest_line, i.text().length());
        }
 
@@ -460,20 +531,11 @@ Hints::open_subtitle (PlayerText text, DCPTimePeriod period)
 }
 
 
-shared_ptr<const Film>
-Hints::film () const
-{
-       shared_ptr<const Film> film = _film.lock ();
-       DCPOMATIC_ASSERT (film);
-       return film;
-}
-
-
 void
 Hints::check_ffec_and_ffmc_in_smpte_feature ()
 {
-       shared_ptr<const Film> f = film();
-       if (!f->interop() && f->dcp_content_type()->libdcp_kind() == dcp::FEATURE && (!f->marker(dcp::FFEC) || !f->marker(dcp::FFMC))) {
+       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."));
        }
 }