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 if (container() == 0) {
292 throw MissingSettingError (_("container"));
295 if (content().empty()) {
296 throw runtime_error (_("you must add some content to the DCP before creating it."));
299 if (dcp_content_type() == 0) {
300 throw MissingSettingError (_("content type"));
303 if (name().empty()) {
304 throw MissingSettingError (_("name"));
307 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
308 if (!i->paths_valid()) {
309 throw runtime_error (_("some of your content is missing."));
311 shared_ptr<const DCPContent> dcp = dynamic_pointer_cast<const DCPContent> (i);
312 if (dcp && dcp->needs_kdm()) {
313 throw runtime_error (_("some of your content needs a KDM."));
315 if (dcp && dcp->needs_assets()) {
316 throw runtime_error (_("some of your content needs an OV."));
320 set_isdcf_date_today ();
322 BOOST_FOREACH (string i, environment_info ()) {
326 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
327 LOG_GENERAL ("Content: %1", i->technical_summary());
329 LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
330 if (Config::instance()->only_servers_encode ()) {
331 LOG_GENERAL_NC ("0 threads: ONLY SERVERS SET TO ENCODE");
333 LOG_GENERAL ("%1 threads", Config::instance()->num_local_encoding_threads());
335 LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
337 JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
340 /** Start a job to send our DCP to the configured TMS */
342 Film::send_dcp_to_tms ()
344 shared_ptr<Job> j (new UploadJob (shared_from_this()));
345 JobManager::instance()->add (j);
348 shared_ptr<xmlpp::Document>
349 Film::metadata (bool with_content_paths) const
351 shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
352 xmlpp::Element* root = doc->create_root_node ("Metadata");
354 root->add_child("Version")->add_child_text (raw_convert<string> (current_state_version));
355 root->add_child("Name")->add_child_text (_name);
356 root->add_child("UseISDCFName")->add_child_text (_use_isdcf_name ? "1" : "0");
358 if (_dcp_content_type) {
359 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->isdcf_name ());
363 root->add_child("Container")->add_child_text (_container->id ());
366 root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
367 root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
368 _isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
369 root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
370 root->add_child("ISDCFDate")->add_child_text (boost::gregorian::to_iso_string (_isdcf_date));
371 root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
372 root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
373 root->add_child("Sequence")->add_child_text (_sequence ? "1" : "0");
374 root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
375 root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
376 root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
377 root->add_child("Key")->add_child_text (_key.hex ());
378 root->add_child("ContextID")->add_child_text (_context_id);
379 if (_audio_processor) {
380 root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
382 root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
383 root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
384 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
385 _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
390 /** Write state to our `metadata' file */
392 Film::write_metadata () const
394 DCPOMATIC_ASSERT (directory());
395 boost::filesystem::create_directories (directory().get());
396 shared_ptr<xmlpp::Document> doc = metadata ();
397 doc->write_to_file_formatted (file("metadata.xml").string ());
401 /** Write a template from this film */
403 Film::write_template (boost::filesystem::path path) const
405 boost::filesystem::create_directories (path.parent_path());
406 shared_ptr<xmlpp::Document> doc = metadata (false);
407 doc->write_to_file_formatted (path.string ());
410 /** Read state from our metadata file.
411 * @return Notes about things that the user should know about, or empty.
414 Film::read_metadata (optional<boost::filesystem::path> path)
417 if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
418 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!"));
421 path = file ("metadata.xml");
424 cxml::Document f ("Metadata");
425 f.read_file (path.get ());
427 _state_version = f.number_child<int> ("Version");
428 if (_state_version > current_state_version) {
429 throw runtime_error (_("This film was created with a newer version of DCP-o-matic, and it cannot be loaded into this version. Sorry!"));
432 _name = f.string_child ("Name");
433 if (_state_version >= 9) {
434 _use_isdcf_name = f.bool_child ("UseISDCFName");
435 _isdcf_metadata = ISDCFMetadata (f.node_child ("ISDCFMetadata"));
436 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("ISDCFDate"));
438 _use_isdcf_name = f.bool_child ("UseDCIName");
439 _isdcf_metadata = ISDCFMetadata (f.node_child ("DCIMetadata"));
440 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
444 optional<string> c = f.optional_string_child ("DCPContentType");
446 _dcp_content_type = DCPContentType::from_isdcf_name (c.get ());
451 optional<string> c = f.optional_string_child ("Container");
453 _container = Ratio::from_id (c.get ());
457 _resolution = string_to_resolution (f.string_child ("Resolution"));
458 _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
459 _video_frame_rate = f.number_child<int> ("VideoFrameRate");
460 _signed = f.optional_bool_child("Signed").get_value_or (true);
461 _encrypted = f.bool_child ("Encrypted");
462 _audio_channels = f.number_child<int> ("AudioChannels");
463 /* We used to allow odd numbers (and zero) channels, but it's just not worth
466 if (_audio_channels == 0) {
468 } else if ((_audio_channels % 2) == 1) {
472 if (f.optional_bool_child("SequenceVideo")) {
473 _sequence = f.bool_child("SequenceVideo");
475 _sequence = f.bool_child("Sequence");
478 _three_d = f.bool_child ("ThreeD");
479 _interop = f.bool_child ("Interop");
480 _key = dcp::Key (f.string_child ("Key"));
481 _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ());
483 if (f.optional_string_child ("AudioProcessor")) {
484 _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
486 _audio_processor = 0;
489 _reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
490 _reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
491 _upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
494 /* This method is the only one that can return notes (so far) */
495 _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
497 /* Write backtraces to this film's directory, until another film is loaded */
499 set_backtrace_file (file ("backtrace.txt"));
506 /** Given a directory name, return its full path within the Film's directory.
507 * The directory (and its parents) will be created if they do not exist.
509 boost::filesystem::path
510 Film::dir (boost::filesystem::path d) const
512 DCPOMATIC_ASSERT (_directory);
514 boost::filesystem::path p;
515 p /= _directory.get();
518 boost::filesystem::create_directories (p);
523 /** Given a file or directory name, return its full path within the Film's directory.
524 * Any required parent directories will be created.
526 boost::filesystem::path
527 Film::file (boost::filesystem::path f) const
529 DCPOMATIC_ASSERT (_directory);
531 boost::filesystem::path p;
532 p /= _directory.get();
535 boost::filesystem::create_directories (p.parent_path ());
541 Film::mapped_audio_channels () const
545 if (audio_processor ()) {
546 /* Processors are mapped 1:1 to DCP outputs so we can work out mappings from there */
547 for (int i = 0; i < audio_processor()->out_channels(); ++i) {
548 mapped.push_back (i);
551 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
553 list<int> c = i->audio->mapping().mapped_output_channels ();
554 copy (c.begin(), c.end(), back_inserter (mapped));
565 /** @return a ISDCF-compliant name for a DCP of this film */
567 Film::isdcf_name (bool if_created_now) const
571 string raw_name = name ();
573 /* Split the raw name up into words */
574 vector<string> words;
575 split (words, raw_name, is_any_of (" _-"));
579 /* Add each word to fixed_name */
580 for (vector<string>::const_iterator i = words.begin(); i != words.end(); ++i) {
583 /* First letter is always capitalised */
584 w[0] = toupper (w[0]);
586 /* Count caps in w */
588 for (size_t i = 0; i < w.size(); ++i) {
589 if (isupper (w[i])) {
594 /* If w is all caps make the rest of it lower case, otherwise
597 if (caps == w.size ()) {
598 for (size_t i = 1; i < w.size(); ++i) {
599 w[i] = tolower (w[i]);
603 for (size_t i = 0; i < w.size(); ++i) {
608 if (fixed_name.length() > 14) {
609 fixed_name = fixed_name.substr (0, 14);
614 if (dcp_content_type()) {
615 d += "_" + dcp_content_type()->isdcf_name();
616 d += "-" + raw_convert<string>(isdcf_metadata().content_version);
619 ISDCFMetadata const dm = isdcf_metadata ();
621 if (dm.temp_version) {
625 if (dm.pre_release) {
633 if (!dm.chain.empty ()) {
641 if (dm.two_d_version_of_three_d) {
645 if (!dm.mastered_luminance.empty ()) {
646 d += "-" + dm.mastered_luminance;
649 if (video_frame_rate() != 24) {
650 d += "-" + raw_convert<string>(video_frame_rate());
654 d += "_" + container()->isdcf_name();
657 /* XXX: this uses the first bit of content only */
659 /* The standard says we don't do this for trailers, for some strange reason */
660 if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
661 Ratio const * content_ratio = 0;
662 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
664 /* Here's the first piece of video content */
665 if (i->video->scale().ratio ()) {
666 content_ratio = i->video->scale().ratio ();
668 content_ratio = Ratio::from_ratio (i->video->size().ratio ());
674 if (content_ratio && content_ratio != container()) {
675 d += "-" + content_ratio->isdcf_name();
679 if (!dm.audio_language.empty ()) {
680 d += "_" + dm.audio_language;
681 if (!dm.subtitle_language.empty()) {
683 bool burnt_in = true;
684 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
689 if (i->subtitle->use() && !i->subtitle->burn()) {
694 string language = dm.subtitle_language;
695 if (burnt_in && language != "XX") {
696 transform (language.begin(), language.end(), language.begin(), ::tolower);
698 transform (language.begin(), language.end(), language.begin(), ::toupper);
707 if (!dm.territory.empty ()) {
708 d += "_" + dm.territory;
709 if (dm.rating.empty ()) {
712 d += "-" + dm.rating;
716 /* Count mapped audio channels */
721 BOOST_FOREACH (int i, mapped_audio_channels ()) {
722 if (i >= audio_channels()) {
723 /* This channel is mapped but is not included in the DCP */
727 if (static_cast<dcp::Channel> (i) == dcp::LFE) {
735 d += String::compose("_%1%2", non_lfe, lfe);
740 d += "_" + resolution_to_string (_resolution);
742 if (!dm.studio.empty ()) {
743 d += "_" + dm.studio;
746 if (if_created_now) {
747 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
749 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
752 if (!dm.facility.empty ()) {
753 d += "_" + dm.facility;
767 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
768 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
769 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
783 /** @return name to give the DCP */
785 Film::dcp_name (bool if_created_now) const
788 if (use_isdcf_name()) {
789 unfiltered = isdcf_name (if_created_now);
791 unfiltered = name ();
794 /* Filter out `bad' characters which cause problems with some systems.
795 There's no apparent list of what really is allowed, so this is a guess.
799 string const allowed = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_";
800 for (size_t i = 0; i < unfiltered.size(); ++i) {
801 if (allowed.find (unfiltered[i]) != string::npos) {
802 filtered += unfiltered[i];
810 Film::set_directory (boost::filesystem::path d)
817 Film::set_name (string n)
820 signal_changed (NAME);
824 Film::set_use_isdcf_name (bool u)
827 signal_changed (USE_ISDCF_NAME);
831 Film::set_dcp_content_type (DCPContentType const * t)
833 _dcp_content_type = t;
834 signal_changed (DCP_CONTENT_TYPE);
838 Film::set_container (Ratio const * c)
841 signal_changed (CONTAINER);
845 Film::set_resolution (Resolution r)
848 signal_changed (RESOLUTION);
852 Film::set_j2k_bandwidth (int b)
855 signal_changed (J2K_BANDWIDTH);
859 Film::set_isdcf_metadata (ISDCFMetadata m)
862 signal_changed (ISDCF_METADATA);
866 Film::set_video_frame_rate (int f)
868 _video_frame_rate = f;
869 signal_changed (VIDEO_FRAME_RATE);
873 Film::set_audio_channels (int c)
876 signal_changed (AUDIO_CHANNELS);
880 Film::set_three_d (bool t)
883 signal_changed (THREE_D);
885 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
886 _isdcf_metadata.two_d_version_of_three_d = false;
887 signal_changed (ISDCF_METADATA);
892 Film::set_interop (bool i)
895 signal_changed (INTEROP);
899 Film::set_audio_processor (AudioProcessor const * processor)
901 _audio_processor = processor;
902 signal_changed (AUDIO_PROCESSOR);
903 signal_changed (AUDIO_CHANNELS);
907 Film::set_reel_type (ReelType t)
910 signal_changed (REEL_TYPE);
913 /** @param r Desired reel length in bytes */
915 Film::set_reel_length (int64_t r)
918 signal_changed (REEL_LENGTH);
922 Film::set_upload_after_make_dcp (bool u)
924 _upload_after_make_dcp = u;
925 signal_changed (UPLOAD_AFTER_MAKE_DCP);
929 Film::signal_changed (Property p)
935 set_video_frame_rate (_playlist->best_video_frame_rate ());
937 case Film::VIDEO_FRAME_RATE:
939 _playlist->maybe_sequence ();
945 emit (boost::bind (boost::ref (Changed), p));
949 Film::set_isdcf_date_today ()
951 _isdcf_date = boost::gregorian::day_clock::local_day ();
954 boost::filesystem::path
955 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
957 boost::filesystem::path p;
959 p /= video_identifier ();
962 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
965 if (eyes == EYES_LEFT) {
967 } else if (eyes == EYES_RIGHT) {
981 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
986 return vector<CPLSummary> ();
989 vector<CPLSummary> out;
991 boost::filesystem::path const dir = directory().get();
992 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
994 boost::filesystem::is_directory (*i) &&
995 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
1001 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
1004 i->path().leaf().string(),
1005 dcp.cpls().front()->id(),
1006 dcp.cpls().front()->annotation_text(),
1007 dcp.cpls().front()->file().get()
1020 Film::set_signed (bool s)
1023 signal_changed (SIGNED);
1027 Film::set_encrypted (bool e)
1030 signal_changed (ENCRYPTED);
1034 Film::set_key (dcp::Key key)
1037 signal_changed (KEY);
1041 Film::content () const
1043 return _playlist->content ();
1047 Film::examine_and_add_content (shared_ptr<Content> c)
1049 if (dynamic_pointer_cast<FFmpegContent> (c) && _directory) {
1050 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
1053 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
1055 _job_connections.push_back (
1056 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job> (j), weak_ptr<Content> (c)))
1059 JobManager::instance()->add (j);
1063 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
1065 shared_ptr<Job> job = j.lock ();
1066 if (!job || !job->finished_ok ()) {
1070 shared_ptr<Content> content = c.lock ();
1075 add_content (content);
1077 if (Config::instance()->automatic_audio_analysis() && content->audio) {
1078 shared_ptr<Playlist> playlist (new Playlist);
1079 playlist->add (content);
1080 boost::signals2::connection c;
1081 JobManager::instance()->analyse_audio (
1082 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1084 _audio_analysis_connections.push_back (c);
1089 Film::add_content (shared_ptr<Content> c)
1091 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1093 c->set_position (_playlist->video_end ());
1094 } else if (c->subtitle) {
1095 c->set_position (_playlist->subtitle_end ());
1098 if (_template_film) {
1099 /* Take settings from the first piece of content of c's type in _template */
1100 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1101 if (typeid(i.get()) == typeid(c.get())) {
1102 c->use_template (i);
1111 Film::remove_content (shared_ptr<Content> c)
1113 _playlist->remove (c);
1117 Film::move_content_earlier (shared_ptr<Content> c)
1119 _playlist->move_earlier (c);
1123 Film::move_content_later (shared_ptr<Content> c)
1125 _playlist->move_later (c);
1128 /** @return length of the film from time 0 to the last thing on the playlist */
1130 Film::length () const
1132 return _playlist->length ();
1136 Film::best_video_frame_rate () const
1138 return _playlist->best_video_frame_rate ();
1142 Film::active_frame_rate_change (DCPTime t) const
1144 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1148 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1152 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1153 set_video_frame_rate (_playlist->best_video_frame_rate ());
1154 } else if (p == AudioContentProperty::STREAMS) {
1155 signal_changed (NAME);
1158 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1162 Film::playlist_changed ()
1164 signal_changed (CONTENT);
1165 signal_changed (NAME);
1169 Film::playlist_order_changed ()
1171 signal_changed (CONTENT_ORDER);
1175 Film::audio_frame_rate () const
1177 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1178 if (i->audio && i->audio->has_rate_above_48k ()) {
1187 Film::set_sequence (bool s)
1190 _playlist->set_sequence (s);
1191 signal_changed (SEQUENCE);
1194 /** @return Size of the largest possible image in whatever resolution we are using */
1196 Film::full_frame () const
1198 switch (_resolution) {
1200 return dcp::Size (2048, 1080);
1202 return dcp::Size (4096, 2160);
1205 DCPOMATIC_ASSERT (false);
1206 return dcp::Size ();
1209 /** @return Size of the frame */
1211 Film::frame_size () const
1213 return fit_ratio_within (container()->ratio(), full_frame ());
1216 /** @param from KDM from time expressed as a local time with an offset from UTC
1217 * @param to KDM to time expressed as a local time with an offset from UTC
1221 dcp::Certificate recipient,
1222 vector<dcp::Certificate> trusted_devices,
1223 boost::filesystem::path cpl_file,
1224 dcp::LocalTime from,
1225 dcp::LocalTime until,
1226 dcp::Formulation formulation
1229 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1230 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1231 if (!signer->valid ()) {
1232 throw InvalidSignerError ();
1235 return dcp::DecryptedKDM (
1236 cpl, key(), from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1237 ).encrypt (signer, recipient, trusted_devices, formulation);
1240 /** @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1241 * @param to KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1245 list<shared_ptr<Screen> > screens,
1246 boost::filesystem::path dcp,
1247 boost::posix_time::ptime from,
1248 boost::posix_time::ptime until,
1249 dcp::Formulation formulation
1252 list<ScreenKDM> kdms;
1254 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1256 dcp::EncryptedKDM const kdm = make_kdm (
1260 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1261 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1265 kdms.push_back (ScreenKDM (i, kdm));
1272 /** @return The approximate disk space required to encode a DCP of this film with the
1273 * current settings, in bytes.
1276 Film::required_disk_space () const
1278 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1281 /** This method checks the disk that the Film is on and tries to decide whether or not
1282 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1283 * false is returned and required and available are filled in with the amount of disk space
1284 * required and available respectively (in Gb).
1286 * Note: the decision made by this method isn't, of course, 100% reliable.
1289 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1291 /* Create a test file and see if we can hard-link it */
1292 boost::filesystem::path test = internal_video_asset_dir() / "test";
1293 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1294 can_hard_link = true;
1295 FILE* f = fopen_boost (test, "w");
1298 boost::system::error_code ec;
1299 boost::filesystem::create_hard_link (test, test2, ec);
1301 can_hard_link = false;
1303 boost::filesystem::remove (test);
1304 boost::filesystem::remove (test2);
1307 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1308 required = double (required_disk_space ()) / 1073741824.0f;
1309 if (!can_hard_link) {
1312 available = double (s.available) / 1073741824.0f;
1313 return (available - required) > 1;
1317 Film::subtitle_language () const
1319 set<string> languages;
1321 ContentList cl = content ();
1322 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1324 languages.insert (c->subtitle->language ());
1329 BOOST_FOREACH (string s, languages) {
1330 if (!all.empty ()) {
1340 /** Change the gains of the supplied AudioMapping to make it a default
1341 * for this film. The defaults are guessed based on what processor (if any)
1342 * is in use, the number of input channels and any filename supplied.
1345 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1347 static string const regex[] = {
1348 ".*[\\._-]L[\\._-].*",
1349 ".*[\\._-]R[\\._-].*",
1350 ".*[\\._-]C[\\._-].*",
1351 ".*[\\._-]Lfe[\\._-].*",
1352 ".*[\\._-]Ls[\\._-].*",
1353 ".*[\\._-]Rs[\\._-].*"
1356 static int const regexes = sizeof(regex) / sizeof(*regex);
1358 if (audio_processor ()) {
1359 audio_processor()->make_audio_mapping_default (mapping);
1361 mapping.make_zero ();
1362 if (mapping.input_channels() == 1) {
1363 bool guessed = false;
1365 /* See if we can guess where this stream should go */
1367 for (int i = 0; i < regexes; ++i) {
1368 boost::regex e (regex[i], boost::regex::icase);
1369 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1370 mapping.set (0, i, 1);
1377 /* If we have no idea, just put it on centre */
1378 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1382 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1383 mapping.set (i, i, 1);
1389 /** @return The names of the channels that audio contents' outputs are passed into;
1390 * this is either the DCP or a AudioProcessor.
1393 Film::audio_output_names () const
1395 if (audio_processor ()) {
1396 return audio_processor()->input_names ();
1399 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1403 for (int i = 0; i < audio_channels(); ++i) {
1404 n.push_back (short_audio_channel_name (i));
1411 Film::repeat_content (ContentList c, int n)
1413 _playlist->repeat (c, n);
1417 Film::remove_content (ContentList c)
1419 _playlist->remove (c);
1423 Film::audio_analysis_finished ()
1429 Film::reels () const
1431 list<DCPTimePeriod> p;
1432 DCPTime const len = length().ceil (video_frame_rate ());
1434 switch (reel_type ()) {
1435 case REELTYPE_SINGLE:
1436 p.push_back (DCPTimePeriod (DCPTime (), len));
1438 case REELTYPE_BY_VIDEO_CONTENT:
1440 optional<DCPTime> last_split;
1441 shared_ptr<Content> last_video;
1442 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1444 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1446 p.push_back (DCPTimePeriod (last_split.get(), t));
1454 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1456 /* Definitely go from the last split to the end of the video content */
1457 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1460 if (video_end < len) {
1461 /* And maybe go after that as well if there is any non-video hanging over the end */
1462 p.push_back (DCPTimePeriod (video_end, len));
1466 case REELTYPE_BY_LENGTH:
1469 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1470 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1471 while (current < len) {
1472 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1473 p.push_back (DCPTimePeriod (current, end));
1484 Film::content_summary (DCPTimePeriod period) const
1486 return _playlist->content_summary (period);
1490 Film::fix_conflicting_settings ()
1494 list<boost::filesystem::path> was_referencing;
1495 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1496 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1498 list<string> reasons;
1500 if (!d->can_reference_video(reasons) && d->reference_video()) {
1501 d->set_reference_video (false);
1504 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1505 d->set_reference_audio (false);
1508 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1509 d->set_reference_subtitle (false);
1513 was_referencing.push_back (d->path(0).parent_path().filename());
1518 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1519 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()));
1526 Film::use_template (string name)
1528 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1529 _template_film->read_metadata (Config::instance()->template_path (name));
1530 _use_isdcf_name = _template_film->_use_isdcf_name;
1531 _dcp_content_type = _template_film->_dcp_content_type;
1532 _container = _template_film->_container;
1533 _resolution = _template_film->_resolution;
1534 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1535 _video_frame_rate = _template_film->_video_frame_rate;
1536 _signed = _template_film->_signed;
1537 _encrypted = _template_film->_encrypted;
1538 _audio_channels = _template_film->_audio_channels;
1539 _sequence = _template_film->_sequence;
1540 _three_d = _template_film->_three_d;
1541 _interop = _template_film->_interop;
1542 _audio_processor = _template_film->_audio_processor;
1543 _reel_type = _template_film->_reel_type;
1544 _reel_length = _template_film->_reel_length;
1545 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;