Merge master.
[dcpomatic.git] / src / lib / film.cc
1 /*
2     Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
3
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <stdexcept>
21 #include <iostream>
22 #include <algorithm>
23 #include <fstream>
24 #include <cstdlib>
25 #include <sstream>
26 #include <iomanip>
27 #include <unistd.h>
28 #include <boost/filesystem.hpp>
29 #include <boost/algorithm/string.hpp>
30 #include <boost/lexical_cast.hpp>
31 #include <libxml++/libxml++.h>
32 #include <libcxml/cxml.h>
33 #include <dcp/signer_chain.h>
34 #include <dcp/cpl.h>
35 #include <dcp/signer.h>
36 #include <dcp/util.h>
37 #include <dcp/local_time.h>
38 #include <dcp/raw_convert.h>
39 #include "film.h"
40 #include "job.h"
41 #include "util.h"
42 #include "job_manager.h"
43 #include "transcode_job.h"
44 #include "scp_dcp_job.h"
45 #include "log.h"
46 #include "exceptions.h"
47 #include "examine_content_job.h"
48 #include "scaler.h"
49 #include "config.h"
50 #include "version.h"
51 #include "ui_signaller.h"
52 #include "playlist.h"
53 #include "player.h"
54 #include "dcp_content_type.h"
55 #include "ratio.h"
56 #include "cross.h"
57 #include "cinema.h"
58
59 #include "i18n.h"
60
61 using std::string;
62 using std::stringstream;
63 using std::multimap;
64 using std::pair;
65 using std::map;
66 using std::vector;
67 using std::setfill;
68 using std::min;
69 using std::make_pair;
70 using std::endl;
71 using std::cout;
72 using std::list;
73 using boost::shared_ptr;
74 using boost::weak_ptr;
75 using boost::dynamic_pointer_cast;
76 using boost::to_upper_copy;
77 using boost::ends_with;
78 using boost::starts_with;
79 using boost::optional;
80 using dcp::Size;
81 using dcp::Signer;
82 using dcp::raw_convert;
83
84 #define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
85 #define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, Log::TYPE_GENERAL);
86
87 /* 5 -> 6
88  * AudioMapping XML changed.
89  * 6 -> 7
90  * Subtitle offset changed to subtitle y offset, and subtitle x offset added.
91  * 7 -> 8
92  * Use <Scale> tag in <VideoContent> rather than <Ratio>.
93  * 8 -> 9
94  * DCI -> ISDCF
95  *
96  * Bumped to 32 for 2.0 branch; some times are expressed in Times rather
97  * than frames now.
98  */
99 int const Film::current_state_version = 32;
100
101 /** Construct a Film object in a given directory.
102  *
103  *  @param dir Film directory.
104  */
105
106 Film::Film (boost::filesystem::path dir, bool log)
107         : _playlist (new Playlist)
108         , _use_isdcf_name (true)
109         , _dcp_content_type (Config::instance()->default_dcp_content_type ())
110         , _container (Config::instance()->default_container ())
111         , _resolution (RESOLUTION_2K)
112         , _scaler (Scaler::from_id ("bicubic"))
113         , _signed (true)
114         , _encrypted (false)
115         , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
116         , _isdcf_metadata (Config::instance()->default_isdcf_metadata ())
117         , _video_frame_rate (24)
118         , _audio_channels (6)
119         , _three_d (false)
120         , _sequence_video (true)
121         , _interop (false)
122         , _state_version (current_state_version)
123         , _dirty (false)
124 {
125         set_isdcf_date_today ();
126
127         _playlist->Changed.connect (bind (&Film::playlist_changed, this));
128         _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
129         
130         /* Make state.directory a complete path without ..s (where possible)
131            (Code swiped from Adam Bowen on stackoverflow)
132         */
133         
134         boost::filesystem::path p (boost::filesystem::system_complete (dir));
135         boost::filesystem::path result;
136         for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
137                 if (*i == "..") {
138                         if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
139                                 result /= *i;
140                         } else {
141                                 result = result.parent_path ();
142                         }
143                 } else if (*i != ".") {
144                         result /= *i;
145                 }
146         }
147
148         set_directory (result);
149         if (log) {
150                 _log.reset (new FileLog (file ("log")));
151         } else {
152                 _log.reset (new NullLog);
153         }
154
155         _playlist->set_sequence_video (_sequence_video);
156 }
157
158 string
159 Film::video_identifier () const
160 {
161         assert (container ());
162
163         stringstream s;
164         s.imbue (std::locale::classic ());
165         
166         s << container()->id()
167           << "_" << resolution_to_string (_resolution)
168           << "_" << _playlist->video_identifier()
169           << "_" << _video_frame_rate
170           << "_" << scaler()->id()
171           << "_" << j2k_bandwidth();
172
173         if (encrypted ()) {
174                 s << "_E";
175         } else {
176                 s << "_P";
177         }
178
179         if (_interop) {
180                 s << "_I";
181         } else {
182                 s << "_S";
183         }
184
185         if (_three_d) {
186                 s << "_3D";
187         }
188
189         return s.str ();
190 }
191           
192 /** @return The path to the directory to write video frame info files to */
193 boost::filesystem::path
194 Film::info_dir () const
195 {
196         boost::filesystem::path p;
197         p /= "info";
198         p /= video_identifier ();
199         return dir (p);
200 }
201
202 boost::filesystem::path
203 Film::internal_video_mxf_dir () const
204 {
205         return dir ("video");
206 }
207
208 boost::filesystem::path
209 Film::internal_video_mxf_filename () const
210 {
211         return video_identifier() + ".mxf";
212 }
213
214 boost::filesystem::path
215 Film::video_mxf_filename () const
216 {
217         return filename_safe_name() + "_video.mxf";
218 }
219
220 boost::filesystem::path
221 Film::audio_mxf_filename () const
222 {
223         return filename_safe_name() + "_audio.mxf";
224 }
225
226 string
227 Film::filename_safe_name () const
228 {
229         string const n = name ();
230         string o;
231         for (size_t i = 0; i < n.length(); ++i) {
232                 if (isalnum (n[i])) {
233                         o += n[i];
234                 } else {
235                         o += "_";
236                 }
237         }
238
239         return o;
240 }
241
242 boost::filesystem::path
243 Film::audio_analysis_dir () const
244 {
245         return dir ("analysis");
246 }
247
248 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
249 void
250 Film::make_dcp ()
251 {
252         set_isdcf_date_today ();
253         
254         if (dcp_name().find ("/") != string::npos) {
255                 throw BadSettingError (_("name"), _("cannot contain slashes"));
256         }
257
258         /* It seems to make sense to auto-save metadata here, since the make DCP may last
259            a long time, and crashes/power failures are moderately likely.
260          */
261         write_metadata ();
262
263         LOG_GENERAL ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary());
264
265         {
266                 char buffer[128];
267                 gethostname (buffer, sizeof (buffer));
268                 LOG_GENERAL ("Starting to make DCP on %1", buffer);
269         }
270
271         ContentList cl = content ();
272         for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
273                 LOG_GENERAL ("Content: %1", (*i)->technical_summary());
274         }
275         LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
276         LOG_GENERAL ("%1 threads", Config::instance()->num_local_encoding_threads());
277         LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
278 #ifdef DCPOMATIC_DEBUG
279         LOG_GENERAL_NC ("DCP-o-matic built in debug mode.");
280 #else
281         LOG_GENERAL_NC ("DCP-o-matic built in optimised mode.");
282 #endif
283 #ifdef LIBDCP_DEBUG
284         LOG_GENERAL_NC ("libdcp built in debug mode.");
285 #else
286         LOG_GENERAL_NC ("libdcp built in optimised mode.");
287 #endif
288
289 #ifdef DCPOMATIC_WINDOWS
290         OSVERSIONINFO info;
291         info.dwOSVersionInfoSize = sizeof (info);
292         GetVersionEx (&info);
293         LOG_GENERAL ("Windows version %1.%2.%3 SP %4", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber, info.szCSDVersion);
294 #endif  
295         
296         LOG_GENERAL ("CPU: %1, %2 processors", cpu_info(), boost::thread::hardware_concurrency ());
297         list<pair<string, string> > const m = mount_info ();
298         for (list<pair<string, string> >::const_iterator i = m.begin(); i != m.end(); ++i) {
299                 LOG_GENERAL ("Mount: %1 %2", i->first, i->second);
300         }
301         
302         if (container() == 0) {
303                 throw MissingSettingError (_("container"));
304         }
305
306         if (content().empty()) {
307                 throw StringError (_("You must add some content to the DCP before creating it"));
308         }
309
310         if (dcp_content_type() == 0) {
311                 throw MissingSettingError (_("content type"));
312         }
313
314         if (name().empty()) {
315                 throw MissingSettingError (_("name"));
316         }
317
318         JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
319 }
320
321 /** Start a job to send our DCP to the configured TMS */
322 void
323 Film::send_dcp_to_tms ()
324 {
325         shared_ptr<Job> j (new SCPDCPJob (shared_from_this()));
326         JobManager::instance()->add (j);
327 }
328
329 /** Count the number of frames that have been encoded for this film.
330  *  @return frame count.
331  */
332 int
333 Film::encoded_frames () const
334 {
335         if (container() == 0) {
336                 return 0;
337         }
338
339         int N = 0;
340         for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (info_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
341                 ++N;
342                 boost::this_thread::interruption_point ();
343         }
344
345         return N;
346 }
347
348 shared_ptr<xmlpp::Document>
349 Film::metadata () const
350 {
351         shared_ptr<xmlpp::Document> doc (new xmlpp::Document);
352         xmlpp::Element* root = doc->create_root_node ("Metadata");
353
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");
357
358         if (_dcp_content_type) {
359                 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->isdcf_name ());
360         }
361
362         if (_container) {
363                 root->add_child("Container")->add_child_text (_container->id ());
364         }
365
366         root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
367         root->add_child("Scaler")->add_child_text (_scaler->id ());
368         root->add_child("J2KBandwidth")->add_child_text (raw_convert<string> (_j2k_bandwidth));
369         _isdcf_metadata.as_xml (root->add_child ("ISDCFMetadata"));
370         root->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate));
371         root->add_child("ISDCFDate")->add_child_text (boost::gregorian::to_iso_string (_isdcf_date));
372         root->add_child("AudioChannels")->add_child_text (raw_convert<string> (_audio_channels));
373         root->add_child("ThreeD")->add_child_text (_three_d ? "1" : "0");
374         root->add_child("SequenceVideo")->add_child_text (_sequence_video ? "1" : "0");
375         root->add_child("Interop")->add_child_text (_interop ? "1" : "0");
376         root->add_child("Signed")->add_child_text (_signed ? "1" : "0");
377         root->add_child("Encrypted")->add_child_text (_encrypted ? "1" : "0");
378         root->add_child("Key")->add_child_text (_key.hex ());
379         _playlist->as_xml (root->add_child ("Playlist"));
380
381         return doc;
382 }
383
384 /** Write state to our `metadata' file */
385 void
386 Film::write_metadata () const
387 {
388         boost::filesystem::create_directories (directory ());
389         shared_ptr<xmlpp::Document> doc = metadata ();
390         doc->write_to_file_formatted (file("metadata.xml").string ());
391         _dirty = false;
392 }
393
394 /** Read state from our metadata file.
395  *  @return Notes about things that the user should know about, or empty.
396  */
397 list<string>
398 Film::read_metadata ()
399 {
400         if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
401                 throw StringError (_("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!"));
402         }
403
404         cxml::Document f ("Metadata");
405         f.read_file (file ("metadata.xml"));
406
407         _state_version = f.number_child<int> ("Version");
408         if (_state_version > current_state_version) {
409                 throw StringError (_("This film was created with a newer version of DCP-o-matic, and it cannot be loaded into this version.  Sorry!"));
410         }
411         
412         _name = f.string_child ("Name");
413         if (_state_version >= 9) {
414                 _use_isdcf_name = f.bool_child ("UseISDCFName");
415                 _isdcf_metadata = ISDCFMetadata (f.node_child ("ISDCFMetadata"));
416                 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("ISDCFDate"));
417         } else {
418                 _use_isdcf_name = f.bool_child ("UseDCIName");
419                 _isdcf_metadata = ISDCFMetadata (f.node_child ("DCIMetadata"));
420                 _isdcf_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
421         }
422
423         {
424                 optional<string> c = f.optional_string_child ("DCPContentType");
425                 if (c) {
426                         _dcp_content_type = DCPContentType::from_isdcf_name (c.get ());
427                 }
428         }
429
430         {
431                 optional<string> c = f.optional_string_child ("Container");
432                 if (c) {
433                         _container = Ratio::from_id (c.get ());
434                 }
435         }
436
437         _resolution = string_to_resolution (f.string_child ("Resolution"));
438         _scaler = Scaler::from_id (f.string_child ("Scaler"));
439         _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
440         _video_frame_rate = f.number_child<int> ("VideoFrameRate");
441         _signed = f.optional_bool_child("Signed").get_value_or (true);
442         _encrypted = f.bool_child ("Encrypted");
443         _audio_channels = f.number_child<int> ("AudioChannels");
444         _sequence_video = f.bool_child ("SequenceVideo");
445         _three_d = f.bool_child ("ThreeD");
446         _interop = f.bool_child ("Interop");
447         _key = dcp::Key (f.string_child ("Key"));
448
449         list<string> notes;
450         /* This method is the only one that can return notes (so far) */
451         _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
452
453         _dirty = false;
454         return notes;
455 }
456
457 /** Given a directory name, return its full path within the Film's directory.
458  *  The directory (and its parents) will be created if they do not exist.
459  */
460 boost::filesystem::path
461 Film::dir (boost::filesystem::path d) const
462 {
463         boost::filesystem::path p;
464         p /= _directory;
465         p /= d;
466         
467         boost::filesystem::create_directories (p);
468         
469         return p;
470 }
471
472 /** Given a file or directory name, return its full path within the Film's directory.
473  *  Any required parent directories will be created.
474  */
475 boost::filesystem::path
476 Film::file (boost::filesystem::path f) const
477 {
478         boost::filesystem::path p;
479         p /= _directory;
480         p /= f;
481
482         boost::filesystem::create_directories (p.parent_path ());
483         
484         return p;
485 }
486
487 /** @return a ISDCF-compliant name for a DCP of this film */
488 string
489 Film::isdcf_name (bool if_created_now) const
490 {
491         stringstream d;
492
493         string raw_name = name ();
494         string fixed_name;
495         bool cap_next = true;
496         for (size_t i = 0; i < raw_name.length(); ++i) {
497                 if (raw_name[i] == ' ') {
498                         cap_next = true;
499                 } else if (cap_next) {
500                         fixed_name += toupper (raw_name[i]);
501                         cap_next = false;
502                 } else {
503                         fixed_name += tolower (raw_name[i]);
504                 }
505         }
506
507         if (fixed_name.length() > 14) {
508                 fixed_name = fixed_name.substr (0, 14);
509         }
510
511         d << fixed_name;
512
513         if (dcp_content_type()) {
514                 d << "_" << dcp_content_type()->isdcf_name();
515                 d << "-" << isdcf_metadata().content_version;
516         }
517
518         ISDCFMetadata const dm = isdcf_metadata ();
519
520         if (dm.temp_version) {
521                 d << "-Temp";
522         }
523         
524         if (dm.pre_release) {
525                 d << "-Pre";
526         }
527         
528         if (dm.red_band) {
529                 d << "-RedBand";
530         }
531         
532         if (!dm.chain.empty ()) {
533                 d << "-" << dm.chain;
534         }
535
536         if (three_d ()) {
537                 d << "-3D";
538         }
539
540         if (dm.two_d_version_of_three_d) {
541                 d << "-2D";
542         }
543
544         if (!dm.mastered_luminance.empty ()) {
545                 d << "-" << dm.mastered_luminance;
546         }
547
548         if (video_frame_rate() != 24) {
549                 d << "-" << video_frame_rate();
550         }
551         
552         if (container()) {
553                 d << "_" << container()->isdcf_name();
554         }
555
556         /* XXX: this only works for content which has been scaled to a given ratio,
557            and uses the first bit of content only.
558         */
559
560         /* The standard says we don't do this for trailers, for some strange reason */
561         if (dcp_content_type() && dcp_content_type()->libdcp_kind() != dcp::TRAILER) {
562                 ContentList cl = content ();
563                 Ratio const * content_ratio = 0;
564                 for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
565                         shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
566                         if (vc && (content_ratio == 0 || vc->scale().ratio() != content_ratio)) {
567                                 content_ratio = vc->scale().ratio();
568                         }
569                 }
570                 
571                 if (content_ratio && content_ratio != container()) {
572                         d << "-" << content_ratio->isdcf_name();
573                 }
574         }
575
576         if (!dm.audio_language.empty ()) {
577                 d << "_" << dm.audio_language;
578                 if (!dm.subtitle_language.empty()) {
579                         d << "-" << dm.subtitle_language;
580                 } else {
581                         d << "-XX";
582                 }
583         }
584
585         if (!dm.territory.empty ()) {
586                 d << "_" << dm.territory;
587                 if (!dm.rating.empty ()) {
588                         d << "-" << dm.rating;
589                 }
590         }
591
592         switch (audio_channels ()) {
593         case 1:
594                 d << "_10";
595                 break;
596         case 2:
597                 d << "_20";
598                 break;
599         case 3:
600                 d << "_30";
601                 break;
602         case 4:
603                 d << "_40";
604                 break;
605         case 5:
606                 d << "_50";
607                 break;
608         case 6:
609                 d << "_51";
610                 break;
611         }
612
613         /* XXX: HI/VI */
614
615         d << "_" << resolution_to_string (_resolution);
616         
617         if (!dm.studio.empty ()) {
618                 d << "_" << dm.studio;
619         }
620
621         if (if_created_now) {
622                 d << "_" << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
623         } else {
624                 d << "_" << boost::gregorian::to_iso_string (_isdcf_date);
625         }
626
627         if (!dm.facility.empty ()) {
628                 d << "_" << dm.facility;
629         }
630
631         if (_interop) {
632                 d << "_IOP";
633         } else {
634                 d << "_SMPTE";
635         }
636         
637         if (three_d ()) {
638                 d << "-3D";
639         }
640
641         if (!dm.package_type.empty ()) {
642                 d << "_" << dm.package_type;
643         }
644
645         return d.str ();
646 }
647
648 /** @return name to give the DCP */
649 string
650 Film::dcp_name (bool if_created_now) const
651 {
652         if (use_isdcf_name()) {
653                 return isdcf_name (if_created_now);
654         }
655
656         return name();
657 }
658
659
660 void
661 Film::set_directory (boost::filesystem::path d)
662 {
663         _directory = d;
664         _dirty = true;
665 }
666
667 void
668 Film::set_name (string n)
669 {
670         _name = n;
671         signal_changed (NAME);
672 }
673
674 void
675 Film::set_use_isdcf_name (bool u)
676 {
677         _use_isdcf_name = u;
678         signal_changed (USE_ISDCF_NAME);
679 }
680
681 void
682 Film::set_dcp_content_type (DCPContentType const * t)
683 {
684         _dcp_content_type = t;
685         signal_changed (DCP_CONTENT_TYPE);
686 }
687
688 void
689 Film::set_container (Ratio const * c)
690 {
691         _container = c;
692         signal_changed (CONTAINER);
693 }
694
695 void
696 Film::set_resolution (Resolution r)
697 {
698         _resolution = r;
699         signal_changed (RESOLUTION);
700 }
701
702 void
703 Film::set_scaler (Scaler const * s)
704 {
705         _scaler = s;
706         signal_changed (SCALER);
707 }
708
709 void
710 Film::set_j2k_bandwidth (int b)
711 {
712         _j2k_bandwidth = b;
713         signal_changed (J2K_BANDWIDTH);
714 }
715
716 void
717 Film::set_isdcf_metadata (ISDCFMetadata m)
718 {
719         _isdcf_metadata = m;
720         signal_changed (ISDCF_METADATA);
721 }
722
723 void
724 Film::set_video_frame_rate (int f)
725 {
726         _video_frame_rate = f;
727         signal_changed (VIDEO_FRAME_RATE);
728 }
729
730 void
731 Film::set_audio_channels (int c)
732 {
733         _audio_channels = c;
734         signal_changed (AUDIO_CHANNELS);
735 }
736
737 void
738 Film::set_three_d (bool t)
739 {
740         _three_d = t;
741         signal_changed (THREE_D);
742 }
743
744 void
745 Film::set_interop (bool i)
746 {
747         _interop = i;
748         signal_changed (INTEROP);
749 }
750
751 void
752 Film::signal_changed (Property p)
753 {
754         _dirty = true;
755
756         switch (p) {
757         case Film::CONTENT:
758                 set_video_frame_rate (_playlist->best_dcp_frame_rate ());
759                 break;
760         case Film::VIDEO_FRAME_RATE:
761         case Film::SEQUENCE_VIDEO:
762                 _playlist->maybe_sequence_video ();
763                 break;
764         default:
765                 break;
766         }
767
768         if (ui_signaller) {
769                 ui_signaller->emit (boost::bind (boost::ref (Changed), p));
770         }
771 }
772
773 void
774 Film::set_isdcf_date_today ()
775 {
776         _isdcf_date = boost::gregorian::day_clock::local_day ();
777 }
778
779 boost::filesystem::path
780 Film::info_path (int f, Eyes e) const
781 {
782         boost::filesystem::path p;
783         p /= info_dir ();
784
785         stringstream s;
786         s.width (8);
787         s << setfill('0') << f;
788
789         if (e == EYES_LEFT) {
790                 s << ".L";
791         } else if (e == EYES_RIGHT) {
792                 s << ".R";
793         }
794
795         s << ".md5";
796         
797         p /= s.str();
798
799         /* info_dir() will already have added any initial bit of the path,
800            so don't call file() on this.
801         */
802         return p;
803 }
804
805 boost::filesystem::path
806 Film::j2c_path (int f, Eyes e, bool t) const
807 {
808         boost::filesystem::path p;
809         p /= "j2c";
810         p /= video_identifier ();
811
812         stringstream s;
813         s.width (8);
814         s << setfill('0') << f;
815
816         if (e == EYES_LEFT) {
817                 s << ".L";
818         } else if (e == EYES_RIGHT) {
819                 s << ".R";
820         }
821         
822         s << ".j2c";
823
824         if (t) {
825                 s << ".tmp";
826         }
827
828         p /= s.str();
829         return file (p);
830 }
831
832 /** Find all the DCPs in our directory that can be libdcp::DCP::read() and return details of their CPLs */
833 vector<CPLSummary>
834 Film::cpls () const
835 {
836         vector<CPLSummary> out;
837         
838         boost::filesystem::path const dir = directory ();
839         for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
840                 if (
841                         boost::filesystem::is_directory (*i) &&
842                         i->path().leaf() != "j2c" && i->path().leaf() != "video" && i->path().leaf() != "info" && i->path().leaf() != "analysis"
843                         ) {
844
845                         try {
846                                 dcp::DCP dcp (*i);
847                                 dcp.read ();
848                                 out.push_back (
849                                         CPLSummary (
850                                                 i->path().leaf().string(),
851                                                 dcp.cpls().front()->id(),
852                                                 dcp.cpls().front()->annotation_text(),
853                                                 dcp.cpls().front()->file()
854                                                 )
855                                         );
856                         } catch (...) {
857
858                         }
859                 }
860         }
861         
862         return out;
863 }
864
865 shared_ptr<Player>
866 Film::make_player () const
867 {
868         return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
869 }
870
871 void
872 Film::set_signed (bool s)
873 {
874         _signed = s;
875         signal_changed (SIGNED);
876 }
877
878 void
879 Film::set_encrypted (bool e)
880 {
881         _encrypted = e;
882         signal_changed (ENCRYPTED);
883 }
884
885 shared_ptr<Playlist>
886 Film::playlist () const
887 {
888         return _playlist;
889 }
890
891 ContentList
892 Film::content () const
893 {
894         return _playlist->content ();
895 }
896
897 void
898 Film::examine_and_add_content (shared_ptr<Content> c)
899 {
900         if (dynamic_pointer_cast<FFmpegContent> (c)) {
901                 run_ffprobe (c->path(0), file ("ffprobe.log"), _log);
902         }
903                         
904         shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
905         j->Finished.connect (bind (&Film::maybe_add_content, this, boost::weak_ptr<Job> (j), boost::weak_ptr<Content> (c)));
906         JobManager::instance()->add (j);
907 }
908
909 void
910 Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
911 {
912         shared_ptr<Job> job = j.lock ();
913         if (!job || !job->finished_ok ()) {
914                 return;
915         }
916         
917         shared_ptr<Content> content = c.lock ();
918         if (content) {
919                 add_content (content);
920         }
921 }
922
923 void
924 Film::add_content (shared_ptr<Content> c)
925 {
926         /* Add video content after any existing content */
927         if (dynamic_pointer_cast<VideoContent> (c)) {
928                 c->set_position (_playlist->video_end ());
929         }
930
931         _playlist->add (c);
932 }
933
934 void
935 Film::remove_content (shared_ptr<Content> c)
936 {
937         _playlist->remove (c);
938 }
939
940 void
941 Film::move_content_earlier (shared_ptr<Content> c)
942 {
943         _playlist->move_earlier (c);
944 }
945
946 void
947 Film::move_content_later (shared_ptr<Content> c)
948 {
949         _playlist->move_later (c);
950 }
951
952 DCPTime
953 Film::length () const
954 {
955         return _playlist->length ();
956 }
957
958 int
959 Film::best_video_frame_rate () const
960 {
961         return _playlist->best_dcp_frame_rate ();
962 }
963
964 FrameRateChange
965 Film::active_frame_rate_change (DCPTime t) const
966 {
967         return _playlist->active_frame_rate_change (t, video_frame_rate ());
968 }
969
970 void
971 Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
972 {
973         if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
974                 set_video_frame_rate (_playlist->best_dcp_frame_rate ());
975         } 
976
977         if (ui_signaller) {
978                 ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
979         }
980 }
981
982 void
983 Film::playlist_changed ()
984 {
985         signal_changed (CONTENT);
986 }       
987
988 int
989 Film::audio_frame_rate () const
990 {
991         /* XXX */
992         return 48000;
993 }
994
995 void
996 Film::set_sequence_video (bool s)
997 {
998         _sequence_video = s;
999         _playlist->set_sequence_video (s);
1000         signal_changed (SEQUENCE_VIDEO);
1001 }
1002
1003 /** @return Size of the largest possible image in whatever resolution we are using */
1004 dcp::Size
1005 Film::full_frame () const
1006 {
1007         switch (_resolution) {
1008         case RESOLUTION_2K:
1009                 return dcp::Size (2048, 1080);
1010         case RESOLUTION_4K:
1011                 return dcp::Size (4096, 2160);
1012         }
1013
1014         assert (false);
1015         return dcp::Size ();
1016 }
1017
1018 /** @return Size of the frame */
1019 dcp::Size
1020 Film::frame_size () const
1021 {
1022         return fit_ratio_within (container()->ratio(), full_frame ());
1023 }
1024
1025 dcp::EncryptedKDM
1026 Film::make_kdm (
1027         shared_ptr<dcp::Certificate> target,
1028         boost::filesystem::path cpl_file,
1029         dcp::LocalTime from,
1030         dcp::LocalTime until
1031         ) const
1032 {
1033         shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
1034         return dcp::DecryptedKDM (
1035                 cpl, from, until, "DCP-o-matic", cpl->content_title_text(), dcp::LocalTime().as_string()
1036                 ).encrypt (make_signer(), target);
1037 }
1038
1039 list<dcp::EncryptedKDM>
1040 Film::make_kdms (
1041         list<shared_ptr<Screen> > screens,
1042         boost::filesystem::path dcp,
1043         dcp::LocalTime from,
1044         dcp::LocalTime until
1045         ) const
1046 {
1047         list<dcp::EncryptedKDM> kdms;
1048
1049         for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
1050                 kdms.push_back (make_kdm ((*i)->certificate, dcp, from, until));
1051         }
1052
1053         return kdms;
1054 }
1055
1056 /** @return The approximate disk space required to encode a DCP of this film with the
1057  *  current settings, in bytes.
1058  */
1059 uint64_t
1060 Film::required_disk_space () const
1061 {
1062         return uint64_t (j2k_bandwidth() / 8) * length().seconds();
1063 }
1064
1065 /** This method checks the disk that the Film is on and tries to decide whether or not
1066  *  there will be enough space to make a DCP for it.  If so, true is returned; if not,
1067  *  false is returned and required and availabe are filled in with the amount of disk space
1068  *  required and available respectively (in Gb).
1069  *
1070  *  Note: the decision made by this method isn't, of course, 100% reliable.
1071  */
1072 bool
1073 Film::should_be_enough_disk_space (double& required, double& available) const
1074 {
1075         boost::filesystem::space_info s = boost::filesystem::space (internal_video_mxf_dir ());
1076         required = double (required_disk_space ()) / 1073741824.0f;
1077         available = double (s.available) / 1073741824.0f;
1078         return (available - required) > 1;
1079 }