Rename Time::round_up to Time::ceil.
[dcpomatic.git] / src / lib / film.cc
index db8da56b49e3ec748f54caf8ee49dd8ebc5269b3..68266b4ec52a3a4f4825eaeb5cd3e5073081052e 100644 (file)
@@ -38,9 +38,7 @@
 #include "dcp_content_type.h"
 #include "ratio.h"
 #include "cross.h"
-#include "safe_stringstream.h"
 #include "environment_info.h"
-#include "raw_convert.h"
 #include "audio_processor.h"
 #include "digester.h"
 #include "compose.hpp"
 #include <dcp/util.h>
 #include <dcp/local_time.h>
 #include <dcp/decrypted_kdm.h>
+#include <dcp/raw_convert.h>
 #include <libxml++/libxml++.h>
 #include <boost/filesystem.hpp>
 #include <boost/algorithm/string.hpp>
 #include <boost/foreach.hpp>
+#include <boost/regex.hpp>
 #include <unistd.h>
 #include <stdexcept>
 #include <iostream>
@@ -88,6 +88,7 @@ using boost::weak_ptr;
 using boost::dynamic_pointer_cast;
 using boost::optional;
 using boost::is_any_of;
+using dcp::raw_convert;
 
 #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
 #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
@@ -111,15 +112,19 @@ using boost::is_any_of;
  * 33 -> 34
  * Content only contains audio/subtitle-related tags if those things
  * are present.
+ * 34 -> 35
+ * VideoFrameType in VideoContent is a string rather than an integer.
+ * 35 -> 36
+ * EffectColour rather than OutlineColour in Subtitle.
  */
-int const Film::current_state_version = 34;
+int const Film::current_state_version = 36;
 
 /** Construct a Film object in a given directory.
  *
  *  @param dir Film directory.
  */
 
-Film::Film (boost::filesystem::path dir, bool log)
+Film::Film (optional<boost::filesystem::path> dir)
        : _playlist (new Playlist)
        , _use_isdcf_name (true)
        , _dcp_content_type (Config::instance()->default_dcp_content_type ())
@@ -127,10 +132,11 @@ Film::Film (boost::filesystem::path dir, bool log)
        , _resolution (RESOLUTION_2K)
        , _signed (true)
        , _encrypted (false)
+       , _context_id (dcp::make_uuid ())
        , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
        , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
        , _video_frame_rate (24)
-       , _audio_channels (6)
+       , _audio_channels (Config::instance()->default_dcp_audio_channels ())
        , _three_d (false)
        , _sequence (true)
        , _interop (Config::instance()->default_interop ())
@@ -147,27 +153,30 @@ Film::Film (boost::filesystem::path dir, bool log)
        _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
        _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
 
-       /* Make state.directory a complete path without ..s (where possible)
-          (Code swiped from Adam Bowen on stackoverflow)
-          XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
-       */
+       if (dir) {
+               /* Make state.directory a complete path without ..s (where possible)
+                  (Code swiped from Adam Bowen on stackoverflow)
+                  XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
+               */
 
-       boost::filesystem::path p (boost::filesystem::system_complete (dir));
-       boost::filesystem::path result;
-       for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
-               if (*i == "..") {
-                       if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
+               boost::filesystem::path p (boost::filesystem::system_complete (dir.get()));
+               boost::filesystem::path result;
+               for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
+                       if (*i == "..") {
+                               if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
+                                       result /= *i;
+                               } else {
+                                       result = result.parent_path ();
+                               }
+                       } else if (*i != ".") {
                                result /= *i;
-                       } else {
-                               result = result.parent_path ();
                        }
-               } else if (*i != ".") {
-                       result /= *i;
                }
+
+               set_directory (result.make_preferred ());
        }
 
