2 Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 * @brief A representation of some audio and video content, and details of
23 * how they should be presented in a DCP.
29 #include "job_manager.h"
30 #include "transcode_job.h"
31 #include "upload_job.h"
34 #include "exceptions.h"
35 #include "examine_content_job.h"
38 #include "dcp_content_type.h"
41 #include "environment_info.h"
42 #include "audio_processor.h"
44 #include "compose.hpp"
46 #include "audio_content.h"
47 #include "video_content.h"
48 #include "subtitle_content.h"
49 #include "ffmpeg_content.h"
50 #include "dcp_content.h"
51 #include "screen_kdm.h"
53 #include <libcxml/cxml.h>
55 #include <dcp/certificate_chain.h>
57 #include <dcp/local_time.h>
58 #include <dcp/decrypted_kdm.h>
59 #include <dcp/raw_convert.h>
60 #include <dcp/reel_mxf.h>
61 #include <dcp/reel_asset.h>
62 #include <libxml++/libxml++.h>
63 #include <boost/filesystem.hpp>
64 #include <boost/algorithm/string.hpp>
65 #include <boost/foreach.hpp>
66 #include <boost/regex.hpp>
87 using std::runtime_error;
89 using std::back_inserter;
91 using boost::shared_ptr;
92 using boost::weak_ptr;
93 using boost::dynamic_pointer_cast;
94 using boost::optional;
95 using boost::is_any_of;
96 using dcp::raw_convert;
98 #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
99 #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
102 * AudioMapping XML changed.
104 * Subtitle offset changed to subtitle y offset, and subtitle x offset added.
106 * Use <Scale> tag in <VideoContent> rather than <Ratio>.
110 * Subtitle X and Y scale.
112 * Bumped to 32 for 2.0 branch; some times are expressed in Times rather
116 * Changed <Period> to <Subtitle> in FFmpegSubtitleStream
118 * Content only contains audio/subtitle-related tags if those things
121 * VideoFrameType in VideoContent is a string rather than an integer.
123 * EffectColour rather than OutlineColour in Subtitle.
125 int const Film::current_state_version = 36;
127 /** Construct a Film object in a given directory.
129 * @param dir Film directory.
132 Film::Film (optional<boost::filesystem::path> dir)
133 : _playlist (new Playlist)
134 , _use_isdcf_name (true)
135 , _dcp_content_type (Config::instance()->default_dcp_content_type ())
136 , _container (Config::instance()->default_container ())
137 , _resolution (RESOLUTION_2K)
140 , _context_id (dcp::make_uuid ())
141 , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
142 , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
143 , _video_frame_rate (24)
144 , _audio_channels (Config::instance()->default_dcp_audio_channels ())
147 , _interop (Config::instance()->default_interop ())
148 , _audio_processor (0)
149 , _reel_type (REELTYPE_SINGLE)
150 , _reel_length (2000000000)
151 , _upload_after_make_dcp (false)
152 , _state_version (current_state_version)
155 set_isdcf_date_today ();
157 _playlist_changed_connection = _playlist->Changed.connect (bind (&Film::playlist_changed, this));
158 _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
159 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
162 /* Make state.directory a complete path without ..s (where possible)
163 (Code swiped from Adam Bowen on stackoverflow)
164 XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
167 boost::filesystem::path p (boost::filesystem::system_complete (dir.get()));
168 boost::filesystem::path result;
169 for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
171 if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
174 result = result.parent_path ();
176 } else if (*i != ".") {
181 set_directory (result.make_preferred ());
185 _log.reset (new FileLog (file ("log")));
187 _log.reset (new NullLog);
190 _playlist->set_sequence (_sequence);
195 BOOST_FOREACH (boost::signals2::connection& i, _job_connections) {
199 BOOST_FOREACH (boost::signals2::connection& i, _audio_analysis_connections) {
205 Film::video_identifier () const
207 DCPOMATIC_ASSERT (container ());
209 string s = container()->id()
210 + "_" + resolution_to_string (_resolution)
211 + "_" + _playlist->video_identifier()
212 + "_" + raw_convert<string>(_video_frame_rate)
213 + "_" + raw_convert<string>(j2k_bandwidth());
234 /** @return The file to write video frame info to */
235 boost::filesystem::path
236 Film::info_file (DCPTimePeriod period) const
238 boost::filesystem::path p;
240 p /= video_identifier () + "_" + raw_convert<string> (period.from.get()) + "_" + raw_convert<string> (period.to.get());
244 boost::filesystem::path
245 Film::internal_video_asset_dir () const
247 return dir ("video");
250 boost::filesystem::path
251 Film::internal_video_asset_filename (DCPTimePeriod p) const
253 return video_identifier() + "_" + raw_convert<string> (p.from.get()) + "_" + raw_convert<string> (p.to.get()) + ".mxf";
256 boost::filesystem::path
257 Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
259 boost::filesystem::path p = dir ("analysis");
262 BOOST_FOREACH (shared_ptr<Content> i, playlist->content ()) {
267 digester.add (i->digest ());
268 digester.add (i->audio->mapping().digest ());
269 if (playlist->content().size() != 1) {
270 /* Analyses should be considered equal regardless of gain
271 if they were made from just one piece of content. This
272 is because we can fake any gain change in a single-content
273 analysis at the plotting stage rather than having to
276 digester.add (i->audio->gain ());
280 if (audio_processor ()) {
281 digester.add (audio_processor()->id ());
284 p /= digester.get ();
288 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
292 if (dcp_name().find ("/") != string::npos) {
293 throw BadSettingError (_("name"), _("cannot contain slashes"));
296 if (container() == 0) {
297 throw MissingSettingError (_("container"));
300 if (content().empty()) {
301 throw runtime_error (_("you must add some content to the DCP before creating it"));
304 if (dcp_content_type() == 0) {
305 throw MissingSettingError (_("content type"));
308 if (name().empty()) {
309 throw MissingSettingError (_("name"));
312 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
313 if (!i->paths_valid()) {
314 throw runtime_error (_("some of your content is missing"));
316 shared_ptr<const DCPContent> dcp = dynamic_pointer_cast<const DCPContent> (i);
317 if (dcp && dcp->needs_kdm()) {
318 throw runtime_error (_("some of your content needs a KDM"));
320 if (dcp && dcp->needs_assets()) {
321 throw runtime_error (_("some of your content needs an OV"));
325 set_isdcf_date_today ();
327 BOOST_FOREACH (string i, environment_info ()) {
331 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
332 LOG_GENERAL ("Content: %1", i->technical_summary());
334 LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
335 if (Config::instance()->only_servers_encode ()) {
336 LOG_GENERAL_NC ("0 threads: ONLY SERVERS SET TO ENCODE");
338 LOG_GENERAL ("%1 threads", Config::instance()->num_local_encoding_threads());
340 LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
342 JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
345 /** Start a job to send our DCP to the configured TMS */
347 Film::send_dcp_to_tms ()
349 shared_ptr<Job> j (new UploadJob (shared_from_this()));
350 JobManager::instance()->add (j);
353 shared_ptr<xmlpp::Document>
354 Film::metadata (bool with_content_paths) const
356 shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
357 xmlpp::Element* root = doc->create_root_node ("Metadata");
359 root->add_child("Version")->add_child_text (raw_convert<string> (current_state_version));
360 root->add_child("Name")->add_child_text (_name);
361 root->add_child("UseISDCFName")->add_child_text (_use_isdcf_name ? "1" : "0");
363 if (_dcp_content_type) {
364 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->isdcf_name ());
368 root->add_child("Container")->add_child_text (_container->id ());
371 root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
372 root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
373 _isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
374 root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
375 root->add_child("ISDCFDate")->add_child_text (boost::gregorian::to_iso_string (_isdcf_date));
376 root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
377 root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
378 root->add_child("Sequence")->add_child_text (_sequence ? "1" : "0");
379 root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
380 root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
381 root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
382 root->add_child("Key")->add_child_text (_key.hex ());
383 root->add_child("ContextID")->add_child_text (_context_id);
384 if (_audio_processor) {
385 root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
387 root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
388 root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
389 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
390 _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
395 /** Write state to our `metadata' file */
397 Film::write_metadata () const
399 DCPOMATIC_ASSERT (directory());
400 boost::filesystem::create_directories (directory().get());
401 shared_ptr<xmlpp::Document> doc = metadata ();
402 doc->write_to_file_formatted (file("metadata.xml").string ());
406 /** Write a template from this film */
408 Film::write_template (boost::filesystem::path path) const
410 boost::filesystem::create_directories (path.parent_path());
411 shared_ptr<xmlpp::Document> doc = metadata (false);
412 doc->write_to_file_formatted (path.string ());
415 /** Read state from our metadata file.
416 * @return Notes about things that the user should know about, or empty.
419 Film::read_metadata (optional<boost::filesystem::path> path)
422 if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
423 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!"));
426 path = file ("metadata.xml");
429 cxml::Document f ("Metadata");
430 f.read_file (path.get ());
432 _state_version = f.number_child<int> ("Version");
433 if (_state_version > current_state_version) {
434 throw runtime_error (_("This film was created with a newer version of DCP-o-matic, and it cannot be loaded into this version. Sorry!"));
437 _name = f.string_child ("Name");
438 if (_state_version >= 9) {
439 _use_isdcf_name = f.bool_child ("UseISDCFName");
440 _isdcf_metadata = ISDCFMetadata (f.node_child ("ISDCFMetadata"));
441 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("ISDCFDate"));
443 _use_isdcf_name = f.bool_child ("UseDCIName");
444 _isdcf_metadata = ISDCFMetadata (f.node_child ("DCIMetadata"));
445 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
449 optional<string> c = f.optional_string_child ("DCPContentType");
451 _dcp_content_type = DCPContentType::from_isdcf_name (c.get ());
456 optional<string> c = f.optional_string_child ("Container");
458 _container = Ratio::from_id (c.get ());
462 _resolution = string_to_resolution (f.string_child ("Resolution"));
463 _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
464 _video_frame_rate = f.number_child<int> ("VideoFrameRate");
465 _signed = f.optional_bool_child("Signed").get_value_or (true);
466 _encrypted = f.bool_child ("Encrypted");
467 _audio_channels = f.number_child<int> ("AudioChannels");
468 /* We used to allow odd numbers (and zero) channels, but it's just not worth
471 if (_audio_channels == 0) {
473 } else if ((_audio_channels % 2) == 1) {
477 if (f.optional_bool_child("SequenceVideo")) {
478 _sequence = f.bool_child("SequenceVideo");
480 _sequence = f.bool_child("Sequence");
483 _three_d = f.bool_child ("ThreeD");
484 _interop = f.bool_child ("Interop");
485 _key = dcp::Key (f.string_child ("Key"));
486 _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ());
488 if (f.optional_string_child ("AudioProcessor")) {
489 _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
491 _audio_processor = 0;
494 _reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
495 _reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
496 _upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
499 /* This method is the only one that can return notes (so far) */
500 _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
502 /* Write backtraces to this film's directory, until another film is loaded */
504 set_backtrace_file (file ("backtrace.txt"));
511 /** Given a directory name, return its full path within the Film's directory.
512 * @param d directory name within the Filn's directory.
513 * @param create true to create the directory (and its parents) if they do not exist.
515 boost::filesystem::path
516 Film::dir (boost::filesystem::path d, bool create) const
518 DCPOMATIC_ASSERT (_directory);
520 boost::filesystem::path p;
521 p /= _directory.get();
525 boost::filesystem::create_directories (p);
531 /** Given a file or directory name, return its full path within the Film's directory.
532 * Any required parent directories will be created.
534 boost::filesystem::path
535 Film::file (boost::filesystem::path f) const
537 DCPOMATIC_ASSERT (_directory);
539 boost::filesystem::path p;
540 p /= _directory.get();
543 boost::filesystem::create_directories (p.parent_path ());
549 Film::mapped_audio_channels () const
553 if (audio_processor ()) {
554 /* Processors are mapped 1:1 to DCP outputs so we can work out mappings from there */
555 for (int i = 0; i < audio_processor()->out_channels(); ++i) {
556 mapped.push_back (i);
559 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
561 list<int> c = i->audio->mapping().mapped_output_channels ();
562 copy (c.begin(), c.end(), back_inserter (mapped));
573 /** @return a ISDCF-compliant name for a DCP of this film */
575 Film::isdcf_name (bool if_created_now) const
579 string raw_name = name ();
581 /* Split the raw name up into words */
582 vector<string> words;
583 split (words, raw_name, is_any_of (" _-"));
587 /* Add each word to fixed_name */
588 for (vector<string>::const_iterator i = words.begin(); i != words.end(); ++i) {
591 /* First letter is always capitalised */
592 w[0] = toupper (w[0]);
594 /* Count caps in w */
596 for (size_t i = 0; i < w.size(); ++i) {
597 if (isupper (w[i])) {
602 /* If w is all caps make the rest of it lower case, otherwise
605 if (caps == w.size ()) {
606 for (size_t i = 1; i < w.size(); ++i) {
607 w[i] = tolower (w[i]);
611 for (size_t i = 0; i < w.size(); ++i) {
616 if (fixed_name.length() > 14) {
617 fixed_name = fixed_name.substr (0, 14);
622 if (dcp_content_type()) {
623 d += "_" + dcp_content_type()->isdcf_name();
624 d += "-" + raw_convert<string>(isdcf_metadata().content_version);
627 ISDCFMetadata const dm = isdcf_metadata ();
629 if (dm.temp_version) {
633 if (dm.pre_release) {
641 if (!dm.chain.empty ()) {
649 if (dm.two_d_version_of_three_d) {
653 if (!dm.mastered_luminance.empty ()) {
654 d += "-" + dm.mastered_luminance;
657 if (video_frame_rate() != 24) {
658 d += "-" + raw_convert<string>(video_frame_rate());
662 d += "_" + container()->isdcf_name();
665 /* XXX: this uses the first bit of content only */
667 /* The standard says we don't do this for trailers, for some strange reason */
668 if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
669 Ratio const * content_ratio = 0;
670 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
672 /* Here's the first piece of video content */
673 if (i->video->scale().ratio ()) {
674 content_ratio = i->video->scale().ratio ();
676 content_ratio = Ratio::from_ratio (i->video->size().ratio ());
682 if (content_ratio && content_ratio != container()) {
683 d += "-" + content_ratio->isdcf_name();
687 if (!dm.audio_language.empty ()) {
688 d += "_" + dm.audio_language;
689 if (!dm.subtitle_language.empty()) {
691 bool burnt_in = true;
692 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
697 if (i->subtitle->use() && !i->subtitle->burn()) {
702 string language = dm.subtitle_language;
703 if (burnt_in && language != "XX") {
704 transform (language.begin(), language.end(), language.begin(), ::tolower);
706 transform (language.begin(), language.end(), language.begin(), ::toupper);
715 if (!dm.territory.empty ()) {
716 d += "_" + dm.territory;
717 if (dm.rating.empty ()) {
720 d += "-" + dm.rating;
724 /* Count mapped audio channels */
729 BOOST_FOREACH (int i, mapped_audio_channels ()) {
730 if (i >= audio_channels()) {
731 /* This channel is mapped but is not included in the DCP */
735 if (static_cast<dcp::Channel> (i) == dcp::LFE) {
743 d += String::compose("_%1%2", non_lfe, lfe);
748 d += "_" + resolution_to_string (_resolution);
750 if (!dm.studio.empty ()) {
751 d += "_" + dm.studio;
754 if (if_created_now) {
755 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
757 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
760 if (!dm.facility.empty ()) {
761 d += "_" + dm.facility;
775 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
776 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
777 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
791 /** @return name to give the DCP */
793 Film::dcp_name (bool if_created_now) const
796 if (use_isdcf_name()) {
797 return careful_string_filter (isdcf_name (if_created_now));
800 return careful_string_filter (name ());
804 Film::set_directory (boost::filesystem::path d)
811 Film::set_name (string n)
814 signal_changed (NAME);
818 Film::set_use_isdcf_name (bool u)
821 signal_changed (USE_ISDCF_NAME);
825 Film::set_dcp_content_type (DCPContentType const * t)
827 _dcp_content_type = t;
828 signal_changed (DCP_CONTENT_TYPE);
832 Film::set_container (Ratio const * c)
835 signal_changed (CONTAINER);
839 Film::set_resolution (Resolution r)
842 signal_changed (RESOLUTION);
846 Film::set_j2k_bandwidth (int b)
849 signal_changed (J2K_BANDWIDTH);
853 Film::set_isdcf_metadata (ISDCFMetadata m)
856 signal_changed (ISDCF_METADATA);
860 Film::set_video_frame_rate (int f)
862 _video_frame_rate = f;
863 signal_changed (VIDEO_FRAME_RATE);
867 Film::set_audio_channels (int c)
870 signal_changed (AUDIO_CHANNELS);
874 Film::set_three_d (bool t)
877 signal_changed (THREE_D);
879 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
880 _isdcf_metadata.two_d_version_of_three_d = false;
881 signal_changed (ISDCF_METADATA);
886 Film::set_interop (bool i)
889 signal_changed (INTEROP);
893 Film::set_audio_processor (AudioProcessor const * processor)
895 _audio_processor = processor;
896 signal_changed (AUDIO_PROCESSOR);
897 signal_changed (AUDIO_CHANNELS);
901 Film::set_reel_type (ReelType t)
904 signal_changed (REEL_TYPE);
907 /** @param r Desired reel length in bytes */
909 Film::set_reel_length (int64_t r)
912 signal_changed (REEL_LENGTH);
916 Film::set_upload_after_make_dcp (bool u)
918 _upload_after_make_dcp = u;
919 signal_changed (UPLOAD_AFTER_MAKE_DCP);
923 Film::signal_changed (Property p)
929 set_video_frame_rate (_playlist->best_video_frame_rate ());
931 case Film::VIDEO_FRAME_RATE:
933 _playlist->maybe_sequence ();
939 emit (boost::bind (boost::ref (Changed), p));
943 Film::set_isdcf_date_today ()
945 _isdcf_date = boost::gregorian::day_clock::local_day ();
948 boost::filesystem::path
949 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
951 boost::filesystem::path p;
953 p /= video_identifier ();
956 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
959 if (eyes == EYES_LEFT) {
961 } else if (eyes == EYES_RIGHT) {
975 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
980 return vector<CPLSummary> ();
983 vector<CPLSummary> out;
985 boost::filesystem::path const dir = directory().get();
986 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
988 boost::filesystem::is_directory (*i) &&
989 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
995 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
998 i->path().leaf().string(),
999 dcp.cpls().front()->id(),
1000 dcp.cpls().front()->annotation_text(),
1001 dcp.cpls().front()->file().get()
1014 Film::set_signed (bool s)
1017 signal_changed (SIGNED);
1021 Film::set_encrypted (bool e)
1024 signal_changed (ENCRYPTED);
1028 Film::set_key (dcp::Key key)
1031 signal_changed (KEY);
1035 Film::content () const
1037 return _playlist->content ();
1041 Film::examine_and_add_content (shared_ptr<Content> c)
1043 if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
1044 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
1047 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1049 _job_connections.push_back (
1050 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
1053 JobManager::instance()->add (j);
1057 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
1059 shared_ptr<Job> job = j.lock ();
1060 if (!job || !job->finished_ok ()) {
1064 shared_ptr<Content> content = c.lock ();
1069 add_content (content);
1071 if (Config::instance()->automatic_audio_analysis() && content->audio) {
1072 shared_ptr<Playlist> playlist (new Playlist);
1073 playlist->add (content);
1074 boost::signals2::connection c;
1075 JobManager::instance()->analyse_audio (
1076 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1078 _audio_analysis_connections.push_back (c);
1083 Film::add_content (shared_ptr<Content> c)
1085 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1087 c->set_position (_playlist->video_end ());
1088 } else if (c->subtitle) {
1089 c->set_position (_playlist->subtitle_end ());
1092 if (_template_film) {
1093 /* Take settings from the first piece of content of c's type in _template */
1094 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1095 if (typeid(i.get()) == typeid(c.get())) {
1096 c->use_template (i);
1105 Film::remove_content (shared_ptr<Content> c)
1107 _playlist->remove (c);
1111 Film::move_content_earlier (shared_ptr<Content> c)
1113 _playlist->move_earlier (c);
1117 Film::move_content_later (shared_ptr<Content> c)
1119 _playlist->move_later (c);
1122 /** @return length of the film from time 0 to the last thing on the playlist */
1124 Film::length () const
1126 return _playlist->length ();
1130 Film::best_video_frame_rate () const
1132 return _playlist->best_video_frame_rate ();
1136 Film::active_frame_rate_change (DCPTime t) const
1138 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1142 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1146 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1147 set_video_frame_rate (_playlist->best_video_frame_rate ());
1148 } else if (p == AudioContentProperty::STREAMS) {
1149 signal_changed (NAME);
1152 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1156 Film::playlist_changed ()
1158 signal_changed (CONTENT);
1159 signal_changed (NAME);
1163 Film::playlist_order_changed ()
1165 signal_changed (CONTENT_ORDER);
1169 Film::audio_frame_rate () const
1171 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1172 if (i->audio && i->audio->has_rate_above_48k ()) {
1181 Film::set_sequence (bool s)
1184 _playlist->set_sequence (s);
1185 signal_changed (SEQUENCE);
1188 /** @return Size of the largest possible image in whatever resolution we are using */
1190 Film::full_frame () const
1192 switch (_resolution) {
1194 return dcp::Size (2048, 1080);
1196 return dcp::Size (4096, 2160);
1199 DCPOMATIC_ASSERT (false);
1200 return dcp::Size ();
1203 /** @return Size of the frame */
1205 Film::frame_size () const
1207 return fit_ratio_within (container()->ratio(), full_frame ());
1210 /** @param from KDM from time expressed as a local time with an offset from UTC
1211 * @param to KDM to time expressed as a local time with an offset from UTC
1215 dcp::Certificate recipient,
1216 vector<dcp::Certificate> trusted_devices,
1217 boost::filesystem::path cpl_file,
1218 dcp::LocalTime from,
1219 dcp::LocalTime until,
1220 dcp::Formulation formulation
1223 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1224 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1225 if (!signer->valid ()) {
1226 throw InvalidSignerError ();
1229 /* Find keys that have been added to imported, encrypted DCP content */
1230 list<dcp::DecryptedKDMKey> imported_keys;
1231 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1232 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1233 if (d && d->kdm()) {
1234 dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
1235 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
1236 copy (keys.begin(), keys.end(), back_inserter (imported_keys));
1240 map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
1242 BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
1243 shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
1244 if (!mxf || !mxf->key_id()) {
1248 /* Get any imported key for this ID */
1250 BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
1251 if (j.id() == mxf->key_id().get()) {
1252 LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
1253 keys[mxf] = j.key();
1259 /* No imported key; it must be an asset that we encrypted */
1260 LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
1265 return dcp::DecryptedKDM (
1266 cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1267 ).encrypt (signer, recipient, trusted_devices, formulation);
1270 /** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1271 * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1275 list<shared_ptr<Screen> > screens,
1276 boost::filesystem::path dcp,
1277 boost::posix_time::ptime from,
1278 boost::posix_time::ptime until,
1279 dcp::Formulation formulation
1282 list<ScreenKDM> kdms;
1284 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1286 dcp::EncryptedKDM const kdm = make_kdm (
1290 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1291 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1295 kdms.push_back (ScreenKDM (i, kdm));
1302 /** @return The approximate disk space required to encode a DCP of this film with the
1303 * current settings, in bytes.
1306 Film::required_disk_space () const
1308 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1311 /** This method checks the disk that the Film is on and tries to decide whether or not
1312 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1313 * false is returned and required and available are filled in with the amount of disk space
1314 * required and available respectively (in Gb).
1316 * Note: the decision made by this method isn't, of course, 100% reliable.
1319 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1321 /* Create a test file and see if we can hard-link it */
1322 boost::filesystem::path test = internal_video_asset_dir() / "test";
1323 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1324 can_hard_link = true;
1325 FILE* f = fopen_boost (test, "w");
1328 boost::system::error_code ec;
1329 boost::filesystem::create_hard_link (test, test2, ec);
1331 can_hard_link = false;
1333 boost::filesystem::remove (test);
1334 boost::filesystem::remove (test2);
1337 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1338 required = double (required_disk_space ()) / 1073741824.0f;
1339 if (!can_hard_link) {
1342 available = double (s.available) / 1073741824.0f;
1343 return (available - required) > 1;
1347 Film::subtitle_language () const
1349 set<string> languages;
1351 ContentList cl = content ();
1352 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1354 languages.insert (c->subtitle->language ());
1359 BOOST_FOREACH (string s, languages) {
1360 if (!all.empty ()) {
1370 /** Change the gains of the supplied AudioMapping to make it a default
1371 * for this film. The defaults are guessed based on what processor (if any)
1372 * is in use, the number of input channels and any filename supplied.
1375 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1377 static string const regex[] = {
1378 ".*[\\._-]L[\\._-].*",
1379 ".*[\\._-]R[\\._-].*",
1380 ".*[\\._-]C[\\._-].*",
1381 ".*[\\._-]Lfe[\\._-].*",
1382 ".*[\\._-]Ls[\\._-].*",
1383 ".*[\\._-]Rs[\\._-].*"
1386 static int const regexes = sizeof(regex) / sizeof(*regex);
1388 if (audio_processor ()) {
1389 audio_processor()->make_audio_mapping_default (mapping);
1391 mapping.make_zero ();
1392 if (mapping.input_channels() == 1) {
1393 bool guessed = false;
1395 /* See if we can guess where this stream should go */
1397 for (int i = 0; i < regexes; ++i) {
1398 boost::regex e (regex[i], boost::regex::icase);
1399 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1400 mapping.set (0, i, 1);
1407 /* If we have no idea, just put it on centre */
1408 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1412 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1413 mapping.set (i, i, 1);
1419 /** @return The names of the channels that audio contents' outputs are passed into;
1420 * this is either the DCP or a AudioProcessor.
1423 Film::audio_output_names () const
1425 if (audio_processor ()) {
1426 return audio_processor()->input_names ();
1429 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1433 for (int i = 0; i < audio_channels(); ++i) {
1434 n.push_back (short_audio_channel_name (i));
1441 Film::repeat_content (ContentList c, int n)
1443 _playlist->repeat (c, n);
1447 Film::remove_content (ContentList c)
1449 _playlist->remove (c);
1453 Film::audio_analysis_finished ()
1459 Film::reels () const
1461 list<DCPTimePeriod> p;
1462 DCPTime const len = length().ceil (video_frame_rate ());
1464 switch (reel_type ()) {
1465 case REELTYPE_SINGLE:
1466 p.push_back (DCPTimePeriod (DCPTime (), len));
1468 case REELTYPE_BY_VIDEO_CONTENT:
1470 optional<DCPTime> last_split;
1471 shared_ptr<Content> last_video;
1472 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1474 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1476 p.push_back (DCPTimePeriod (last_split.get(), t));
1484 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1486 /* Definitely go from the last split to the end of the video content */
1487 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1490 if (video_end < len) {
1491 /* And maybe go after that as well if there is any non-video hanging over the end */
1492 p.push_back (DCPTimePeriod (video_end, len));
1496 case REELTYPE_BY_LENGTH:
1499 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1500 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1501 while (current < len) {
1502 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1503 p.push_back (DCPTimePeriod (current, end));
1514 Film::content_summary (DCPTimePeriod period) const
1516 return _playlist->content_summary (period);
1520 Film::fix_conflicting_settings ()
1524 list<boost::filesystem::path> was_referencing;
1525 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1526 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1528 list<string> reasons;
1530 if (!d->can_reference_video(reasons) && d->reference_video()) {
1531 d->set_reference_video (false);
1534 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1535 d->set_reference_audio (false);
1538 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1539 d->set_reference_subtitle (false);
1543 was_referencing.push_back (d->path(0).parent_path().filename());
1548 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1549 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()));
1556 Film::use_template (string name)
1558 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1559 _template_film->read_metadata (Config::instance()->template_path (name));
1560 _use_isdcf_name = _template_film->_use_isdcf_name;
1561 _dcp_content_type = _template_film->_dcp_content_type;
1562 _container = _template_film->_container;
1563 _resolution = _template_film->_resolution;
1564 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1565 _video_frame_rate = _template_film->_video_frame_rate;
1566 _signed = _template_film->_signed;
1567 _encrypted = _template_film->_encrypted;
1568 _audio_channels = _template_film->_audio_channels;
1569 _sequence = _template_film->_sequence;
1570 _three_d = _template_film->_three_d;
1571 _interop = _template_film->_interop;
1572 _audio_processor = _template_film->_audio_processor;
1573 _reel_type = _template_film->_reel_type;
1574 _reel_length = _template_film->_reel_length;
1575 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;