Make Atmos content work more like other content. Now its MXFs
[dcpomatic.git] / src / lib / util.cc
index cd2d5a3685d506d9d84a09a557f4e9e512f39499..74951fc64e1ef9bb076e5959cdd43dddd140e25f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2020 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 #include "image.h"
 #include "text_decoder.h"
 #include "job_manager.h"
+#include <dcp/decrypted_kdm.h>
 #include <dcp/locale_convert.h>
 #include <dcp/util.h>
 #include <dcp/raw_convert.h>
 #include <dcp/picture_asset.h>
 #include <dcp/sound_asset.h>
 #include <dcp/subtitle_asset.h>
+#include <dcp/atmos_asset.h>
 extern "C" {
 #include <libavfilter/avfilter.h>
 #include <libavformat/avformat.h>
@@ -361,7 +363,7 @@ dcpomatic_setup ()
        /* Add our library directory to the libltdl search path so that
           xmlsec can find xmlsec1-openssl.
        */
-       boost::filesystem::path lib = app_contents ();
+       boost::filesystem::path lib = directory_containing_executable().parent_path();
        lib /= "Frameworks";
        setenv ("LTDL_LIBRARY_PATH", lib.c_str (), 1);
 #endif
@@ -394,7 +396,6 @@ dcpomatic_setup ()
 
        Ratio::setup_ratios ();
        PresetColourConversion::setup_colour_conversion_presets ();
-       VideoContentScale::setup_scales ();
        DCPContentType::setup_dcp_content_types ();
        Filter::setup_filters ();
        CinemaSoundProcessor::setup_cinema_sound_processors ();
@@ -727,6 +728,21 @@ audio_asset_filename (shared_ptr<dcp::SoundAsset> asset, int reel_index, int ree
        return Config::instance()->dcp_asset_filename_format().get(values, "_" + asset->id() + ".mxf");
 }
 
+
+string
+atmos_asset_filename (shared_ptr<dcp::AtmosAsset> asset, int reel_index, int reel_count, optional<string> summary)
+{
+       dcp::NameFormat::Map values;
+       values['t'] = "atmos";
+       values['r'] = raw_convert<string> (reel_index + 1);
+       values['n'] = raw_convert<string> (reel_count);
+       if (summary) {
+               values['c'] = careful_string_filter (summary.get());
+       }
+       return Config::instance()->dcp_asset_filename_format().get(values, "_" + asset->id() + ".mxf");
+}
+
+
 float
 relaxed_string_to_float (string s)
 {
@@ -823,7 +839,9 @@ remap (shared_ptr<const AudioBuffers> input, int output_channels, AudioMapping m
        shared_ptr<AudioBuffers> mapped (new AudioBuffers (output_channels, input->frames()));
        mapped->make_silent ();
 
-       for (int i = 0; i < map.input_channels(); ++i) {
+       int to_do = min (map.input_channels(), input->channels());
+
+       for (int i = 0; i < to_do; ++i) {
                for (int j = 0; j < mapped->channels(); ++j) {
                        if (map.get (i, static_cast<dcp::Channel> (j)) > 0) {
                                mapped->accumulate_channel (
@@ -927,7 +945,7 @@ emit_subtitle_image (ContentTimePeriod period, dcp::SubtitleImage sub, dcp::Size
 {
        /* XXX: this is rather inefficient; decoding the image just to get its size */
        FFmpegImageProxy proxy (sub.png_image());
-       shared_ptr<Image> image = proxy.image().first;
+       shared_ptr<Image> image = proxy.image().image;
        /* set up rect with height and width */
        dcpomatic::Rect<double> rect(0, 0, image->size().width / double(size.width), image->size().height / double(size.height));
 
@@ -967,7 +985,7 @@ show_jobs_on_console (bool progress)
        bool error = false;
        while (true) {
 
-               dcpomatic_sleep (5);
+               dcpomatic_sleep_seconds (5);
 
                list<shared_ptr<Job> > jobs = JobManager::instance()->get();
 
@@ -1015,6 +1033,58 @@ show_jobs_on_console (bool progress)
        return error;
 }
 
+/** XXX: could use mmap? */
+void
+copy_in_bits (boost::filesystem::path from, boost::filesystem::path to, boost::function<void (float)> progress)
+{
+       FILE* f = fopen_boost (from, "rb");
+       if (!f) {
+               throw OpenFileError (from, errno, OpenFileError::READ);
+       }
+       FILE* t = fopen_boost (to, "wb");
+       if (!t) {
+               fclose (f);
+               throw OpenFileError (to, errno, OpenFileError::WRITE);
+       }
+
+       /* on the order of a second's worth of copying */
+       boost::uintmax_t const chunk = 20 * 1024 * 1024;
+
+       uint8_t* buffer = static_cast<uint8_t*> (malloc(chunk));
+       if (!buffer) {
+               throw std::bad_alloc ();
+       }
+
+       boost::uintmax_t const total = boost::filesystem::file_size (from);
+       boost::uintmax_t remaining = total;
+
+       while (remaining) {
+               boost::uintmax_t this_time = min (chunk, remaining);
+               size_t N = fread (buffer, 1, chunk, f);
+               if (N < this_time) {
+                       fclose (f);
+                       fclose (t);
+                       free (buffer);
+                       throw ReadFileError (from, errno);
+               }
+
+               N = fwrite (buffer, 1, this_time, t);
+               if (N < this_time) {
+                       fclose (f);
+                       fclose (t);
+                       free (buffer);
+                       throw WriteFileError (to, errno);
+               }
+
+               progress (1 - float(remaining) / total);
+               remaining -= this_time;
+       }
+
+       fclose (f);
+       fclose (t);
+       free (buffer);
+}
+
 #ifdef DCPOMATIC_VARIANT_SWAROOP
 
 /* Make up a key from the machine UUID */
@@ -1119,3 +1189,60 @@ write_swaroop_chain (shared_ptr<const dcp::CertificateChain> chain, boost::files
 }
 
 #endif
+
+double
+db_to_linear (double db)
+{
+       return pow(10, db / 20);
+}
+
+double
+linear_to_db (double linear)
+{
+       return 20 * log10(linear);
+}
+
+
+dcp::Size
+scale_for_display (dcp::Size s, dcp::Size display_container, dcp::Size film_container)
+{
+       /* Now scale it down if the display container is smaller than the film container */
+       if (display_container != film_container) {
+               float const scale = min (
+                       float (display_container.width) / film_container.width,
+                       float (display_container.height) / film_container.height
+                       );
+
+               s.width = lrintf (s.width * scale);
+               s.height = lrintf (s.height * scale);
+       }
+
+       return s;
+}
+
+
+dcp::DecryptedKDM
+decrypt_kdm_with_helpful_error (dcp::EncryptedKDM kdm)
+{
+       try {
+               return dcp::DecryptedKDM (kdm, Config::instance()->decryption_chain()->key().get());
+       } catch (dcp::KDMDecryptionError& e) {
+               /* Try to flesh out the error a bit */
+               string const kdm_subject_name = kdm.recipient_x509_subject_name();
+               bool on_chain = false;
+               shared_ptr<const dcp::CertificateChain> dc = Config::instance()->decryption_chain();
+               BOOST_FOREACH (dcp::Certificate i, dc->root_to_leaf()) {
+                       if (i.subject() == kdm_subject_name) {
+                               on_chain = true;
+                       }
+               }
+               if (!on_chain) {
+                       throw KDMError (_("This KDM was not made for DCP-o-matic's decryption certificate."), e.what());
+               } else if (on_chain && kdm_subject_name != dc->leaf().subject()) {
+                       throw KDMError (_("This KDM was made for DCP-o-matic but not for its leaf certificate."), e.what());
+               } else {
+                       throw;
+               }
+       }
+}
+