-       set_directory (result.make_preferred ());
-       if (log) {
+       if (_directory) {
                _log.reset (new FileLog (file ("log")));
        } else {
                _log.reset (new NullLog);
@@ -192,32 +201,29 @@ Film::video_identifier () const
 {
        DCPOMATIC_ASSERT (container ());
 
-       SafeStringStream s;
-       s.imbue (std::locale::classic ());
-
-       s << container()->id()
-         << "_" << resolution_to_string (_resolution)
-         << "_" << _playlist->video_identifier()
-         << "_" << _video_frame_rate
-         << "_" << j2k_bandwidth();
+       string s = container()->id()
+               + "_" + resolution_to_string (_resolution)
+               + "_" + _playlist->video_identifier()
+               + "_" + raw_convert<string>(_video_frame_rate)
+               + "_" + raw_convert<string>(j2k_bandwidth());
 
        if (encrypted ()) {
-               s << "_E";
+               s += "_E";
        } else {
-               s << "_P";
+               s += "_P";
        }
 
        if (_interop) {
-               s << "_I";
+               s += "_I";
        } else {
-               s << "_S";
+               s += "_S";
        }
 
        if (_three_d) {
-               s << "_3D";
+               s += "_3D";
        }
 
-       return s.str ();
+       return s;
 }
 
 /** @return The file to write video frame info to */
@@ -327,7 +333,7 @@ Film::send_dcp_to_tms ()
 }
 
 shared_ptr<xmlpp::Document>
-Film::metadata () const
+Film::metadata (bool with_content_paths) const
 {
        shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
        xmlpp::Element* root = doc->create_root_node ("Metadata");
@@ -356,13 +362,14 @@ Film::metadata () const
        root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
        root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
        root->add_child("Key")->add_child_text (_key.hex ());
+       root->add_child("ContextID")->add_child_text (_context_id);
        if (_audio_processor) {
                root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
        }
-       root->add_child("ReelType")->add_child_text (raw_convert<string> (_reel_type));
+       root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
        root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
        root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
-       _playlist->as_xml (root->add_child ("Playlist"));
+       _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
 }
@@ -371,24 +378,38 @@ Film::metadata () const
 void
 Film::write_metadata () const
 {
-       boost::filesystem::create_directories (directory ());
+       DCPOMATIC_ASSERT (directory());
+       boost::filesystem::create_directories (directory().get());
        shared_ptr<xmlpp::Document> doc = metadata ();
        doc->write_to_file_formatted (file("metadata.xml").string ());
        _dirty = false;
 }
 
+/** Write a template from this film */
+void
+Film::write_template (boost::filesystem::path path) const
+{
+       boost::filesystem::create_directories (path.parent_path());
+       shared_ptr<xmlpp::Document> doc = metadata (false);
+       doc->write_to_file_formatted (path.string ());
+}
+
 /** Read state from our metadata file.
  *  @return Notes about things that the user should know about, or empty.
  */
 list<string>
-Film::read_metadata ()
+Film::read_metadata (optional<boost::filesystem::path> path)
 {
-       if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
-               throw runtime_error (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version.  You will need to create a new Film, re-add your content and set it up again.  Sorry!"));
+       if (!path) {
+               if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
+                       throw runtime_error (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version.  You will need to create a new Film, re-add your content and set it up again.  Sorry!"));
+               }
+
+               path = file ("metadata.xml");
        }
 
        cxml::Document f ("Metadata");
-       f.read_file (file ("metadata.xml"));
+       f.read_file (path.get ());
 
        _state_version = f.number_child<int> ("Version");
        if (_state_version > current_state_version) {
@@ -444,6 +465,7 @@ Film::read_metadata ()
        _three_d = f.bool_child ("ThreeD");
        _interop = f.bool_child ("Interop");
        _key = dcp::Key (f.string_child ("Key"));
+       _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ());
 
        if (f.optional_string_child ("AudioProcessor")) {
                _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
@@ -460,7 +482,9 @@ Film::read_metadata ()
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
 
        /* Write backtraces to this film's directory, until another film is loaded */
-       set_backtrace_file (file ("backtrace.txt"));
+       if (_directory) {
+               set_backtrace_file (file ("backtrace.txt"));
+       }
 
        _dirty = false;
        return notes;
@@ -472,8 +496,10 @@ Film::read_metadata ()
 boost::filesystem::path
 Film::dir (boost::filesystem::path d) const
 {
+       DCPOMATIC_ASSERT (_directory);
+
        boost::filesystem::path p;
-       p /= _directory;
+       p /= _directory.get();
        p /= d;
 
        boost::filesystem::create_directories (p);
@@ -487,8 +513,10 @@ Film::dir (boost::filesystem::path d) const
 boost::filesystem::path
 Film::file (boost::filesystem::path f) const
 {
+       DCPOMATIC_ASSERT (_directory);
+
        boost::filesystem::path p;
-       p /= _directory;
+       p /= _directory.get();
        p /= f;
 
        boost::filesystem::create_directories (p.parent_path ());
@@ -525,7 +553,7 @@ Film::mapped_audio_channels () const
 string
 Film::isdcf_name (bool if_created_now) const
 {
-       SafeStringStream d;
+       string d;
 
        string raw_name = name ();
 
@@ -568,49 +596,49 @@ Film::isdcf_name (bool if_created_now) const
                fixed_name = fixed_name.substr (0, 14);
        }
 
-       d << fixed_name;
+       d += fixed_name;
 
        if (dcp_content_type()) {
-               d << "_" << dcp_content_type()->isdcf_name();
-               d << "-" << isdcf_metadata().content_version;
+               d += "_" + dcp_content_type()->isdcf_name();
+               d += "-" + raw_convert<string>(isdcf_metadata().content_version);
        }
 
        ISDCFMetadata const dm = isdcf_metadata ();
 
        if (dm.temp_version) {
-               d << "-Temp";
+               d += "-Temp";
        }
 
        if (dm.pre_release) {
-               d << "-Pre";
+               d += "-Pre";
        }
 
        if (dm.red_band) {
-               d << "-RedBand";
+               d += "-RedBand";
        }
 
        if (!dm.chain.empty ()) {
-               d << "-" << dm.chain;
+               d += "-" + dm.chain;
        }
 
        if (three_d ()) {
-               d << "-3D";
+               d += "-3D";
        }
 
        if (dm.two_d_version_of_three_d) {
-               d << "-2D";
+               d += "-2D";
        }
 
        if (!dm.mastered_luminance.empty ()) {
-               d << "-" << dm.mastered_luminance;
+               d += "-" + dm.mastered_luminance;
        }
 
        if (video_frame_rate() != 24) {
-               d << "-" << video_frame_rate();
+               d += "-" + raw_convert<string>(video_frame_rate());
        }
 
        if (container()) {
-               d << "_" << container()->isdcf_name();
+               d += "_" + container()->isdcf_name();
        }
 
        /* XXX: this uses the first bit of content only */
@@ -631,12 +659,12 @@ Film::isdcf_name (bool if_created_now) const
                }
 
                if (content_ratio && content_ratio != container()) {
-                       d << "-" << content_ratio->isdcf_name();
+                       d += "-" + content_ratio->isdcf_name();
                }
        }
 
        if (!dm.audio_language.empty ()) {
-               d << "_" << dm.audio_language;
+               d += "_" + dm.audio_language;
                if (!dm.subtitle_language.empty()) {
 
                        bool burnt_in = true;
@@ -657,18 +685,18 @@ Film::isdcf_name (bool if_created_now) const
                                transform (language.begin(), language.end(), language.begin(), ::toupper);
                        }
 
-                       d << "-" << language;
+                       d += "-" + language;
                } else {
-                       d << "-XX";
+                       d += "-XX";
                }
        }
 
        if (!dm.territory.empty ()) {
-               d << "_" << dm.territory;
+               d += "_" + dm.territory;
                if (dm.rating.empty ()) {
-                       d << "-NR";
+                       d += "-NR";
                } else {
-                       d << "-" << dm.rating;
+                       d += "-" + dm.rating;
                }
        }
 
@@ -691,35 +719,35 @@ Film::isdcf_name (bool if_created_now) const
        }
 
        if (non_lfe) {
-               d << "_" << non_lfe << lfe;
+               d += String::compose("_%1%2", non_lfe, lfe);
        }
 
        /* XXX: HI/VI */
 
-       d << "_" << resolution_to_string (_resolution);
+       d += "_" + resolution_to_string (_resolution);
 
        if (!dm.studio.empty ()) {
-               d << "_" << dm.studio;
+               d += "_" + dm.studio;
        }
 
        if (if_created_now) {
-               d << "_" << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
+               d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
        } else {
-               d << "_" << boost::gregorian::to_iso_string (_isdcf_date);
+               d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
        }
 
        if (!dm.facility.empty ()) {
-               d << "_" << dm.facility;
+               d += "_" + dm.facility;
        }
 
        if (_interop) {
-               d << "_IOP";
+               d += "_IOP";
        } else {
-               d << "_SMPTE";
+               d += "_SMPTE";
        }
 
        if (three_d ()) {
-               d << "-3D";
+               d += "-3D";
        }
 
        bool vf = false;
@@ -731,12 +759,12 @@ Film::isdcf_name (bool if_created_now) const
        }
 
        if (vf) {
-               d << "_VF";
+               d += "_VF";
        } else {
-               d << "_OV";
+               d += "_OV";
        }
 
-       return d.str ();
+       return d;
 }
 
 /** @return name to give the DCP */
