Use dcp::File in DCP-o-matic (#2231).
[dcpomatic.git] / src / lib / film.cc
index d9ed8c53000b8430c0f02f3838b6f4bdf59b09cb..54267bc5686038d1006fd1c75ba62de41e641e3f 100644 (file)
 #include <boost/filesystem.hpp>
 #include <boost/regex.hpp>
 #include <unistd.h>
-#include <stdexcept>
-#include <iostream>
 #include <algorithm>
 #include <cstdlib>
 #include <iomanip>
+#include <iostream>
 #include <set>
+#include <stdexcept>
 
 #include "i18n.h"
 
@@ -377,69 +377,6 @@ Film::subtitle_analysis_path (shared_ptr<const Content> content) const
 }
 
 
-/** Add suitable Jobs to the JobManager to create a DCP for this Film */
-void
-Film::make_dcp (TranscodeJob::ChangedBehaviour behaviour)
-{
-       if (dcp_name().find ("/") != string::npos) {
-               throw BadSettingError (_("name"), _("Cannot contain slashes"));
-       }
-
-       if (container() == nullptr) {
-               throw MissingSettingError (_("container"));
-       }
-
-       if (content().empty()) {
-               throw runtime_error (_("You must add some content to the DCP before creating it"));
-       }
-
-       if (length() == DCPTime()) {
-               throw runtime_error (_("The DCP is empty, perhaps because all the content has zero length."));
-       }
-
-       if (dcp_content_type() == nullptr) {
-               throw MissingSettingError (_("content type"));
-       }
-
-       if (name().empty()) {
-               set_name ("DCP");
-       }
-
-       for (auto i: content ()) {
-               if (!i->paths_valid()) {
-                       throw runtime_error (_("some of your content is missing"));
-               }
-               auto dcp = dynamic_pointer_cast<const DCPContent>(i);
-               if (dcp && dcp->needs_kdm()) {
-                       throw runtime_error (_("Some of your content needs a KDM"));
-               }
-               if (dcp && dcp->needs_assets()) {
-                       throw runtime_error (_("Some of your content needs an OV"));
-               }
-       }
-
-       set_isdcf_date_today ();
-
-       for (auto i: environment_info ()) {
-               LOG_GENERAL_NC (i);
-       }
-
-       for (auto i: content ()) {
-               LOG_GENERAL ("Content: %1", i->technical_summary());
-       }
-       LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
-       if (Config::instance()->only_servers_encode ()) {
-               LOG_GENERAL_NC ("0 threads: ONLY SERVERS SET TO ENCODE");
-       } else {
-               LOG_GENERAL ("%1 threads", Config::instance()->master_encoding_threads());
-       }
-       LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
-
-       auto tj = make_shared<TranscodeJob>(shared_from_this(), behaviour);
-       tj->set_encoder (make_shared<DCPEncoder>(shared_from_this(), tj));
-       JobManager::instance()->add (tj);
-}
-
 /** Start a job to send our DCP to the configured TMS */
 void
 Film::send_dcp_to_tms ()
@@ -471,6 +408,7 @@ Film::metadata (bool with_content_paths) const
        root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
        root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
        root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
+       root->add_child("AudioFrameRate")->add_child_text(raw_convert<string>(_audio_frame_rate));
        root->add_child("ISDCFDate")->add_child_text (boost::gregorian::to_iso_string (_isdcf_date));
        root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
        root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
@@ -544,12 +482,12 @@ Film::write_metadata (boost::filesystem::path path) const
 
 /** Write state to our `metadata' file */
 void
-Film::write_metadata () const
+Film::write_metadata ()
 {
        DCPOMATIC_ASSERT (directory());
        boost::filesystem::create_directories (directory().get());
        metadata()->write_to_file_formatted(file(metadata_file).string());
-       _dirty = false;
+       set_dirty (false);
 }
 
 /** Write a template from this film */
@@ -624,6 +562,7 @@ Film::read_metadata (optional<boost::filesystem::path> path)
        _resolution = string_to_resolution (f.string_child ("Resolution"));
        _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
        _video_frame_rate = f.number_child<int> ("VideoFrameRate");
+       _audio_frame_rate = f.optional_number_child<int>("AudioFrameRate").get_value_or(48000);
        _encrypted = f.bool_child ("Encrypted");
        _audio_channels = f.number_child<int> ("AudioChannels");
        /* We used to allow odd numbers (and zero) channels, but it's just not worth
@@ -766,7 +705,7 @@ Film::read_metadata (optional<boost::filesystem::path> path)
                set_backtrace_file (file ("backtrace.txt"));
        }
 
-       _dirty = false;
+       set_dirty (false);
        return notes;
 }
 
@@ -978,7 +917,18 @@ Film::isdcf_name (bool if_created_now) const
                }
        }
 
-       auto audio_language = (_audio_language && _audio_language->language()) ? _audio_language->language()->subtag() : "XX";
+       auto entry_for_language = [](dcp::LanguageTag const& tag) {
+               /* Look up what we should be using for this tag in the DCNC name */
+               for (auto const& dcnc: dcp::dcnc_tags()) {
+                       if (tag.to_string() == dcnc.first) {
+                               return dcnc.second;
+                       }
+               }
+               /* Fallback to the language subtag, if there is one */
+               return tag.language() ? tag.language()->subtag() : "XX";
+       };
+
+       auto audio_language = _audio_language ? entry_for_language(*_audio_language) : "XX";
 
        d += "_" + to_upper (audio_language);
 
