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 /* 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 d += "-" + content_ratio->isdcf_name();
692 if (!dm.audio_language.empty ()) {
693 d += "_" + dm.audio_language;
694 if (!dm.subtitle_language.empty()) {
696 bool burnt_in = true;
697 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
702 if (i->subtitle->use() && !i->subtitle->burn()) {
707 string language = dm.subtitle_language;
708 if (burnt_in && language != "XX") {
709 transform (language.begin(), language.end(), language.begin(), ::tolower);
711 transform (language.begin(), language.end(), language.begin(), ::toupper);
720 if (!dm.territory.empty ()) {
721 d += "_" + dm.territory;
722 if (dm.rating.empty ()) {
725 d += "-" + dm.rating;
729 /* Count mapped audio channels */
731 pair<int, int> ch = audio_channel_types (mapped_audio_channels(), audio_channels());
733 d += String::compose("_%1%2", ch.first, ch.second);
738 d += "_" + resolution_to_string (_resolution);
740 if (!dm.studio.empty ()) {
741 d += "_" + dm.studio;
744 if (if_created_now) {
745 d += "_" + boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
747 d += "_" + boost::gregorian::to_iso_string (_isdcf_date);
750 if (!dm.facility.empty ()) {
751 d += "_" + dm.facility;
765 BOOST_FOREACH (shared_ptr<Content> i, content ()) {
766 shared_ptr<const DCPContent> dc = dynamic_pointer_cast<const DCPContent> (i);
767 if (dc && (dc->reference_video() || dc->reference_audio() || dc->reference_subtitle())) {
781 /** @return name to give the DCP */
783 Film::dcp_name (bool if_created_now) const
786 if (use_isdcf_name()) {
787 return careful_string_filter (isdcf_name (if_created_now));
790 return careful_string_filter (name ());
794 Film::set_directory (boost::filesystem::path d)
801 Film::set_name (string n)
804 signal_changed (NAME);
808 Film::set_use_isdcf_name (bool u)
811 signal_changed (USE_ISDCF_NAME);
815 Film::set_dcp_content_type (DCPContentType const * t)
817 _dcp_content_type = t;
818 signal_changed (DCP_CONTENT_TYPE);
822 Film::set_container (Ratio const * c)
825 signal_changed (CONTAINER);
829 Film::set_resolution (Resolution r)
832 signal_changed (RESOLUTION);
836 Film::set_j2k_bandwidth (int b)
839 signal_changed (J2K_BANDWIDTH);
843 Film::set_isdcf_metadata (ISDCFMetadata m)
846 signal_changed (ISDCF_METADATA);
850 Film::set_video_frame_rate (int f)
852 _video_frame_rate = f;
853 signal_changed (VIDEO_FRAME_RATE);
857 Film::set_audio_channels (int c)
860 signal_changed (AUDIO_CHANNELS);
864 Film::set_three_d (bool t)
867 signal_changed (THREE_D);
869 if (_three_d && _isdcf_metadata.two_d_version_of_three_d) {
870 _isdcf_metadata.two_d_version_of_three_d = false;
871 signal_changed (ISDCF_METADATA);
876 Film::set_interop (bool i)
879 signal_changed (INTEROP);
883 Film::set_audio_processor (AudioProcessor const * processor)
885 _audio_processor = processor;
886 signal_changed (AUDIO_PROCESSOR);
887 signal_changed (AUDIO_CHANNELS);
891 Film::set_reel_type (ReelType t)
894 signal_changed (REEL_TYPE);
897 /** @param r Desired reel length in bytes */
899 Film::set_reel_length (int64_t r)
902 signal_changed (REEL_LENGTH);
906 Film::set_upload_after_make_dcp (bool u)
908 _upload_after_make_dcp = u;
909 signal_changed (UPLOAD_AFTER_MAKE_DCP);
913 Film::signal_changed (Property p)
919 set_video_frame_rate (_playlist->best_video_frame_rate ());
921 case Film::VIDEO_FRAME_RATE:
923 _playlist->maybe_sequence ();
929 emit (boost::bind (boost::ref (Changed), p));
933 Film::set_isdcf_date_today ()
935 _isdcf_date = boost::gregorian::day_clock::local_day ();
938 boost::filesystem::path
939 Film::j2c_path (int reel, Frame frame, Eyes eyes, bool tmp) const
941 boost::filesystem::path p;
943 p /= video_identifier ();
946 snprintf(buffer, sizeof(buffer), "%08d_%08" PRId64, reel, frame);
949 if (eyes == EYES_LEFT) {
951 } else if (eyes == EYES_RIGHT) {
965 /** Find all the DCPs in our directory that can be dcp::DCP::read() and return details of their CPLs */
970 return vector<CPLSummary> ();
973 vector<CPLSummary> out;
975 boost::filesystem::path const dir = directory().get();
976 for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
978 boost::filesystem::is_directory (*i) &&
979 i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
985 DCPOMATIC_ASSERT (dcp.cpls().front()->file());
988 i->path().leaf().string(),
989 dcp.cpls().front()->id(),
990 dcp.cpls().front()->annotation_text(),
991 dcp.cpls().front()->file().get()
1004 Film::set_signed (bool s)
1007 signal_changed (SIGNED);
1011 Film::set_encrypted (bool e)
1014 signal_changed (ENCRYPTED);
1018 Film::set_key (dcp::Key key)
1021 signal_changed (KEY);
1025 Film::content () const
1027 return _playlist->content ();
1030 /** @param content Content to add.
1031 * @param disable_audio_analysis true to never do automatic audio analysis, even if it is enabled in configuration.
1034 Film::examine_and_add_content (shared_ptr<Content> content, bool disable_audio_analysis)
1036 if (dynamic_pointer_cast<FFmpegContent> (content) && _directory) {
1037 run_ffprobe (content->path(0), file ("ffprobe.log"), _log);
1040 shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), content));
1042 _job_connections.push_back (
1043 j->Finished.connect (bind (&Film::maybe_add_content, this, weak_ptr<Job>(j), weak_ptr<Content>(content), disable_audio_analysis))
1046 JobManager::instance()->add (j);
1050 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c, bool disable_audio_analysis)
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 && !disable_audio_analysis) {
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().ceil(video_frame_rate());
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 recipient KDM recipient certificate.
1204 * @param trusted_devices Certificates of other trusted devices (can be empty).
1205 * @param cpl_file CPL filename.
1206 * @param from KDM from time expressed as a local time with an offset from UTC.
1207 * @param until KDM to time expressed as a local time with an offset from UTC.
1208 * @param formulation KDM formulation to use.
1212 dcp::Certificate recipient,
1213 vector<dcp::Certificate> trusted_devices,
1214 boost::filesystem::path cpl_file,
1215 dcp::LocalTime from,
1216 dcp::LocalTime until,
1217 dcp::Formulation formulation
1220 shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1221 shared_ptr<const dcp::CertificateChain> signer = Config::instance()->signer_chain ();
1222 if (!signer->valid ()) {
1223 throw InvalidSignerError ();
1226 /* Find keys that have been added to imported, encrypted DCP content */
1227 list<dcp::DecryptedKDMKey> imported_keys;
1228 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1229 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1230 if (d && d->kdm()) {
1231 dcp::DecryptedKDM kdm (d->kdm().get(), Config::instance()->decryption_chain()->key().get());
1232 list<dcp::DecryptedKDMKey> keys = kdm.keys ();
1233 copy (keys.begin(), keys.end(), back_inserter (imported_keys));
1237 map<shared_ptr<const dcp::ReelMXF>, dcp::Key> keys;
1239 BOOST_FOREACH(shared_ptr<const dcp::ReelAsset> i, cpl->reel_assets ()) {
1240 shared_ptr<const dcp::ReelMXF> mxf = boost::dynamic_pointer_cast<const dcp::ReelMXF> (i);
1241 if (!mxf || !mxf->key_id()) {
1245 /* Get any imported key for this ID */
1247 BOOST_FOREACH (dcp::DecryptedKDMKey j, imported_keys) {
1248 if (j.id() == mxf->key_id().get()) {
1249 LOG_GENERAL ("Using imported key for %1", mxf->key_id().get());
1250 keys[mxf] = j.key();
1256 /* No imported key; it must be an asset that we encrypted */
1257 LOG_GENERAL ("Using our own key for %1", mxf->key_id().get());
1262 return dcp::DecryptedKDM (
1263 cpl->id(), keys, from, until, cpl->content_title_text(), cpl->content_title_text(), dcp::LocalTime().as_string()
1264 ).encrypt (signer, recipient, trusted_devices, formulation);
1267 /** @param screens Screens to make KDMs for.
1268 * @param cpl_file Path to CPL to make KDMs for.
1269 * @param from KDM from time expressed as a local time in the time zone of the Screen's Cinema.
1270 * @param until KDM to time expressed as a local time in the time zone of the Screen's Cinema.
1271 * @param formulation KDM formulation to use.
1275 list<shared_ptr<Screen> > screens,
1276 boost::filesystem::path cpl_file,
1277 boost::posix_time::ptime from,
1278 boost::posix_time::ptime until,
1279 dcp::Formulation formulation
1282 list<ScreenKDM> kdms;
1284 BOOST_FOREACH (shared_ptr<Screen> i, screens) {
1286 dcp::EncryptedKDM const kdm = make_kdm (
1290 dcp::LocalTime (from, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1291 dcp::LocalTime (until, i->cinema->utc_offset_hour(), i->cinema->utc_offset_minute()),
1295 kdms.push_back (ScreenKDM (i, kdm));
1302 /** @return The approximate disk space required to encode a DCP of this film with the
1303 * current settings, in bytes.
1306 Film::required_disk_space () const
1308 return _playlist->required_disk_space (j2k_bandwidth(), audio_channels(), audio_frame_rate());
1311 /** This method checks the disk that the Film is on and tries to decide whether or not
1312 * there will be enough space to make a DCP for it. If so, true is returned; if not,
1313 * false is returned and required and available are filled in with the amount of disk space
1314 * required and available respectively (in Gb).
1316 * Note: the decision made by this method isn't, of course, 100% reliable.
1319 Film::should_be_enough_disk_space (double& required, double& available, bool& can_hard_link) const
1321 /* Create a test file and see if we can hard-link it */
1322 boost::filesystem::path test = internal_video_asset_dir() / "test";
1323 boost::filesystem::path test2 = internal_video_asset_dir() / "test2";
1324 can_hard_link = true;
1325 FILE* f = fopen_boost (test, "w");
1328 boost::system::error_code ec;
1329 boost::filesystem::create_hard_link (test, test2, ec);
1331 can_hard_link = false;
1333 boost::filesystem::remove (test);
1334 boost::filesystem::remove (test2);
1337 boost::filesystem::space_info s = boost::filesystem::space (internal_video_asset_dir ());
1338 required = double (required_disk_space ()) / 1073741824.0f;
1339 if (!can_hard_link) {
1342 available = double (s.available) / 1073741824.0f;
1343 return (available - required) > 1;
1347 Film::subtitle_language () const
1349 set<string> languages;
1351 ContentList cl = content ();
1352 BOOST_FOREACH (shared_ptr<Content>& c, cl) {
1354 languages.insert (c->subtitle->language ());
1359 BOOST_FOREACH (string s, languages) {
1360 if (!all.empty ()) {
1370 /** Change the gains of the supplied AudioMapping to make it a default
1371 * for this film. The defaults are guessed based on what processor (if any)
1372 * is in use, the number of input channels and any filename supplied.
1375 Film::make_audio_mapping_default (AudioMapping& mapping, optional<boost::filesystem::path> filename) const
1377 static string const regex[] = {
1378 ".*[\\._-]L[\\._-].*",
1379 ".*[\\._-]R[\\._-].*",
1380 ".*[\\._-]C[\\._-].*",
1381 ".*[\\._-]Lfe[\\._-].*",
1382 ".*[\\._-]Ls[\\._-].*",
1383 ".*[\\._-]Rs[\\._-].*"
1386 static int const regexes = sizeof(regex) / sizeof(*regex);
1388 if (audio_processor ()) {
1389 audio_processor()->make_audio_mapping_default (mapping);
1391 mapping.make_zero ();
1392 if (mapping.input_channels() == 1) {
1393 bool guessed = false;
1395 /* See if we can guess where this stream should go */
1397 for (int i = 0; i < regexes; ++i) {
1398 boost::regex e (regex[i], boost::regex::icase);
1399 if (boost::regex_match (filename->string(), e) && i < mapping.output_channels()) {
1400 mapping.set (0, i, 1);
1407 /* If we have no idea, just put it on centre */
1408 mapping.set (0, static_cast<int> (dcp::CENTRE), 1);
1412 for (int i = 0; i < min (mapping.input_channels(), mapping.output_channels()); ++i) {
1413 mapping.set (i, i, 1);
1419 /** @return The names of the channels that audio contents' outputs are passed into;
1420 * this is either the DCP or a AudioProcessor.
1423 Film::audio_output_names () const
1425 if (audio_processor ()) {
1426 return audio_processor()->input_names ();
1429 DCPOMATIC_ASSERT (MAX_DCP_AUDIO_CHANNELS == 16);
1433 for (int i = 0; i < audio_channels(); ++i) {
1434 n.push_back (short_audio_channel_name (i));
1441 Film::repeat_content (ContentList c, int n)
1443 _playlist->repeat (c, n);
1447 Film::remove_content (ContentList c)
1449 _playlist->remove (c);
1453 Film::audio_analysis_finished ()
1459 Film::reels () const
1461 list<DCPTimePeriod> p;
1462 DCPTime const len = length();
1464 switch (reel_type ()) {
1465 case REELTYPE_SINGLE:
1466 p.push_back (DCPTimePeriod (DCPTime (), len));
1468 case REELTYPE_BY_VIDEO_CONTENT:
1470 optional<DCPTime> last_split;
1471 shared_ptr<Content> last_video;
1472 BOOST_FOREACH (shared_ptr<Content> c, content ()) {
1474 BOOST_FOREACH (DCPTime t, c->reel_split_points()) {
1476 p.push_back (DCPTimePeriod (last_split.get(), t));
1484 DCPTime video_end = last_video ? last_video->end() : DCPTime(0);
1486 /* Definitely go from the last split to the end of the video content */
1487 p.push_back (DCPTimePeriod (last_split.get(), video_end));
1490 if (video_end < len) {
1491 /* And maybe go after that as well if there is any non-video hanging over the end */
1492 p.push_back (DCPTimePeriod (video_end, len));
1496 case REELTYPE_BY_LENGTH:
1499 /* Integer-divide reel length by the size of one frame to give the number of frames per reel */
1500 Frame const reel_in_frames = _reel_length / ((j2k_bandwidth() / video_frame_rate()) / 8);
1501 while (current < len) {
1502 DCPTime end = min (len, current + DCPTime::from_frames (reel_in_frames, video_frame_rate ()));
1503 p.push_back (DCPTimePeriod (current, end));
1513 /** @param period A period within the DCP
1514 * @return Name of the content which most contributes to the given period.
1517 Film::content_summary (DCPTimePeriod period) const
1519 return _playlist->content_summary (period);
1523 Film::fix_conflicting_settings ()
1527 list<boost::filesystem::path> was_referencing;
1528 BOOST_FOREACH (shared_ptr<Content> i, content()) {
1529 shared_ptr<DCPContent> d = dynamic_pointer_cast<DCPContent> (i);
1531 list<string> reasons;
1533 if (!d->can_reference_video(reasons) && d->reference_video()) {
1534 d->set_reference_video (false);
1537 if (!d->can_reference_audio(reasons) && d->reference_audio()) {
1538 d->set_reference_audio (false);
1541 if (!d->can_reference_subtitle(reasons) && d->reference_subtitle()) {
1542 d->set_reference_subtitle (false);
1546 was_referencing.push_back (d->path(0).parent_path().filename());
1551 BOOST_FOREACH (boost::filesystem::path d, was_referencing) {
1552 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()));
1559 Film::use_template (string name)
1561 _template_film.reset (new Film (optional<boost::filesystem::path>()));
1562 _template_film->read_metadata (Config::instance()->template_path (name));
1563 _use_isdcf_name = _template_film->_use_isdcf_name;
1564 _dcp_content_type = _template_film->_dcp_content_type;
1565 _container = _template_film->_container;
1566 _resolution = _template_film->_resolution;
1567 _j2k_bandwidth = _template_film->_j2k_bandwidth;
1568 _video_frame_rate = _template_film->_video_frame_rate;
1569 _signed = _template_film->_signed;
1570 _encrypted = _template_film->_encrypted;
1571 _audio_channels = _template_film->_audio_channels;
1572 _sequence = _template_film->_sequence;
1573 _three_d = _template_film->_three_d;
1574 _interop = _template_film->_interop;
1575 _audio_processor = _template_film->_audio_processor;
1576 _reel_type = _template_film->_reel_type;
1577 _reel_length = _template_film->_reel_length;
1578 _upload_after_make_dcp = _template_film->_upload_after_make_dcp;
1581 pair<double, double>
1582 Film::speed_up_range (int dcp_frame_rate) const
1584 return _playlist->speed_up_range (dcp_frame_rate);
1588 Film::copy_from (shared_ptr<const Film> film)
1590 read_metadata (film->file (metadata_file));