@@ -917,23 +945,23 @@ Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
        p /= "j2c";
        p /= video_identifier ();
 
-       SafeStringStream s;
-       s.width (8);
-       s << setfill('0') << reel << "_" << frame;
+       char buffer[256];
+       snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
+       string s (buffer);
 
        if (eyes == EYES_LEFT) {
-               s << ".L";
+               s += ".L";
        } else if (eyes == EYES_RIGHT) {
-               s << ".R";
+               s += ".R";
        }
 
-       s << ".j2c";
+       s += ".j2c";
 
        if (tmp) {
-               s << ".tmp";
+               s += ".tmp";
        }
 
-       p /= s.str();
+       p /= s;
        return file (p);
 }
 
@@ -941,9 +969,13 @@ Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
 vector<CPLSummary>
 Film::cpls () const
 {
+       if (!directory ()) {
+               return vector<CPLSummary> ();
+       }
+
        vector<CPLSummary> out;
 
-       boost::filesystem::path const dir = directory ();
+       boost::filesystem::path const dir = directory().get();
        for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
                if (
                        boost::filesystem::is_directory (*i) &&
@@ -953,12 +985,13 @@ Film::cpls () const
                        try {
                                dcp::DCP dcp (*i);
                                dcp.read ();
+                               DCPOMATIC_ASSERT (dcp.cpls().front()->file());
                                out.push_back (
                                        CPLSummary (
                                                i->path().leaf().string(),
                                                dcp.cpls().front()->id(),
                                                dcp.cpls().front()->annotation_text(),
-                                               dcp.cpls().front()->file()
+                                               dcp.cpls().front()->file().get()
                                                )
                                        );
                        } catch (...) {
@@ -997,17 +1030,10 @@ Film::content () const
        return _playlist->content ();
 }
 
-void
-Film::examine_content (shared_ptr<Content> c)
-{
-       shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
-       JobManager::instance()->add (j);
-}
-
 void
 Film::examine_and_add_content (shared_ptr<Content> c)
 {
-       if (dynamic_pointer_cast<FFmpegContent> (c) && !_directory.empty ()) {
+       if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
                run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
        }
 
@@ -1034,6 +1060,7 @@ Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
        }
 
        add_content (content);
+
        if (Config::instance()->automatic_audio_analysis() && content->audio) {
                shared_ptr<Playlist> playlist (new Playlist);
                playlist->add (content);
@@ -1055,6 +1082,15 @@ Film::add_content (shared_ptr<Content> c)
                c->set_position (_playlist->subtitle_end ());
        }
 
+       if (_template_film) {
+               /* Take settings from the first piece of content of c's type in _template */
+               BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
+                       if (typeid(i.get()) == typeid(c.get())) {
+                               c->use_template (i);
+                       }
+               }
+       }
+
        _playlist->add (c);
 }
 
