Tidy up now we have a x-thread signaller.
[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 "imagemagick_encoder.h"
35 #include "job.h"
36 #include "filter.h"
37 #include "transcoder.h"
38 #include "util.h"
39 #include "job_manager.h"
40 #include "ab_transcode_job.h"
41 #include "transcode_job.h"
42 #include "scp_dcp_job.h"
43 #include "copy_from_dvd_job.h"
44 #include "make_dcp_job.h"
45 #include "log.h"
46 #include "options.h"
47 #include "exceptions.h"
48 #include "examine_content_job.h"
49 #include "scaler.h"
50 #include "decoder_factory.h"
51 #include "config.h"
52 #include "check_hashes_job.h"
53 #include "version.h"
54 #include "ui_signaller.h"
55
56 using namespace std;
57 using namespace boost;
58
59 /** Construct a Film object in a given directory, reading any metadata
60  *  file that exists in that directory.  An exception will be thrown if
61  *  must_exist is true, and the specified directory does not exist.
62  *
63  *  @param d Film directory.
64  *  @param must_exist true to throw an exception if does not exist.
65  */
66
67 Film::Film (string d, bool must_exist)
68         : _use_dci_name (false)
69         , _dcp_content_type (0)
70         , _format (0)
71         , _scaler (Scaler::from_id ("bicubic"))
72         , _dcp_frames (0)
73         , _dcp_trim_action (CUT)
74         , _dcp_ab (false)
75         , _audio_stream (-1)
76         , _audio_gain (0)
77         , _audio_delay (0)
78         , _still_duration (10)
79         , _subtitle_stream (-1)
80         , _with_subtitles (false)
81         , _subtitle_offset (0)
82         , _subtitle_scale (1)
83         , _length (0)
84         , _audio_sample_rate (0)
85         , _has_subtitles (false)
86         , _frames_per_second (0)
87         , _dirty (false)
88 {
89         /* Make state.directory a complete path without ..s (where possible)
90            (Code swiped from Adam Bowen on stackoverflow)
91         */
92         
93         filesystem::path p (filesystem::system_complete (d));
94         filesystem::path result;
95         for (filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
96                 if (*i == "..") {
97                         if (filesystem::is_symlink (result) || result.filename() == "..") {
98                                 result /= *i;
99                         } else {
100                                 result = result.parent_path ();
101                         }
102                 } else if (*i != ".") {
103                         result /= *i;
104                 }
105         }
106
107         set_directory (result.string ());
108         
109         if (!filesystem::exists (directory())) {
110                 if (must_exist) {
111                         throw OpenFileError (directory());
112                 } else {
113                         filesystem::create_directory (directory());
114                 }
115         }
116
117         read_metadata ();
118
119         _log = new FileLog (file ("log"));
120 }
121
122 Film::Film (Film const & o)
123         : _log (0)
124         , _directory         (o._directory)
125         , _name              (o._name)
126         , _use_dci_name      (o._use_dci_name)
127         , _content           (o._content)
128         , _dcp_content_type  (o._dcp_content_type)
129         , _format            (o._format)
130         , _crop              (o._crop)
131         , _filters           (o._filters)
132         , _scaler            (o._scaler)
133         , _dcp_frames        (o._dcp_frames)
134         , _dcp_trim_action   (o._dcp_trim_action)
135         , _dcp_ab            (o._dcp_ab)
136         , _audio_stream      (o._audio_stream)
137         , _audio_gain        (o._audio_gain)
138         , _audio_delay       (o._audio_delay)
139         , _still_duration    (o._still_duration)
140         , _subtitle_stream   (o._subtitle_stream)
141         , _with_subtitles    (o._with_subtitles)
142         , _subtitle_offset   (o._subtitle_offset)
143         , _subtitle_scale    (o._subtitle_scale)
144         , _audio_language    (o._audio_language)
145         , _subtitle_language (o._subtitle_language)
146         , _territory         (o._territory)
147         , _rating            (o._rating)
148         , _studio            (o._studio)
149         , _facility          (o._facility)
150         , _package_type      (o._package_type)
151         , _thumbs            (o._thumbs)
152         , _size              (o._size)
153         , _length            (o._length)
154         , _audio_sample_rate (o._audio_sample_rate)
155         , _content_digest    (o._content_digest)
156         , _has_subtitles     (o._has_subtitles)
157         , _audio_streams     (o._audio_streams)
158         , _subtitle_streams  (o._subtitle_streams)
159         , _frames_per_second (o._frames_per_second)
160         , _dirty             (o._dirty)
161 {
162
163 }
164
165 Film::~Film ()
166 {
167         delete _log;
168 }
169           
170 /** @return The path to the directory to write JPEG2000 files to */
171 string
172 Film::j2k_dir () const
173 {
174         assert (format());
175
176         filesystem::path p;
177
178         /* Start with j2c */
179         p /= "j2c";
180
181         pair<string, string> f = Filter::ffmpeg_strings (filters());
182
183         /* Write stuff to specify the filter / post-processing settings that are in use,
184            so that we don't get confused about J2K files generated using different
185            settings.
186         */
187         stringstream s;
188         s << format()->id()
189           << "_" << content_digest()
190           << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
191           << "_" << f.first << "_" << f.second
192           << "_" << scaler()->id();
193
194         p /= s.str ();
195
196         /* Similarly for the A/B case */
197         if (dcp_ab()) {
198                 stringstream s;
199                 pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
200                 s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
201                 p /= s.str ();
202         }
203         
204         return dir (p.string());
205 }
206
207 /** Add suitable Jobs to the JobManager to create a DCP for this Film.
208  *  @param true to transcode, false to use the WAV and J2K files that are already there.
209  */
210 void
211 Film::make_dcp (bool transcode)
212 {
213         if (dcp_name().find ("/") != string::npos) {
214                 throw BadSettingError ("name", "cannot contain slashes");
215         }
216         
217         log()->log (String::compose ("DVD-o-matic %1 git %2 using %3", dvdomatic_version, dvdomatic_git_commit, dependency_version_summary()));
218
219         {
220                 char buffer[128];
221                 gethostname (buffer, sizeof (buffer));
222                 log()->log (String::compose ("Starting to make DCP on %1", buffer));
223         }
224                 
225         if (format() == 0) {
226                 throw MissingSettingError ("format");
227         }
228
229         if (content().empty ()) {
230                 throw MissingSettingError ("content");
231         }
232
233         if (dcp_content_type() == 0) {
234                 throw MissingSettingError ("content type");
235         }
236
237         if (name().empty()) {
238                 throw MissingSettingError ("name");
239         }
240
241         shared_ptr<Options> o (new Options (j2k_dir(), ".j2c", dir ("wavs")));
242         o->out_size = format()->dcp_size ();
243         if (dcp_frames() == 0) {
244                 /* Decode the whole film, no blacking */
245                 o->black_after = 0;
246         } else {
247                 switch (dcp_trim_action()) {
248                 case CUT:
249                         /* Decode only part of the film, no blacking */
250                         o->black_after = 0;
251                         break;
252                 case BLACK_OUT:
253                         /* Decode the whole film, but black some frames out */
254                         o->black_after = dcp_frames ();
255                 }
256         }
257         
258         o->padding = format()->dcp_padding (this);
259         o->ratio = format()->ratio_as_float (this);
260         o->decode_subtitles = with_subtitles ();
261
262         shared_ptr<Job> r;
263
264         if (transcode) {
265                 if (dcp_ab()) {
266                         r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), o, shared_ptr<Job> ())));
267                 } else {
268                         r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), o, shared_ptr<Job> ())));
269                 }
270         }
271
272         r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), o, r)));
273         JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), o, r)));
274 }
275
276 /** Start a job to examine our content file */
277 void
278 Film::examine_content ()
279 {
280         if (_examine_content_job) {
281                 return;
282         }
283
284         set_thumbs (vector<int> ());
285         filesystem::remove_all (dir ("thumbs"));
286
287         /* This call will recreate the directory */
288         dir ("thumbs");
289         
290         _examine_content_job.reset (new ExamineContentJob (shared_from_this(), shared_ptr<Job> ()));
291         _examine_content_job->Finished.connect (sigc::mem_fun (*this, &Film::examine_content_finished));
292         JobManager::instance()->add (_examine_content_job);
293 }
294
295 void
296 Film::examine_content_finished ()
297 {
298         _examine_content_job.reset ();
299 }
300
301 /** @return full paths to any audio files that this Film has */
302 vector<string>
303 Film::audio_files () const
304 {
305         vector<string> f;
306         for (filesystem::directory_iterator i = filesystem::directory_iterator (dir("wavs")); i != filesystem::directory_iterator(); ++i) {
307                 f.push_back (i->path().string ());
308         }
309
310         return f;
311 }
312
313 /** Start a job to send our DCP to the configured TMS */
314 void
315 Film::send_dcp_to_tms ()
316 {
317         shared_ptr<Job> j (new SCPDCPJob (shared_from_this(), shared_ptr<Job> ()));
318         JobManager::instance()->add (j);
319 }
320
321 void
322 Film::copy_from_dvd ()
323 {
324         shared_ptr<Job> j (new CopyFromDVDJob (shared_from_this(), shared_ptr<Job> ()));
325         JobManager::instance()->add (j);
326 }
327
328 /** Count the number of frames that have been encoded for this film.
329  *  @return frame count.
330  */
331 int
332 Film::encoded_frames () const
333 {
334         if (format() == 0) {
335                 return 0;
336         }
337
338         int N = 0;
339         for (filesystem::directory_iterator i = filesystem::directory_iterator (j2k_dir ()); i != filesystem::directory_iterator(); ++i) {
340                 ++N;
341                 this_thread::interruption_point ();
342         }
343
344         return N;
345 }
346
347 /** Return the filename of a subtitle image if one exists for a given thumb index.
348  *  @param Thumbnail index.
349  *  @return Position of the image within the source frame, and the image filename, if one exists.
350  *  Otherwise the filename will be empty.
351  */
352 pair<Position, string>
353 Film::thumb_subtitle (int n) const
354 {
355         string sub_file = thumb_base(n) + ".sub";
356         if (!filesystem::exists (sub_file)) {
357                 return pair<Position, string> ();
358         }
359
360         pair<Position, string> sub;
361         
362         ifstream f (sub_file.c_str ());
363         multimap<string, string> kv = read_key_value (f);
364         for (map<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
365                 if (i->first == "x") {
366                         sub.first.x = lexical_cast<int> (i->second);
367                 } else if (i->first == "y") {
368                         sub.first.y = lexical_cast<int> (i->second);
369                         sub.second = String::compose ("%1.sub.png", thumb_base(n));
370                 }
371         }
372         
373         return sub;
374 }
375
376 /** Write state to our `metadata' file */
377 void
378 Film::write_metadata () const
379 {
380         boost::mutex::scoped_lock lm (_state_mutex);
381         
382         filesystem::create_directories (directory());
383
384         string const m = file_locked ("metadata");
385         ofstream f (m.c_str ());
386         if (!f.good ()) {
387                 throw CreateFileError (m);
388         }
389
390         /* User stuff */
391         f << "name " << _name << "\n";
392         f << "use_dci_name " << _use_dci_name << "\n";
393         f << "content " << _content << "\n";
394         if (_dcp_content_type) {
395                 f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
396         }
397         if (_format) {
398                 f << "format " << _format->as_metadata () << "\n";
399         }
400         f << "left_crop " << _crop.left << "\n";
401         f << "right_crop " << _crop.right << "\n";
402         f << "top_crop " << _crop.top << "\n";
403         f << "bottom_crop " << _crop.bottom << "\n";
404         for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
405                 f << "filter " << (*i)->id () << "\n";
406         }
407         f << "scaler " << _scaler->id () << "\n";
408         f << "dcp_frames " << _dcp_frames << "\n";
409
410         f << "dcp_trim_action ";
411         switch (_dcp_trim_action) {
412         case CUT:
413                 f << "cut\n";
414                 break;
415         case BLACK_OUT:
416                 f << "black_out\n";
417                 break;
418         }
419         
420         f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
421         f << "selected_audio_stream " << _audio_stream << "\n";
422         f << "audio_gain " << _audio_gain << "\n";
423         f << "audio_delay " << _audio_delay << "\n";
424         f << "still_duration " << _still_duration << "\n";
425         f << "selected_subtitle_stream " << _subtitle_stream << "\n";
426         f << "with_subtitles " << _with_subtitles << "\n";
427         f << "subtitle_offset " << _subtitle_offset << "\n";
428         f << "subtitle_scale " << _subtitle_scale << "\n";
429         f << "audio_language " << _audio_language << "\n";
430         f << "subtitle_language " << _subtitle_language << "\n";
431         f << "territory " << _territory << "\n";
432         f << "rating " << _rating << "\n";
433         f << "studio " << _studio << "\n";
434         f << "facility " << _facility << "\n";
435         f << "package_type " << _package_type << "\n";
436
437         /* Cached stuff; this is information about our content; we could
438            look it up each time, but that's slow.
439         */
440         for (vector<int>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) {
441                 f << "thumb " << *i << "\n";
442         }
443         f << "width " << _size.width << "\n";
444         f << "height " << _size.height << "\n";
445         f << "length " << _length << "\n";
446         f << "audio_sample_rate " << _audio_sample_rate << "\n";
447         f << "content_digest " << _content_digest << "\n";
448         f << "has_subtitles " << _has_subtitles << "\n";
449
450         for (vector<AudioStream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
451                 f << "audio_stream " << i->to_string () << "\n";
452         }
453
454         for (vector<SubtitleStream>::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
455                 f << "subtitle_stream " << i->to_string () << "\n";
456         }
457
458         f << "frames_per_second " << _frames_per_second << "\n";
459         
460         _dirty = false;
461 }
462
463 /** Read state from our metadata file */
464 void
465 Film::read_metadata ()
466 {
467         boost::mutex::scoped_lock lm (_state_mutex);
468         
469         ifstream f (file_locked("metadata").c_str());
470         multimap<string, string> kv = read_key_value (f);
471         for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
472                 string const k = i->first;
473                 string const v = i->second;
474
475                 /* User-specified stuff */
476                 if (k == "name") {
477                         _name = v;
478                 } else if (k == "use_dci_name") {
479                         _use_dci_name = (v == "1");
480                 } else if (k == "content") {
481                         _content = v;
482                 } else if (k == "dcp_content_type") {
483                         _dcp_content_type = DCPContentType::from_pretty_name (v);
484                 } else if (k == "format") {
485                         _format = Format::from_metadata (v);
486                 } else if (k == "left_crop") {
487                         _crop.left = atoi (v.c_str ());
488                 } else if (k == "right_crop") {
489                         _crop.right = atoi (v.c_str ());
490                 } else if (k == "top_crop") {
491                         _crop.top = atoi (v.c_str ());
492                 } else if (k == "bottom_crop") {
493                         _crop.bottom = atoi (v.c_str ());
494                 } else if (k == "filter") {
495                         _filters.push_back (Filter::from_id (v));
496                 } else if (k == "scaler") {
497                         _scaler = Scaler::from_id (v);
498                 } else if (k == "dcp_frames") {
499                         _dcp_frames = atoi (v.c_str ());
500                 } else if (k == "dcp_trim_action") {
501                         if (v == "cut") {
502                                 _dcp_trim_action = CUT;
503                         } else if (v == "black_out") {
504                                 _dcp_trim_action = BLACK_OUT;
505                         }
506                 } else if (k == "dcp_ab") {
507                         _dcp_ab = (v == "1");
508                 } else if (k == "selected_audio_stream") {
509                         _audio_stream = atoi (v.c_str ());
510                 } else if (k == "audio_gain") {
511                         _audio_gain = atof (v.c_str ());
512                 } else if (k == "audio_delay") {
513                         _audio_delay = atoi (v.c_str ());
514                 } else if (k == "still_duration") {
515                         _still_duration = atoi (v.c_str ());
516                 } else if (k == "selected_subtitle_stream") {
517                         _subtitle_stream = atoi (v.c_str ());
518                 } else if (k == "with_subtitles") {
519                         _with_subtitles = (v == "1");
520                 } else if (k == "subtitle_offset") {
521                         _subtitle_offset = atoi (v.c_str ());
522                 } else if (k == "subtitle_scale") {
523                         _subtitle_scale = atof (v.c_str ());
524                 } else if (k == "audio_language") {
525                         _audio_language = v;
526                 } else if (k == "subtitle_language") {
527                         _subtitle_language = v;
528                 } else if (k == "territory") {
529                         _territory = v;
530                 } else if (k == "rating") {
531                         _rating = v;
532                 } else if (k == "studio") {
533                         _studio = v;
534                 } else if (k == "facility") {
535                         _facility = v;
536                 } else if (k == "package_type") {
537                         _package_type = v;
538                 }
539                 
540                 /* Cached stuff */
541                 if (k == "thumb") {
542                         int const n = atoi (v.c_str ());
543                         /* Only add it to the list if it still exists */
544                         if (filesystem::exists (thumb_file_for_frame (n))) {
545                                 _thumbs.push_back (n);
546                         }
547                 } else if (k == "width") {
548                         _size.width = atoi (v.c_str ());
549                 } else if (k == "height") {
550                         _size.height = atoi (v.c_str ());
551                 } else if (k == "length") {
552                         _length = atof (v.c_str ());
553                 } else if (k == "audio_sample_rate") {
554                         _audio_sample_rate = atoi (v.c_str ());
555                 } else if (k == "content_digest") {
556                         _content_digest = v;
557                 } else if (k == "has_subtitles") {
558                         _has_subtitles = (v == "1");
559                 } else if (k == "audio_stream") {
560                         _audio_streams.push_back (AudioStream (v));
561                 } else if (k == "subtitle_stream") {
562                         _subtitle_streams.push_back (SubtitleStream (v));
563                 } else if (k == "frames_per_second") {
564                         _frames_per_second = atof (v.c_str ());
565                 }
566         }
567                 
568         _dirty = false;
569 }
570
571 /** @param n A thumb index.
572  *  @return The path to the thumb's image file.
573  */
574 string
575 Film::thumb_file (int n) const
576 {
577         return thumb_file_for_frame (thumb_frame (n));
578 }
579
580 /** @param n A frame index within the Film.
581  *  @return The path to the thumb's image file for this frame;
582  *  we assume that it exists.
583  */
584 string
585 Film::thumb_file_for_frame (int n) const
586 {
587         return thumb_base_for_frame(n) + ".png";
588 }
589
590 string
591 Film::thumb_base (int n) const
592 {
593         return thumb_base_for_frame (thumb_frame (n));
594 }
595
596 string
597 Film::thumb_base_for_frame (int n) const
598 {
599         stringstream s;
600         s.width (8);
601         s << setfill('0') << n;
602         
603         filesystem::path p;
604         p /= dir ("thumbs");
605         p /= s.str ();
606                 
607         return p.string ();
608 }
609
610
611 /** @param n A thumb index.
612  *  @return The frame within the Film that it is for.
613  */
614 int
615 Film::thumb_frame (int n) const
616 {
617         boost::mutex::scoped_lock lm (_state_mutex);
618         assert (n < int (_thumbs.size ()));
619         return _thumbs[n];
620 }
621
622 Size
623 Film::cropped_size (Size s) const
624 {
625         boost::mutex::scoped_lock lm (_state_mutex);
626         s.width -= _crop.left + _crop.right;
627         s.height -= _crop.top + _crop.bottom;
628         return s;
629 }
630
631 /** Given a directory name, return its full path within the Film's directory.
632  *  The directory (and its parents) will be created if they do not exist.
633  */
634 string
635 Film::dir (string d) const
636 {
637         boost::mutex::scoped_lock lm (_state_mutex);
638         filesystem::path p;
639         p /= _directory;
640         p /= d;
641         filesystem::create_directories (p);
642         return p.string ();
643 }
644
645 /** Given a file or directory name, return its full path within the Film's directory */
646 string
647 Film::file (string f) const
648 {
649         boost::mutex::scoped_lock lm (_state_mutex);
650         return file_locked (f);
651 }
652
653 string
654 Film::file_locked (string f) const
655 {
656         filesystem::path p;
657         p /= _directory;
658         p /= f;
659         return p.string ();
660 }
661
662 /** @return full path of the content (actual video) file
663  *  of the Film.
664  */
665 string
666 Film::content_path () const
667 {
668         boost::mutex::scoped_lock lm (_state_mutex);
669         if (filesystem::path(_content).has_root_directory ()) {
670                 return _content;
671         }
672
673         return file_locked (_content);
674 }
675
676 ContentType
677 Film::content_type () const
678 {
679 #if BOOST_FILESYSTEM_VERSION == 3
680         string ext = filesystem::path(_content).extension().string();
681 #else
682         string ext = filesystem::path(_content).extension();
683 #endif
684
685         transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
686         
687         if (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png") {
688                 return STILL;
689         }
690
691         return VIDEO;
692 }
693
694 /** @return The sampling rate that we will resample the audio to */
695 int
696 Film::target_audio_sample_rate () const
697 {
698         /* Resample to a DCI-approved sample rate */
699         double t = dcp_audio_sample_rate (audio_sample_rate());
700
701         /* Compensate for the fact that video will be rounded to the
702            nearest integer number of frames per second.
703         */
704         if (rint (frames_per_second()) != frames_per_second()) {
705                 t *= _frames_per_second / rint (frames_per_second());
706         }
707
708         return rint (t);
709 }
710
711 int
712 Film::dcp_length () const
713 {
714         if (dcp_frames()) {
715                 return dcp_frames();
716         }
717
718         return length();
719 }
720
721 /** @return a DCI-compliant name for a DCP of this film */
722 string
723 Film::dci_name () const
724 {
725         boost::mutex::scoped_lock lm (_state_mutex);
726         
727         stringstream d;
728
729         string fixed_name = to_upper_copy (_name);
730         for (size_t i = 0; i < fixed_name.length(); ++i) {
731                 if (fixed_name[i] == ' ') {
732                         fixed_name[i] = '-';
733                 }
734         }
735
736         /* Spec is that the name part should be maximum 14 characters, as I understand it */
737         if (fixed_name.length() > 14) {
738                 fixed_name = fixed_name.substr (0, 14);
739         }
740
741         d << fixed_name << "_";
742
743         if (_dcp_content_type) {
744                 d << _dcp_content_type->dci_name() << "_";
745         }
746
747         if (_format) {
748                 d << _format->dci_name() << "_";
749         }
750
751         if (!_audio_language.empty ()) {
752                 d << _audio_language;
753                 if (!_subtitle_language.empty() && _with_subtitles) {
754                         d << "-" << _subtitle_language;
755                 } else {
756                         d << "-XX";
757                 }
758                         
759                 d << "_";
760         }
761
762         if (!_territory.empty ()) {
763                 d << _territory;
764                 if (!_rating.empty ()) {
765                         d << "-" << _rating;
766                 }
767                 d << "_";
768         }
769
770         switch (_audio_streams[_audio_stream].channels()) {
771         case 1:
772                 d << "10_";
773                 break;
774         case 2:
775                 d << "20_";
776                 break;
777         case 6:
778                 d << "51_";
779                 break;
780         case 8:
781                 d << "71_";
782                 break;
783         }
784
785         d << "2K_";
786
787         if (!_studio.empty ()) {
788                 d << _studio << "_";
789         }
790
791         gregorian::date today = gregorian::day_clock::local_day ();
792         d << gregorian::to_iso_string (today) << "_";
793
794         if (!_facility.empty ()) {
795                 d << _facility << "_";
796         }
797
798         if (!_package_type.empty ()) {
799                 d << _package_type;
800         }
801
802         return d.str ();
803 }
804
805 /** @return name to give the DCP */
806 string
807 Film::dcp_name () const
808 {
809         if (use_dci_name()) {
810                 return dci_name ();
811         }
812
813         return name();
814 }
815
816
817 void
818 Film::set_directory (string d)
819 {
820         boost::mutex::scoped_lock lm (_state_mutex);
821         _directory = d;
822         _dirty = true;
823 }
824
825 void
826 Film::set_name (string n)
827 {
828         {
829                 boost::mutex::scoped_lock lm (_state_mutex);
830                 _name = n;
831         }
832         signal_changed (NAME);
833 }
834
835 void
836 Film::set_use_dci_name (bool u)
837 {
838         {
839                 boost::mutex::scoped_lock lm (_state_mutex);
840                 _use_dci_name = u;
841         }
842         signal_changed (USE_DCI_NAME);
843 }
844
845 void
846 Film::set_content (string c)
847 {
848         string check = directory ();
849
850 #if BOOST_FILESYSTEM_VERSION == 3
851         filesystem::path slash ("/");
852         string platform_slash = slash.make_preferred().string ();
853 #else
854 #ifdef DVDOMATIC_WINDOWS
855         string platform_slash = "\\";
856 #else
857         string platform_slash = "/";
858 #endif
859 #endif  
860
861         if (!ends_with (check, platform_slash)) {
862                 check += platform_slash;
863         }
864         
865         if (filesystem::path(c).has_root_directory () && starts_with (c, check)) {
866                 c = c.substr (_directory.length() + 1);
867         }
868
869         {
870                 boost::mutex::scoped_lock lm (_state_mutex);
871                 if (c == _content) {
872                         return;
873                 }
874
875                 _content = c;
876         }
877                 
878         /* Create a temporary decoder so that we can get information
879            about the content.
880         */
881         
882
883         shared_ptr<Options> o (new Options ("", "", ""));
884         o->out_size = Size (1024, 1024);
885         
886         shared_ptr<Decoder> d = decoder_factory (shared_from_this(), o, 0, 0);
887         
888         set_size (d->native_size ());
889         set_frames_per_second (d->frames_per_second ());
890         set_audio_sample_rate (d->audio_sample_rate ());
891         set_has_subtitles (d->has_subtitles ());
892         set_audio_streams (d->audio_streams ());
893         set_subtitle_streams (d->subtitle_streams ());
894         set_audio_stream (audio_streams().empty() ? -1 : 0);
895         set_subtitle_stream (subtitle_streams().empty() ? -1 : 0);
896         
897         signal_changed (CONTENT);
898
899         set_content_digest (md5_digest (content_path ()));
900 }
901                
902 void
903 Film::set_dcp_content_type (DCPContentType const * t)
904 {
905         {
906                 boost::mutex::scoped_lock lm (_state_mutex);
907                 _dcp_content_type = t;
908         }
909         signal_changed (DCP_CONTENT_TYPE);
910 }
911
912 void
913 Film::set_format (Format const * f)
914 {
915         {
916                 boost::mutex::scoped_lock lm (_state_mutex);
917                 _format = f;
918         }
919         signal_changed (FORMAT);
920 }
921
922 void
923 Film::set_crop (Crop c)
924 {
925         {
926                 boost::mutex::scoped_lock lm (_state_mutex);
927                 _crop = c;
928         }
929         signal_changed (CROP);
930 }
931
932 void
933 Film::set_left_crop (int c)
934 {
935         {
936                 boost::mutex::scoped_lock lm (_state_mutex);
937                 
938                 if (_crop.left == c) {
939                         return;
940                 }
941                 
942                 _crop.left = c;
943         }
944         signal_changed (CROP);
945 }
946
947 void
948 Film::set_right_crop (int c)
949 {
950         {
951                 boost::mutex::scoped_lock lm (_state_mutex);
952                 if (_crop.right == c) {
953                         return;
954                 }
955                 
956                 _crop.right = c;
957         }
958         signal_changed (CROP);
959 }
960
961 void
962 Film::set_top_crop (int c)
963 {
964         {
965                 boost::mutex::scoped_lock lm (_state_mutex);
966                 if (_crop.top == c) {
967                         return;
968                 }
969                 
970                 _crop.top = c;
971         }
972         signal_changed (CROP);
973 }
974
975 void
976 Film::set_bottom_crop (int c)
977 {
978         {
979                 boost::mutex::scoped_lock lm (_state_mutex);
980                 if (_crop.bottom == c) {
981                         return;
982                 }
983                 
984                 _crop.bottom = c;
985         }
986         signal_changed (CROP);
987 }
988
989 void
990 Film::set_filters (vector<Filter const *> f)
991 {
992         {
993                 boost::mutex::scoped_lock lm (_state_mutex);
994                 _filters = f;
995         }
996         signal_changed (FILTERS);
997 }
998
999 void
1000 Film::set_scaler (Scaler const * s)
1001 {
1002         {
1003                 boost::mutex::scoped_lock lm (_state_mutex);
1004                 _scaler = s;
1005         }
1006         signal_changed (SCALER);
1007 }
1008
1009 void
1010 Film::set_dcp_frames (int f)
1011 {
1012         {
1013                 boost::mutex::scoped_lock lm (_state_mutex);
1014                 _dcp_frames = f;
1015         }
1016         signal_changed (DCP_FRAMES);
1017 }
1018
1019 void
1020 Film::set_dcp_trim_action (TrimAction a)
1021 {
1022         {
1023                 boost::mutex::scoped_lock lm (_state_mutex);
1024                 _dcp_trim_action = a;
1025         }
1026         signal_changed (DCP_TRIM_ACTION);
1027 }
1028
1029 void
1030 Film::set_dcp_ab (bool a)
1031 {
1032         {
1033                 boost::mutex::scoped_lock lm (_state_mutex);
1034                 _dcp_ab = a;
1035         }
1036         signal_changed (DCP_AB);
1037 }
1038
1039 void
1040 Film::set_audio_stream (int s)
1041 {
1042         {
1043                 boost::mutex::scoped_lock lm (_state_mutex);
1044                 _audio_stream = s;
1045         }
1046         signal_changed (AUDIO_STREAM);
1047 }
1048
1049 void
1050 Film::set_audio_gain (float g)
1051 {
1052         {
1053                 boost::mutex::scoped_lock lm (_state_mutex);
1054                 _audio_gain = g;
1055         }
1056         signal_changed (AUDIO_GAIN);
1057 }
1058
1059 void
1060 Film::set_audio_delay (int d)
1061 {
1062         {
1063                 boost::mutex::scoped_lock lm (_state_mutex);
1064                 _audio_delay = d;
1065         }
1066         signal_changed (AUDIO_DELAY);
1067 }
1068
1069 void
1070 Film::set_still_duration (int d)
1071 {
1072         {
1073                 boost::mutex::scoped_lock lm (_state_mutex);
1074                 _still_duration = d;
1075         }
1076         signal_changed (STILL_DURATION);
1077 }
1078
1079 void
1080 Film::set_subtitle_stream (int s)
1081 {
1082         {
1083                 boost::mutex::scoped_lock lm (_state_mutex);
1084                 _subtitle_stream = s;
1085         }
1086         signal_changed (SUBTITLE_STREAM);
1087 }
1088
1089 void
1090 Film::set_with_subtitles (bool w)
1091 {
1092         {
1093                 boost::mutex::scoped_lock lm (_state_mutex);
1094                 _with_subtitles = w;
1095         }
1096         signal_changed (WITH_SUBTITLES);
1097 }
1098
1099 void
1100 Film::set_subtitle_offset (int o)
1101 {
1102         {
1103                 boost::mutex::scoped_lock lm (_state_mutex);
1104                 _subtitle_offset = o;
1105         }
1106         signal_changed (SUBTITLE_OFFSET);
1107 }
1108
1109 void
1110 Film::set_subtitle_scale (float s)
1111 {
1112         {
1113                 boost::mutex::scoped_lock lm (_state_mutex);
1114                 _subtitle_scale = s;
1115         }
1116         signal_changed (SUBTITLE_SCALE);
1117 }
1118
1119 void
1120 Film::set_audio_language (string l)
1121 {
1122         {
1123                 boost::mutex::scoped_lock lm (_state_mutex);
1124                 _audio_language = l;
1125         }
1126         signal_changed (DCI_METADATA);
1127 }
1128
1129 void
1130 Film::set_subtitle_language (string l)
1131 {
1132         {
1133                 boost::mutex::scoped_lock lm (_state_mutex);
1134                 _subtitle_language = l;
1135         }
1136         signal_changed (DCI_METADATA);
1137 }
1138
1139 void
1140 Film::set_territory (string t)
1141 {
1142         {
1143                 boost::mutex::scoped_lock lm (_state_mutex);
1144                 _territory = t;
1145         }
1146         signal_changed (DCI_METADATA);
1147 }
1148
1149 void
1150 Film::set_rating (string r)
1151 {
1152         {
1153                 boost::mutex::scoped_lock lm (_state_mutex);
1154                 _rating = r;
1155         }
1156         signal_changed (DCI_METADATA);
1157 }
1158
1159 void
1160 Film::set_studio (string s)
1161 {
1162         {
1163                 boost::mutex::scoped_lock lm (_state_mutex);
1164                 _studio = s;
1165         }
1166         signal_changed (DCI_METADATA);
1167 }
1168
1169 void
1170 Film::set_facility (string f)
1171 {
1172         {
1173                 boost::mutex::scoped_lock lm (_state_mutex);
1174                 _facility = f;
1175         }
1176         signal_changed (DCI_METADATA);
1177 }
1178
1179 void
1180 Film::set_package_type (string p)
1181 {
1182         {
1183                 boost::mutex::scoped_lock lm (_state_mutex);
1184                 _package_type = p;
1185         }
1186         signal_changed (DCI_METADATA);
1187 }
1188
1189 void
1190 Film::set_thumbs (vector<int> t)
1191 {
1192         {
1193                 boost::mutex::scoped_lock lm (_state_mutex);
1194                 _thumbs = t;
1195         }
1196         signal_changed (THUMBS);
1197 }
1198
1199 void
1200 Film::set_size (Size s)
1201 {
1202         {
1203                 boost::mutex::scoped_lock lm (_state_mutex);
1204                 _size = s;
1205         }
1206         signal_changed (SIZE);
1207 }
1208
1209 void
1210 Film::set_length (int l)
1211 {
1212         {
1213                 boost::mutex::scoped_lock lm (_state_mutex);
1214                 _length = l;
1215         }
1216         signal_changed (LENGTH);
1217 }
1218
1219 void
1220 Film::set_audio_sample_rate (int r)
1221 {
1222         {
1223                 boost::mutex::scoped_lock lm (_state_mutex);
1224                 _audio_sample_rate = r;
1225         }
1226         signal_changed (AUDIO_SAMPLE_RATE);
1227 }
1228
1229 void
1230 Film::set_content_digest (string d)
1231 {
1232         {
1233                 boost::mutex::scoped_lock lm (_state_mutex);
1234                 _content_digest = d;
1235         }
1236         _dirty = true;
1237 }
1238
1239 void
1240 Film::set_has_subtitles (bool s)
1241 {
1242         {
1243                 boost::mutex::scoped_lock lm (_state_mutex);
1244                 _has_subtitles = s;
1245         }
1246         signal_changed (HAS_SUBTITLES);
1247 }
1248
1249 void
1250 Film::set_audio_streams (vector<AudioStream> s)
1251 {
1252         {
1253                 boost::mutex::scoped_lock lm (_state_mutex);
1254                 _audio_streams = s;
1255         }
1256         _dirty = true;
1257 }
1258
1259 void
1260 Film::set_subtitle_streams (vector<SubtitleStream> s)
1261 {
1262         {
1263                 boost::mutex::scoped_lock lm (_state_mutex);
1264                 _subtitle_streams = s;
1265         }
1266         _dirty = true;
1267 }
1268
1269 void
1270 Film::set_frames_per_second (float f)
1271 {
1272         {
1273                 boost::mutex::scoped_lock lm (_state_mutex);
1274                 _frames_per_second = f;
1275         }
1276         signal_changed (FRAMES_PER_SECOND);
1277 }
1278         
1279 void
1280 Film::signal_changed (Property p)
1281 {
1282         {
1283                 boost::mutex::scoped_lock lm (_state_mutex);
1284                 _dirty = true;
1285         }
1286         ui_signaller->emit (boost::bind (boost::ref (Changed), p));
1287 }
1288
1289 int
1290 Film::audio_channels () const
1291 {
1292         boost::mutex::scoped_lock lm (_state_mutex);
1293         if (_audio_stream == -1) {
1294                 return 0;
1295         }
1296         
1297         return _audio_streams[_audio_stream].channels ();
1298 }