@@ -1000,7 +950,7 @@ Film::isdcf_name (bool if_created_now) const
 
        auto sub_langs = subtitle_languages();
        if (sub_langs.first && sub_langs.first->language()) {
-               auto lang = sub_langs.first->language()->subtag();
+               auto lang = entry_for_language(*sub_langs.first);
                if (burnt_in) {
                        transform (lang.begin(), lang.end(), lang.begin(), ::tolower);
                } else {
@@ -1113,7 +1063,7 @@ void
 Film::set_directory (boost::filesystem::path d)
 {
        _directory = d;
-       _dirty = true;
+       set_dirty (true);
 }
 
 void
@@ -1256,7 +1206,7 @@ void
 Film::signal_change (ChangeType type, Property p)
 {
        if (type == ChangeType::DONE) {
-               _dirty = true;
+               set_dirty (true);
 
                if (p == Property::CONTENT) {
                        if (!_user_explicit_video_frame_rate) {
@@ -1536,7 +1486,7 @@ Film::playlist_content_change (ChangeType type, weak_ptr<Content> c, int p, bool
                ContentChange (type, c, p, frequent);
        }
 
-       _dirty = true;
+       set_dirty (true);
 }
 
 void
@@ -1555,7 +1505,7 @@ Film::playlist_change (ChangeType type)
                check_settings_consistency ();
        }
 
-       _dirty = true;
+       set_dirty (true);
 }
 
 /** Check for (and if necessary fix) impossible settings combinations, like
@@ -1617,14 +1567,6 @@ Film::playlist_order_changed ()
        signal_change (ChangeType::DONE, Property::CONTENT_ORDER);
 }
 
-int
-Film::audio_frame_rate () const
-{
-       /* It seems that nobody makes 96kHz DCPs at the moment, so let's avoid them.
-          See #1436.
-       */
-       return 48000;
-}
 
 void
 Film::set_sequence (bool s)
@@ -1776,9 +1718,9 @@ Film::should_be_enough_disk_space (double& required, double& available, bool& ca
        boost::filesystem::path test = internal_video_asset_dir() / "test";
        boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
        can_hard_link = true;
-       auto f = fopen_boost (test, "w");
+       dcp::File f(test, "w");
        if (f) {
-               fclose (f);
+               f.close();
                boost::system::error_code ec;
                boost::filesystem::create_hard_link (test, test2, ec);
                if (ec) {
@@ -2129,34 +2071,15 @@ Film::info_file_handle (DCPTimePeriod period, bool read) const
        return std::make_shared<InfoFileHandle>(_info_file_mutex, info_file(period), read);
 }
 
-InfoFileHandle::InfoFileHandle (boost::mutex& mutex, boost::filesystem::path file, bool read)
+InfoFileHandle::InfoFileHandle (boost::mutex& mutex, boost::filesystem::path path, bool read)
        : _lock (mutex)
-       , _file (file)
+       , _file (path, read ? "rb" : (boost::filesystem::exists(path) ? "r+b" : "wb"))
 {
-       if (read) {
-               _handle = fopen_boost (file, "rb");
-               if (!_handle) {
-                       throw OpenFileError (file, errno, OpenFileError::READ);
-               }
-       } else {
-               auto const exists = boost::filesystem::exists (file);
-               if (exists) {
-                       _handle = fopen_boost (file, "r+b");
-               } else {
-                       _handle = fopen_boost (file, "wb");
-               }
-
-               if (!_handle) {
-                       throw OpenFileError (file, errno, exists ? OpenFileError::READ_WRITE : OpenFileError::WRITE);
-               }
+       if (!_file) {
+               throw OpenFileError (path, errno, read ? OpenFileError::READ : (boost::filesystem::exists(path) ? OpenFileError::READ_WRITE : OpenFileError::WRITE));
        }
 }
 
-InfoFileHandle::~InfoFileHandle ()
-{
-       fclose (_handle);
-}
-
 
 /** Add FFOC and LFOC markers to a list if they are not already there */
 void
@@ -2212,6 +2135,14 @@ Film::set_audio_language (optional<dcp::LanguageTag> language)
 }
 
 
+void
+Film::set_audio_frame_rate (int rate)
+{
+       FilmChangeSignaller ch (this, Property::AUDIO_FRAME_RATE);
+       _audio_frame_rate = rate;
+}
+
+
 bool
 Film::has_sign_language_video_channel () const
 {
@@ -2226,3 +2157,14 @@ Film::set_sign_language_video_language (optional<dcp::LanguageTag> lang)
        _sign_language_video_language = lang;
 }
 
+
+void
+Film::set_dirty (bool dirty)
+{
+       auto const changed = dirty != _dirty;
+       _dirty = dirty;
+       if (changed) {
+               emit (boost::bind(boost::ref(DirtyChange), _dirty));
+       }
+}
+