@@ -1290,18 +1326,44 @@ Film::subtitle_language () const
 
 /** Change the gains of the supplied AudioMapping to make it a default
  *  for this film.  The defaults are guessed based on what processor (if any)
- *  is in use and the number of input channels.
+ *  is in use, the number of input channels and any filename supplied.
  */
 void
-Film::make_audio_mapping_default (AudioMapping& mapping) const
+Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
 {
+       static string const regex[] = {
+               ".*[\\._-]L[\\._-].*",
+               ".*[\\._-]R[\\._-].*",
+               ".*[\\._-]C[\\._-].*",
+               ".*[\\._-]Lfe[\\._-].*",
+               ".*[\\._-]Ls[\\._-].*",
+               ".*[\\._-]Rs[\\._-].*"
+       };
+
+       static int const regexes = sizeof(regex) / sizeof(*regex);
+
        if (audio_processor ()) {
                audio_processor()->make_audio_mapping_default (mapping);
        } else {
                mapping.make_zero ();
                if (mapping.input_channels() == 1) {
-                       /* Mono -> Centre */
-                       mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
+                       bool guessed = false;
+
+                       /* See if we can guess where this stream should go */
+                       if (filename) {
+                               for (int i = 0; i < regexes; ++i) {
+                                       boost::regex e (regex[i], boost::regex::icase);
+                                       if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
+                                               mapping.set (0, i, 1);
+                                               guessed = true;
+                                       }
+                               }
+                       }
+
+                       if (!guessed) {
+                               /* If we have no idea, just put it on centre */
+                               mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
+                       }
                } else {
                        /* 1:1 mapping */
                        for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
@@ -1324,24 +1386,12 @@ Film::audio_output_names () const
        DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
 
        vector<string> n;
-       n.push_back (_("L"));
-       n.push_back (_("R"));
-       n.push_back (_("C"));
-       n.push_back (_("Lfe"));
-       n.push_back (_("Ls"));
-       n.push_back (_("Rs"));
-       n.push_back (_("HI"));
-       n.push_back (_("VI"));
-       n.push_back (_("Lc"));
-       n.push_back (_("Rc"));
-       n.push_back (_("BsL"));
-       n.push_back (_("BsR"));
-       n.push_back (_("DBP"));
-       n.push_back (_("DBS"));
-       n.push_back ("");
-       n.push_back ("");
-
-       return vector<string> (n.begin(), n.begin() + audio_channels ());
+
+       for (int i = 0; i < audio_channels(); ++i) {
+               n.push_back (short_audio_channel_name (i));
+       }
+
+       return n;
 }
 
 void
@@ -1366,7 +1416,7 @@ list<DCPTimePeriod>
 Film::reels () const
 {
        list<DCPTimePeriod> p;
-       DCPTime const len = length().round_up (video_frame_rate ());
+       DCPTime const len = length().ceil (video_frame_rate ());
 
        switch (reel_type ()) {
        case REELTYPE_SINGLE:
@@ -1376,7 +1426,6 @@ Film::reels () const
        {
                optional<DCPTime> last_split;
                shared_ptr<Content> last_video;
-               ContentList cl = content ();
                BOOST_FOREACH (shared_ptr<Content> c, content ()) {
                        if (c->video) {
                                BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
@@ -1417,3 +1466,68 @@ Film::reels () const
 
        return p;
 }
+
+string
+Film::content_summary (DCPTimePeriod period) const
+{
+       return _playlist->content_summary (period);
+}
+
+list<string>
+Film::fix_conflicting_settings ()
+{
+       list<string> notes;
+
+       list<boost::filesystem::path> was_referencing;
+       BOOST_FOREACH (shared_ptr<Content> i, content()) {
+               shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
+               if (d) {
+                       list<string> reasons;
+                       bool was = false;
+                       if (!d->can_reference_video(reasons) && d->reference_video()) {
+                               d->set_reference_video (false);
+                               was = true;
+                       }
+                       if (!d->can_reference_audio(reasons) && d->reference_audio()) {
+                               d->set_reference_audio (false);
+                               was = true;
+                       }
+                       if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
+                               d->set_reference_subtitle (false);
+                               was = true;
+                       }
+                       if (was) {
+                               was_referencing.push_back (d->path(0).parent_path().filename());
+                       }
+               }
+       }
+
+       BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
+               notes.push_back (String::compose (_("The DCP %1 was being referred to by this film.  This not now possible because the reel sizes in the film no longer agree with those in the imported DCP.\n\nSetting the 'Reel type' to 'split by video content' will probably help.\n\nAfter doing that you would need to re-tick the appropriate 'refer to existing DCP' checkboxes."), d.string()));
+       }
+
+       return notes;
+}
+
+void
+Film::use_template (string name)
+{
+       _template_film.reset (new Film (optional<boost::filesystem::path>()));
+       _template_film->read_metadata (Config::instance()->template_path (name));
+       _use_isdcf_name = _template_film->_use_isdcf_name;
+       _dcp_content_type = _template_film->_dcp_content_type;
+       _container = _template_film->_container;
+       _resolution = _template_film->_resolution;
+       _j2k_bandwidth = _template_film->_j2k_bandwidth;
+       _video_frame_rate = _template_film->_video_frame_rate;
+       _signed = _template_film->_signed;
+       _encrypted = _template_film->_encrypted;
+       _audio_channels = _template_film->_audio_channels;
+       _sequence = _template_film->_sequence;
+       _three_d = _template_film->_three_d;
+       _interop = _template_film->_interop;
+       _audio_processor = _template_film->_audio_processor;
+       _reel_type = _template_film->_reel_type;
+       _reel_length = _template_film->_reel_length;
+       _upload_after_make_dcp = _template_film->_upload_after_make_dcp;
+}