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 * The directory (and its parents) will be created if they do not exist.
514 boost::filesystem::path
515 Film::dir (boost::filesystem::path d) const
517 DCPOMATIC_ASSERT (_directory);
519 boost::filesystem::path p;
520 p /= _directory.get();
523 boost::filesystem::create_directories (p);
528 /** Given a file or directory name, return its full path within the Film's directory.
529 * Any required parent directories will be created.
531 boost::filesystem::path
532 Film::file (boost::filesystem::path f) const
534 DCPOMATIC_ASSERT (_directory);
536 boost::filesystem::path p;
537 p /= _directory.get();
540 boost::filesystem::create_directories (p.parent_path ());
546 Film::mapped_audio_channels () const
550 if (audio_processor ()) {
551 /* Processors are mapped 1:1 to DCP outputs so we can work out mappings from there */
552 for (int i = 0; i < audio_processor()->out_channels(); ++i) {
553 mapped.push_back (i);
556 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
558 list<int> c = i->audio->mapping().mapped_output_channels ();
559 copy (c.begin(), c.end(), back_inserter (mapped));
570 /** @return a ISDCF-compliant name for a DCP of this film */
572 Film::isdcf_name (bool if_created_now) const
576 string raw_name = name ();
578 /* Split the raw name up into words */
579 vector<string> words;
580 split (words, raw_name, is_any_of (" _-"));
584 /* Add each word to fixed_name */
585 for (vector<string>::const_iterator i = words.begin(); i != words.end(); ++i) {
588 /* First letter is always capitalised */
589 w[0] = toupper (w[0]);
591 /* Count caps in w */
593 for (size_t i = 0; i < w.size(); ++i) {
594 if (isupper (w[i])) {
599 /* If w is all caps make the rest of it lower case, otherwise
602 if (caps == w.size ()) {
603 for (size_t i = 1; i < w.size(); ++i) {
604 w[i] = tolower (w[i]);
608 for (size_t i = 0; i < w.size(); ++i) {
613 if (fixed_name.length() > 14) {
614 fixed_name = fixed_name.substr (0, 14);
619 if (dcp_content_type()) {
620 d += "_" + dcp_content_type()->isdcf_name();
621 d += "-" + raw_convert<string>(isdcf_metadata().content_version);
624 ISDCFMetadata const dm = isdcf_metadata ();
626 if (dm.temp_version) {
630 if (dm.pre_release) {
638 if (!dm.chain.empty ()) {
646 if (dm.two_d_version_of_three_d) {
650 if (!dm.mastered_luminance.empty ()) {
651 d += "-" + dm.mastered_luminance;
654 if (video_frame_rate() != 24) {
655 d += "-" + raw_convert<string>(video_frame_rate());
659 d += "_" + container()->isdcf_name();
662 /* XXX: this uses the first bit of content only */
664 /* The standard says we don't do this for trailers, for some strange reason */
665 if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
666 Ratio const * content_ratio = 0;
667 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
669 /* Here's the first piece of video content */
670 if (i->video->scale().ratio ()) {
671 content_ratio = i->video->scale().ratio ();
673 content_ratio = Ratio::from_ratio (i->video->size().ratio ());
679 if (content_ratio && content_ratio != container()) {
680 d += "-" + content_ratio->isdcf_name();
684 if (!dm.audio_language.empty ()) {
685 d += "_" + dm.audio_language;
686 if (!dm.subtitle_language.empty()) {
688 bool burnt_in = true;
689 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
694 if (i->subtitle->use() && !i->subtitle->burn()) {
699 string language = dm.subtitle_language;
700 if (burnt_in && language != "XX") {
701 transform (language.begin(), language.end(), language.begin(), ::tolower);
703 transform (language.begin(), language.end(), language.begin(), ::toupper);
712 if (!dm.territory.empty ()) {
713 d += "_" + dm.territory;
714 if (dm.rating.empty ()) {
717 d += "-" + dm.rating;
721 /* Count mapped audio channels */
726 BOOST_FOREACH (int i, mapped_audio_channels ()) {
727 if (i >= audio_channels()) {
728 /* This channel is mapped but is not included in the DCP */
732 if (static_cast<dcp::Channel> (i) == dcp::LFE) {
740 d += String::compose("_%1%2", non_lfe, lfe);
745 d += "_" + resolution_to_string (_resolution);
747 if (!dm.studio.empty ()) {
748 d += "_" + dm.studio;
751 if (if_created_now) {
752 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
754 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
757 if (!dm.facility.empty ()) {
758 d += "_" + dm.facility;
772 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
773 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
774 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
788 /** @return name to give the DCP */
790 Film::dcp_name (bool if_created_now) const
793 if (use_isdcf_name()) {
794 return careful_string_filter (isdcf_name (if_created_now));
797 return careful_string_filter (name ());
801 Film::set_directory (boost::filesystem::path d)
808 Film::set_name (string n)
811 signal_changed (NAME);
815 Film::set_use_isdcf_name (bool u)
818 signal_changed (USE_ISDCF_NAME);
822 Film::set_dcp_content_type (DCPContentType const * t)
824 _dcp_content_type = t;
825 signal_changed (DCP_CONTENT_TYPE);
829 Film::set_container (Ratio const * c)
832 signal_changed (CONTAINER);
836 Film::set_resolution (Resolution r)
839 signal_changed (RESOLUTION);
843 Film::set_j2k_bandwidth (int b)
846 signal_changed (J2K_BANDWIDTH);
850 Film::set_isdcf_metadata (ISDCFMetadata m)
853 signal_changed (ISDCF_METADATA);
857 Film::set_video_frame_rate (int f)
859 _video_frame_rate = f;
860 signal_changed (VIDEO_FRAME_RATE);
864 Film::set_audio_channels (int c)
867 signal_changed (AUDIO_CHANNELS);
871 Film::set_three_d (bool t)
874 signal_changed (THREE_D);
876 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
877 _isdcf_metadata.two_d_version_of_three_d = false;
878 signal_changed (ISDCF_METADATA);
883 Film::set_interop (bool i)
886 signal_changed (INTEROP);
890 Film::set_audio_processor (AudioProcessor const * processor)
892 _audio_processor = processor;
893 signal_changed (AUDIO_PROCESSOR);
894 signal_changed (AUDIO_CHANNELS);
898 Film::set_reel_type (ReelType t)
901 signal_changed (REEL_TYPE);
904 /** @param r Desired reel length in bytes */
906 Film::set_reel_length (int64_t r)
909 signal_changed (REEL_LENGTH);
913 Film::set_upload_after_make_dcp (bool u)
915 _upload_after_make_dcp = u;
916 signal_changed (UPLOAD_AFTER_MAKE_DCP);
920 Film::signal_changed (Property p)
926 set_video_frame_rate (_playlist->best_video_frame_rate ());
928 case Film::VIDEO_FRAME_RATE:
930 _playlist->maybe_sequence ();
936 emit (boost::bind (boost::ref (Changed), p));
940 Film::set_isdcf_date_today ()
942 _isdcf_date = boost::gregorian::day_clock::local_day ();
945 boost::filesystem::path
946 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
948 boost::filesystem::path p;
950 p /= video_identifier ();
953 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
956 if (eyes == EYES_LEFT) {
958 } else if (eyes == EYES_RIGHT) {
972 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
977 return vector<CPLSummary> ();
980 vector<CPLSummary> out;
982 boost::filesystem::path const dir = directory().get();
983 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
985 boost::filesystem::is_directory (*i) &&
986 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
992 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
995 i->path().leaf().string(),
996 dcp.cpls().front()->id(),
997 dcp.cpls().front()->annotation_text(),
998 dcp.cpls().front()->file().get()
1011 Film::set_signed (bool s)
1014 signal_changed (SIGNED);
1018 Film::set_encrypted (bool e)
1021 signal_changed (ENCRYPTED);
1025 Film::set_key (dcp::Key key)
1028 signal_changed (KEY);
1032 Film::content () const
1034 return _playlist->content ();
1038 Film::examine_and_add_content (shared_ptr<Content> c)
1040 if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
1041 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
1044 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1046 _job_connections.push_back (
1047 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
1050 JobManager::instance()->add (j);
1054 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
1056 shared_ptr<Job> job = j.lock ();
1057 if (!job || !job->finished_ok ()) {
1061 shared_ptr<Content> content = c.lock ();
1066 add_content (content);
1068 if (Config::instance()->automatic_audio_analysis() && content->audio) {
1069 shared_ptr<Playlist> playlist (new Playlist);
1070 playlist->add (content);
1071 boost::signals2::connection c;
1072 JobManager::instance()->analyse_audio (
1073 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1075 _audio_analysis_connections.push_back (c);
1080 Film::add_content (shared_ptr<Content> c)
1082 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1084 c->set_position (_playlist->video_end ());
1085 } else if (c->subtitle) {
1086 c->set_position (_playlist->subtitle_end ());
1089 if (_template_film) {
1090 /* Take settings from the first piece of content of c's type in _template */
1091 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1092 if (typeid(i.get()) == typeid(c.get())) {
1093 c->use_template (i);
1102 Film::remove_content (shared_ptr<Content> c)
1104 _playlist->remove (c);
1108 Film::move_content_earlier (shared_ptr<Content> c)
1110 _playlist->move_earlier (c);
1114 Film::move_content_later (shared_ptr<Content> c)
1116 _playlist->move_later (c);
1119 /** @return length of the film from time 0 to the last thing on the playlist */
1121 Film::length () const
1123 return _playlist->length ();
1127 Film::best_video_frame_rate () const
1129 return _playlist->best_video_frame_rate ();
1133 Film::active_frame_rate_change (DCPTime t) const
1135 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1139 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1143 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1144 set_video_frame_rate (_playlist->best_video_frame_rate ());
1145 } else if (p == AudioContentProperty::STREAMS) {
1146 signal_changed (NAME);
1149 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1153 Film::playlist_changed ()
1155 signal_changed (CONTENT);
1156 signal_changed (NAME);
1160 Film::playlist_order_changed ()
1162 signal_changed (CONTENT_ORDER);
1166 Film::audio_frame_rate () const
1168 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1169 if (i->audio && i->audio->has_rate_above_48k ()) {
1178 Film::set_sequence (bool s)
1181 _playlist->set_sequence (s);
1182 signal_changed (SEQUENCE);
1185 /** @return Size of the largest possible image in whatever resolution we are using */
1187 Film::full_frame () const
1189 switch (_resolution) {
1191 return dcp::Size (2048, 1080);
1193 return dcp::Size (4096, 2160);
1196 DCPOMATIC_ASSERT (false);
1197 return dcp::Size ();
1200 /** @return Size of the frame */
1202 Film::frame_size () const
1204 return fit_ratio_within (container()->ratio(), full_frame ());
1207 /** @param from KDM from time expressed as a local time with an offset from UTC
1208 * @param to KDM to time expressed as a local time with an offset from UTC
1212 dcp::Certificate recipient,
1213 vector<dcp::Certificate> trusted_devices,
1214 boost::filesystem::path cpl_file,
1215 dcp::LocalTime from,
1216 dcp::LocalTime until,
1217 dcp::Formulation formulation
1220 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1221 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1222 if (!signer->valid ()) {
1223 throw InvalidSignerError ();
1226 /* Find keys that have been added to imported, encrypted DCP content */
1227 list<dcp::DecryptedKDMKey> imported_keys;
1228 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1229 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1230 if (d && d->kdm()) {
1231 dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
1232 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
1233 copy (keys.begin(), keys.end(), back_inserter (imported_keys));
1237 map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
1239 BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
1240 shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
1241 if (!mxf || !mxf->key_id()) {
1245 /* Get any imported key for this ID */
1247 BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
1248 if (j.id() == mxf->key_id().get()) {
1249 LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
1250 keys[mxf] = j.key();
1256 /* No imported key; it must be an asset that we encrypted */
1257 LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
1262 return dcp::DecryptedKDM (
1263 cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1264 ).encrypt (signer, recipient, trusted_devices, formulation);
1267 /** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1268 * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1272 list<shared_ptr<Screen> > screens,
1273 boost::filesystem::path dcp,
1274 boost::posix_time::ptime from,
1275 boost::posix_time::ptime until,
1276 dcp::Formulation formulation
1279 list<ScreenKDM> kdms;
1281 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1283 dcp::EncryptedKDM const kdm = make_kdm (
1287 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1288 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1292 kdms.push_back (ScreenKDM (i, kdm));
1299 /** @return The approximate disk space required to encode a DCP of this film with the
1300 * current settings, in bytes.
1303 Film::required_disk_space () const
1305 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1308 /** This method checks the disk that the Film is on and tries to decide whether or not
1309 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1310 * false is returned and required and available are filled in with the amount of disk space
1311 * required and available respectively (in Gb).
1313 * Note: the decision made by this method isn't, of course, 100% reliable.
1316 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1318 /* Create a test file and see if we can hard-link it */
1319 boost::filesystem::path test = internal_video_asset_dir() / "test";
1320 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1321 can_hard_link = true;
1322 FILE* f = fopen_boost (test, "w");
1325 boost::system::error_code ec;
1326 boost::filesystem::create_hard_link (test, test2, ec);
1328 can_hard_link = false;
1330 boost::filesystem::remove (test);
1331 boost::filesystem::remove (test2);
1334 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1335 required = double (required_disk_space ()) / 1073741824.0f;
1336 if (!can_hard_link) {
1339 available = double (s.available) / 1073741824.0f;
1340 return (available - required) > 1;
1344 Film::subtitle_language () const
1346 set<string> languages;
1348 ContentList cl = content ();
1349 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1351 languages.insert (c->subtitle->language ());
1356 BOOST_FOREACH (string s, languages) {
1357 if (!all.empty ()) {
1367 /** Change the gains of the supplied AudioMapping to make it a default
1368 * for this film. The defaults are guessed based on what processor (if any)
1369 * is in use, the number of input channels and any filename supplied.
1372 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1374 static string const regex[] = {
1375 ".*[\\._-]L[\\._-].*",
1376 ".*[\\._-]R[\\._-].*",
1377 ".*[\\._-]C[\\._-].*",
1378 ".*[\\._-]Lfe[\\._-].*",
1379 ".*[\\._-]Ls[\\._-].*",
1380 ".*[\\._-]Rs[\\._-].*"
1383 static int const regexes = sizeof(regex) / sizeof(*regex);
1385 if (audio_processor ()) {
1386 audio_processor()->make_audio_mapping_default (mapping);
1388 mapping.make_zero ();
1389 if (mapping.input_channels() == 1) {
1390 bool guessed = false;
1392 /* See if we can guess where this stream should go */
1394 for (int i = 0; i < regexes; ++i) {
1395 boost::regex e (regex[i], boost::regex::icase);
1396 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1397 mapping.set (0, i, 1);
1404 /* If we have no idea, just put it on centre */
1405 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1409 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1410 mapping.set (i, i, 1);
1416 /** @return The names of the channels that audio contents' outputs are passed into;
1417 * this is either the DCP or a AudioProcessor.
1420 Film::audio_output_names () const
1422 if (audio_processor ()) {
1423 return audio_processor()->input_names ();
1426 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1430 for (int i = 0; i < audio_channels(); ++i) {
1431 n.push_back (short_audio_channel_name (i));
1438 Film::repeat_content (ContentList c, int n)
1440 _playlist->repeat (c, n);
1444 Film::remove_content (ContentList c)
1446 _playlist->remove (c);
1450 Film::audio_analysis_finished ()
1456 Film::reels () const
1458 list<DCPTimePeriod> p;
1459 DCPTime const len = length().ceil (video_frame_rate ());
1461 switch (reel_type ()) {
1462 case REELTYPE_SINGLE:
1463 p.push_back (DCPTimePeriod (DCPTime (), len));
1465 case REELTYPE_BY_VIDEO_CONTENT:
1467 optional<DCPTime> last_split;
1468 shared_ptr<Content> last_video;
1469 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1471 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1473 p.push_back (DCPTimePeriod (last_split.get(), t));
1481 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1483 /* Definitely go from the last split to the end of the video content */
1484 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1487 if (video_end < len) {
1488 /* And maybe go after that as well if there is any non-video hanging over the end */
1489 p.push_back (DCPTimePeriod (video_end, len));
1493 case REELTYPE_BY_LENGTH:
1496 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1497 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1498 while (current < len) {
1499 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1500 p.push_back (DCPTimePeriod (current, end));
1511 Film::content_summary (DCPTimePeriod period) const
1513 return _playlist->content_summary (period);
1517 Film::fix_conflicting_settings ()
1521 list<boost::filesystem::path> was_referencing;
1522 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1523 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1525 list<string> reasons;
1527 if (!d->can_reference_video(reasons) && d->reference_video()) {
1528 d->set_reference_video (false);
1531 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1532 d->set_reference_audio (false);
1535 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1536 d->set_reference_subtitle (false);
1540 was_referencing.push_back (d->path(0).parent_path().filename());
1545 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1546 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()));
1553 Film::use_template (string name)
1555 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1556 _template_film->read_metadata (Config::instance()->template_path (name));
1557 _use_isdcf_name = _template_film->_use_isdcf_name;
1558 _dcp_content_type = _template_film->_dcp_content_type;
1559 _container = _template_film->_container;
1560 _resolution = _template_film->_resolution;
1561 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1562 _video_frame_rate = _template_film->_video_frame_rate;
1563 _signed = _template_film->_signed;
1564 _encrypted = _template_film->_encrypted;
1565 _audio_channels = _template_film->_audio_channels;
1566 _sequence = _template_film->_sequence;
1567 _three_d = _template_film->_three_d;
1568 _interop = _template_film->_interop;
1569 _audio_processor = _template_film->_audio_processor;
1570 _reel_type = _template_film->_reel_type;
1571 _reel_length = _template_film->_reel_length;
1572 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;