2 Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
4 This file is part of DCP-o-matic.
6 DCP-o-matic is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 DCP-o-matic is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
22 * @brief A representation of some audio, video and subtitle content, and details of
23 * how they should be presented in a DCP.
29 #include "job_manager.h"
30 #include "dcp_encoder.h"
31 #include "transcode_job.h"
32 #include "upload_job.h"
35 #include "exceptions.h"
36 #include "examine_content_job.h"
39 #include "dcp_content_type.h"
42 #include "environment_info.h"
43 #include "audio_processor.h"
45 #include "compose.hpp"
47 #include "audio_content.h"
48 #include "video_content.h"
49 #include "subtitle_content.h"
50 #include "ffmpeg_content.h"
51 #include "dcp_content.h"
52 #include "screen_kdm.h"
54 #include <libcxml/cxml.h>
56 #include <dcp/certificate_chain.h>
58 #include <dcp/local_time.h>
59 #include <dcp/decrypted_kdm.h>
60 #include <dcp/raw_convert.h>
61 #include <dcp/reel_mxf.h>
62 #include <dcp/reel_asset.h>
63 #include <libxml++/libxml++.h>
64 #include <boost/filesystem.hpp>
65 #include <boost/algorithm/string.hpp>
66 #include <boost/foreach.hpp>
67 #include <boost/regex.hpp>
88 using std::runtime_error;
90 using std::back_inserter;
92 using boost::shared_ptr;
93 using boost::weak_ptr;
94 using boost::dynamic_pointer_cast;
95 using boost::optional;
96 using boost::is_any_of;
97 using dcp::raw_convert;
99 #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), LogEntry::TYPE_GENERAL);
100 #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, LogEntry::TYPE_GENERAL);
102 string const Film::metadata_file = "metadata.xml";
105 * AudioMapping XML changed.
107 * Subtitle offset changed to subtitle y offset, and subtitle x offset added.
109 * Use <Scale> tag in <VideoContent> rather than <Ratio>.
113 * Subtitle X and Y scale.
115 * Bumped to 32 for 2.0 branch; some times are expressed in Times rather
119 * Changed <Period> to <Subtitle> in FFmpegSubtitleStream
121 * Content only contains audio/subtitle-related tags if those things
124 * VideoFrameType in VideoContent is a string rather than an integer.
126 * EffectColour rather than OutlineColour in Subtitle.
128 int const Film::current_state_version = 36;
130 /** Construct a Film object in a given directory.
132 * @param dir Film directory.
135 Film::Film (optional<boost::filesystem::path> dir)
136 : _playlist (new Playlist)
137 , _use_isdcf_name (true)
138 , _dcp_content_type (Config::instance()->default_dcp_content_type ())
139 , _container (Config::instance()->default_container ())
140 , _resolution (RESOLUTION_2K)
143 , _context_id (dcp::make_uuid ())
144 , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
145 , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
146 , _video_frame_rate (24)
147 , _audio_channels (Config::instance()->default_dcp_audio_channels ())
150 , _interop (Config::instance()->default_interop ())
151 , _audio_processor (0)
152 , _reel_type (REELTYPE_SINGLE)
153 , _reel_length (2000000000)
154 , _upload_after_make_dcp (false)
155 , _state_version (current_state_version)
158 set_isdcf_date_today ();
160 _playlist_changed_connection = _playlist->Changed.connect (bind (&Film::playlist_changed, this));
161 _playlist_order_changed_connection = _playlist->OrderChanged.connect (bind (&Film::playlist_order_changed, this));
162 _playlist_content_changed_connection = _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2, _3));
165 /* Make state.directory a complete path without ..s (where possible)
166 (Code swiped from Adam Bowen on stackoverflow)
167 XXX: couldn't/shouldn't this just be boost::filesystem::canonical?
170 boost::filesystem::path p (boost::filesystem::system_complete (dir.get()));
171 boost::filesystem::path result;
172 for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
174 if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
177 result = result.parent_path ();
179 } else if (*i != ".") {
184 set_directory (result.make_preferred ());
188 _log.reset (new FileLog (file ("log")));
190 _log.reset (new NullLog);
193 _playlist->set_sequence (_sequence);
198 BOOST_FOREACH (boost::signals2::connection& i, _job_connections) {
202 BOOST_FOREACH (boost::signals2::connection& i, _audio_analysis_connections) {
208 Film::video_identifier () const
210 DCPOMATIC_ASSERT (container ());
212 string s = container()->id()
213 + "_" + resolution_to_string (_resolution)
214 + "_" + _playlist->video_identifier()
215 + "_" + raw_convert<string>(_video_frame_rate)
216 + "_" + raw_convert<string>(j2k_bandwidth());
237 /** @return The file to write video frame info to */
238 boost::filesystem::path
239 Film::info_file (DCPTimePeriod period) const
241 boost::filesystem::path p;
243 p /= video_identifier () + "_" + raw_convert<string> (period.from.get()) + "_" + raw_convert<string> (period.to.get());
247 boost::filesystem::path
248 Film::internal_video_asset_dir () const
250 return dir ("video");
253 boost::filesystem::path
254 Film::internal_video_asset_filename (DCPTimePeriod p) const
256 return video_identifier() + "_" + raw_convert<string> (p.from.get()) + "_" + raw_convert<string> (p.to.get()) + ".mxf";
259 boost::filesystem::path
260 Film::audio_analysis_path (shared_ptr<const Playlist> playlist) const
262 boost::filesystem::path p = dir ("analysis");
265 BOOST_FOREACH (shared_ptr<Content> i, playlist->content ()) {
270 digester.add (i->digest ());
271 digester.add (i->audio->mapping().digest ());
272 if (playlist->content().size() != 1) {
273 /* Analyses should be considered equal regardless of gain
274 if they were made from just one piece of content. This
275 is because we can fake any gain change in a single-content
276 analysis at the plotting stage rather than having to
279 digester.add (i->audio->gain ());
283 if (audio_processor ()) {
284 digester.add (audio_processor()->id ());
287 p /= digester.get ();
291 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
295 if (dcp_name().find ("/") != string::npos) {
296 throw BadSettingError (_("name"), _("cannot contain slashes"));
299 if (container() == 0) {
300 throw MissingSettingError (_("container"));
303 if (content().empty()) {
304 throw runtime_error (_("you must add some content to the DCP before creating it"));
307 if (dcp_content_type() == 0) {
308 throw MissingSettingError (_("content type"));
311 if (name().empty()) {
315 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
316 if (!i->paths_valid()) {
317 throw runtime_error (_("some of your content is missing"));
319 shared_ptr<const DCPContent> dcp = dynamic_pointer_cast<const DCPContent> (i);
320 if (dcp && dcp->needs_kdm()) {
321 throw runtime_error (_("some of your content needs a KDM"));
323 if (dcp && dcp->needs_assets()) {
324 throw runtime_error (_("some of your content needs an OV"));
328 set_isdcf_date_today ();
330 BOOST_FOREACH (string i, environment_info ()) {
334 BOOST_FOREACH (shared_ptr<const Content> i, content ()) {
335 LOG_GENERAL ("Content: %1", i->technical_summary());
337 LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
338 if (Config::instance()->only_servers_encode ()) {
339 LOG_GENERAL_NC ("0 threads: ONLY SERVERS SET TO ENCODE");
341 LOG_GENERAL ("%1 threads", Config::instance()->master_encoding_threads());
343 LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
345 shared_ptr<TranscodeJob> tj (new TranscodeJob (shared_from_this()));
346 tj->set_encoder (shared_ptr<Encoder> (new DCPEncoder (shared_from_this(), tj)));
347 JobManager::instance()->add (tj);
350 /** Start a job to send our DCP to the configured TMS */
352 Film::send_dcp_to_tms ()
354 shared_ptr<Job> j (new UploadJob (shared_from_this()));
355 JobManager::instance()->add (j);
358 shared_ptr<xmlpp::Document>
359 Film::metadata (bool with_content_paths) const
361 shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
362 xmlpp::Element* root = doc->create_root_node ("Metadata");
364 root->add_child("Version")->add_child_text (raw_convert<string> (current_state_version));
365 root->add_child("Name")->add_child_text (_name);
366 root->add_child("UseISDCFName")->add_child_text (_use_isdcf_name ? "1" : "0");
368 if (_dcp_content_type) {
369 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->isdcf_name ());
373 root->add_child("Container")->add_child_text (_container->id ());
376 root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
377 root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
378 _isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
379 root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
380 root->add_child("ISDCFDate")->add_child_text (boost::gregorian::to_iso_string (_isdcf_date));
381 root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
382 root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
383 root->add_child("Sequence")->add_child_text (_sequence ? "1" : "0");
384 root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
385 root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
386 root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
387 root->add_child("Key")->add_child_text (_key.hex ());
388 root->add_child("ContextID")->add_child_text (_context_id);
389 if (_audio_processor) {
390 root->add_child("AudioProcessor")->add_child_text (_audio_processor->id ());
392 root->add_child("ReelType")->add_child_text (raw_convert<string> (static_cast<int> (_reel_type)));
393 root->add_child("ReelLength")->add_child_text (raw_convert<string> (_reel_length));
394 root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
395 _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
400 /** Write state to our `metadata' file */
402 Film::write_metadata () const
404 DCPOMATIC_ASSERT (directory());
405 boost::filesystem::create_directories (directory().get());
406 shared_ptr<xmlpp::Document> doc = metadata ();
407 doc->write_to_file_formatted (file(metadata_file).string ());
411 /** Write a template from this film */
413 Film::write_template (boost::filesystem::path path) const
415 boost::filesystem::create_directories (path.parent_path());
416 shared_ptr<xmlpp::Document> doc = metadata (false);
417 doc->write_to_file_formatted (path.string ());
420 /** Read state from our metadata file.
421 * @return Notes about things that the user should know about, or empty.
424 Film::read_metadata (optional<boost::filesystem::path> path)
427 if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file (metadata_file))) {
428 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!"));
431 path = file (metadata_file);
434 cxml::Document f ("Metadata");
435 f.read_file (path.get ());
437 _state_version = f.number_child<int> ("Version");
438 if (_state_version > current_state_version) {
439 throw runtime_error (_("This film was created with a newer version of DCP-o-matic, and it cannot be loaded into this version. Sorry!"));
442 _name = f.string_child ("Name");
443 if (_state_version >= 9) {
444 _use_isdcf_name = f.bool_child ("UseISDCFName");
445 _isdcf_metadata = ISDCFMetadata (f.node_child ("ISDCFMetadata"));
446 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("ISDCFDate"));
448 _use_isdcf_name = f.bool_child ("UseDCIName");
449 _isdcf_metadata = ISDCFMetadata (f.node_child ("DCIMetadata"));
450 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
454 optional<string> c = f.optional_string_child ("DCPContentType");
456 _dcp_content_type = DCPContentType::from_isdcf_name (c.get ());
461 optional<string> c = f.optional_string_child ("Container");
463 _container = Ratio::from_id (c.get ());
467 _resolution = string_to_resolution (f.string_child ("Resolution"));
468 _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
469 _video_frame_rate = f.number_child<int> ("VideoFrameRate");
470 _signed = f.optional_bool_child("Signed").get_value_or (true);
471 _encrypted = f.bool_child ("Encrypted");
472 _audio_channels = f.number_child<int> ("AudioChannels");
473 /* We used to allow odd numbers (and zero) channels, but it's just not worth
476 if (_audio_channels == 0) {
478 } else if ((_audio_channels % 2) == 1) {
482 if (f.optional_bool_child("SequenceVideo")) {
483 _sequence = f.bool_child("SequenceVideo");
485 _sequence = f.bool_child("Sequence");
488 _three_d = f.bool_child ("ThreeD");
489 _interop = f.bool_child ("Interop");
490 _key = dcp::Key (f.string_child ("Key"));
491 _context_id = f.optional_string_child("ContextID").get_value_or (dcp::make_uuid ());
493 if (f.optional_string_child ("AudioProcessor")) {
494 _audio_processor = AudioProcessor::from_id (f.string_child ("AudioProcessor"));
496 _audio_processor = 0;
499 _reel_type = static_cast<ReelType> (f.optional_number_child<int>("ReelType").get_value_or (static_cast<int>(REELTYPE_SINGLE)));
500 _reel_length = f.optional_number_child<int64_t>("ReelLength").get_value_or (2000000000);
501 _upload_after_make_dcp = f.optional_bool_child("UploadAfterMakeDCP").get_value_or (false);
504 /* This method is the only one that can return notes (so far) */
505 _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
507 /* Write backtraces to this film's directory, until another film is loaded */
509 set_backtrace_file (file ("backtrace.txt"));
516 /** Given a directory name, return its full path within the Film's directory.
517 * @param d directory name within the Film's directory.
518 * @param create true to create the directory (and its parents) if they do not exist.
520 boost::filesystem::path
521 Film::dir (boost::filesystem::path d, bool create) const
523 DCPOMATIC_ASSERT (_directory);
525 boost::filesystem::path p;
526 p /= _directory.get();
530 boost::filesystem::create_directories (p);
536 /** Given a file or directory name, return its full path within the Film's directory.
537 * Any required parent directories will be created.
539 boost::filesystem::path
540 Film::file (boost::filesystem::path f) const
542 DCPOMATIC_ASSERT (_directory);
544 boost::filesystem::path p;
545 p /= _directory.get();
548 boost::filesystem::create_directories (p.parent_path ());
554 Film::mapped_audio_channels () const
558 if (audio_processor ()) {
559 /* Processors are mapped 1:1 to DCP outputs so we can work out mappings from there */
560 for (int i = 0; i < audio_processor()->out_channels(); ++i) {
561 mapped.push_back (i);
564 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
566 list<int> c = i->audio->mapping().mapped_output_channels ();
567 copy (c.begin(), c.end(), back_inserter (mapped));
578 /** @return a ISDCF-compliant name for a DCP of this film */
580 Film::isdcf_name (bool if_created_now) const
584 string raw_name = name ();
586 /* Split the raw name up into words */
587 vector<string> words;
588 split (words, raw_name, is_any_of (" _-"));
592 /* Add each word to fixed_name */
593 for (vector<string>::const_iterator i = words.begin(); i != words.end(); ++i) {
596 /* First letter is always capitalised */
597 w[0] = toupper (w[0]);
599 /* Count caps in w */
601 for (size_t i = 0; i < w.size(); ++i) {
602 if (isupper (w[i])) {
607 /* If w is all caps make the rest of it lower case, otherwise
610 if (caps == w.size ()) {
611 for (size_t i = 1; i < w.size(); ++i) {
612 w[i] = tolower (w[i]);
616 for (size_t i = 0; i < w.size(); ++i) {
621 if (fixed_name.length() > 14) {
622 fixed_name = fixed_name.substr (0, 14);
627 if (dcp_content_type()) {
628 d += "_" + dcp_content_type()->isdcf_name();
629 d += "-" + raw_convert<string>(isdcf_metadata().content_version);
632 ISDCFMetadata const dm = isdcf_metadata ();
634 if (dm.temp_version) {
638 if (dm.pre_release) {
646 if (!dm.chain.empty ()) {
654 if (dm.two_d_version_of_three_d) {
658 if (!dm.mastered_luminance.empty ()) {
659 d += "-" + dm.mastered_luminance;
662 if (video_frame_rate() != 24) {
663 d += "-" + raw_convert<string>(video_frame_rate());
667 d += "_" + container()->isdcf_name();
670 /* XXX: this uses the first bit of content only */
672 /* Interior aspect ratio. The standard says we don't do this for trailers, for some strange reason */
673 if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
674 Ratio const * content_ratio = 0;
675 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
677 /* Here's the first piece of video content */
678 if (i->video->scale().ratio ()) {
679 content_ratio = i->video->scale().ratio ();
681 content_ratio = Ratio::from_ratio (i->video->size().ratio ());
687 if (content_ratio && content_ratio != container()) {
688 /* This needs to be the numeric version of the ratio, and ::id() is close enough */
689 d += "-" + content_ratio->id();
693 if (!dm.audio_language.empty ()) {
694 d += "_" + dm.audio_language;
695 if (!dm.subtitle_language.empty()) {
697 bool burnt_in = true;
698 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
703 if (i->subtitle->use() && !i->subtitle->burn()) {
708 string language = dm.subtitle_language;
709 if (burnt_in && language != "XX") {
710 transform (language.begin(), language.end(), language.begin(), ::tolower);
712 transform (language.begin(), language.end(), language.begin(), ::toupper);
721 if (!dm.territory.empty ()) {
722 d += "_" + dm.territory;
723 if (dm.rating.empty ()) {
726 d += "-" + dm.rating;
730 /* Count mapped audio channels */
732 pair<int, int> ch = audio_channel_types (mapped_audio_channels(), audio_channels());
734 d += String::compose("_%1%2", ch.first, ch.second);
739 d += "_" + resolution_to_string (_resolution);
741 if (!dm.studio.empty ()) {
742 d += "_" + dm.studio;
745 if (if_created_now) {
746 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
748 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
751 if (!dm.facility.empty ()) {
752 d += "_" + dm.facility;
766 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
767 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
768 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
782 /** @return name to give the DCP */
784 Film::dcp_name (bool if_created_now) const
787 if (use_isdcf_name()) {
788 return careful_string_filter (isdcf_name (if_created_now));
791 return careful_string_filter (name ());
795 Film::set_directory (boost::filesystem::path d)
802 Film::set_name (string n)
805 signal_changed (NAME);
809 Film::set_use_isdcf_name (bool u)
812 signal_changed (USE_ISDCF_NAME);
816 Film::set_dcp_content_type (DCPContentType const * t)
818 _dcp_content_type = t;
819 signal_changed (DCP_CONTENT_TYPE);
823 Film::set_container (Ratio const * c)
826 signal_changed (CONTAINER);
830 Film::set_resolution (Resolution r)
833 signal_changed (RESOLUTION);
837 Film::set_j2k_bandwidth (int b)
840 signal_changed (J2K_BANDWIDTH);
844 Film::set_isdcf_metadata (ISDCFMetadata m)
847 signal_changed (ISDCF_METADATA);
851 Film::set_video_frame_rate (int f)
853 _video_frame_rate = f;
854 signal_changed (VIDEO_FRAME_RATE);
858 Film::set_audio_channels (int c)
861 signal_changed (AUDIO_CHANNELS);
865 Film::set_three_d (bool t)
868 signal_changed (THREE_D);
870 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
871 _isdcf_metadata.two_d_version_of_three_d = false;
872 signal_changed (ISDCF_METADATA);
877 Film::set_interop (bool i)
880 signal_changed (INTEROP);
884 Film::set_audio_processor (AudioProcessor const * processor)
886 _audio_processor = processor;
887 signal_changed (AUDIO_PROCESSOR);
888 signal_changed (AUDIO_CHANNELS);
892 Film::set_reel_type (ReelType t)
895 signal_changed (REEL_TYPE);
898 /** @param r Desired reel length in bytes */
900 Film::set_reel_length (int64_t r)
903 signal_changed (REEL_LENGTH);
907 Film::set_upload_after_make_dcp (bool u)
909 _upload_after_make_dcp = u;
910 signal_changed (UPLOAD_AFTER_MAKE_DCP);
914 Film::signal_changed (Property p)
920 set_video_frame_rate (_playlist->best_video_frame_rate ());
922 case Film::VIDEO_FRAME_RATE:
924 _playlist->maybe_sequence ();
930 emit (boost::bind (boost::ref (Changed), p));
934 Film::set_isdcf_date_today ()
936 _isdcf_date = boost::gregorian::day_clock::local_day ();
939 boost::filesystem::path
940 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
942 boost::filesystem::path p;
944 p /= video_identifier ();
947 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
950 if (eyes == EYES_LEFT) {
952 } else if (eyes == EYES_RIGHT) {
966 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
971 return vector<CPLSummary> ();
974 vector<CPLSummary> out;
976 boost::filesystem::path const dir = directory().get();
977 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
979 boost::filesystem::is_directory (*i) &&
980 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
986 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
989 i->path().leaf().string(),
990 dcp.cpls().front()->id(),
991 dcp.cpls().front()->annotation_text(),
992 dcp.cpls().front()->file().get()
1005 Film::set_signed (bool s)
1008 signal_changed (SIGNED);
1012 Film::set_encrypted (bool e)
1015 signal_changed (ENCRYPTED);
1019 Film::set_key (dcp::Key key)
1022 signal_changed (KEY);
1026 Film::content () const
1028 return _playlist->content ();
1031 /** @param content Content to add.
1032 * @param disable_audio_analysis true to never do automatic audio analysis, even if it is enabled in configuration.
1035 Film::examine_and_add_content (shared_ptr<Content> content, bool disable_audio_analysis)
1037 if (dynamic_pointer_cast<FFmpegContent> (content) && _directory) {
1038 run_ffprobe (content->path(0), file ("ffprobe.log"), _log);
1041 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), content));
1043 _job_connections.push_back (
1044 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job>(j), weak_ptr<Content>(content), disable_audio_analysis))
1047 JobManager::instance()->add (j);
1051 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c, bool disable_audio_analysis)
1053 shared_ptr<Job> job = j.lock ();
1054 if (!job || !job->finished_ok ()) {
1058 shared_ptr<Content> content = c.lock ();
1063 add_content (content);
1065 if (Config::instance()->automatic_audio_analysis() && content->audio && !disable_audio_analysis) {
1066 shared_ptr<Playlist> playlist (new Playlist);
1067 playlist->add (content);
1068 boost::signals2::connection c;
1069 JobManager::instance()->analyse_audio (
1070 shared_from_this (), playlist, c, bind (&Film::audio_analysis_finished, this)
1072 _audio_analysis_connections.push_back (c);
1077 Film::add_content (shared_ptr<Content> c)
1079 /* Add {video,subtitle} content after any existing {video,subtitle} content */
1081 c->set_position (_playlist->video_end ());
1082 } else if (c->subtitle) {
1083 c->set_position (_playlist->subtitle_end ());
1086 if (_template_film) {
1087 /* Take settings from the first piece of content of c's type in _template */
1088 BOOST_FOREACH (shared_ptr<Content> i, _template_film->content()) {
1089 if (typeid(i.get()) == typeid(c.get())) {
1090 c->take_settings_from (i);
1099 Film::remove_content (shared_ptr<Content> c)
1101 _playlist->remove (c);
1105 Film::move_content_earlier (shared_ptr<Content> c)
1107 _playlist->move_earlier (c);
1111 Film::move_content_later (shared_ptr<Content> c)
1113 _playlist->move_later (c);
1116 /** @return length of the film from time 0 to the last thing on the playlist */
1118 Film::length () const
1120 return _playlist->length().ceil(video_frame_rate());
1124 Film::best_video_frame_rate () const
1126 return _playlist->best_video_frame_rate ();
1130 Film::active_frame_rate_change (DCPTime t) const
1132 return _playlist->active_frame_rate_change (t, video_frame_rate ());
1136 Film::playlist_content_changed (weak_ptr<Content> c, int p, bool frequent)
1140 if (p == ContentProperty::VIDEO_FRAME_RATE) {
1141 set_video_frame_rate (_playlist->best_video_frame_rate ());
1142 } else if (p == AudioContentProperty::STREAMS) {
1143 signal_changed (NAME);
1146 emit (boost::bind (boost::ref (ContentChanged), c, p, frequent));
1150 Film::playlist_changed ()
1152 signal_changed (CONTENT);
1153 signal_changed (NAME);
1157 Film::playlist_order_changed ()
1159 signal_changed (CONTENT_ORDER);
1163 Film::audio_frame_rate () const
1165 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
1166 if (i->audio && i->audio->has_rate_above_48k ()) {
1175 Film::set_sequence (bool s)
1178 _playlist->set_sequence (s);
1179 signal_changed (SEQUENCE);
1182 /** @return Size of the largest possible image in whatever resolution we are using */
1184 Film::full_frame () const
1186 switch (_resolution) {
1188 return dcp::Size (2048, 1080);
1190 return dcp::Size (4096, 2160);
1193 DCPOMATIC_ASSERT (false);
1194 return dcp::Size ();
1197 /** @return Size of the frame */
1199 Film::frame_size () const
1201 return fit_ratio_within (container()->ratio(), full_frame ());
1204 /** @param recipient KDM recipient certificate.
1205 * @param trusted_devices Certificates of other trusted devices (can be empty).
1206 * @param cpl_file CPL filename.
1207 * @param from KDM from time expressed as a local time with an offset from UTC.
1208 * @param until KDM to time expressed as a local time with an offset from UTC.
1209 * @param formulation KDM formulation to use.
1213 dcp::Certificate recipient,
1214 vector<dcp::Certificate> trusted_devices,
1215 boost::filesystem::path cpl_file,
1216 dcp::LocalTime from,
1217 dcp::LocalTime until,
1218 dcp::Formulation formulation
1221 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1222 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1223 if (!signer->valid ()) {
1224 throw InvalidSignerError ();
1227 /* Find keys that have been added to imported, encrypted DCP content */
1228 list<dcp::DecryptedKDMKey> imported_keys;
1229 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1230 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1231 if (d && d->kdm()) {
1232 dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
1233 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
1234 copy (keys.begin(), keys.end(), back_inserter (imported_keys));
1238 map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
1240 BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
1241 shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
1242 if (!mxf || !mxf->key_id()) {
1246 /* Get any imported key for this ID */
1248 BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
1249 if (j.id() == mxf->key_id().get()) {
1250 LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
1251 keys[mxf] = j.key();
1257 /* No imported key; it must be an asset that we encrypted */
1258 LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
1263 return dcp::DecryptedKDM (
1264 cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1265 ).encrypt (signer, recipient, trusted_devices, formulation);
1268 /** @param screens Screens to make KDMs for.
1269 * @param cpl_file Path to CPL to make KDMs for.
1270 * @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1271 * @param until KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1272 * @param formulation KDM formulation to use.
1276 list<shared_ptr<Screen> > screens,
1277 boost::filesystem::path cpl_file,
1278 boost::posix_time::ptime from,
1279 boost::posix_time::ptime until,
1280 dcp::Formulation formulation
1283 list<ScreenKDM> kdms;
1285 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1287 dcp::EncryptedKDM const kdm = make_kdm (
1291 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1292 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1296 kdms.push_back (ScreenKDM (i, kdm));
1303 /** @return The approximate disk space required to encode a DCP of this film with the
1304 * current settings, in bytes.
1307 Film::required_disk_space () const
1309 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1312 /** This method checks the disk that the Film is on and tries to decide whether or not
1313 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1314 * false is returned and required and available are filled in with the amount of disk space
1315 * required and available respectively (in Gb).
1317 * Note: the decision made by this method isn't, of course, 100% reliable.
1320 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1322 /* Create a test file and see if we can hard-link it */
1323 boost::filesystem::path test = internal_video_asset_dir() / "test";
1324 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1325 can_hard_link = true;
1326 FILE* f = fopen_boost (test, "w");
1329 boost::system::error_code ec;
1330 boost::filesystem::create_hard_link (test, test2, ec);
1332 can_hard_link = false;
1334 boost::filesystem::remove (test);
1335 boost::filesystem::remove (test2);
1338 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1339 required = double (required_disk_space ()) / 1073741824.0f;
1340 if (!can_hard_link) {
1343 available = double (s.available) / 1073741824.0f;
1344 return (available - required) > 1;
1348 Film::subtitle_language () const
1350 set<string> languages;
1352 ContentList cl = content ();
1353 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1355 languages.insert (c->subtitle->language ());
1360 BOOST_FOREACH (string s, languages) {
1361 if (!all.empty ()) {
1371 /** Change the gains of the supplied AudioMapping to make it a default
1372 * for this film. The defaults are guessed based on what processor (if any)
1373 * is in use, the number of input channels and any filename supplied.
1376 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1378 static string const regex[] = {
1379 ".*[\\._-]L[\\._-].*",
1380 ".*[\\._-]R[\\._-].*",
1381 ".*[\\._-]C[\\._-].*",
1382 ".*[\\._-]Lfe[\\._-].*",
1383 ".*[\\._-]Ls[\\._-].*",
1384 ".*[\\._-]Rs[\\._-].*"
1387 static int const regexes = sizeof(regex) / sizeof(*regex);
1389 if (audio_processor ()) {
1390 audio_processor()->make_audio_mapping_default (mapping);
1392 mapping.make_zero ();
1393 if (mapping.input_channels() == 1) {
1394 bool guessed = false;
1396 /* See if we can guess where this stream should go */
1398 for (int i = 0; i < regexes; ++i) {
1399 boost::regex e (regex[i], boost::regex::icase);
1400 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1401 mapping.set (0, i, 1);
1408 /* If we have no idea, just put it on centre */
1409 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1413 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1414 mapping.set (i, i, 1);
1420 /** @return The names of the channels that audio contents' outputs are passed into;
1421 * this is either the DCP or a AudioProcessor.
1424 Film::audio_output_names () const
1426 if (audio_processor ()) {
1427 return audio_processor()->input_names ();
1430 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1434 for (int i = 0; i < audio_channels(); ++i) {
1435 n.push_back (short_audio_channel_name (i));
1442 Film::repeat_content (ContentList c, int n)
1444 _playlist->repeat (c, n);
1448 Film::remove_content (ContentList c)
1450 _playlist->remove (c);
1454 Film::audio_analysis_finished ()
1460 Film::reels () const
1462 list<DCPTimePeriod> p;
1463 DCPTime const len = length();
1465 switch (reel_type ()) {
1466 case REELTYPE_SINGLE:
1467 p.push_back (DCPTimePeriod (DCPTime (), len));
1469 case REELTYPE_BY_VIDEO_CONTENT:
1471 optional<DCPTime> last_split;
1472 shared_ptr<Content> last_video;
1473 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1475 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1477 p.push_back (DCPTimePeriod (last_split.get(), t));
1485 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1487 /* Definitely go from the last split to the end of the video content */
1488 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1491 if (video_end < len) {
1492 /* And maybe go after that as well if there is any non-video hanging over the end */
1493 p.push_back (DCPTimePeriod (video_end, len));
1497 case REELTYPE_BY_LENGTH:
1500 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1501 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1502 while (current < len) {
1503 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1504 p.push_back (DCPTimePeriod (current, end));
1514 /** @param period A period within the DCP
1515 * @return Name of the content which most contributes to the given period.
1518 Film::content_summary (DCPTimePeriod period) const
1520 return _playlist->content_summary (period);
1524 Film::fix_conflicting_settings ()
1528 list<boost::filesystem::path> was_referencing;
1529 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1530 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1532 list<string> reasons;
1534 if (!d->can_reference_video(reasons) && d->reference_video()) {
1535 d->set_reference_video (false);
1538 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1539 d->set_reference_audio (false);
1542 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1543 d->set_reference_subtitle (false);
1547 was_referencing.push_back (d->path(0).parent_path().filename());
1552 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1553 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()));
1560 Film::use_template (string name)
1562 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1563 _template_film->read_metadata (Config::instance()->template_path (name));
1564 _use_isdcf_name = _template_film->_use_isdcf_name;
1565 _dcp_content_type = _template_film->_dcp_content_type;
1566 _container = _template_film->_container;
1567 _resolution = _template_film->_resolution;
1568 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1569 _video_frame_rate = _template_film->_video_frame_rate;
1570 _signed = _template_film->_signed;
1571 _encrypted = _template_film->_encrypted;
1572 _audio_channels = _template_film->_audio_channels;
1573 _sequence = _template_film->_sequence;
1574 _three_d = _template_film->_three_d;
1575 _interop = _template_film->_interop;
1576 _audio_processor = _template_film->_audio_processor;
1577 _reel_type = _template_film->_reel_type;
1578 _reel_length = _template_film->_reel_length;
1579 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;
1582 pair<double, double>
1583 Film::speed_up_range (int dcp_frame_rate) const
1585 return _playlist->speed_up_range (dcp_frame_rate);
1589 Film::copy_from (shared_ptr<const Film> film)
1591 read_metadata (film->file (metadata_file));