Runs.
[dcpomatic.git] / src / lib / film.cc
1 /*
2     Copyright (C) 2012 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 <boost/date_time.hpp>
32 #include "film.h"
33 #include "format.h"
34 #include "job.h"
35 #include "filter.h"
36 #include "transcoder.h"
37 #include "util.h"
38 #include "job_manager.h"
39 #include "ab_transcode_job.h"
40 #include "transcode_job.h"
41 #include "scp_dcp_job.h"
42 #include "log.h"
43 #include "options.h"
44 #include "exceptions.h"
45 #include "examine_content_job.h"
46 #include "scaler.h"
47 #include "decoder_factory.h"
48 #include "config.h"
49 #include "version.h"
50 #include "ui_signaller.h"
51 #include "video_decoder.h"
52 #include "audio_decoder.h"
53 #include "sndfile_decoder.h"
54 #include "analyse_audio_job.h"
55 #include "playlist.h"
56
57 #include "i18n.h"
58
59 using std::string;
60 using std::stringstream;
61 using std::multimap;
62 using std::pair;
63 using std::map;
64 using std::vector;
65 using std::ifstream;
66 using std::ofstream;
67 using std::setfill;
68 using std::min;
69 using std::make_pair;
70 using std::endl;
71 using boost::shared_ptr;
72 using boost::lexical_cast;
73 using boost::to_upper_copy;
74 using boost::ends_with;
75 using boost::starts_with;
76 using boost::optional;
77 using libdcp::Size;
78
79 int const Film::state_version = 4;
80
81 /** Construct a Film object in a given directory, reading any metadata
82  *  file that exists in that directory.  An exception will be thrown if
83  *  must_exist is true and the specified directory does not exist.
84  *
85  *  @param d Film directory.
86  *  @param must_exist true to throw an exception if does not exist.
87  */
88
89 Film::Film (string d, bool must_exist)
90         : _use_dci_name (true)
91         , _trust_content_header (true)
92         , _dcp_content_type (0)
93         , _format (0)
94         , _scaler (Scaler::from_id ("bicubic"))
95         , _trim_start (0)
96         , _trim_end (0)
97         , _dcp_ab (false)
98         , _audio_gain (0)
99         , _audio_delay (0)
100         , _with_subtitles (false)
101         , _subtitle_offset (0)
102         , _subtitle_scale (1)
103         , _colour_lut (0)
104         , _j2k_bandwidth (200000000)
105         , _dci_metadata (Config::instance()->default_dci_metadata ())
106         , _dcp_frame_rate (0)
107         , _dirty (false)
108 {
109         set_dci_date_today ();
110         
111         /* Make state.directory a complete path without ..s (where possible)
112            (Code swiped from Adam Bowen on stackoverflow)
113         */
114         
115         boost::filesystem::path p (boost::filesystem::system_complete (d));
116         boost::filesystem::path result;
117         for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
118                 if (*i == "..") {
119                         if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
120                                 result /= *i;
121                         } else {
122                                 result = result.parent_path ();
123                         }
124                 } else if (*i != ".") {
125                         result /= *i;
126                 }
127         }
128
129         set_directory (result.string ());
130         
131         if (!boost::filesystem::exists (directory())) {
132                 if (must_exist) {
133                         throw OpenFileError (directory());
134                 } else {
135                         boost::filesystem::create_directory (directory());
136                 }
137         }
138
139         if (must_exist) {
140                 read_metadata ();
141         }
142
143         _log.reset (new FileLog (file ("log")));
144 }
145
146 Film::Film (Film const & o)
147         : boost::enable_shared_from_this<Film> (o)
148         /* note: the copied film shares the original's log */
149         , _log               (o._log)
150         , _directory         (o._directory)
151         , _name              (o._name)
152         , _use_dci_name      (o._use_dci_name)
153         , _trust_content_header (o._trust_content_header)
154         , _dcp_content_type  (o._dcp_content_type)
155         , _format            (o._format)
156         , _crop              (o._crop)
157         , _filters           (o._filters)
158         , _scaler            (o._scaler)
159         , _trim_start        (o._trim_start)
160         , _trim_end          (o._trim_end)
161         , _dcp_ab            (o._dcp_ab)
162         , _audio_gain        (o._audio_gain)
163         , _audio_delay       (o._audio_delay)
164         , _with_subtitles    (o._with_subtitles)
165         , _subtitle_offset   (o._subtitle_offset)
166         , _subtitle_scale    (o._subtitle_scale)
167         , _colour_lut        (o._colour_lut)
168         , _j2k_bandwidth     (o._j2k_bandwidth)
169         , _dci_metadata      (o._dci_metadata)
170         , _dci_date          (o._dci_date)
171         , _dcp_frame_rate    (o._dcp_frame_rate)
172         , _dirty             (o._dirty)
173 {
174         
175 }
176
177 Film::~Film ()
178 {
179
180 }
181
182 string
183 Film::video_state_identifier () const
184 {
185         assert (format ());
186
187         return "XXX";
188
189 #if 0   
190
191         pair<string, string> f = Filter::ffmpeg_strings (filters());
192
193         stringstream s;
194         s << format()->id()
195           << "_" << content_digest()
196           << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
197           << "_" << _dcp_frame_rate
198           << "_" << f.first << "_" << f.second
199           << "_" << scaler()->id()
200           << "_" << j2k_bandwidth()
201           << "_" << boost::lexical_cast<int> (colour_lut());
202
203         if (dcp_ab()) {
204                 pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
205                 s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
206         }
207
208         return s.str ();
209 #endif  
210 }
211           
212 /** @return The path to the directory to write video frame info files to */
213 string
214 Film::info_dir () const
215 {
216         boost::filesystem::path p;
217         p /= "info";
218         p /= video_state_identifier ();
219         return dir (p.string());
220 }
221
222 string
223 Film::video_mxf_dir () const
224 {
225         boost::filesystem::path p;
226         return dir ("video");
227 }
228
229 string
230 Film::video_mxf_filename () const
231 {
232         return video_state_identifier() + ".mxf";
233 }
234
235 string
236 Film::audio_analysis_path () const
237 {
238         boost::filesystem::path p;
239         p /= "analysis";
240         p /= "XXX";//content_digest();
241         return file (p.string ());
242 }
243
244 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
245 void
246 Film::make_dcp ()
247 {
248         set_dci_date_today ();
249         
250         if (dcp_name().find ("/") != string::npos) {
251                 throw BadSettingError (_("name"), _("cannot contain slashes"));
252         }
253         
254         log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary()));
255
256         {
257                 char buffer[128];
258                 gethostname (buffer, sizeof (buffer));
259                 log()->log (String::compose ("Starting to make DCP on %1", buffer));
260         }
261         
262 //      log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video"))));
263 //      if (length()) {
264 //              log()->log (String::compose ("Content length %1", length().get()));
265 //      }
266 //      log()->log (String::compose ("Content digest %1", content_digest()));
267 //      log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate()));
268         log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
269         log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
270 #ifdef DVDOMATIC_DEBUG
271         log()->log ("DVD-o-matic built in debug mode.");
272 #else
273         log()->log ("DVD-o-matic built in optimised mode.");
274 #endif
275 #ifdef LIBDCP_DEBUG
276         log()->log ("libdcp built in debug mode.");
277 #else
278         log()->log ("libdcp built in optimised mode.");
279 #endif
280         pair<string, int> const c = cpu_info ();
281         log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
282         
283         if (format() == 0) {
284                 throw MissingSettingError (_("format"));
285         }
286
287         if (content().empty ()) {
288                 throw MissingSettingError (_("content"));
289         }
290
291         if (dcp_content_type() == 0) {
292                 throw MissingSettingError (_("content type"));
293         }
294
295         if (name().empty()) {
296                 throw MissingSettingError (_("name"));
297         }
298
299         DecodeOptions od;
300         od.decode_subtitles = with_subtitles ();
301
302         shared_ptr<Job> r;
303
304         if (dcp_ab()) {
305                 r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od)));
306         } else {
307                 r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od)));
308         }
309 }
310
311 /** Start a job to analyse the audio of our content file */
312 void
313 Film::analyse_audio ()
314 {
315         if (_analyse_audio_job) {
316                 return;
317         }
318
319         _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this()));
320         _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this));
321         JobManager::instance()->add (_analyse_audio_job);
322 }
323
324 /** Start a job to examine a piece of content */
325 void
326 Film::examine_content (shared_ptr<Content> c)
327 {
328         shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c, trust_content_header ()));
329         j->Finished.connect (bind (&Film::examine_content_finished, this));
330         JobManager::instance()->add (j);
331 }
332
333 void
334 Film::analyse_audio_finished ()
335 {
336         ensure_ui_thread ();
337
338         if (_analyse_audio_job->finished_ok ()) {
339                 AudioAnalysisSucceeded ();
340         }
341         
342         _analyse_audio_job.reset ();
343 }
344
345 void
346 Film::examine_content_finished ()
347 {
348         /* XXX */
349 }
350
351 /** Start a job to send our DCP to the configured TMS */
352 void
353 Film::send_dcp_to_tms ()
354 {
355         shared_ptr<Job> j (new SCPDCPJob (shared_from_this()));
356         JobManager::instance()->add (j);
357 }
358
359 /** Count the number of frames that have been encoded for this film.
360  *  @return frame count.
361  */
362 int
363 Film::encoded_frames () const
364 {
365         if (format() == 0) {
366                 return 0;
367         }
368
369         int N = 0;
370         for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (info_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
371                 ++N;
372                 boost::this_thread::interruption_point ();
373         }
374
375         return N;
376 }
377
378 /** Write state to our `metadata' file */
379 void
380 Film::write_metadata () const
381 {
382         boost::mutex::scoped_lock lm (_state_mutex);
383
384         boost::filesystem::create_directories (directory());
385
386         string const m = file ("metadata");
387         ofstream f (m.c_str ());
388         if (!f.good ()) {
389                 throw CreateFileError (m);
390         }
391
392         f << "version " << state_version << endl;
393
394         /* User stuff */
395         f << "name " << _name << endl;
396         f << "use_dci_name " << _use_dci_name << endl;
397 //      f << "content " << _content << endl;
398         f << "trust_content_header " << (_trust_content_header ? "1" : "0") << endl;
399         if (_dcp_content_type) {
400                 f << "dcp_content_type " << _dcp_content_type->dci_name () << endl;
401         }
402         if (_format) {
403                 f << "format " << _format->as_metadata () << endl;
404         }
405         f << "left_crop " << _crop.left << endl;
406         f << "right_crop " << _crop.right << endl;
407         f << "top_crop " << _crop.top << endl;
408         f << "bottom_crop " << _crop.bottom << endl;
409         for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
410                 f << "filter " << (*i)->id () << endl;
411         }
412         f << "scaler " << _scaler->id () << endl;
413         f << "trim_start " << _trim_start << endl;
414         f << "trim_end " << _trim_end << endl;
415         f << "dcp_ab " << (_dcp_ab ? "1" : "0") << endl;
416 //      if (_content_audio_stream) {
417 //              f << "selected_content_audio_stream " << _content_audio_stream->to_string() << endl;
418 //      }
419 //      for (vector<string>::const_iterator i = _external_audio.begin(); i != _external_audio.end(); ++i) {
420 //              f << "external_audio " << *i << endl;
421 //      }
422 //      f << "use_content_audio " << (_use_content_audio ? "1" : "0") << endl;
423         f << "audio_gain " << _audio_gain << endl;
424         f << "audio_delay " << _audio_delay << endl;
425 //      f << "still_duration " << _still_duration << endl;
426 //      if (_subtitle_stream) {
427 //              f << "selected_subtitle_stream " << _subtitle_stream->to_string() << endl;
428 //      }
429         f << "with_subtitles " << _with_subtitles << endl;
430         f << "subtitle_offset " << _subtitle_offset << endl;
431         f << "subtitle_scale " << _subtitle_scale << endl;
432         f << "colour_lut " << _colour_lut << endl;
433         f << "j2k_bandwidth " << _j2k_bandwidth << endl;
434         _dci_metadata.write (f);
435         f << "dci_date " << boost::gregorian::to_iso_string (_dci_date) << endl;
436         f << "dcp_frame_rate " << _dcp_frame_rate << endl;
437 //      f << "width " << _size.width << endl;
438 //      f << "height " << _size.height << endl;
439 //      f << "length " << _length.get_value_or(0) << endl;
440 //      f << "content_digest " << _content_digest << endl;
441
442 //      for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
443 //              f << "content_audio_stream " << (*i)->to_string () << endl;
444 //      }
445
446 //      f << "external_audio_stream " << _sndfile_stream->to_string() << endl;
447
448 //      for (vector<shared_ptr<SubtitleStream> >::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
449 //              f << "subtitle_stream " << (*i)->to_string () << endl;
450 //      }
451
452 //      f << "source_frame_rate " << _source_frame_rate << endl;
453         
454         _dirty = false;
455 }
456
457 /** Read state from our metadata file */
458 void
459 Film::read_metadata ()
460 {
461         boost::mutex::scoped_lock lm (_state_mutex);
462
463 //      _external_audio.clear ();
464 //      _content_audio_streams.clear ();
465 //      _subtitle_streams.clear ();
466
467         boost::optional<int> version;
468
469         /* Backward compatibility things */
470         boost::optional<int> audio_sample_rate;
471         boost::optional<int> audio_stream_index;
472         boost::optional<int> subtitle_stream_index;
473
474         ifstream f (file ("metadata").c_str());
475         if (!f.good()) {
476                 throw OpenFileError (file ("metadata"));
477         }
478         
479         multimap<string, string> kv = read_key_value (f);
480
481         /* We need version before anything else */
482         multimap<string, string>::iterator v = kv.find ("version");
483         if (v != kv.end ()) {
484                 version = atoi (v->second.c_str());
485         }
486         
487         for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
488                 string const k = i->first;
489                 string const v = i->second;
490
491                 if (k == "audio_sample_rate") {
492                         audio_sample_rate = atoi (v.c_str());
493                 }
494
495                 /* User-specified stuff */
496                 if (k == "name") {
497                         _name = v;
498                 } else if (k == "use_dci_name") {
499                         _use_dci_name = (v == "1");
500                 } else if (k == "content") {
501 //                      _content = v;
502                 } else if (k == "trust_content_header") {
503                         _trust_content_header = (v == "1");
504                 } else if (k == "dcp_content_type") {
505                         if (version < 3) {
506                                 _dcp_content_type = DCPContentType::from_pretty_name (v);
507                         } else {
508                                 _dcp_content_type = DCPContentType::from_dci_name (v);
509                         }
510                 } else if (k == "format") {
511                         _format = Format::from_metadata (v);
512                 } else if (k == "left_crop") {
513                         _crop.left = atoi (v.c_str ());
514                 } else if (k == "right_crop") {
515                         _crop.right = atoi (v.c_str ());
516                 } else if (k == "top_crop") {
517                         _crop.top = atoi (v.c_str ());
518                 } else if (k == "bottom_crop") {
519                         _crop.bottom = atoi (v.c_str ());
520                 } else if (k == "filter") {
521                         _filters.push_back (Filter::from_id (v));
522                 } else if (k == "scaler") {
523                         _scaler = Scaler::from_id (v);
524                 } else if ( ((!version || version < 2) && k == "dcp_trim_start") || k == "trim_start") {
525                         _trim_start = atoi (v.c_str ());
526                 } else if ( ((!version || version < 2) && k == "dcp_trim_end") || k == "trim_end") {
527                         _trim_end = atoi (v.c_str ());
528                 } else if (k == "dcp_ab") {
529                         _dcp_ab = (v == "1");
530                 } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
531                         if (!version) {
532                                 audio_stream_index = atoi (v.c_str ());
533                         } else {
534 //                              _content_audio_stream = audio_stream_factory (v, version);
535                         }
536                 } else if (k == "external_audio") {
537 //                      _external_audio.push_back (v);
538                 } else if (k == "use_content_audio") {
539 //                      _use_content_audio = (v == "1");
540                 } else if (k == "audio_gain") {
541                         _audio_gain = atof (v.c_str ());
542                 } else if (k == "audio_delay") {
543                         _audio_delay = atoi (v.c_str ());
544                 } else if (k == "still_duration") {
545 //                      _still_duration = atoi (v.c_str ());
546                 } else if (k == "selected_subtitle_stream") {
547                         if (!version) {
548                                 subtitle_stream_index = atoi (v.c_str ());
549                         } else {
550 //                              _subtitle_stream = subtitle_stream_factory (v, version);
551                         }
552                 } else if (k == "with_subtitles") {
553                         _with_subtitles = (v == "1");
554                 } else if (k == "subtitle_offset") {
555                         _subtitle_offset = atoi (v.c_str ());
556                 } else if (k == "subtitle_scale") {
557                         _subtitle_scale = atof (v.c_str ());
558                 } else if (k == "colour_lut") {
559                         _colour_lut = atoi (v.c_str ());
560                 } else if (k == "j2k_bandwidth") {
561                         _j2k_bandwidth = atoi (v.c_str ());
562                 } else if (k == "dci_date") {
563                         _dci_date = boost::gregorian::from_undelimited_string (v);
564                 } else if (k == "dcp_frame_rate") {
565                         _dcp_frame_rate = atoi (v.c_str ());
566                 }
567
568                 _dci_metadata.read (k, v);
569                 
570                 /* Cached stuff */
571                 if (k == "width") {
572 //                      _size.width = atoi (v.c_str ());
573                 } else if (k == "height") {
574 //                      _size.height = atoi (v.c_str ());
575                 } else if (k == "length") {
576                         int const vv = atoi (v.c_str ());
577                         if (vv) {
578 //                              _length = vv;
579                         }
580                 } else if (k == "content_digest") {
581 //                      _content_digest = v;
582                 } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
583 //                      _content_audio_streams.push_back (audio_stream_factory (v, version));
584                 } else if (k == "external_audio_stream") {
585 //                      _sndfile_stream = audio_stream_factory (v, version);
586                 } else if (k == "subtitle_stream") {
587 //                      _subtitle_streams.push_back (subtitle_stream_factory (v, version));
588                 } else if (k == "source_frame_rate") {
589 //                      _source_frame_rate = atof (v.c_str ());
590                 } else if (version < 4 && k == "frames_per_second") {
591 //                      _source_frame_rate = atof (v.c_str ());
592                         /* Fill in what would have been used for DCP frame rate by the older version */
593 //                      _dcp_frame_rate = best_dcp_frame_rate (_source_frame_rate);
594                 }
595         }
596
597         _dirty = false;
598 }
599
600 libdcp::Size
601 Film::cropped_size (libdcp::Size s) const
602 {
603         boost::mutex::scoped_lock lm (_state_mutex);
604         s.width -= _crop.left + _crop.right;
605         s.height -= _crop.top + _crop.bottom;
606         return s;
607 }
608
609 /** Given a directory name, return its full path within the Film's directory.
610  *  The directory (and its parents) will be created if they do not exist.
611  */
612 string
613 Film::dir (string d) const
614 {
615         boost::mutex::scoped_lock lm (_directory_mutex);
616         
617         boost::filesystem::path p;
618         p /= _directory;
619         p /= d;
620         
621         boost::filesystem::create_directories (p);
622         
623         return p.string ();
624 }
625
626 /** Given a file or directory name, return its full path within the Film's directory.
627  *  _directory_mutex must not be locked on entry.
628  *  Any required parent directories will be created.
629  */
630 string
631 Film::file (string f) const
632 {
633         boost::mutex::scoped_lock lm (_directory_mutex);
634
635         boost::filesystem::path p;
636         p /= _directory;
637         p /= f;
638
639         boost::filesystem::create_directories (p.parent_path ());
640         
641         return p.string ();
642 }
643
644 /** @return The sampling rate that we will resample the audio to */
645 int
646 Film::target_audio_sample_rate () const
647 {
648         /* XXX: how often is this method called? */
649         
650         boost::shared_ptr<Playlist> p = playlist ();
651         if (p->has_audio ()) {
652                 return 0;
653         }
654         
655         /* Resample to a DCI-approved sample rate */
656         double t = dcp_audio_sample_rate (p->audio_frame_rate());
657
658         FrameRateConversion frc (p->video_frame_rate(), dcp_frame_rate());
659
660         /* Compensate if the DCP is being run at a different frame rate
661            to the source; that is, if the video is run such that it will
662            look different in the DCP compared to the source (slower or faster).
663            skip/repeat doesn't come into effect here.
664         */
665
666         if (frc.change_speed) {
667                 t *= p->video_frame_rate() * frc.factor() / dcp_frame_rate();
668         }
669
670         return rint (t);
671 }
672
673 /** @return a DCI-compliant name for a DCP of this film */
674 string
675 Film::dci_name (bool if_created_now) const
676 {
677         stringstream d;
678
679         string fixed_name = to_upper_copy (name());
680         for (size_t i = 0; i < fixed_name.length(); ++i) {
681                 if (fixed_name[i] == ' ') {
682                         fixed_name[i] = '-';
683                 }
684         }
685
686         /* Spec is that the name part should be maximum 14 characters, as I understand it */
687         if (fixed_name.length() > 14) {
688                 fixed_name = fixed_name.substr (0, 14);
689         }
690
691         d << fixed_name;
692
693         if (dcp_content_type()) {
694                 d << "_" << dcp_content_type()->dci_name();
695         }
696
697         if (format()) {
698                 d << "_" << format()->dci_name();
699         }
700
701         DCIMetadata const dm = dci_metadata ();
702
703         if (!dm.audio_language.empty ()) {
704                 d << "_" << dm.audio_language;
705                 if (!dm.subtitle_language.empty()) {
706                         d << "-" << dm.subtitle_language;
707                 } else {
708                         d << "-XX";
709                 }
710         }
711
712         if (!dm.territory.empty ()) {
713                 d << "_" << dm.territory;
714                 if (!dm.rating.empty ()) {
715                         d << "-" << dm.rating;
716                 }
717         }
718
719         /* XXX */
720         switch (2) {
721         case 1:
722                 d << "_10";
723                 break;
724         case 2:
725                 d << "_20";
726                 break;
727         case 6:
728                 d << "_51";
729                 break;
730         case 8:
731                 d << "_71";
732                 break;
733         }
734
735         d << "_2K";
736
737         if (!dm.studio.empty ()) {
738                 d << "_" << dm.studio;
739         }
740
741         if (if_created_now) {
742                 d << "_" << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
743         } else {
744                 d << "_" << boost::gregorian::to_iso_string (_dci_date);
745         }
746
747         if (!dm.facility.empty ()) {
748                 d << "_" << dm.facility;
749         }
750
751         if (!dm.package_type.empty ()) {
752                 d << "_" << dm.package_type;
753         }
754
755         return d.str ();
756 }
757
758 /** @return name to give the DCP */
759 string
760 Film::dcp_name (bool if_created_now) const
761 {
762         if (use_dci_name()) {
763                 return dci_name (if_created_now);
764         }
765
766         return name();
767 }
768
769
770 void
771 Film::set_directory (string d)
772 {
773         boost::mutex::scoped_lock lm (_state_mutex);
774         _directory = d;
775         _dirty = true;
776 }
777
778 void
779 Film::set_name (string n)
780 {
781         {
782                 boost::mutex::scoped_lock lm (_state_mutex);
783                 _name = n;
784         }
785         signal_changed (NAME);
786 }
787
788 void
789 Film::set_use_dci_name (bool u)
790 {
791         {
792                 boost::mutex::scoped_lock lm (_state_mutex);
793                 _use_dci_name = u;
794         }
795         signal_changed (USE_DCI_NAME);
796 }
797
798 void
799 Film::set_trust_content_header (bool t)
800 {
801         {
802                 boost::mutex::scoped_lock lm (_state_mutex);
803                 _trust_content_header = t;
804         }
805         
806         signal_changed (TRUST_CONTENT_HEADER);
807
808         if (!_trust_content_header && !content().empty()) {
809                 /* We just said that we don't trust the content's header */
810                 /* XXX */
811 //              examine_content ();
812         }
813 }
814                
815 void
816 Film::set_dcp_content_type (DCPContentType const * t)
817 {
818         {
819                 boost::mutex::scoped_lock lm (_state_mutex);
820                 _dcp_content_type = t;
821         }
822         signal_changed (DCP_CONTENT_TYPE);
823 }
824
825 void
826 Film::set_format (Format const * f)
827 {
828         {
829                 boost::mutex::scoped_lock lm (_state_mutex);
830                 _format = f;
831         }
832         signal_changed (FORMAT);
833 }
834
835 void
836 Film::set_crop (Crop c)
837 {
838         {
839                 boost::mutex::scoped_lock lm (_state_mutex);
840                 _crop = c;
841         }
842         signal_changed (CROP);
843 }
844
845 void
846 Film::set_left_crop (int c)
847 {
848         {
849                 boost::mutex::scoped_lock lm (_state_mutex);
850                 
851                 if (_crop.left == c) {
852                         return;
853                 }
854                 
855                 _crop.left = c;
856         }
857         signal_changed (CROP);
858 }
859
860 void
861 Film::set_right_crop (int c)
862 {
863         {
864                 boost::mutex::scoped_lock lm (_state_mutex);
865                 if (_crop.right == c) {
866                         return;
867                 }
868                 
869                 _crop.right = c;
870         }
871         signal_changed (CROP);
872 }
873
874 void
875 Film::set_top_crop (int c)
876 {
877         {
878                 boost::mutex::scoped_lock lm (_state_mutex);
879                 if (_crop.top == c) {
880                         return;
881                 }
882                 
883                 _crop.top = c;
884         }
885         signal_changed (CROP);
886 }
887
888 void
889 Film::set_bottom_crop (int c)
890 {
891         {
892                 boost::mutex::scoped_lock lm (_state_mutex);
893                 if (_crop.bottom == c) {
894                         return;
895                 }
896                 
897                 _crop.bottom = c;
898         }
899         signal_changed (CROP);
900 }
901
902 void
903 Film::set_filters (vector<Filter const *> f)
904 {
905         {
906                 boost::mutex::scoped_lock lm (_state_mutex);
907                 _filters = f;
908         }
909         signal_changed (FILTERS);
910 }
911
912 void
913 Film::set_scaler (Scaler const * s)
914 {
915         {
916                 boost::mutex::scoped_lock lm (_state_mutex);
917                 _scaler = s;
918         }
919         signal_changed (SCALER);
920 }
921
922 void
923 Film::set_trim_start (int t)
924 {
925         {
926                 boost::mutex::scoped_lock lm (_state_mutex);
927                 _trim_start = t;
928         }
929         signal_changed (TRIM_START);
930 }
931
932 void
933 Film::set_trim_end (int t)
934 {
935         {
936                 boost::mutex::scoped_lock lm (_state_mutex);
937                 _trim_end = t;
938         }
939         signal_changed (TRIM_END);
940 }
941
942 void
943 Film::set_dcp_ab (bool a)
944 {
945         {
946                 boost::mutex::scoped_lock lm (_state_mutex);
947                 _dcp_ab = a;
948         }
949         signal_changed (DCP_AB);
950 }
951
952 void
953 Film::set_audio_gain (float g)
954 {
955         {
956                 boost::mutex::scoped_lock lm (_state_mutex);
957                 _audio_gain = g;
958         }
959         signal_changed (AUDIO_GAIN);
960 }
961
962 void
963 Film::set_audio_delay (int d)
964 {
965         {
966                 boost::mutex::scoped_lock lm (_state_mutex);
967                 _audio_delay = d;
968         }
969         signal_changed (AUDIO_DELAY);
970 }
971
972 void
973 Film::set_with_subtitles (bool w)
974 {
975         {
976                 boost::mutex::scoped_lock lm (_state_mutex);
977                 _with_subtitles = w;
978         }
979         signal_changed (WITH_SUBTITLES);
980 }
981
982 void
983 Film::set_subtitle_offset (int o)
984 {
985         {
986                 boost::mutex::scoped_lock lm (_state_mutex);
987                 _subtitle_offset = o;
988         }
989         signal_changed (SUBTITLE_OFFSET);
990 }
991
992 void
993 Film::set_subtitle_scale (float s)
994 {
995         {
996                 boost::mutex::scoped_lock lm (_state_mutex);
997                 _subtitle_scale = s;
998         }
999         signal_changed (SUBTITLE_SCALE);
1000 }
1001
1002 void
1003 Film::set_colour_lut (int i)
1004 {
1005         {
1006                 boost::mutex::scoped_lock lm (_state_mutex);
1007                 _colour_lut = i;
1008         }
1009         signal_changed (COLOUR_LUT);
1010 }
1011
1012 void
1013 Film::set_j2k_bandwidth (int b)
1014 {
1015         {
1016                 boost::mutex::scoped_lock lm (_state_mutex);
1017                 _j2k_bandwidth = b;
1018         }
1019         signal_changed (J2K_BANDWIDTH);
1020 }
1021
1022 void
1023 Film::set_dci_metadata (DCIMetadata m)
1024 {
1025         {
1026                 boost::mutex::scoped_lock lm (_state_mutex);
1027                 _dci_metadata = m;
1028         }
1029         signal_changed (DCI_METADATA);
1030 }
1031
1032
1033 void
1034 Film::set_dcp_frame_rate (int f)
1035 {
1036         {
1037                 boost::mutex::scoped_lock lm (_state_mutex);
1038                 _dcp_frame_rate = f;
1039         }
1040         signal_changed (DCP_FRAME_RATE);
1041 }
1042
1043 void
1044 Film::signal_changed (Property p)
1045 {
1046         {
1047                 boost::mutex::scoped_lock lm (_state_mutex);
1048                 _dirty = true;
1049         }
1050
1051         if (ui_signaller) {
1052                 ui_signaller->emit (boost::bind (boost::ref (Changed), p));
1053         }
1054 }
1055
1056 void
1057 Film::set_dci_date_today ()
1058 {
1059         _dci_date = boost::gregorian::day_clock::local_day ();
1060 }
1061
1062 string
1063 Film::info_path (int f) const
1064 {
1065         boost::filesystem::path p;
1066         p /= info_dir ();
1067
1068         stringstream s;
1069         s.width (8);
1070         s << setfill('0') << f << ".md5";
1071
1072         p /= s.str();
1073
1074         /* info_dir() will already have added any initial bit of the path,
1075            so don't call file() on this.
1076         */
1077         return p.string ();
1078 }
1079
1080 string
1081 Film::j2c_path (int f, bool t) const
1082 {
1083         boost::filesystem::path p;
1084         p /= "j2c";
1085         p /= video_state_identifier ();
1086
1087         stringstream s;
1088         s.width (8);
1089         s << setfill('0') << f << ".j2c";
1090
1091         if (t) {
1092                 s << ".tmp";
1093         }
1094
1095         p /= s.str();
1096         return file (p.string ());
1097 }
1098
1099 /** Make an educated guess as to whether we have a complete DCP
1100  *  or not.
1101  *  @return true if we do.
1102  */
1103
1104 bool
1105 Film::have_dcp () const
1106 {
1107         try {
1108                 libdcp::DCP dcp (dir (dcp_name()));
1109                 dcp.read ();
1110         } catch (...) {
1111                 return false;
1112         }
1113
1114         return true;
1115 }
1116
1117 shared_ptr<Playlist>
1118 Film::playlist () const
1119 {
1120         boost::mutex::scoped_lock lm (_state_mutex);
1121         return shared_ptr<Playlist> (new Playlist (shared_from_this (), _content));
1122 }
1123
1124 void
1125 Film::add_content (shared_ptr<Content> c)
1126 {
1127         {
1128                 boost::mutex::scoped_lock lm (_state_mutex);
1129                 _content.push_back (c);
1130         }
1131
1132         signal_changed (CONTENT);
1133
1134         examine_content (c);
1135 }