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 unfiltered = isdcf_name (if_created_now);
796 unfiltered = name ();
799 /* Filter out `bad' characters which cause problems with some systems.
800 There's no apparent list of what really is allowed, so this is a guess.
804 string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
805 for (size_t i = 0; i < unfiltered.size(); ++i) {
806 if (allowed.find (unfiltered[i]) != string::npos) {
807 filtered += unfiltered[i];
815 Film::set_directory (boost::filesystem::path d)
822 Film::set_name (string n)
825 signal_changed (NAME);
829 Film::set_use_isdcf_name (bool u)
832 signal_changed (USE_ISDCF_NAME);
836 Film::set_dcp_content_type (DCPContentType const * t)
838 _dcp_content_type = t;
839 signal_changed (DCP_CONTENT_TYPE);
843 Film::set_container (Ratio const * c)
846 signal_changed (CONTAINER);
850 Film::set_resolution (Resolution r)
853 signal_changed (RESOLUTION);
857 Film::set_j2k_bandwidth (int b)
860 signal_changed (J2K_BANDWIDTH);
864 Film::set_isdcf_metadata (ISDCFMetadata m)
867 signal_changed (ISDCF_METADATA);
871 Film::set_video_frame_rate (int f)
873 _video_frame_rate = f;
874 signal_changed (VIDEO_FRAME_RATE);
878 Film::set_audio_channels (int c)
881 signal_changed (AUDIO_CHANNELS);
885 Film::set_three_d (bool t)
888 signal_changed (THREE_D);
890 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
891 _isdcf_metadata.two_d_version_of_three_d = false;
892 signal_changed (ISDCF_METADATA);
897 Film::set_interop (bool i)
900 signal_changed (INTEROP);
904 Film::set_audio_processor (AudioProcessor const * processor)
906 _audio_processor = processor;
907 signal_changed (AUDIO_PROCESSOR);
908 signal_changed (AUDIO_CHANNELS);
912 Film::set_reel_type (ReelType t)
915 signal_changed (REEL_TYPE);
918 /** @param r Desired reel length in bytes */
920 Film::set_reel_length (int64_t r)
923 signal_changed (REEL_LENGTH);
927 Film::set_upload_after_make_dcp (bool u)
929 _upload_after_make_dcp = u;
930 signal_changed (UPLOAD_AFTER_MAKE_DCP);
934 Film::signal_changed (Property p)
940 set_video_frame_rate (_playlist->best_video_frame_rate ());
942 case Film::VIDEO_FRAME_RATE:
944 _playlist->maybe_sequence ();
950 emit (boost::bind (boost::ref (Changed), p));
954 Film::set_isdcf_date_today ()
956 _isdcf_date = boost::gregorian::day_clock::local_day ();
959 boost::filesystem::path
960 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
962 boost::filesystem::path p;
964 p /= video_identifier ();
967 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
970 if (eyes == EYES_LEFT) {
972 } else if (eyes == EYES_RIGHT) {
986 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
991 return vector<CPLSummary> ();
994 vector<CPLSummary> out;
996 boost::filesystem::path const dir = directory().get();
997 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
999 boost::filesystem::is_directory (*i) &&
1000 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
1006 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
1009 i->path().leaf().string(),
1010 dcp.cpls().front()->id(),
1011 dcp.cpls().front()->annotation_text(),
1012 dcp.cpls().front()->file().get()
1025 Film::set_signed (bool s)
1028 signal_changed (SIGNED);
1032 Film::set_encrypted (bool e)
1035 signal_changed (ENCRYPTED);
1039 Film::set_key (dcp::Key key)
1042 signal_changed (KEY);
1046 Film::content () const
1048 return _playlist->content ();
1052 Film::examine_and_add_content (shared_ptr<Content> c)
1054 if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
1055 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
1058 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1060 _job_connections.push_back (
1061 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
1064 JobManager::instance()->add (j);
1068 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
1070 shared_ptr<Job> job = j.lock ();
1071 if (!job || !job->finished_ok ()) {
1075 shared_ptr<Content> content = c.lock ();
1080 add_content (content);
1082 if (Config::instance()->automatic_audio_analysis() && content->audio) {
1083 shared_ptr<Playlist> playlist (new Playlist);
1084 playlist->add (content);
1085 boost::signals2::connection c;
1086 JobManager::instance()->analyse_audio (
1087 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1089 _audio_analysis_connections.push_back (c);
1094 Film::add_content (shared_ptr<Content> c)
1096 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1098 c->set_position (_playlist->video_end ());
1099 } else if (c->subtitle) {
1100 c->set_position (_playlist->subtitle_end ());
1103 if (_template_film) {
1104 /* Take settings from the first piece of content of c's type in _template */
1105 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1106 if (typeid(i.get()) == typeid(c.get())) {
1107 c->use_template (i);
1116 Film::remove_content (shared_ptr<Content> c)
1118 _playlist->remove (c);
1122 Film::move_content_earlier (shared_ptr<Content> c)
1124 _playlist->move_earlier (c);
1128 Film::move_content_later (shared_ptr<Content> c)
1130 _playlist->move_later (c);
1133 /** @return length of the film from time 0 to the last thing on the playlist */
1135 Film::length () const
1137 return _playlist->length ();
1141 Film::best_video_frame_rate () const
1143 return _playlist->best_video_frame_rate ();
1147 Film::active_frame_rate_change (DCPTime t) const
1149 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1153 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1157 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1158 set_video_frame_rate (_playlist->best_video_frame_rate ());
1159 } else if (p == AudioContentProperty::STREAMS) {
1160 signal_changed (NAME);
1163 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1167 Film::playlist_changed ()
1169 signal_changed (CONTENT);
1170 signal_changed (NAME);
1174 Film::playlist_order_changed ()
1176 signal_changed (CONTENT_ORDER);
1180 Film::audio_frame_rate () const
1182 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1183 if (i->audio && i->audio->has_rate_above_48k ()) {
1192 Film::set_sequence (bool s)
1195 _playlist->set_sequence (s);
1196 signal_changed (SEQUENCE);
1199 /** @return Size of the largest possible image in whatever resolution we are using */
1201 Film::full_frame () const
1203 switch (_resolution) {
1205 return dcp::Size (2048, 1080);
1207 return dcp::Size (4096, 2160);
1210 DCPOMATIC_ASSERT (false);
1211 return dcp::Size ();
1214 /** @return Size of the frame */
1216 Film::frame_size () const
1218 return fit_ratio_within (container()->ratio(), full_frame ());
1221 /** @param from KDM from time expressed as a local time with an offset from UTC
1222 * @param to KDM to time expressed as a local time with an offset from UTC
1226 dcp::Certificate recipient,
1227 vector<dcp::Certificate> trusted_devices,
1228 boost::filesystem::path cpl_file,
1229 dcp::LocalTime from,
1230 dcp::LocalTime until,
1231 dcp::Formulation formulation
1234 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1235 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1236 if (!signer->valid ()) {
1237 throw InvalidSignerError ();
1240 /* Find keys that have been added to imported, encrypted DCP content */
1241 list<dcp::DecryptedKDMKey> imported_keys;
1242 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1243 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1244 if (d && d->kdm()) {
1245 dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
1246 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
1247 copy (keys.begin(), keys.end(), back_inserter (imported_keys));
1251 map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
1253 BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
1254 shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
1255 if (!mxf || !mxf->key_id()) {
1259 /* Get any imported key for this ID */
1261 BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
1262 if (j.id() == mxf->key_id().get()) {
1263 keys[mxf] = j.key();
1269 /* No imported key; it must be an asset that we encrypted */
1274 return dcp::DecryptedKDM (
1275 cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1276 ).encrypt (signer, recipient, trusted_devices, formulation);
1279 /** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1280 * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1284 list<shared_ptr<Screen> > screens,
1285 boost::filesystem::path dcp,
1286 boost::posix_time::ptime from,
1287 boost::posix_time::ptime until,
1288 dcp::Formulation formulation
1291 list<ScreenKDM> kdms;
1293 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1295 dcp::EncryptedKDM const kdm = make_kdm (
1299 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1300 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1304 kdms.push_back (ScreenKDM (i, kdm));
1311 /** @return The approximate disk space required to encode a DCP of this film with the
1312 * current settings, in bytes.
1315 Film::required_disk_space () const
1317 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1320 /** This method checks the disk that the Film is on and tries to decide whether or not
1321 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1322 * false is returned and required and available are filled in with the amount of disk space
1323 * required and available respectively (in Gb).
1325 * Note: the decision made by this method isn't, of course, 100% reliable.
1328 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1330 /* Create a test file and see if we can hard-link it */
1331 boost::filesystem::path test = internal_video_asset_dir() / "test";
1332 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1333 can_hard_link = true;
1334 FILE* f = fopen_boost (test, "w");
1337 boost::system::error_code ec;
1338 boost::filesystem::create_hard_link (test, test2, ec);
1340 can_hard_link = false;
1342 boost::filesystem::remove (test);
1343 boost::filesystem::remove (test2);
1346 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1347 required = double (required_disk_space ()) / 1073741824.0f;
1348 if (!can_hard_link) {
1351 available = double (s.available) / 1073741824.0f;
1352 return (available - required) > 1;
1356 Film::subtitle_language () const
1358 set<string> languages;
1360 ContentList cl = content ();
1361 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1363 languages.insert (c->subtitle->language ());
1368 BOOST_FOREACH (string s, languages) {
1369 if (!all.empty ()) {
1379 /** Change the gains of the supplied AudioMapping to make it a default
1380 * for this film. The defaults are guessed based on what processor (if any)
1381 * is in use, the number of input channels and any filename supplied.
1384 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1386 static string const regex[] = {
1387 ".*[\\._-]L[\\._-].*",
1388 ".*[\\._-]R[\\._-].*",
1389 ".*[\\._-]C[\\._-].*",
1390 ".*[\\._-]Lfe[\\._-].*",
1391 ".*[\\._-]Ls[\\._-].*",
1392 ".*[\\._-]Rs[\\._-].*"
1395 static int const regexes = sizeof(regex) / sizeof(*regex);
1397 if (audio_processor ()) {
1398 audio_processor()->make_audio_mapping_default (mapping);
1400 mapping.make_zero ();
1401 if (mapping.input_channels() == 1) {
1402 bool guessed = false;
1404 /* See if we can guess where this stream should go */
1406 for (int i = 0; i < regexes; ++i) {
1407 boost::regex e (regex[i], boost::regex::icase);
1408 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1409 mapping.set (0, i, 1);
1416 /* If we have no idea, just put it on centre */
1417 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1421 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1422 mapping.set (i, i, 1);
1428 /** @return The names of the channels that audio contents' outputs are passed into;
1429 * this is either the DCP or a AudioProcessor.
1432 Film::audio_output_names () const
1434 if (audio_processor ()) {
1435 return audio_processor()->input_names ();
1438 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1442 for (int i = 0; i < audio_channels(); ++i) {
1443 n.push_back (short_audio_channel_name (i));
1450 Film::repeat_content (ContentList c, int n)
1452 _playlist->repeat (c, n);
1456 Film::remove_content (ContentList c)
1458 _playlist->remove (c);
1462 Film::audio_analysis_finished ()
1468 Film::reels () const
1470 list<DCPTimePeriod> p;
1471 DCPTime const len = length().ceil (video_frame_rate ());
1473 switch (reel_type ()) {
1474 case REELTYPE_SINGLE:
1475 p.push_back (DCPTimePeriod (DCPTime (), len));
1477 case REELTYPE_BY_VIDEO_CONTENT:
1479 optional<DCPTime> last_split;
1480 shared_ptr<Content> last_video;
1481 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1483 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1485 p.push_back (DCPTimePeriod (last_split.get(), t));
1493 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1495 /* Definitely go from the last split to the end of the video content */
1496 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1499 if (video_end < len) {
1500 /* And maybe go after that as well if there is any non-video hanging over the end */
1501 p.push_back (DCPTimePeriod (video_end, len));
1505 case REELTYPE_BY_LENGTH:
1508 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1509 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1510 while (current < len) {
1511 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1512 p.push_back (DCPTimePeriod (current, end));
1523 Film::content_summary (DCPTimePeriod period) const
1525 return _playlist->content_summary (period);
1529 Film::fix_conflicting_settings ()
1533 list<boost::filesystem::path> was_referencing;
1534 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1535 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1537 list<string> reasons;
1539 if (!d->can_reference_video(reasons) && d->reference_video()) {
1540 d->set_reference_video (false);
1543 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1544 d->set_reference_audio (false);
1547 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1548 d->set_reference_subtitle (false);
1552 was_referencing.push_back (d->path(0).parent_path().filename());
1557 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1558 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()));
1565 Film::use_template (string name)
1567 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1568 _template_film->read_metadata (Config::instance()->template_path (name));
1569 _use_isdcf_name = _template_film->_use_isdcf_name;
1570 _dcp_content_type = _template_film->_dcp_content_type;
1571 _container = _template_film->_container;
1572 _resolution = _template_film->_resolution;
1573 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1574 _video_frame_rate = _template_film->_video_frame_rate;
1575 _signed = _template_film->_signed;
1576 _encrypted = _template_film->_encrypted;
1577 _audio_channels = _template_film->_audio_channels;
1578 _sequence = _template_film->_sequence;
1579 _three_d = _template_film->_three_d;
1580 _interop = _template_film->_interop;
1581 _audio_processor = _template_film->_audio_processor;
1582 _reel_type = _template_film->_reel_type;
1583 _reel_length = _template_film->_reel_length;
1584 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;