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 recipient KDM recipient certificate.
1211 * @param trusted_devices Certificates of other trusted devices (can be empty).
1212 * @param cpl_file CPL filename.
1213 * @param from KDM from time expressed as a local time with an offset from UTC.
1214 * @param until KDM to time expressed as a local time with an offset from UTC.
1215 * @param formulation KDM formulation to use.
1219 dcp::Certificate recipient,
1220 vector<dcp::Certificate> trusted_devices,
1221 boost::filesystem::path cpl_file,
1222 dcp::LocalTime from,
1223 dcp::LocalTime until,
1224 dcp::Formulation formulation
1227 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1228 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1229 if (!signer->valid ()) {
1230 throw InvalidSignerError ();
1233 /* Find keys that have been added to imported, encrypted DCP content */
1234 list<dcp::DecryptedKDMKey> imported_keys;
1235 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1236 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1237 if (d && d->kdm()) {
1238 dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
1239 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
1240 copy (keys.begin(), keys.end(), back_inserter (imported_keys));
1244 map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
1246 BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
1247 shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
1248 if (!mxf || !mxf->key_id()) {
1252 /* Get any imported key for this ID */
1254 BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
1255 if (j.id() == mxf->key_id().get()) {
1256 LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
1257 keys[mxf] = j.key();
1263 /* No imported key; it must be an asset that we encrypted */
1264 LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
1269 return dcp::DecryptedKDM (
1270 cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1271 ).encrypt (signer, recipient, trusted_devices, formulation);
1274 /** @param screens Screens to make KDMs for.
1275 * @param cpl_file Path to CPL to make KDMs for.
1276 * @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1277 * @param until KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1278 * @param formulation KDM formulation to use.
1282 list<shared_ptr<Screen> > screens,
1283 boost::filesystem::path cpl_file,
1284 boost::posix_time::ptime from,
1285 boost::posix_time::ptime until,
1286 dcp::Formulation formulation
1289 list<ScreenKDM> kdms;
1291 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1293 dcp::EncryptedKDM const kdm = make_kdm (
1297 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1298 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1302 kdms.push_back (ScreenKDM (i, kdm));
1309 /** @return The approximate disk space required to encode a DCP of this film with the
1310 * current settings, in bytes.
1313 Film::required_disk_space () const
1315 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1318 /** This method checks the disk that the Film is on and tries to decide whether or not
1319 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1320 * false is returned and required and available are filled in with the amount of disk space
1321 * required and available respectively (in Gb).
1323 * Note: the decision made by this method isn't, of course, 100% reliable.
1326 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1328 /* Create a test file and see if we can hard-link it */
1329 boost::filesystem::path test = internal_video_asset_dir() / "test";
1330 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1331 can_hard_link = true;
1332 FILE* f = fopen_boost (test, "w");
1335 boost::system::error_code ec;
1336 boost::filesystem::create_hard_link (test, test2, ec);
1338 can_hard_link = false;
1340 boost::filesystem::remove (test);
1341 boost::filesystem::remove (test2);
1344 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1345 required = double (required_disk_space ()) / 1073741824.0f;
1346 if (!can_hard_link) {
1349 available = double (s.available) / 1073741824.0f;
1350 return (available - required) > 1;
1354 Film::subtitle_language () const
1356 set<string> languages;
1358 ContentList cl = content ();
1359 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1361 languages.insert (c->subtitle->language ());
1366 BOOST_FOREACH (string s, languages) {
1367 if (!all.empty ()) {
1377 /** Change the gains of the supplied AudioMapping to make it a default
1378 * for this film. The defaults are guessed based on what processor (if any)
1379 * is in use, the number of input channels and any filename supplied.
1382 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1384 static string const regex[] = {
1385 ".*[\\._-]L[\\._-].*",
1386 ".*[\\._-]R[\\._-].*",
1387 ".*[\\._-]C[\\._-].*",
1388 ".*[\\._-]Lfe[\\._-].*",
1389 ".*[\\._-]Ls[\\._-].*",
1390 ".*[\\._-]Rs[\\._-].*"
1393 static int const regexes = sizeof(regex) / sizeof(*regex);
1395 if (audio_processor ()) {
1396 audio_processor()->make_audio_mapping_default (mapping);
1398 mapping.make_zero ();
1399 if (mapping.input_channels() == 1) {
1400 bool guessed = false;
1402 /* See if we can guess where this stream should go */
1404 for (int i = 0; i < regexes; ++i) {
1405 boost::regex e (regex[i], boost::regex::icase);
1406 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1407 mapping.set (0, i, 1);
1414 /* If we have no idea, just put it on centre */
1415 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1419 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1420 mapping.set (i, i, 1);
1426 /** @return The names of the channels that audio contents' outputs are passed into;
1427 * this is either the DCP or a AudioProcessor.
1430 Film::audio_output_names () const
1432 if (audio_processor ()) {
1433 return audio_processor()->input_names ();
1436 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1440 for (int i = 0; i < audio_channels(); ++i) {
1441 n.push_back (short_audio_channel_name (i));
1448 Film::repeat_content (ContentList c, int n)
1450 _playlist->repeat (c, n);
1454 Film::remove_content (ContentList c)
1456 _playlist->remove (c);
1460 Film::audio_analysis_finished ()
1466 Film::reels () const
1468 list<DCPTimePeriod> p;
1469 DCPTime const len = length().ceil (video_frame_rate ());
1471 switch (reel_type ()) {
1472 case REELTYPE_SINGLE:
1473 p.push_back (DCPTimePeriod (DCPTime (), len));
1475 case REELTYPE_BY_VIDEO_CONTENT:
1477 optional<DCPTime> last_split;
1478 shared_ptr<Content> last_video;
1479 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1481 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1483 p.push_back (DCPTimePeriod (last_split.get(), t));
1491 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1493 /* Definitely go from the last split to the end of the video content */
1494 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1497 if (video_end < len) {
1498 /* And maybe go after that as well if there is any non-video hanging over the end */
1499 p.push_back (DCPTimePeriod (video_end, len));
1503 case REELTYPE_BY_LENGTH:
1506 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1507 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1508 while (current < len) {
1509 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1510 p.push_back (DCPTimePeriod (current, end));
1520 /** @param period A period within the DCP
1521 * @return Name of the content which most contributes to the given period.
1524 Film::content_summary (DCPTimePeriod period) const
1526 return _playlist->content_summary (period);
1530 Film::fix_conflicting_settings ()
1534 list<boost::filesystem::path> was_referencing;
1535 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1536 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1538 list<string> reasons;
1540 if (!d->can_reference_video(reasons) && d->reference_video()) {
1541 d->set_reference_video (false);
1544 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1545 d->set_reference_audio (false);
1548 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1549 d->set_reference_subtitle (false);
1553 was_referencing.push_back (d->path(0).parent_path().filename());
1558 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1559 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()));
1566 Film::use_template (string name)
1568 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1569 _template_film->read_metadata (Config::instance()->template_path (name));
1570 _use_isdcf_name = _template_film->_use_isdcf_name;
1571 _dcp_content_type = _template_film->_dcp_content_type;
1572 _container = _template_film->_container;
1573 _resolution = _template_film->_resolution;
1574 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1575 _video_frame_rate = _template_film->_video_frame_rate;
1576 _signed = _template_film->_signed;
1577 _encrypted = _template_film->_encrypted;
1578 _audio_channels = _template_film->_audio_channels;
1579 _sequence = _template_film->_sequence;
1580 _three_d = _template_film->_three_d;
1581 _interop = _template_film->_interop;
1582 _audio_processor = _template_film->_audio_processor;
1583 _reel_type = _template_film->_reel_type;
1584 _reel_length = _template_film->_reel_length;
1585 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;