2 Copyright (C) 2012-2016 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 <libxml++/libxml++.h>
61 #include <boost/filesystem.hpp>
62 #include <boost/algorithm/string.hpp>
63 #include <boost/foreach.hpp>
64 #include <boost/regex.hpp>
85 using std::runtime_error;
86 using boost::shared_ptr;
87 using boost::weak_ptr;
88 using boost::dynamic_pointer_cast;
89 using boost::optional;
90 using boost::is_any_of;
91 using dcp::raw_convert;
93 #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
94 #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
97 * AudioMapping XML changed.
99 * Subtitle offset changed to subtitle y offset, and subtitle x offset added.
101 * Use <Scale> tag in <VideoContent> rather than <Ratio>.
105 * Subtitle X and Y scale.
107 * Bumped to 32 for 2.0 branch; some times are expressed in Times rather
111 * Changed <Period> to <Subtitle> in FFmpegSubtitleStream
113 * Content only contains audio/subtitle-related tags if those things
116 * VideoFrameType in VideoContent is a string rather than an integer.
118 * EffectColour rather than OutlineColour in Subtitle.
120 int const Film::current_state_version = 36;
122 /** Construct a Film object in a given directory.
124 * @param dir Film directory.
127 Film::Film (optional<boost::filesystem::path> dir)
128 : _playlist (new Playlist)
129 , _use_isdcf_name (true)
130 , _dcp_content_type (Config::instance()->default_dcp_content_type ())
131 , _container (Config::instance()->default_container ())
132 , _resolution (RESOLUTION_2K)
135 , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
136 , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
137 , _video_frame_rate (24)
138 , _audio_channels (Config::instance()->default_dcp_audio_channels ())
141 , _interop (Config::instance()->default_interop ())
142 , _audio_processor (0)
143 , _reel_type (REELTYPE_SINGLE)
144 , _reel_length (2000000000)
145 , _upload_after_make_dcp (false)
146 , _state_version (current_state_version)
149 set_isdcf_date_today ();
151 _playlist_changed_connection = _playlist->Changed.connect (bind (&Film::playlist_changed, this));
152 _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
153 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
156 /* Make state.directory a complete path without ..s (where possible)
157 (Code swiped from Adam Bowen on stackoverflow)
158 XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
161 boost::filesystem::path p (boost::filesystem::system_complete (dir.get()));
162 boost::filesystem::path result;
163 for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
165 if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
168 result = result.parent_path ();
170 } else if (*i != ".") {
175 set_directory (result.make_preferred ());
179 _log.reset (new FileLog (file ("log")));
181 _log.reset (new NullLog);
184 _playlist->set_sequence (_sequence);
189 BOOST_FOREACH (boost::signals2::connection& i, _job_connections) {
193 BOOST_FOREACH (boost::signals2::connection& i, _audio_analysis_connections) {
199 Film::video_identifier () const
201 DCPOMATIC_ASSERT (container ());
203 string s = container()->id()
204 + "_" + resolution_to_string (_resolution)
205 + "_" + _playlist->video_identifier()
206 + "_" + raw_convert<string>(_video_frame_rate)
207 + "_" + raw_convert<string>(j2k_bandwidth());
228 /** @return The file to write video frame info to */
229 boost::filesystem::path
230 Film::info_file (DCPTimePeriod period) const
232 boost::filesystem::path p;
234 p /= video_identifier () + "_" + raw_convert<string> (period.from.get()) + "_" + raw_convert<string> (period.to.get());
238 boost::filesystem::path
239 Film::internal_video_asset_dir () const
241 return dir ("video");
244 boost::filesystem::path
245 Film::internal_video_asset_filename (DCPTimePeriod p) const
247 return video_identifier() + "_" + raw_convert<string> (p.from.get()) + "_" + raw_convert<string> (p.to.get()) + ".mxf";
250 boost::filesystem::path
251 Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
253 boost::filesystem::path p = dir ("analysis");
256 BOOST_FOREACH (shared_ptr<Content> i, playlist->content ()) {
261 digester.add (i->digest ());
262 digester.add (i->audio->mapping().digest ());
263 if (playlist->content().size() != 1) {
264 /* Analyses should be considered equal regardless of gain
265 if they were made from just one piece of content. This
266 is because we can fake any gain change in a single-content
267 analysis at the plotting stage rather than having to
270 digester.add (i->audio->gain ());
274 if (audio_processor ()) {
275 digester.add (audio_processor()->id ());
278 p /= digester.get ();
282 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
286 if (dcp_name().find ("/") != string::npos) {
287 throw BadSettingError (_("name"), _("cannot contain slashes"));
290 set_isdcf_date_today ();
292 BOOST_FOREACH (string i, environment_info ()) {
296 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
297 LOG_GENERAL ("Content: %1", i->technical_summary());
299 LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
300 if (Config::instance()->only_servers_encode ()) {
301 LOG_GENERAL_NC ("0 threads: ONLY SERVERS SET TO ENCODE");
303 LOG_GENERAL ("%1 threads", Config::instance()->num_local_encoding_threads());
305 LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
307 if (container() == 0) {
308 throw MissingSettingError (_("container"));
311 if (content().empty()) {
312 throw runtime_error (_("You must add some content to the DCP before creating it"));
315 if (dcp_content_type() == 0) {
316 throw MissingSettingError (_("content type"));
319 if (name().empty()) {
320 throw MissingSettingError (_("name"));
323 JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
326 /** Start a job to send our DCP to the configured TMS */
328 Film::send_dcp_to_tms ()
330 shared_ptr<Job> j (new UploadJob (shared_from_this()));
331 JobManager::instance()->add (j);
334 shared_ptr<xmlpp::Document>
335 Film::metadata (bool with_content_paths) const
337 shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
338 xmlpp::Element* root = doc->create_root_node ("Metadata");
340 root->add_child("Version")->add_child_text (raw_convert<string> (current_state_version));
341 root->add_child("Name")->add_child_text (_name);
342 root->add_child("UseISDCFName")->add_child_text (_use_isdcf_name ? "1" : "0");
344 if (_dcp_content_type) {
345 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->isdcf_name ());
349 root->add_child("Container")->add_child_text (_container->id ());
352 root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
353 root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
354 _isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
355 root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
356 root->add_child("ISDCFDate")->add_child_text (boost::gregorian::to_iso_string (_isdcf_date));
357 root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
358 root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
359 root->add_child("Sequence")->add_child_text (_sequence ? "1" : "0");
360 root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
361 root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
362 root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
363 root->add_child("Key")->add_child_text (_key.hex ());
364 if (_audio_processor) {
365 root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
367 root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
368 root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
369 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
370 _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
375 /** Write state to our `metadata' file */
377 Film::write_metadata () const
379 DCPOMATIC_ASSERT (directory());
380 boost::filesystem::create_directories (directory().get());
381 shared_ptr<xmlpp::Document> doc = metadata ();
382 doc->write_to_file_formatted (file("metadata.xml").string ());
386 /** Write a template from this film */
388 Film::write_template (boost::filesystem::path path) const
390 boost::filesystem::create_directories (path.parent_path());
391 shared_ptr<xmlpp::Document> doc = metadata (false);
392 doc->write_to_file_formatted (path.string ());
395 /** Read state from our metadata file.
396 * @return Notes about things that the user should know about, or empty.
399 Film::read_metadata (optional<boost::filesystem::path> path)
402 if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
403 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!"));
406 path = file ("metadata.xml");
409 cxml::Document f ("Metadata");
410 f.read_file (path.get ());
412 _state_version = f.number_child<int> ("Version");
413 if (_state_version > current_state_version) {
414 throw runtime_error (_("This film was created with a newer version of DCP-o-matic, and it cannot be loaded into this version. Sorry!"));
417 _name = f.string_child ("Name");
418 if (_state_version >= 9) {
419 _use_isdcf_name = f.bool_child ("UseISDCFName");
420 _isdcf_metadata = ISDCFMetadata (f.node_child ("ISDCFMetadata"));
421 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("ISDCFDate"));
423 _use_isdcf_name = f.bool_child ("UseDCIName");
424 _isdcf_metadata = ISDCFMetadata (f.node_child ("DCIMetadata"));
425 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
429 optional<string> c = f.optional_string_child ("DCPContentType");
431 _dcp_content_type = DCPContentType::from_isdcf_name (c.get ());
436 optional<string> c = f.optional_string_child ("Container");
438 _container = Ratio::from_id (c.get ());
442 _resolution = string_to_resolution (f.string_child ("Resolution"));
443 _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
444 _video_frame_rate = f.number_child<int> ("VideoFrameRate");
445 _signed = f.optional_bool_child("Signed").get_value_or (true);
446 _encrypted = f.bool_child ("Encrypted");
447 _audio_channels = f.number_child<int> ("AudioChannels");
448 /* We used to allow odd numbers (and zero) channels, but it's just not worth
451 if (_audio_channels == 0) {
453 } else if ((_audio_channels % 2) == 1) {
457 if (f.optional_bool_child("SequenceVideo")) {
458 _sequence = f.bool_child("SequenceVideo");
460 _sequence = f.bool_child("Sequence");
463 _three_d = f.bool_child ("ThreeD");
464 _interop = f.bool_child ("Interop");
465 _key = dcp::Key (f.string_child ("Key"));
467 if (f.optional_string_child ("AudioProcessor")) {
468 _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
470 _audio_processor = 0;
473 _reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
474 _reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
475 _upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
478 /* This method is the only one that can return notes (so far) */
479 _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
481 /* Write backtraces to this film's directory, until another film is loaded */
483 set_backtrace_file (file ("backtrace.txt"));
490 /** Given a directory name, return its full path within the Film's directory.
491 * The directory (and its parents) will be created if they do not exist.
493 boost::filesystem::path
494 Film::dir (boost::filesystem::path d) const
496 DCPOMATIC_ASSERT (_directory);
498 boost::filesystem::path p;
499 p /= _directory.get();
502 boost::filesystem::create_directories (p);
507 /** Given a file or directory name, return its full path within the Film's directory.
508 * Any required parent directories will be created.
510 boost::filesystem::path
511 Film::file (boost::filesystem::path f) const
513 DCPOMATIC_ASSERT (_directory);
515 boost::filesystem::path p;
516 p /= _directory.get();
519 boost::filesystem::create_directories (p.parent_path ());
525 Film::mapped_audio_channels () const
529 if (audio_processor ()) {
530 /* Processors are mapped 1:1 to DCP outputs so we can work out mappings from there */
531 for (int i = 0; i < audio_processor()->out_channels(); ++i) {
532 mapped.push_back (i);
535 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
537 list<int> c = i->audio->mapping().mapped_output_channels ();
538 copy (c.begin(), c.end(), back_inserter (mapped));
549 /** @return a ISDCF-compliant name for a DCP of this film */
551 Film::isdcf_name (bool if_created_now) const
555 string raw_name = name ();
557 /* Split the raw name up into words */
558 vector<string> words;
559 split (words, raw_name, is_any_of (" _-"));
563 /* Add each word to fixed_name */
564 for (vector<string>::const_iterator i = words.begin(); i != words.end(); ++i) {
567 /* First letter is always capitalised */
568 w[0] = toupper (w[0]);
570 /* Count caps in w */
572 for (size_t i = 0; i < w.size(); ++i) {
573 if (isupper (w[i])) {
578 /* If w is all caps make the rest of it lower case, otherwise
581 if (caps == w.size ()) {
582 for (size_t i = 1; i < w.size(); ++i) {
583 w[i] = tolower (w[i]);
587 for (size_t i = 0; i < w.size(); ++i) {
592 if (fixed_name.length() > 14) {
593 fixed_name = fixed_name.substr (0, 14);
598 if (dcp_content_type()) {
599 d += "_" + dcp_content_type()->isdcf_name();
600 d += "-" + raw_convert<string>(isdcf_metadata().content_version);
603 ISDCFMetadata const dm = isdcf_metadata ();
605 if (dm.temp_version) {
609 if (dm.pre_release) {
617 if (!dm.chain.empty ()) {
625 if (dm.two_d_version_of_three_d) {
629 if (!dm.mastered_luminance.empty ()) {
630 d += "-" + dm.mastered_luminance;
633 if (video_frame_rate() != 24) {
634 d += "-" + raw_convert<string>(video_frame_rate());
638 d += "_" + container()->isdcf_name();
641 /* XXX: this uses the first bit of content only */
643 /* The standard says we don't do this for trailers, for some strange reason */
644 if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
645 Ratio const * content_ratio = 0;
646 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
648 /* Here's the first piece of video content */
649 if (i->video->scale().ratio ()) {
650 content_ratio = i->video->scale().ratio ();
652 content_ratio = Ratio::from_ratio (i->video->size().ratio ());
658 if (content_ratio && content_ratio != container()) {
659 d += "-" + content_ratio->isdcf_name();
663 if (!dm.audio_language.empty ()) {
664 d += "_" + dm.audio_language;
665 if (!dm.subtitle_language.empty()) {
667 bool burnt_in = true;
668 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
673 if (i->subtitle->use() && !i->subtitle->burn()) {
678 string language = dm.subtitle_language;
679 if (burnt_in && language != "XX") {
680 transform (language.begin(), language.end(), language.begin(), ::tolower);
682 transform (language.begin(), language.end(), language.begin(), ::toupper);
691 if (!dm.territory.empty ()) {
692 d += "_" + dm.territory;
693 if (dm.rating.empty ()) {
696 d += "-" + dm.rating;
700 /* Count mapped audio channels */
705 BOOST_FOREACH (int i, mapped_audio_channels ()) {
706 if (i >= audio_channels()) {
707 /* This channel is mapped but is not included in the DCP */
711 if (static_cast<dcp::Channel> (i) == dcp::LFE) {
719 d += String::compose("_%1%2", non_lfe, lfe);
724 d += "_" + resolution_to_string (_resolution);
726 if (!dm.studio.empty ()) {
727 d += "_" + dm.studio;
730 if (if_created_now) {
731 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
733 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
736 if (!dm.facility.empty ()) {
737 d += "_" + dm.facility;
751 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
752 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
753 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
767 /** @return name to give the DCP */
769 Film::dcp_name (bool if_created_now) const
772 if (use_isdcf_name()) {
773 unfiltered = isdcf_name (if_created_now);
775 unfiltered = name ();
778 /* Filter out `bad' characters which cause problems with some systems.
779 There's no apparent list of what really is allowed, so this is a guess.
783 string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
784 for (size_t i = 0; i < unfiltered.size(); ++i) {
785 if (allowed.find (unfiltered[i]) != string::npos) {
786 filtered += unfiltered[i];
794 Film::set_directory (boost::filesystem::path d)
801 Film::set_name (string n)
804 signal_changed (NAME);
808 Film::set_use_isdcf_name (bool u)
811 signal_changed (USE_ISDCF_NAME);
815 Film::set_dcp_content_type (DCPContentType const * t)
817 _dcp_content_type = t;
818 signal_changed (DCP_CONTENT_TYPE);
822 Film::set_container (Ratio const * c)
825 signal_changed (CONTAINER);
829 Film::set_resolution (Resolution r)
832 signal_changed (RESOLUTION);
836 Film::set_j2k_bandwidth (int b)
839 signal_changed (J2K_BANDWIDTH);
843 Film::set_isdcf_metadata (ISDCFMetadata m)
846 signal_changed (ISDCF_METADATA);
850 Film::set_video_frame_rate (int f)
852 _video_frame_rate = f;
853 signal_changed (VIDEO_FRAME_RATE);
857 Film::set_audio_channels (int c)
860 signal_changed (AUDIO_CHANNELS);
864 Film::set_three_d (bool t)
867 signal_changed (THREE_D);
869 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
870 _isdcf_metadata.two_d_version_of_three_d = false;
871 signal_changed (ISDCF_METADATA);
876 Film::set_interop (bool i)
879 signal_changed (INTEROP);
883 Film::set_audio_processor (AudioProcessor const * processor)
885 _audio_processor = processor;
886 signal_changed (AUDIO_PROCESSOR);
887 signal_changed (AUDIO_CHANNELS);
891 Film::set_reel_type (ReelType t)
894 signal_changed (REEL_TYPE);
897 /** @param r Desired reel length in bytes */
899 Film::set_reel_length (int64_t r)
902 signal_changed (REEL_LENGTH);
906 Film::set_upload_after_make_dcp (bool u)
908 _upload_after_make_dcp = u;
909 signal_changed (UPLOAD_AFTER_MAKE_DCP);
913 Film::signal_changed (Property p)
919 set_video_frame_rate (_playlist->best_video_frame_rate ());
921 case Film::VIDEO_FRAME_RATE:
923 _playlist->maybe_sequence ();
929 emit (boost::bind (boost::ref (Changed), p));
933 Film::set_isdcf_date_today ()
935 _isdcf_date = boost::gregorian::day_clock::local_day ();
938 boost::filesystem::path
939 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
941 boost::filesystem::path p;
943 p /= video_identifier ();
946 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
949 if (eyes == EYES_LEFT) {
951 } else if (eyes == EYES_RIGHT) {
965 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
970 return vector<CPLSummary> ();
973 vector<CPLSummary> out;
975 boost::filesystem::path const dir = directory().get();
976 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
978 boost::filesystem::is_directory (*i) &&
979 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
985 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
988 i->path().leaf().string(),
989 dcp.cpls().front()->id(),
990 dcp.cpls().front()->annotation_text(),
991 dcp.cpls().front()->file().get()
1004 Film::set_signed (bool s)
1007 signal_changed (SIGNED);
1011 Film::set_encrypted (bool e)
1014 signal_changed (ENCRYPTED);
1018 Film::set_key (dcp::Key key)
1021 signal_changed (KEY);
1025 Film::content () const
1027 return _playlist->content ();
1031 Film::examine_content (shared_ptr<Content> c)
1033 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1034 JobManager::instance()->add (j);
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 return dcp::DecryptedKDM (
1227 cpl, key(), from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1228 ).encrypt (signer, recipient, trusted_devices, formulation);
1231 /** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1232 * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1236 list<shared_ptr<Screen> > screens,
1237 boost::filesystem::path dcp,
1238 boost::posix_time::ptime from,
1239 boost::posix_time::ptime until,
1240 dcp::Formulation formulation
1243 list<ScreenKDM> kdms;
1245 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1247 dcp::EncryptedKDM const kdm = make_kdm (
1251 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1252 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1256 kdms.push_back (ScreenKDM (i, kdm));
1263 /** @return The approximate disk space required to encode a DCP of this film with the
1264 * current settings, in bytes.
1267 Film::required_disk_space () const
1269 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1272 /** This method checks the disk that the Film is on and tries to decide whether or not
1273 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1274 * false is returned and required and available are filled in with the amount of disk space
1275 * required and available respectively (in Gb).
1277 * Note: the decision made by this method isn't, of course, 100% reliable.
1280 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1282 /* Create a test file and see if we can hard-link it */
1283 boost::filesystem::path test = internal_video_asset_dir() / "test";
1284 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1285 can_hard_link = true;
1286 FILE* f = fopen_boost (test, "w");
1289 boost::system::error_code ec;
1290 boost::filesystem::create_hard_link (test, test2, ec);
1292 can_hard_link = false;
1294 boost::filesystem::remove (test);
1295 boost::filesystem::remove (test2);
1298 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1299 required = double (required_disk_space ()) / 1073741824.0f;
1300 if (!can_hard_link) {
1303 available = double (s.available) / 1073741824.0f;
1304 return (available - required) > 1;
1308 Film::subtitle_language () const
1310 set<string> languages;
1312 ContentList cl = content ();
1313 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1315 languages.insert (c->subtitle->language ());
1320 BOOST_FOREACH (string s, languages) {
1321 if (!all.empty ()) {
1331 /** Change the gains of the supplied AudioMapping to make it a default
1332 * for this film. The defaults are guessed based on what processor (if any)
1333 * is in use, the number of input channels and any filename supplied.
1336 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1338 static string const regex[] = {
1339 ".*[\\._-]L[\\._-].*",
1340 ".*[\\._-]R[\\._-].*",
1341 ".*[\\._-]C[\\._-].*",
1342 ".*[\\._-]Lfe[\\._-].*",
1343 ".*[\\._-]Ls[\\._-].*",
1344 ".*[\\._-]Rs[\\._-].*"
1347 static int const regexes = sizeof(regex) / sizeof(*regex);
1349 if (audio_processor ()) {
1350 audio_processor()->make_audio_mapping_default (mapping);
1352 mapping.make_zero ();
1353 if (mapping.input_channels() == 1) {
1354 bool guessed = false;
1356 /* See if we can guess where this stream should go */
1358 for (int i = 0; i < regexes; ++i) {
1359 boost::regex e (regex[i], boost::regex::icase);
1360 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1361 mapping.set (0, i, 1);
1368 /* If we have no idea, just put it on centre */
1369 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1373 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1374 mapping.set (i, i, 1);
1380 /** @return The names of the channels that audio contents' outputs are passed into;
1381 * this is either the DCP or a AudioProcessor.
1384 Film::audio_output_names () const
1386 if (audio_processor ()) {
1387 return audio_processor()->input_names ();
1390 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1393 n.push_back (_("L"));
1394 n.push_back (_("R"));
1395 n.push_back (_("C"));
1396 n.push_back (_("Lfe"));
1397 n.push_back (_("Ls"));
1398 n.push_back (_("Rs"));
1399 n.push_back (_("HI"));
1400 n.push_back (_("VI"));
1401 n.push_back (_("Lc"));
1402 n.push_back (_("Rc"));
1403 n.push_back (_("BsL"));
1404 n.push_back (_("BsR"));
1405 n.push_back (_("DBP"));
1406 n.push_back (_("DBS"));
1410 return vector<string> (n.begin(), n.begin() + audio_channels ());
1414 Film::repeat_content (ContentList c, int n)
1416 _playlist->repeat (c, n);
1420 Film::remove_content (ContentList c)
1422 _playlist->remove (c);
1426 Film::audio_analysis_finished ()
1432 Film::reels () const
1434 list<DCPTimePeriod> p;
1435 DCPTime const len = length().round_up (video_frame_rate ());
1437 switch (reel_type ()) {
1438 case REELTYPE_SINGLE:
1439 p.push_back (DCPTimePeriod (DCPTime (), len));
1441 case REELTYPE_BY_VIDEO_CONTENT:
1443 optional<DCPTime> last_split;
1444 shared_ptr<Content> last_video;
1445 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1447 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1449 p.push_back (DCPTimePeriod (last_split.get(), t));
1457 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1459 /* Definitely go from the last split to the end of the video content */
1460 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1463 if (video_end < len) {
1464 /* And maybe go after that as well if there is any non-video hanging over the end */
1465 p.push_back (DCPTimePeriod (video_end, len));
1469 case REELTYPE_BY_LENGTH:
1472 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1473 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1474 while (current < len) {
1475 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1476 p.push_back (DCPTimePeriod (current, end));
1487 Film::content_summary (DCPTimePeriod period) const
1489 return _playlist->content_summary (period);
1493 Film::fix_conflicting_settings ()
1497 list<boost::filesystem::path> was_referencing;
1498 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1499 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1501 list<string> reasons;
1503 if (!d->can_reference_video(reasons) && d->reference_video()) {
1504 d->set_reference_video (false);
1507 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1508 d->set_reference_audio (false);
1511 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1512 d->set_reference_subtitle (false);
1516 was_referencing.push_back (d->path(0).parent_path().filename());
1521 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1522 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()));
1529 Film::use_template (string name)
1531 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1532 _template_film->read_metadata (Config::instance()->template_path (name));
1533 _use_isdcf_name = _template_film->_use_isdcf_name;
1534 _dcp_content_type = _template_film->_dcp_content_type;
1535 _container = _template_film->_container;
1536 _resolution = _template_film->_resolution;
1537 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1538 _video_frame_rate = _template_film->_video_frame_rate;
1539 _signed = _template_film->_signed;
1540 _encrypted = _template_film->_encrypted;
1541 _audio_channels = _template_film->_audio_channels;
1542 _sequence = _template_film->_sequence;
1543 _three_d = _template_film->_three_d;
1544 _interop = _template_film->_interop;
1545 _audio_processor = _template_film->_audio_processor;
1546 _reel_type = _template_film->_reel_type;
1547 _reel_length = _template_film->_reel_length;
1548 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;