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()->master_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 */
726 pair<int, int> ch = audio_channel_types (mapped_audio_channels(), audio_channels());
728 d += String::compose("_%1%2", ch.first, ch.second);
733 d += "_" + resolution_to_string (_resolution);
735 if (!dm.studio.empty ()) {
736 d += "_" + dm.studio;
739 if (if_created_now) {
740 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
742 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
745 if (!dm.facility.empty ()) {
746 d += "_" + dm.facility;
760 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
761 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
762 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
776 /** @return name to give the DCP */
778 Film::dcp_name (bool if_created_now) const
781 if (use_isdcf_name()) {
782 return careful_string_filter (isdcf_name (if_created_now));
785 return careful_string_filter (name ());
789 Film::set_directory (boost::filesystem::path d)
796 Film::set_name (string n)
799 signal_changed (NAME);
803 Film::set_use_isdcf_name (bool u)
806 signal_changed (USE_ISDCF_NAME);
810 Film::set_dcp_content_type (DCPContentType const * t)
812 _dcp_content_type = t;
813 signal_changed (DCP_CONTENT_TYPE);
817 Film::set_container (Ratio const * c)
820 signal_changed (CONTAINER);
824 Film::set_resolution (Resolution r)
827 signal_changed (RESOLUTION);
831 Film::set_j2k_bandwidth (int b)
834 signal_changed (J2K_BANDWIDTH);
838 Film::set_isdcf_metadata (ISDCFMetadata m)
841 signal_changed (ISDCF_METADATA);
845 Film::set_video_frame_rate (int f)
847 _video_frame_rate = f;
848 signal_changed (VIDEO_FRAME_RATE);
852 Film::set_audio_channels (int c)
855 signal_changed (AUDIO_CHANNELS);
859 Film::set_three_d (bool t)
862 signal_changed (THREE_D);
864 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
865 _isdcf_metadata.two_d_version_of_three_d = false;
866 signal_changed (ISDCF_METADATA);
871 Film::set_interop (bool i)
874 signal_changed (INTEROP);
878 Film::set_audio_processor (AudioProcessor const * processor)
880 _audio_processor = processor;
881 signal_changed (AUDIO_PROCESSOR);
882 signal_changed (AUDIO_CHANNELS);
886 Film::set_reel_type (ReelType t)
889 signal_changed (REEL_TYPE);
892 /** @param r Desired reel length in bytes */
894 Film::set_reel_length (int64_t r)
897 signal_changed (REEL_LENGTH);
901 Film::set_upload_after_make_dcp (bool u)
903 _upload_after_make_dcp = u;
904 signal_changed (UPLOAD_AFTER_MAKE_DCP);
908 Film::signal_changed (Property p)
914 set_video_frame_rate (_playlist->best_video_frame_rate ());
916 case Film::VIDEO_FRAME_RATE:
918 _playlist->maybe_sequence ();
924 emit (boost::bind (boost::ref (Changed), p));
928 Film::set_isdcf_date_today ()
930 _isdcf_date = boost::gregorian::day_clock::local_day ();
933 boost::filesystem::path
934 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
936 boost::filesystem::path p;
938 p /= video_identifier ();
941 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
944 if (eyes == EYES_LEFT) {
946 } else if (eyes == EYES_RIGHT) {
960 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
965 return vector<CPLSummary> ();
968 vector<CPLSummary> out;
970 boost::filesystem::path const dir = directory().get();
971 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
973 boost::filesystem::is_directory (*i) &&
974 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
980 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
983 i->path().leaf().string(),
984 dcp.cpls().front()->id(),
985 dcp.cpls().front()->annotation_text(),
986 dcp.cpls().front()->file().get()
999 Film::set_signed (bool s)
1002 signal_changed (SIGNED);
1006 Film::set_encrypted (bool e)
1009 signal_changed (ENCRYPTED);
1013 Film::set_key (dcp::Key key)
1016 signal_changed (KEY);
1020 Film::content () const
1022 return _playlist->content ();
1026 Film::examine_and_add_content (shared_ptr<Content> c)
1028 if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
1029 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
1032 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1034 _job_connections.push_back (
1035 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
1038 JobManager::instance()->add (j);
1042 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
1044 shared_ptr<Job> job = j.lock ();
1045 if (!job || !job->finished_ok ()) {
1049 shared_ptr<Content> content = c.lock ();
1054 add_content (content);
1056 if (Config::instance()->automatic_audio_analysis() && content->audio) {
1057 shared_ptr<Playlist> playlist (new Playlist);
1058 playlist->add (content);
1059 boost::signals2::connection c;
1060 JobManager::instance()->analyse_audio (
1061 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1063 _audio_analysis_connections.push_back (c);
1068 Film::add_content (shared_ptr<Content> c)
1070 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1072 c->set_position (_playlist->video_end ());
1073 } else if (c->subtitle) {
1074 c->set_position (_playlist->subtitle_end ());
1077 if (_template_film) {
1078 /* Take settings from the first piece of content of c's type in _template */
1079 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1080 if (typeid(i.get()) == typeid(c.get())) {
1081 c->use_template (i);
1090 Film::remove_content (shared_ptr<Content> c)
1092 _playlist->remove (c);
1096 Film::move_content_earlier (shared_ptr<Content> c)
1098 _playlist->move_earlier (c);
1102 Film::move_content_later (shared_ptr<Content> c)
1104 _playlist->move_later (c);
1107 /** @return length of the film from time 0 to the last thing on the playlist */
1109 Film::length () const
1111 return _playlist->length ();
1115 Film::best_video_frame_rate () const
1117 return _playlist->best_video_frame_rate ();
1121 Film::active_frame_rate_change (DCPTime t) const
1123 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1127 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1131 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1132 set_video_frame_rate (_playlist->best_video_frame_rate ());
1133 } else if (p == AudioContentProperty::STREAMS) {
1134 signal_changed (NAME);
1137 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1141 Film::playlist_changed ()
1143 signal_changed (CONTENT);
1144 signal_changed (NAME);
1148 Film::playlist_order_changed ()
1150 signal_changed (CONTENT_ORDER);
1154 Film::audio_frame_rate () const
1156 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1157 if (i->audio && i->audio->has_rate_above_48k ()) {
1166 Film::set_sequence (bool s)
1169 _playlist->set_sequence (s);
1170 signal_changed (SEQUENCE);
1173 /** @return Size of the largest possible image in whatever resolution we are using */
1175 Film::full_frame () const
1177 switch (_resolution) {
1179 return dcp::Size (2048, 1080);
1181 return dcp::Size (4096, 2160);
1184 DCPOMATIC_ASSERT (false);
1185 return dcp::Size ();
1188 /** @return Size of the frame */
1190 Film::frame_size () const
1192 return fit_ratio_within (container()->ratio(), full_frame ());
1195 /** @param recipient KDM recipient certificate.
1196 * @param trusted_devices Certificates of other trusted devices (can be empty).
1197 * @param cpl_file CPL filename.
1198 * @param from KDM from time expressed as a local time with an offset from UTC.
1199 * @param until KDM to time expressed as a local time with an offset from UTC.
1200 * @param formulation KDM formulation to use.
1204 dcp::Certificate recipient,
1205 vector<dcp::Certificate> trusted_devices,
1206 boost::filesystem::path cpl_file,
1207 dcp::LocalTime from,
1208 dcp::LocalTime until,
1209 dcp::Formulation formulation
1212 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1213 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1214 if (!signer->valid ()) {
1215 throw InvalidSignerError ();
1218 /* Find keys that have been added to imported, encrypted DCP content */
1219 list<dcp::DecryptedKDMKey> imported_keys;
1220 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1221 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1222 if (d && d->kdm()) {
1223 dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
1224 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
1225 copy (keys.begin(), keys.end(), back_inserter (imported_keys));
1229 map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
1231 BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
1232 shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
1233 if (!mxf || !mxf->key_id()) {
1237 /* Get any imported key for this ID */
1239 BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
1240 if (j.id() == mxf->key_id().get()) {
1241 LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
1242 keys[mxf] = j.key();
1248 /* No imported key; it must be an asset that we encrypted */
1249 LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
1254 return dcp::DecryptedKDM (
1255 cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1256 ).encrypt (signer, recipient, trusted_devices, formulation);
1259 /** @param screens Screens to make KDMs for.
1260 * @param cpl_file Path to CPL to make KDMs for.
1261 * @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1262 * @param until KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1263 * @param formulation KDM formulation to use.
1267 list<shared_ptr<Screen> > screens,
1268 boost::filesystem::path cpl_file,
1269 boost::posix_time::ptime from,
1270 boost::posix_time::ptime until,
1271 dcp::Formulation formulation
1274 list<ScreenKDM> kdms;
1276 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1278 dcp::EncryptedKDM const kdm = make_kdm (
1282 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1283 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1287 kdms.push_back (ScreenKDM (i, kdm));
1294 /** @return The approximate disk space required to encode a DCP of this film with the
1295 * current settings, in bytes.
1298 Film::required_disk_space () const
1300 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1303 /** This method checks the disk that the Film is on and tries to decide whether or not
1304 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1305 * false is returned and required and available are filled in with the amount of disk space
1306 * required and available respectively (in Gb).
1308 * Note: the decision made by this method isn't, of course, 100% reliable.
1311 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1313 /* Create a test file and see if we can hard-link it */
1314 boost::filesystem::path test = internal_video_asset_dir() / "test";
1315 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1316 can_hard_link = true;
1317 FILE* f = fopen_boost (test, "w");
1320 boost::system::error_code ec;
1321 boost::filesystem::create_hard_link (test, test2, ec);
1323 can_hard_link = false;
1325 boost::filesystem::remove (test);
1326 boost::filesystem::remove (test2);
1329 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1330 required = double (required_disk_space ()) / 1073741824.0f;
1331 if (!can_hard_link) {
1334 available = double (s.available) / 1073741824.0f;
1335 return (available - required) > 1;
1339 Film::subtitle_language () const
1341 set<string> languages;
1343 ContentList cl = content ();
1344 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1346 languages.insert (c->subtitle->language ());
1351 BOOST_FOREACH (string s, languages) {
1352 if (!all.empty ()) {
1362 /** Change the gains of the supplied AudioMapping to make it a default
1363 * for this film. The defaults are guessed based on what processor (if any)
1364 * is in use, the number of input channels and any filename supplied.
1367 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1369 static string const regex[] = {
1370 ".*[\\._-]L[\\._-].*",
1371 ".*[\\._-]R[\\._-].*",
1372 ".*[\\._-]C[\\._-].*",
1373 ".*[\\._-]Lfe[\\._-].*",
1374 ".*[\\._-]Ls[\\._-].*",
1375 ".*[\\._-]Rs[\\._-].*"
1378 static int const regexes = sizeof(regex) / sizeof(*regex);
1380 if (audio_processor ()) {
1381 audio_processor()->make_audio_mapping_default (mapping);
1383 mapping.make_zero ();
1384 if (mapping.input_channels() == 1) {
1385 bool guessed = false;
1387 /* See if we can guess where this stream should go */
1389 for (int i = 0; i < regexes; ++i) {
1390 boost::regex e (regex[i], boost::regex::icase);
1391 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1392 mapping.set (0, i, 1);
1399 /* If we have no idea, just put it on centre */
1400 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1404 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1405 mapping.set (i, i, 1);
1411 /** @return The names of the channels that audio contents' outputs are passed into;
1412 * this is either the DCP or a AudioProcessor.
1415 Film::audio_output_names () const
1417 if (audio_processor ()) {
1418 return audio_processor()->input_names ();
1421 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1425 for (int i = 0; i < audio_channels(); ++i) {
1426 n.push_back (short_audio_channel_name (i));
1433 Film::repeat_content (ContentList c, int n)
1435 _playlist->repeat (c, n);
1439 Film::remove_content (ContentList c)
1441 _playlist->remove (c);
1445 Film::audio_analysis_finished ()
1451 Film::reels () const
1453 list<DCPTimePeriod> p;
1454 DCPTime const len = length().ceil (video_frame_rate ());
1456 switch (reel_type ()) {
1457 case REELTYPE_SINGLE:
1458 p.push_back (DCPTimePeriod (DCPTime (), len));
1460 case REELTYPE_BY_VIDEO_CONTENT:
1462 optional<DCPTime> last_split;
1463 shared_ptr<Content> last_video;
1464 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1466 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1468 p.push_back (DCPTimePeriod (last_split.get(), t));
1476 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1478 /* Definitely go from the last split to the end of the video content */
1479 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1482 if (video_end < len) {
1483 /* And maybe go after that as well if there is any non-video hanging over the end */
1484 p.push_back (DCPTimePeriod (video_end, len));
1488 case REELTYPE_BY_LENGTH:
1491 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1492 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1493 while (current < len) {
1494 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1495 p.push_back (DCPTimePeriod (current, end));
1505 /** @param period A period within the DCP
1506 * @return Name of the content which most contributes to the given period.
1509 Film::content_summary (DCPTimePeriod period) const
1511 return _playlist->content_summary (period);
1515 Film::fix_conflicting_settings ()
1519 list<boost::filesystem::path> was_referencing;
1520 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1521 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1523 list<string> reasons;
1525 if (!d->can_reference_video(reasons) && d->reference_video()) {
1526 d->set_reference_video (false);
1529 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1530 d->set_reference_audio (false);
1533 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1534 d->set_reference_subtitle (false);
1538 was_referencing.push_back (d->path(0).parent_path().filename());
1543 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1544 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()));
1551 Film::use_template (string name)
1553 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1554 _template_film->read_metadata (Config::instance()->template_path (name));
1555 _use_isdcf_name = _template_film->_use_isdcf_name;
1556 _dcp_content_type = _template_film->_dcp_content_type;
1557 _container = _template_film->_container;
1558 _resolution = _template_film->_resolution;
1559 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1560 _video_frame_rate = _template_film->_video_frame_rate;
1561 _signed = _template_film->_signed;
1562 _encrypted = _template_film->_encrypted;
1563 _audio_channels = _template_film->_audio_channels;
1564 _sequence = _template_film->_sequence;
1565 _three_d = _template_film->_three_d;
1566 _interop = _template_film->_interop;
1567 _audio_processor = _template_film->_audio_processor;
1568 _reel_type = _template_film->_reel_type;
1569 _reel_length = _template_film->_reel_length;
1570 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;
1573 pair<double, double>
1574 Film::speed_up_range (int dcp_frame_rate) const
1576 return _playlist->speed_up_range (dcp_frame_rate);