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_and_add_content (shared_ptr<Content> c)
1033 if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
1034 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
1037 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1039 _job_connections.push_back (
1040 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
1043 JobManager::instance()->add (j);
1047 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
1049 shared_ptr<Job> job = j.lock ();
1050 if (!job || !job->finished_ok ()) {
1054 shared_ptr<Content> content = c.lock ();
1059 add_content (content);
1061 if (Config::instance()->automatic_audio_analysis() && content->audio) {
1062 shared_ptr<Playlist> playlist (new Playlist);
1063 playlist->add (content);
1064 boost::signals2::connection c;
1065 JobManager::instance()->analyse_audio (
1066 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1068 _audio_analysis_connections.push_back (c);
1073 Film::add_content (shared_ptr<Content> c)
1075 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1077 c->set_position (_playlist->video_end ());
1078 } else if (c->subtitle) {
1079 c->set_position (_playlist->subtitle_end ());
1082 if (_template_film) {
1083 /* Take settings from the first piece of content of c's type in _template */
1084 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1085 if (typeid(i.get()) == typeid(c.get())) {
1086 c->use_template (i);
1095 Film::remove_content (shared_ptr<Content> c)
1097 _playlist->remove (c);
1101 Film::move_content_earlier (shared_ptr<Content> c)
1103 _playlist->move_earlier (c);
1107 Film::move_content_later (shared_ptr<Content> c)
1109 _playlist->move_later (c);
1112 /** @return length of the film from time 0 to the last thing on the playlist */
1114 Film::length () const
1116 return _playlist->length ();
1120 Film::best_video_frame_rate () const
1122 return _playlist->best_video_frame_rate ();
1126 Film::active_frame_rate_change (DCPTime t) const
1128 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1132 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1136 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1137 set_video_frame_rate (_playlist->best_video_frame_rate ());
1138 } else if (p == AudioContentProperty::STREAMS) {
1139 signal_changed (NAME);
1142 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1146 Film::playlist_changed ()
1148 signal_changed (CONTENT);
1149 signal_changed (NAME);
1153 Film::playlist_order_changed ()
1155 signal_changed (CONTENT_ORDER);
1159 Film::audio_frame_rate () const
1161 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1162 if (i->audio && i->audio->has_rate_above_48k ()) {
1171 Film::set_sequence (bool s)
1174 _playlist->set_sequence (s);
1175 signal_changed (SEQUENCE);
1178 /** @return Size of the largest possible image in whatever resolution we are using */
1180 Film::full_frame () const
1182 switch (_resolution) {
1184 return dcp::Size (2048, 1080);
1186 return dcp::Size (4096, 2160);
1189 DCPOMATIC_ASSERT (false);
1190 return dcp::Size ();
1193 /** @return Size of the frame */
1195 Film::frame_size () const
1197 return fit_ratio_within (container()->ratio(), full_frame ());
1200 /** @param from KDM from time expressed as a local time with an offset from UTC
1201 * @param to KDM to time expressed as a local time with an offset from UTC
1205 dcp::Certificate recipient,
1206 vector<dcp::Certificate> trusted_devices,
1207 boost::filesystem::path cpl_file,
1208 dcp::LocalTime from,
1209 dcp::LocalTime until,
1210 dcp::Formulation formulation
1213 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1214 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1215 if (!signer->valid ()) {
1216 throw InvalidSignerError ();
1219 return dcp::DecryptedKDM (
1220 cpl, key(), from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1221 ).encrypt (signer, recipient, trusted_devices, formulation);
1224 /** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1225 * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1229 list<shared_ptr<Screen> > screens,
1230 boost::filesystem::path dcp,
1231 boost::posix_time::ptime from,
1232 boost::posix_time::ptime until,
1233 dcp::Formulation formulation
1236 list<ScreenKDM> kdms;
1238 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1240 dcp::EncryptedKDM const kdm = make_kdm (
1244 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1245 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1249 kdms.push_back (ScreenKDM (i, kdm));
1256 /** @return The approximate disk space required to encode a DCP of this film with the
1257 * current settings, in bytes.
1260 Film::required_disk_space () const
1262 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1265 /** This method checks the disk that the Film is on and tries to decide whether or not
1266 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1267 * false is returned and required and available are filled in with the amount of disk space
1268 * required and available respectively (in Gb).
1270 * Note: the decision made by this method isn't, of course, 100% reliable.
1273 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1275 /* Create a test file and see if we can hard-link it */
1276 boost::filesystem::path test = internal_video_asset_dir() / "test";
1277 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1278 can_hard_link = true;
1279 FILE* f = fopen_boost (test, "w");
1282 boost::system::error_code ec;
1283 boost::filesystem::create_hard_link (test, test2, ec);
1285 can_hard_link = false;
1287 boost::filesystem::remove (test);
1288 boost::filesystem::remove (test2);
1291 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1292 required = double (required_disk_space ()) / 1073741824.0f;
1293 if (!can_hard_link) {
1296 available = double (s.available) / 1073741824.0f;
1297 return (available - required) > 1;
1301 Film::subtitle_language () const
1303 set<string> languages;
1305 ContentList cl = content ();
1306 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1308 languages.insert (c->subtitle->language ());
1313 BOOST_FOREACH (string s, languages) {
1314 if (!all.empty ()) {
1324 /** Change the gains of the supplied AudioMapping to make it a default
1325 * for this film. The defaults are guessed based on what processor (if any)
1326 * is in use, the number of input channels and any filename supplied.
1329 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1331 static string const regex[] = {
1332 ".*[\\._-]L[\\._-].*",
1333 ".*[\\._-]R[\\._-].*",
1334 ".*[\\._-]C[\\._-].*",
1335 ".*[\\._-]Lfe[\\._-].*",
1336 ".*[\\._-]Ls[\\._-].*",
1337 ".*[\\._-]Rs[\\._-].*"
1340 static int const regexes = sizeof(regex) / sizeof(*regex);
1342 if (audio_processor ()) {
1343 audio_processor()->make_audio_mapping_default (mapping);
1345 mapping.make_zero ();
1346 if (mapping.input_channels() == 1) {
1347 bool guessed = false;
1349 /* See if we can guess where this stream should go */
1351 for (int i = 0; i < regexes; ++i) {
1352 boost::regex e (regex[i], boost::regex::icase);
1353 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1354 mapping.set (0, i, 1);
1361 /* If we have no idea, just put it on centre */
1362 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1366 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1367 mapping.set (i, i, 1);
1373 /** @return The names of the channels that audio contents' outputs are passed into;
1374 * this is either the DCP or a AudioProcessor.
1377 Film::audio_output_names () const
1379 if (audio_processor ()) {
1380 return audio_processor()->input_names ();
1383 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1387 for (int i = 0; i < audio_channels(); ++i) {
1388 n.push_back (short_audio_channel_name (i));
1395 Film::repeat_content (ContentList c, int n)
1397 _playlist->repeat (c, n);
1401 Film::remove_content (ContentList c)
1403 _playlist->remove (c);
1407 Film::audio_analysis_finished ()
1413 Film::reels () const
1415 list<DCPTimePeriod> p;
1416 DCPTime const len = length().round_up (video_frame_rate ());
1418 switch (reel_type ()) {
1419 case REELTYPE_SINGLE:
1420 p.push_back (DCPTimePeriod (DCPTime (), len));
1422 case REELTYPE_BY_VIDEO_CONTENT:
1424 optional<DCPTime> last_split;
1425 shared_ptr<Content> last_video;
1426 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1428 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1430 p.push_back (DCPTimePeriod (last_split.get(), t));
1438 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1440 /* Definitely go from the last split to the end of the video content */
1441 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1444 if (video_end < len) {
1445 /* And maybe go after that as well if there is any non-video hanging over the end */
1446 p.push_back (DCPTimePeriod (video_end, len));
1450 case REELTYPE_BY_LENGTH:
1453 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1454 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1455 while (current < len) {
1456 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1457 p.push_back (DCPTimePeriod (current, end));
1468 Film::content_summary (DCPTimePeriod period) const
1470 return _playlist->content_summary (period);
1474 Film::fix_conflicting_settings ()
1478 list<boost::filesystem::path> was_referencing;
1479 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1480 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1482 list<string> reasons;
1484 if (!d->can_reference_video(reasons) && d->reference_video()) {
1485 d->set_reference_video (false);
1488 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1489 d->set_reference_audio (false);
1492 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1493 d->set_reference_subtitle (false);
1497 was_referencing.push_back (d->path(0).parent_path().filename());
1502 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1503 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()));
1510 Film::use_template (string name)
1512 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1513 _template_film->read_metadata (Config::instance()->template_path (name));
1514 _use_isdcf_name = _template_film->_use_isdcf_name;
1515 _dcp_content_type = _template_film->_dcp_content_type;
1516 _container = _template_film->_container;
1517 _resolution = _template_film->_resolution;
1518 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1519 _video_frame_rate = _template_film->_video_frame_rate;
1520 _signed = _template_film->_signed;
1521 _encrypted = _template_film->_encrypted;
1522 _audio_channels = _template_film->_audio_channels;
1523 _sequence = _template_film->_sequence;
1524 _three_d = _template_film->_three_d;
1525 _interop = _template_film->_interop;
1526 _audio_processor = _template_film->_audio_processor;
1527 _reel_type = _template_film->_reel_type;
1528 _reel_length = _template_film->_reel_length;
1529 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;