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 , _context_id (dcp::make_uuid ())
136 , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
137 , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
138 , _video_frame_rate (24)
139 , _audio_channels (Config::instance()->default_dcp_audio_channels ())
142 , _interop (Config::instance()->default_interop ())
143 , _audio_processor (0)
144 , _reel_type (REELTYPE_SINGLE)
145 , _reel_length (2000000000)
146 , _upload_after_make_dcp (false)
147 , _state_version (current_state_version)
150 set_isdcf_date_today ();
152 _playlist_changed_connection = _playlist->Changed.connect (bind (&Film::playlist_changed, this));
153 _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
154 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
157 /* Make state.directory a complete path without ..s (where possible)
158 (Code swiped from Adam Bowen on stackoverflow)
159 XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
162 boost::filesystem::path p (boost::filesystem::system_complete (dir.get()));
163 boost::filesystem::path result;
164 for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
166 if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
169 result = result.parent_path ();
171 } else if (*i != ".") {
176 set_directory (result.make_preferred ());
180 _log.reset (new FileLog (file ("log")));
182 _log.reset (new NullLog);
185 _playlist->set_sequence (_sequence);
190 BOOST_FOREACH (boost::signals2::connection& i, _job_connections) {
194 BOOST_FOREACH (boost::signals2::connection& i, _audio_analysis_connections) {
200 Film::video_identifier () const
202 DCPOMATIC_ASSERT (container ());
204 string s = container()->id()
205 + "_" + resolution_to_string (_resolution)
206 + "_" + _playlist->video_identifier()
207 + "_" + raw_convert<string>(_video_frame_rate)
208 + "_" + raw_convert<string>(j2k_bandwidth());
229 /** @return The file to write video frame info to */
230 boost::filesystem::path
231 Film::info_file (DCPTimePeriod period) const
233 boost::filesystem::path p;
235 p /= video_identifier () + "_" + raw_convert<string> (period.from.get()) + "_" + raw_convert<string> (period.to.get());
239 boost::filesystem::path
240 Film::internal_video_asset_dir () const
242 return dir ("video");
245 boost::filesystem::path
246 Film::internal_video_asset_filename (DCPTimePeriod p) const
248 return video_identifier() + "_" + raw_convert<string> (p.from.get()) + "_" + raw_convert<string> (p.to.get()) + ".mxf";
251 boost::filesystem::path
252 Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
254 boost::filesystem::path p = dir ("analysis");
257 BOOST_FOREACH (shared_ptr<Content> i, playlist->content ()) {
262 digester.add (i->digest ());
263 digester.add (i->audio->mapping().digest ());
264 if (playlist->content().size() != 1) {
265 /* Analyses should be considered equal regardless of gain
266 if they were made from just one piece of content. This
267 is because we can fake any gain change in a single-content
268 analysis at the plotting stage rather than having to
271 digester.add (i->audio->gain ());
275 if (audio_processor ()) {
276 digester.add (audio_processor()->id ());
279 p /= digester.get ();
283 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
287 if (dcp_name().find ("/") != string::npos) {
288 throw BadSettingError (_("name"), _("cannot contain slashes"));
291 set_isdcf_date_today ();
293 BOOST_FOREACH (string i, environment_info ()) {
297 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
298 LOG_GENERAL ("Content: %1", i->technical_summary());
300 LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
301 if (Config::instance()->only_servers_encode ()) {
302 LOG_GENERAL_NC ("0 threads: ONLY SERVERS SET TO ENCODE");
304 LOG_GENERAL ("%1 threads", Config::instance()->num_local_encoding_threads());
306 LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
308 if (container() == 0) {
309 throw MissingSettingError (_("container"));
312 if (content().empty()) {
313 throw runtime_error (_("You must add some content to the DCP before creating it"));
316 if (dcp_content_type() == 0) {
317 throw MissingSettingError (_("content type"));
320 if (name().empty()) {
321 throw MissingSettingError (_("name"));
324 JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
327 /** Start a job to send our DCP to the configured TMS */
329 Film::send_dcp_to_tms ()
331 shared_ptr<Job> j (new UploadJob (shared_from_this()));
332 JobManager::instance()->add (j);
335 shared_ptr<xmlpp::Document>
336 Film::metadata (bool with_content_paths) const
338 shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
339 xmlpp::Element* root = doc->create_root_node ("Metadata");
341 root->add_child("Version")->add_child_text (raw_convert<string> (current_state_version));
342 root->add_child("Name")->add_child_text (_name);
343 root->add_child("UseISDCFName")->add_child_text (_use_isdcf_name ? "1" : "0");
345 if (_dcp_content_type) {
346 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->isdcf_name ());
350 root->add_child("Container")->add_child_text (_container->id ());
353 root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
354 root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
355 _isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
356 root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
357 root->add_child("ISDCFDate")->add_child_text (boost::gregorian::to_iso_string (_isdcf_date));
358 root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
359 root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
360 root->add_child("Sequence")->add_child_text (_sequence ? "1" : "0");
361 root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
362 root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
363 root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
364 root->add_child("Key")->add_child_text (_key.hex ());
365 root->add_child("ContextID")->add_child_text (_context_id);
366 if (_audio_processor) {
367 root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
369 root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
370 root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
371 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
372 _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
377 /** Write state to our `metadata' file */
379 Film::write_metadata () const
381 DCPOMATIC_ASSERT (directory());
382 boost::filesystem::create_directories (directory().get());
383 shared_ptr<xmlpp::Document> doc = metadata ();
384 doc->write_to_file_formatted (file("metadata.xml").string ());
388 /** Write a template from this film */
390 Film::write_template (boost::filesystem::path path) const
392 boost::filesystem::create_directories (path.parent_path());
393 shared_ptr<xmlpp::Document> doc = metadata (false);
394 doc->write_to_file_formatted (path.string ());
397 /** Read state from our metadata file.
398 * @return Notes about things that the user should know about, or empty.
401 Film::read_metadata (optional<boost::filesystem::path> path)
404 if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
405 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!"));
408 path = file ("metadata.xml");
411 cxml::Document f ("Metadata");
412 f.read_file (path.get ());
414 _state_version = f.number_child<int> ("Version");
415 if (_state_version > current_state_version) {
416 throw runtime_error (_("This film was created with a newer version of DCP-o-matic, and it cannot be loaded into this version. Sorry!"));
419 _name = f.string_child ("Name");
420 if (_state_version >= 9) {
421 _use_isdcf_name = f.bool_child ("UseISDCFName");
422 _isdcf_metadata = ISDCFMetadata (f.node_child ("ISDCFMetadata"));
423 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("ISDCFDate"));
425 _use_isdcf_name = f.bool_child ("UseDCIName");
426 _isdcf_metadata = ISDCFMetadata (f.node_child ("DCIMetadata"));
427 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
431 optional<string> c = f.optional_string_child ("DCPContentType");
433 _dcp_content_type = DCPContentType::from_isdcf_name (c.get ());
438 optional<string> c = f.optional_string_child ("Container");
440 _container = Ratio::from_id (c.get ());
444 _resolution = string_to_resolution (f.string_child ("Resolution"));
445 _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
446 _video_frame_rate = f.number_child<int> ("VideoFrameRate");
447 _signed = f.optional_bool_child("Signed").get_value_or (true);
448 _encrypted = f.bool_child ("Encrypted");
449 _audio_channels = f.number_child<int> ("AudioChannels");
450 /* We used to allow odd numbers (and zero) channels, but it's just not worth
453 if (_audio_channels == 0) {
455 } else if ((_audio_channels % 2) == 1) {
459 if (f.optional_bool_child("SequenceVideo")) {
460 _sequence = f.bool_child("SequenceVideo");
462 _sequence = f.bool_child("Sequence");
465 _three_d = f.bool_child ("ThreeD");
466 _interop = f.bool_child ("Interop");
467 _key = dcp::Key (f.string_child ("Key"));
468 _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ());
470 if (f.optional_string_child ("AudioProcessor")) {
471 _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
473 _audio_processor = 0;
476 _reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
477 _reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
478 _upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
481 /* This method is the only one that can return notes (so far) */
482 _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
484 /* Write backtraces to this film's directory, until another film is loaded */
486 set_backtrace_file (file ("backtrace.txt"));
493 /** Given a directory name, return its full path within the Film's directory.
494 * The directory (and its parents) will be created if they do not exist.
496 boost::filesystem::path
497 Film::dir (boost::filesystem::path d) const
499 DCPOMATIC_ASSERT (_directory);
501 boost::filesystem::path p;
502 p /= _directory.get();
505 boost::filesystem::create_directories (p);
510 /** Given a file or directory name, return its full path within the Film's directory.
511 * Any required parent directories will be created.
513 boost::filesystem::path
514 Film::file (boost::filesystem::path f) const
516 DCPOMATIC_ASSERT (_directory);
518 boost::filesystem::path p;
519 p /= _directory.get();
522 boost::filesystem::create_directories (p.parent_path ());
528 Film::mapped_audio_channels () const
532 if (audio_processor ()) {
533 /* Processors are mapped 1:1 to DCP outputs so we can work out mappings from there */
534 for (int i = 0; i < audio_processor()->out_channels(); ++i) {
535 mapped.push_back (i);
538 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
540 list<int> c = i->audio->mapping().mapped_output_channels ();
541 copy (c.begin(), c.end(), back_inserter (mapped));
552 /** @return a ISDCF-compliant name for a DCP of this film */
554 Film::isdcf_name (bool if_created_now) const
558 string raw_name = name ();
560 /* Split the raw name up into words */
561 vector<string> words;
562 split (words, raw_name, is_any_of (" _-"));
566 /* Add each word to fixed_name */
567 for (vector<string>::const_iterator i = words.begin(); i != words.end(); ++i) {
570 /* First letter is always capitalised */
571 w[0] = toupper (w[0]);
573 /* Count caps in w */
575 for (size_t i = 0; i < w.size(); ++i) {
576 if (isupper (w[i])) {
581 /* If w is all caps make the rest of it lower case, otherwise
584 if (caps == w.size ()) {
585 for (size_t i = 1; i < w.size(); ++i) {
586 w[i] = tolower (w[i]);
590 for (size_t i = 0; i < w.size(); ++i) {
595 if (fixed_name.length() > 14) {
596 fixed_name = fixed_name.substr (0, 14);
601 if (dcp_content_type()) {
602 d += "_" + dcp_content_type()->isdcf_name();
603 d += "-" + raw_convert<string>(isdcf_metadata().content_version);
606 ISDCFMetadata const dm = isdcf_metadata ();
608 if (dm.temp_version) {
612 if (dm.pre_release) {
620 if (!dm.chain.empty ()) {
628 if (dm.two_d_version_of_three_d) {
632 if (!dm.mastered_luminance.empty ()) {
633 d += "-" + dm.mastered_luminance;
636 if (video_frame_rate() != 24) {
637 d += "-" + raw_convert<string>(video_frame_rate());
641 d += "_" + container()->isdcf_name();
644 /* XXX: this uses the first bit of content only */
646 /* The standard says we don't do this for trailers, for some strange reason */
647 if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
648 Ratio const * content_ratio = 0;
649 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
651 /* Here's the first piece of video content */
652 if (i->video->scale().ratio ()) {
653 content_ratio = i->video->scale().ratio ();
655 content_ratio = Ratio::from_ratio (i->video->size().ratio ());
661 if (content_ratio && content_ratio != container()) {
662 d += "-" + content_ratio->isdcf_name();
666 if (!dm.audio_language.empty ()) {
667 d += "_" + dm.audio_language;
668 if (!dm.subtitle_language.empty()) {
670 bool burnt_in = true;
671 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
676 if (i->subtitle->use() && !i->subtitle->burn()) {
681 string language = dm.subtitle_language;
682 if (burnt_in && language != "XX") {
683 transform (language.begin(), language.end(), language.begin(), ::tolower);
685 transform (language.begin(), language.end(), language.begin(), ::toupper);
694 if (!dm.territory.empty ()) {
695 d += "_" + dm.territory;
696 if (dm.rating.empty ()) {
699 d += "-" + dm.rating;
703 /* Count mapped audio channels */
708 BOOST_FOREACH (int i, mapped_audio_channels ()) {
709 if (i >= audio_channels()) {
710 /* This channel is mapped but is not included in the DCP */
714 if (static_cast<dcp::Channel> (i) == dcp::LFE) {
722 d += String::compose("_%1%2", non_lfe, lfe);
727 d += "_" + resolution_to_string (_resolution);
729 if (!dm.studio.empty ()) {
730 d += "_" + dm.studio;
733 if (if_created_now) {
734 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
736 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
739 if (!dm.facility.empty ()) {
740 d += "_" + dm.facility;
754 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
755 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
756 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
770 /** @return name to give the DCP */
772 Film::dcp_name (bool if_created_now) const
775 if (use_isdcf_name()) {
776 unfiltered = isdcf_name (if_created_now);
778 unfiltered = name ();
781 /* Filter out `bad' characters which cause problems with some systems.
782 There's no apparent list of what really is allowed, so this is a guess.
786 string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
787 for (size_t i = 0; i < unfiltered.size(); ++i) {
788 if (allowed.find (unfiltered[i]) != string::npos) {
789 filtered += unfiltered[i];
797 Film::set_directory (boost::filesystem::path d)
804 Film::set_name (string n)
807 signal_changed (NAME);
811 Film::set_use_isdcf_name (bool u)
814 signal_changed (USE_ISDCF_NAME);
818 Film::set_dcp_content_type (DCPContentType const * t)
820 _dcp_content_type = t;
821 signal_changed (DCP_CONTENT_TYPE);
825 Film::set_container (Ratio const * c)
828 signal_changed (CONTAINER);
832 Film::set_resolution (Resolution r)
835 signal_changed (RESOLUTION);
839 Film::set_j2k_bandwidth (int b)
842 signal_changed (J2K_BANDWIDTH);
846 Film::set_isdcf_metadata (ISDCFMetadata m)
849 signal_changed (ISDCF_METADATA);
853 Film::set_video_frame_rate (int f)
855 _video_frame_rate = f;
856 signal_changed (VIDEO_FRAME_RATE);
860 Film::set_audio_channels (int c)
863 signal_changed (AUDIO_CHANNELS);
867 Film::set_three_d (bool t)
870 signal_changed (THREE_D);
872 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
873 _isdcf_metadata.two_d_version_of_three_d = false;
874 signal_changed (ISDCF_METADATA);
879 Film::set_interop (bool i)
882 signal_changed (INTEROP);
886 Film::set_audio_processor (AudioProcessor const * processor)
888 _audio_processor = processor;
889 signal_changed (AUDIO_PROCESSOR);
890 signal_changed (AUDIO_CHANNELS);
894 Film::set_reel_type (ReelType t)
897 signal_changed (REEL_TYPE);
900 /** @param r Desired reel length in bytes */
902 Film::set_reel_length (int64_t r)
905 signal_changed (REEL_LENGTH);
909 Film::set_upload_after_make_dcp (bool u)
911 _upload_after_make_dcp = u;
912 signal_changed (UPLOAD_AFTER_MAKE_DCP);
916 Film::signal_changed (Property p)
922 set_video_frame_rate (_playlist->best_video_frame_rate ());
924 case Film::VIDEO_FRAME_RATE:
926 _playlist->maybe_sequence ();
932 emit (boost::bind (boost::ref (Changed), p));
936 Film::set_isdcf_date_today ()
938 _isdcf_date = boost::gregorian::day_clock::local_day ();
941 boost::filesystem::path
942 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
944 boost::filesystem::path p;
946 p /= video_identifier ();
949 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
952 if (eyes == EYES_LEFT) {
954 } else if (eyes == EYES_RIGHT) {
968 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
973 return vector<CPLSummary> ();
976 vector<CPLSummary> out;
978 boost::filesystem::path const dir = directory().get();
979 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
981 boost::filesystem::is_directory (*i) &&
982 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
988 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
991 i->path().leaf().string(),
992 dcp.cpls().front()->id(),
993 dcp.cpls().front()->annotation_text(),
994 dcp.cpls().front()->file().get()
1007 Film::set_signed (bool s)
1010 signal_changed (SIGNED);
1014 Film::set_encrypted (bool e)
1017 signal_changed (ENCRYPTED);
1021 Film::set_key (dcp::Key key)
1024 signal_changed (KEY);
1028 Film::content () const
1030 return _playlist->content ();
1034 Film::examine_and_add_content (shared_ptr<Content> c)
1036 if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
1037 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
1040 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1042 _job_connections.push_back (
1043 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
1046 JobManager::instance()->add (j);
1050 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
1052 shared_ptr<Job> job = j.lock ();
1053 if (!job || !job->finished_ok ()) {
1057 shared_ptr<Content> content = c.lock ();
1062 add_content (content);
1064 if (Config::instance()->automatic_audio_analysis() && content->audio) {
1065 shared_ptr<Playlist> playlist (new Playlist);
1066 playlist->add (content);
1067 boost::signals2::connection c;
1068 JobManager::instance()->analyse_audio (
1069 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1071 _audio_analysis_connections.push_back (c);
1076 Film::add_content (shared_ptr<Content> c)
1078 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1080 c->set_position (_playlist->video_end ());
1081 } else if (c->subtitle) {
1082 c->set_position (_playlist->subtitle_end ());
1085 if (_template_film) {
1086 /* Take settings from the first piece of content of c's type in _template */
1087 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1088 if (typeid(i.get()) == typeid(c.get())) {
1089 c->use_template (i);
1098 Film::remove_content (shared_ptr<Content> c)
1100 _playlist->remove (c);
1104 Film::move_content_earlier (shared_ptr<Content> c)
1106 _playlist->move_earlier (c);
1110 Film::move_content_later (shared_ptr<Content> c)
1112 _playlist->move_later (c);
1115 /** @return length of the film from time 0 to the last thing on the playlist */
1117 Film::length () const
1119 return _playlist->length ();
1123 Film::best_video_frame_rate () const
1125 return _playlist->best_video_frame_rate ();
1129 Film::active_frame_rate_change (DCPTime t) const
1131 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1135 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1139 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1140 set_video_frame_rate (_playlist->best_video_frame_rate ());
1141 } else if (p == AudioContentProperty::STREAMS) {
1142 signal_changed (NAME);
1145 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1149 Film::playlist_changed ()
1151 signal_changed (CONTENT);
1152 signal_changed (NAME);
1156 Film::playlist_order_changed ()
1158 signal_changed (CONTENT_ORDER);
1162 Film::audio_frame_rate () const
1164 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1165 if (i->audio && i->audio->has_rate_above_48k ()) {
1174 Film::set_sequence (bool s)
1177 _playlist->set_sequence (s);
1178 signal_changed (SEQUENCE);
1181 /** @return Size of the largest possible image in whatever resolution we are using */
1183 Film::full_frame () const
1185 switch (_resolution) {
1187 return dcp::Size (2048, 1080);
1189 return dcp::Size (4096, 2160);
1192 DCPOMATIC_ASSERT (false);
1193 return dcp::Size ();
1196 /** @return Size of the frame */
1198 Film::frame_size () const
1200 return fit_ratio_within (container()->ratio(), full_frame ());
1203 /** @param from KDM from time expressed as a local time with an offset from UTC
1204 * @param to KDM to time expressed as a local time with an offset from UTC
1208 dcp::Certificate recipient,
1209 vector<dcp::Certificate> trusted_devices,
1210 boost::filesystem::path cpl_file,
1211 dcp::LocalTime from,
1212 dcp::LocalTime until,
1213 dcp::Formulation formulation
1216 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1217 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1218 if (!signer->valid ()) {
1219 throw InvalidSignerError ();
1222 return dcp::DecryptedKDM (
1223 cpl, key(), from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1224 ).encrypt (signer, recipient, trusted_devices, formulation);
1227 /** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1228 * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1232 list<shared_ptr<Screen> > screens,
1233 boost::filesystem::path dcp,
1234 boost::posix_time::ptime from,
1235 boost::posix_time::ptime until,
1236 dcp::Formulation formulation
1239 list<ScreenKDM> kdms;
1241 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1243 dcp::EncryptedKDM const kdm = make_kdm (
1247 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1248 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1252 kdms.push_back (ScreenKDM (i, kdm));
1259 /** @return The approximate disk space required to encode a DCP of this film with the
1260 * current settings, in bytes.
1263 Film::required_disk_space () const
1265 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1268 /** This method checks the disk that the Film is on and tries to decide whether or not
1269 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1270 * false is returned and required and available are filled in with the amount of disk space
1271 * required and available respectively (in Gb).
1273 * Note: the decision made by this method isn't, of course, 100% reliable.
1276 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1278 /* Create a test file and see if we can hard-link it */
1279 boost::filesystem::path test = internal_video_asset_dir() / "test";
1280 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1281 can_hard_link = true;
1282 FILE* f = fopen_boost (test, "w");
1285 boost::system::error_code ec;
1286 boost::filesystem::create_hard_link (test, test2, ec);
1288 can_hard_link = false;
1290 boost::filesystem::remove (test);
1291 boost::filesystem::remove (test2);
1294 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1295 required = double (required_disk_space ()) / 1073741824.0f;
1296 if (!can_hard_link) {
1299 available = double (s.available) / 1073741824.0f;
1300 return (available - required) > 1;
1304 Film::subtitle_language () const
1306 set<string> languages;
1308 ContentList cl = content ();
1309 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1311 languages.insert (c->subtitle->language ());
1316 BOOST_FOREACH (string s, languages) {
1317 if (!all.empty ()) {
1327 /** Change the gains of the supplied AudioMapping to make it a default
1328 * for this film. The defaults are guessed based on what processor (if any)
1329 * is in use, the number of input channels and any filename supplied.
1332 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1334 static string const regex[] = {
1335 ".*[\\._-]L[\\._-].*",
1336 ".*[\\._-]R[\\._-].*",
1337 ".*[\\._-]C[\\._-].*",
1338 ".*[\\._-]Lfe[\\._-].*",
1339 ".*[\\._-]Ls[\\._-].*",
1340 ".*[\\._-]Rs[\\._-].*"
1343 static int const regexes = sizeof(regex) / sizeof(*regex);
1345 if (audio_processor ()) {
1346 audio_processor()->make_audio_mapping_default (mapping);
1348 mapping.make_zero ();
1349 if (mapping.input_channels() == 1) {
1350 bool guessed = false;
1352 /* See if we can guess where this stream should go */
1354 for (int i = 0; i < regexes; ++i) {
1355 boost::regex e (regex[i], boost::regex::icase);
1356 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1357 mapping.set (0, i, 1);
1364 /* If we have no idea, just put it on centre */
1365 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1369 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1370 mapping.set (i, i, 1);
1376 /** @return The names of the channels that audio contents' outputs are passed into;
1377 * this is either the DCP or a AudioProcessor.
1380 Film::audio_output_names () const
1382 if (audio_processor ()) {
1383 return audio_processor()->input_names ();
1386 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1390 for (int i = 0; i < audio_channels(); ++i) {
1391 n.push_back (short_audio_channel_name (i));
1398 Film::repeat_content (ContentList c, int n)
1400 _playlist->repeat (c, n);
1404 Film::remove_content (ContentList c)
1406 _playlist->remove (c);
1410 Film::audio_analysis_finished ()
1416 Film::reels () const
1418 list<DCPTimePeriod> p;
1419 DCPTime const len = length().round_up (video_frame_rate ());
1421 switch (reel_type ()) {
1422 case REELTYPE_SINGLE:
1423 p.push_back (DCPTimePeriod (DCPTime (), len));
1425 case REELTYPE_BY_VIDEO_CONTENT:
1427 optional<DCPTime> last_split;
1428 shared_ptr<Content> last_video;
1429 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1431 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1433 p.push_back (DCPTimePeriod (last_split.get(), t));
1441 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1443 /* Definitely go from the last split to the end of the video content */
1444 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1447 if (video_end < len) {
1448 /* And maybe go after that as well if there is any non-video hanging over the end */
1449 p.push_back (DCPTimePeriod (video_end, len));
1453 case REELTYPE_BY_LENGTH:
1456 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1457 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1458 while (current < len) {
1459 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1460 p.push_back (DCPTimePeriod (current, end));
1471 Film::content_summary (DCPTimePeriod period) const
1473 return _playlist->content_summary (period);
1477 Film::fix_conflicting_settings ()
1481 list<boost::filesystem::path> was_referencing;
1482 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1483 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1485 list<string> reasons;
1487 if (!d->can_reference_video(reasons) && d->reference_video()) {
1488 d->set_reference_video (false);
1491 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1492 d->set_reference_audio (false);
1495 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1496 d->set_reference_subtitle (false);
1500 was_referencing.push_back (d->path(0).parent_path().filename());
1505 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1506 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()));
1513 Film::use_template (string name)
1515 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1516 _template_film->read_metadata (Config::instance()->template_path (name));
1517 _use_isdcf_name = _template_film->_use_isdcf_name;
1518 _dcp_content_type = _template_film->_dcp_content_type;
1519 _container = _template_film->_container;
1520 _resolution = _template_film->_resolution;
1521 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1522 _video_frame_rate = _template_film->_video_frame_rate;
1523 _signed = _template_film->_signed;
1524 _encrypted = _template_film->_encrypted;
1525 _audio_channels = _template_film->_audio_channels;
1526 _sequence = _template_film->_sequence;
1527 _three_d = _template_film->_three_d;
1528 _interop = _template_film->_interop;
1529 _audio_processor = _template_film->_audio_processor;
1530 _reel_type = _template_film->_reel_type;
1531 _reel_length = _template_film->_reel_length;
1532 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;