Make Film state thread-safe.
[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 void
277 Film::copy_from_dvd_post_gui ()
278 {
279         const string dvd_dir = dir ("dvd");
280
281         string largest_file;
282         uintmax_t largest_size = 0;
283         for (filesystem::directory_iterator i = filesystem::directory_iterator (dvd_dir); i != filesystem::directory_iterator(); ++i) {
284                 uintmax_t const s = filesystem::file_size (*i);
285                 if (s > largest_size) {
286
287 #if BOOST_FILESYSTEM_VERSION == 3               
288                         largest_file = filesystem::path(*i).generic_string();
289 #else
290                         largest_file = i->string ();
291 #endif
292                         largest_size = s;
293                 }
294         }
295
296         set_content (largest_file);
297 }
298
299 /** Start a job to examine our content file */
300 void
301 Film::examine_content ()
302 {
303         if (_examine_content_job) {
304                 return;
305         }
306
307         set_thumbs (vector<int> ());
308         filesystem::remove_all (dir ("thumbs"));
309
310         /* This call will recreate the directory */
311         dir ("thumbs");
312         
313         _examine_content_job.reset (new ExamineContentJob (shared_from_this(), shared_ptr<Job> ()));
314         _examine_content_job->Finished.connect (sigc::mem_fun (*this, &Film::examine_content_post_gui));
315         JobManager::instance()->add (_examine_content_job);
316 }
317
318 void
319 Film::examine_content_post_gui ()
320 {
321         set_length (_examine_content_job->last_video_frame ());
322         _examine_content_job.reset ();
323
324         string const tdir = dir ("thumbs");
325         vector<int> thumbs;
326
327         for (filesystem::directory_iterator i = filesystem::directory_iterator (tdir); i != filesystem::directory_iterator(); ++i) {
328
329                 /* Aah, the sweet smell of progress */
330 #if BOOST_FILESYSTEM_VERSION == 3               
331                 string const l = filesystem::path(*i).leaf().generic_string();
332 #else
333                 string const l = i->leaf ();
334 #endif
335                 
336                 size_t const d = l.find (".png");
337                 size_t const t = l.find (".tmp");
338                 if (d != string::npos && t == string::npos) {
339                         thumbs.push_back (atoi (l.substr (0, d).c_str()));
340                 }
341         }
342
343         sort (thumbs.begin(), thumbs.end());
344         set_thumbs (thumbs);    
345 }
346
347
348 /** @return full paths to any audio files that this Film has */
349 vector<string>
350 Film::audio_files () const
351 {
352         vector<string> f;
353         for (filesystem::directory_iterator i = filesystem::directory_iterator (dir("wavs")); i != filesystem::directory_iterator(); ++i) {
354                 f.push_back (i->path().string ());
355         }
356
357         return f;
358 }
359
360 /** Start a job to send our DCP to the configured TMS */
361 void
362 Film::send_dcp_to_tms ()
363 {
364         shared_ptr<Job> j (new SCPDCPJob (shared_from_this(), shared_ptr<Job> ()));
365         JobManager::instance()->add (j);
366 }
367
368 void
369 Film::copy_from_dvd ()
370 {
371         shared_ptr<Job> j (new CopyFromDVDJob (shared_from_this(), shared_ptr<Job> ()));
372         j->Finished.connect (sigc::mem_fun (*this, &Film::copy_from_dvd_post_gui));
373         JobManager::instance()->add (j);
374 }
375
376 /** Count the number of frames that have been encoded for this film.
377  *  @return frame count.
378  */
379 int
380 Film::encoded_frames () const
381 {
382         if (format() == 0) {
383                 return 0;
384         }
385
386         int N = 0;
387         for (filesystem::directory_iterator i = filesystem::directory_iterator (j2k_dir ()); i != filesystem::directory_iterator(); ++i) {
388                 ++N;
389                 this_thread::interruption_point ();
390         }
391
392         return N;
393 }
394
395 /** Return the filename of a subtitle image if one exists for a given thumb index.
396  *  @param Thumbnail index.
397  *  @return Position of the image within the source frame, and the image filename, if one exists.
398  *  Otherwise the filename will be empty.
399  */
400 pair<Position, string>
401 Film::thumb_subtitle (int n) const
402 {
403         string sub_file = thumb_base(n) + ".sub";
404         if (!filesystem::exists (sub_file)) {
405                 return pair<Position, string> ();
406         }
407
408         pair<Position, string> sub;
409         
410         ifstream f (sub_file.c_str ());
411         multimap<string, string> kv = read_key_value (f);
412         for (map<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
413                 if (i->first == "x") {
414                         sub.first.x = lexical_cast<int> (i->second);
415                 } else if (i->first == "y") {
416                         sub.first.y = lexical_cast<int> (i->second);
417                         sub.second = String::compose ("%1.sub.png", thumb_base(n));
418                 }
419         }
420         
421         return sub;
422 }
423
424 /** Write state to our `metadata' file */
425 void
426 Film::write_metadata () const
427 {
428         boost::mutex::scoped_lock lm (_state_mutex);
429         
430         filesystem::create_directories (directory());
431
432         string const m = file_locked ("metadata");
433         ofstream f (m.c_str ());
434         if (!f.good ()) {
435                 throw CreateFileError (m);
436         }
437
438         /* User stuff */
439         f << "name " << _name << "\n";
440         f << "use_dci_name " << _use_dci_name << "\n";
441         f << "content " << _content << "\n";
442         if (_dcp_content_type) {
443                 f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
444         }
445         if (_format) {
446                 f << "format " << _format->as_metadata () << "\n";
447         }
448         f << "left_crop " << _crop.left << "\n";
449         f << "right_crop " << _crop.right << "\n";
450         f << "top_crop " << _crop.top << "\n";
451         f << "bottom_crop " << _crop.bottom << "\n";
452         for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
453                 f << "filter " << (*i)->id () << "\n";
454         }
455         f << "scaler " << _scaler->id () << "\n";
456         f << "dcp_frames " << _dcp_frames << "\n";
457
458         f << "dcp_trim_action ";
459         switch (_dcp_trim_action) {
460         case CUT:
461                 f << "cut\n";
462                 break;
463         case BLACK_OUT:
464                 f << "black_out\n";
465                 break;
466         }
467         
468         f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
469         f << "selected_audio_stream " << _audio_stream << "\n";
470         f << "audio_gain " << _audio_gain << "\n";
471         f << "audio_delay " << _audio_delay << "\n";
472         f << "still_duration " << _still_duration << "\n";
473         f << "selected_subtitle_stream " << _subtitle_stream << "\n";
474         f << "with_subtitles " << _with_subtitles << "\n";
475         f << "subtitle_offset " << _subtitle_offset << "\n";
476         f << "subtitle_scale " << _subtitle_scale << "\n";
477         f << "audio_language " << _audio_language << "\n";
478         f << "subtitle_language " << _subtitle_language << "\n";
479         f << "territory " << _territory << "\n";
480         f << "rating " << _rating << "\n";
481         f << "studio " << _studio << "\n";
482         f << "facility " << _facility << "\n";
483         f << "package_type " << _package_type << "\n";
484
485         /* Cached stuff; this is information about our content; we could
486            look it up each time, but that's slow.
487         */
488         for (vector<int>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) {
489                 f << "thumb " << *i << "\n";
490         }
491         f << "width " << _size.width << "\n";
492         f << "height " << _size.height << "\n";
493         f << "length " << _length << "\n";
494         f << "audio_sample_rate " << _audio_sample_rate << "\n";
495         f << "content_digest " << _content_digest << "\n";
496         f << "has_subtitles " << _has_subtitles << "\n";
497
498         for (vector<AudioStream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
499                 f << "audio_stream " << i->to_string () << "\n";
500         }
501
502         for (vector<SubtitleStream>::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
503                 f << "subtitle_stream " << i->to_string () << "\n";
504         }
505
506         f << "frames_per_second " << _frames_per_second << "\n";
507         
508         _dirty = false;
509 }
510
511 /** Read state from our metadata file */
512 void
513 Film::read_metadata ()
514 {
515         boost::mutex::scoped_lock lm (_state_mutex);
516         
517         ifstream f (file_locked("metadata").c_str());
518         multimap<string, string> kv = read_key_value (f);
519         for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
520                 string const k = i->first;
521                 string const v = i->second;
522
523                 /* User-specified stuff */
524                 if (k == "name") {
525                         _name = v;
526                 } else if (k == "use_dci_name") {
527                         _use_dci_name = (v == "1");
528                 } else if (k == "content") {
529                         _content = v;
530                 } else if (k == "dcp_content_type") {
531                         _dcp_content_type = DCPContentType::from_pretty_name (v);
532                 } else if (k == "format") {
533                         _format = Format::from_metadata (v);
534                 } else if (k == "left_crop") {
535                         _crop.left = atoi (v.c_str ());
536                 } else if (k == "right_crop") {
537                         _crop.right = atoi (v.c_str ());
538                 } else if (k == "top_crop") {
539                         _crop.top = atoi (v.c_str ());
540                 } else if (k == "bottom_crop") {
541                         _crop.bottom = atoi (v.c_str ());
542                 } else if (k == "filter") {
543                         _filters.push_back (Filter::from_id (v));
544                 } else if (k == "scaler") {
545                         _scaler = Scaler::from_id (v);
546                 } else if (k == "dcp_frames") {
547                         _dcp_frames = atoi (v.c_str ());
548                 } else if (k == "dcp_trim_action") {
549                         if (v == "cut") {
550                                 _dcp_trim_action = CUT;
551                         } else if (v == "black_out") {
552                                 _dcp_trim_action = BLACK_OUT;
553                         }
554                 } else if (k == "dcp_ab") {
555                         _dcp_ab = (v == "1");
556                 } else if (k == "selected_audio_stream") {
557                         _audio_stream = atoi (v.c_str ());
558                 } else if (k == "audio_gain") {
559                         _audio_gain = atof (v.c_str ());
560                 } else if (k == "audio_delay") {
561                         _audio_delay = atoi (v.c_str ());
562                 } else if (k == "still_duration") {
563                         _still_duration = atoi (v.c_str ());
564                 } else if (k == "selected_subtitle_stream") {
565                         _subtitle_stream = atoi (v.c_str ());
566                 } else if (k == "with_subtitles") {
567                         _with_subtitles = (v == "1");
568                 } else if (k == "subtitle_offset") {
569                         _subtitle_offset = atoi (v.c_str ());
570                 } else if (k == "subtitle_scale") {
571                         _subtitle_scale = atof (v.c_str ());
572                 } else if (k == "audio_language") {
573                         _audio_language = v;
574                 } else if (k == "subtitle_language") {
575                         _subtitle_language = v;
576                 } else if (k == "territory") {
577                         _territory = v;
578                 } else if (k == "rating") {
579                         _rating = v;
580                 } else if (k == "studio") {
581                         _studio = v;
582                 } else if (k == "facility") {
583                         _facility = v;
584                 } else if (k == "package_type") {
585                         _package_type = v;
586                 }
587                 
588                 /* Cached stuff */
589                 if (k == "thumb") {
590                         int const n = atoi (v.c_str ());
591                         /* Only add it to the list if it still exists */
592                         if (filesystem::exists (thumb_file_for_frame (n))) {
593                                 _thumbs.push_back (n);
594                         }
595                 } else if (k == "width") {
596                         _size.width = atoi (v.c_str ());
597                 } else if (k == "height") {
598                         _size.height = atoi (v.c_str ());
599                 } else if (k == "length") {
600                         _length = atof (v.c_str ());
601                 } else if (k == "audio_sample_rate") {
602                         _audio_sample_rate = atoi (v.c_str ());
603                 } else if (k == "content_digest") {
604                         _content_digest = v;
605                 } else if (k == "has_subtitles") {
606                         _has_subtitles = (v == "1");
607                 } else if (k == "audio_stream") {
608                         _audio_streams.push_back (AudioStream (v));
609                 } else if (k == "subtitle_stream") {
610                         _subtitle_streams.push_back (SubtitleStream (v));
611                 } else if (k == "frames_per_second") {
612                         _frames_per_second = atof (v.c_str ());
613                 }
614         }
615                 
616         _dirty = false;
617 }
618
619 /** @param n A thumb index.
620  *  @return The path to the thumb's image file.
621  */
622 string
623 Film::thumb_file (int n) const
624 {
625         return thumb_file_for_frame (thumb_frame (n));
626 }
627
628 /** @param n A frame index within the Film.
629  *  @return The path to the thumb's image file for this frame;
630  *  we assume that it exists.
631  */
632 string
633 Film::thumb_file_for_frame (int n) const
634 {
635         return thumb_base_for_frame(n) + ".png";
636 }
637
638 string
639 Film::thumb_base (int n) const
640 {
641         return thumb_base_for_frame (thumb_frame (n));
642 }
643
644 string
645 Film::thumb_base_for_frame (int n) const
646 {
647         stringstream s;
648         s.width (8);
649         s << setfill('0') << n;
650         
651         filesystem::path p;
652         p /= dir ("thumbs");
653         p /= s.str ();
654                 
655         return p.string ();
656 }
657
658
659 /** @param n A thumb index.
660  *  @return The frame within the Film that it is for.
661  */
662 int
663 Film::thumb_frame (int n) const
664 {
665         boost::mutex::scoped_lock lm (_state_mutex);
666         assert (n < int (_thumbs.size ()));
667         return _thumbs[n];
668 }
669
670 Size
671 Film::cropped_size (Size s) const
672 {
673         boost::mutex::scoped_lock lm (_state_mutex);
674         s.width -= _crop.left + _crop.right;
675         s.height -= _crop.top + _crop.bottom;
676         return s;
677 }
678
679 /** Given a directory name, return its full path within the Film's directory.
680  *  The directory (and its parents) will be created if they do not exist.
681  */
682 string
683 Film::dir (string d) const
684 {
685         boost::mutex::scoped_lock lm (_state_mutex);
686         filesystem::path p;
687         p /= _directory;
688         p /= d;
689         filesystem::create_directories (p);
690         return p.string ();
691 }
692
693 /** Given a file or directory name, return its full path within the Film's directory */
694 string
695 Film::file (string f) const
696 {
697         boost::mutex::scoped_lock lm (_state_mutex);
698         return file_locked (f);
699 }
700
701 string
702 Film::file_locked (string f) const
703 {
704         filesystem::path p;
705         p /= _directory;
706         p /= f;
707         return p.string ();
708 }
709
710 /** @return full path of the content (actual video) file
711  *  of the Film.
712  */
713 string
714 Film::content_path () const
715 {
716         boost::mutex::scoped_lock lm (_state_mutex);
717         if (filesystem::path(_content).has_root_directory ()) {
718                 return _content;
719         }
720
721         return file_locked (_content);
722 }
723
724 ContentType
725 Film::content_type () const
726 {
727 #if BOOST_FILESYSTEM_VERSION == 3
728         string ext = filesystem::path(_content).extension().string();
729 #else
730         string ext = filesystem::path(_content).extension();
731 #endif
732
733         transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
734         
735         if (ext == ".tif" || ext == ".tiff" || ext == ".jpg" || ext == ".jpeg" || ext == ".png") {
736                 return STILL;
737         }
738
739         return VIDEO;
740 }
741
742 /** @return The sampling rate that we will resample the audio to */
743 int
744 Film::target_audio_sample_rate () const
745 {
746         /* Resample to a DCI-approved sample rate */
747         double t = dcp_audio_sample_rate (audio_sample_rate());
748
749         /* Compensate for the fact that video will be rounded to the
750            nearest integer number of frames per second.
751         */
752         if (rint (frames_per_second()) != frames_per_second()) {
753                 t *= _frames_per_second / rint (frames_per_second());
754         }
755
756         return rint (t);
757 }
758
759 int
760 Film::dcp_length () const
761 {
762         if (dcp_frames()) {
763                 return dcp_frames();
764         }
765
766         return length();
767 }
768
769 /** @return a DCI-compliant name for a DCP of this film */
770 string
771 Film::dci_name () const
772 {
773         boost::mutex::scoped_lock lm (_state_mutex);
774         
775         stringstream d;
776
777         string fixed_name = to_upper_copy (_name);
778         for (size_t i = 0; i < fixed_name.length(); ++i) {
779                 if (fixed_name[i] == ' ') {
780                         fixed_name[i] = '-';
781                 }
782         }
783
784         /* Spec is that the name part should be maximum 14 characters, as I understand it */
785         if (fixed_name.length() > 14) {
786                 fixed_name = fixed_name.substr (0, 14);
787         }
788
789         d << fixed_name << "_";
790
791         if (_dcp_content_type) {
792                 d << _dcp_content_type->dci_name() << "_";
793         }
794
795         if (_format) {
796                 d << _format->dci_name() << "_";
797         }
798
799         if (!_audio_language.empty ()) {
800                 d << _audio_language;
801                 if (!_subtitle_language.empty() && _with_subtitles) {
802                         d << "-" << _subtitle_language;
803                 } else {
804                         d << "-XX";
805                 }
806                         
807                 d << "_";
808         }
809
810         if (!_territory.empty ()) {
811                 d << _territory;
812                 if (!_rating.empty ()) {
813                         d << "-" << _rating;
814                 }
815                 d << "_";
816         }
817
818         switch (_audio_streams[_audio_stream].channels()) {
819         case 1:
820                 d << "10_";
821                 break;
822         case 2:
823                 d << "20_";
824                 break;
825         case 6:
826                 d << "51_";
827                 break;
828         case 8:
829                 d << "71_";
830                 break;
831         }
832
833         d << "2K_";
834
835         if (!_studio.empty ()) {
836                 d << _studio << "_";
837         }
838
839         gregorian::date today = gregorian::day_clock::local_day ();
840         d << gregorian::to_iso_string (today) << "_";
841
842         if (!_facility.empty ()) {
843                 d << _facility << "_";
844         }
845
846         if (!_package_type.empty ()) {
847                 d << _package_type;
848         }
849
850         return d.str ();
851 }
852
853 /** @return name to give the DCP */
854 string
855 Film::dcp_name () const
856 {
857         if (use_dci_name()) {
858                 return dci_name ();
859         }
860
861         return name();
862 }
863
864
865 void
866 Film::set_directory (string d)
867 {
868         boost::mutex::scoped_lock lm (_state_mutex);
869         _directory = d;
870         _dirty = true;
871 }
872
873 void
874 Film::set_name (string n)
875 {
876         {
877                 boost::mutex::scoped_lock lm (_state_mutex);
878                 _name = n;
879         }
880         signal_changed (NAME);
881 }
882
883 void
884 Film::set_use_dci_name (bool u)
885 {
886         {
887                 boost::mutex::scoped_lock lm (_state_mutex);
888                 _use_dci_name = u;
889         }
890         signal_changed (USE_DCI_NAME);
891 }
892
893 void
894 Film::set_content (string c)
895 {
896         string check = directory ();
897
898 #if BOOST_FILESYSTEM_VERSION == 3
899         filesystem::path slash ("/");
900         string platform_slash = slash.make_preferred().string ();
901 #else
902 #ifdef DVDOMATIC_WINDOWS
903         string platform_slash = "\\";
904 #else
905         string platform_slash = "/";
906 #endif
907 #endif  
908
909         if (!ends_with (check, platform_slash)) {
910                 check += platform_slash;
911         }
912         
913         if (filesystem::path(c).has_root_directory () && starts_with (c, check)) {
914                 c = c.substr (_directory.length() + 1);
915         }
916
917         {
918                 boost::mutex::scoped_lock lm (_state_mutex);
919                 if (c == _content) {
920                         return;
921                 }
922
923                 _content = c;
924         }
925                 
926         /* Create a temporary decoder so that we can get information
927            about the content.
928         */
929         
930
931         shared_ptr<Options> o (new Options ("", "", ""));
932         o->out_size = Size (1024, 1024);
933         
934         shared_ptr<Decoder> d = decoder_factory (shared_from_this(), o, 0, 0);
935         
936         set_size (d->native_size ());
937         set_frames_per_second (d->frames_per_second ());
938         set_audio_sample_rate (d->audio_sample_rate ());
939         set_has_subtitles (d->has_subtitles ());
940         set_audio_streams (d->audio_streams ());
941         set_subtitle_streams (d->subtitle_streams ());
942         set_audio_stream (audio_streams().empty() ? -1 : 0);
943         set_subtitle_stream (subtitle_streams().empty() ? -1 : 0);
944         
945         signal_changed (CONTENT);
946
947         set_content_digest (md5_digest (content_path ()));
948 }
949                
950 void
951 Film::set_dcp_content_type (DCPContentType const * t)
952 {
953         {
954                 boost::mutex::scoped_lock lm (_state_mutex);
955                 _dcp_content_type = t;
956         }
957         signal_changed (DCP_CONTENT_TYPE);
958 }
959
960 void
961 Film::set_format (Format const * f)
962 {
963         {
964                 boost::mutex::scoped_lock lm (_state_mutex);
965                 _format = f;
966         }
967         signal_changed (FORMAT);
968 }
969
970 void
971 Film::set_crop (Crop c)
972 {
973         {
974                 boost::mutex::scoped_lock lm (_state_mutex);
975                 _crop = c;
976         }
977         signal_changed (CROP);
978 }
979
980 void
981 Film::set_left_crop (int c)
982 {
983         {
984                 boost::mutex::scoped_lock lm (_state_mutex);
985                 
986                 if (_crop.left == c) {
987                         return;
988                 }
989                 
990                 _crop.left = c;
991         }
992         signal_changed (CROP);
993 }
994
995 void
996 Film::set_right_crop (int c)
997 {
998         {
999                 boost::mutex::scoped_lock lm (_state_mutex);
1000                 if (_crop.right == c) {
1001                         return;
1002                 }
1003                 
1004                 _crop.right = c;
1005         }
1006         signal_changed (CROP);
1007 }
1008
1009 void
1010 Film::set_top_crop (int c)
1011 {
1012         {
1013                 boost::mutex::scoped_lock lm (_state_mutex);
1014                 if (_crop.top == c) {
1015                         return;
1016                 }
1017                 
1018                 _crop.top = c;
1019         }
1020         signal_changed (CROP);
1021 }
1022
1023 void
1024 Film::set_bottom_crop (int c)
1025 {
1026         {
1027                 boost::mutex::scoped_lock lm (_state_mutex);
1028                 if (_crop.bottom == c) {
1029                         return;
1030                 }
1031                 
1032                 _crop.bottom = c;
1033         }
1034         signal_changed (CROP);
1035 }
1036
1037 void
1038 Film::set_filters (vector<Filter const *> f)
1039 {
1040         {
1041                 boost::mutex::scoped_lock lm (_state_mutex);
1042                 _filters = f;
1043         }
1044         signal_changed (FILTERS);
1045 }
1046
1047 void
1048 Film::set_scaler (Scaler const * s)
1049 {
1050         {
1051                 boost::mutex::scoped_lock lm (_state_mutex);
1052                 _scaler = s;
1053         }
1054         signal_changed (SCALER);
1055 }
1056
1057 void
1058 Film::set_dcp_frames (int f)
1059 {
1060         {
1061                 boost::mutex::scoped_lock lm (_state_mutex);
1062                 _dcp_frames = f;
1063         }
1064         signal_changed (DCP_FRAMES);
1065 }
1066
1067 void
1068 Film::set_dcp_trim_action (TrimAction a)
1069 {
1070         {
1071                 boost::mutex::scoped_lock lm (_state_mutex);
1072                 _dcp_trim_action = a;
1073         }
1074         signal_changed (DCP_TRIM_ACTION);
1075 }
1076
1077 void
1078 Film::set_dcp_ab (bool a)
1079 {
1080         {
1081                 boost::mutex::scoped_lock lm (_state_mutex);
1082                 _dcp_ab = a;
1083         }
1084         signal_changed (DCP_AB);
1085 }
1086
1087 void
1088 Film::set_audio_stream (int s)
1089 {
1090         {
1091                 boost::mutex::scoped_lock lm (_state_mutex);
1092                 _audio_stream = s;
1093         }
1094         signal_changed (AUDIO_STREAM);
1095 }
1096
1097 void
1098 Film::set_audio_gain (float g)
1099 {
1100         {
1101                 boost::mutex::scoped_lock lm (_state_mutex);
1102                 _audio_gain = g;
1103         }
1104         signal_changed (AUDIO_GAIN);
1105 }
1106
1107 void
1108 Film::set_audio_delay (int d)
1109 {
1110         {
1111                 boost::mutex::scoped_lock lm (_state_mutex);
1112                 _audio_delay = d;
1113         }
1114         signal_changed (AUDIO_DELAY);
1115 }
1116
1117 void
1118 Film::set_still_duration (int d)
1119 {
1120         {
1121                 boost::mutex::scoped_lock lm (_state_mutex);
1122                 _still_duration = d;
1123         }
1124         signal_changed (STILL_DURATION);
1125 }
1126
1127 void
1128 Film::set_subtitle_stream (int s)
1129 {
1130         {
1131                 boost::mutex::scoped_lock lm (_state_mutex);
1132                 _subtitle_stream = s;
1133         }
1134         signal_changed (SUBTITLE_STREAM);
1135 }
1136
1137 void
1138 Film::set_with_subtitles (bool w)
1139 {
1140         {
1141                 boost::mutex::scoped_lock lm (_state_mutex);
1142                 _with_subtitles = w;
1143         }
1144         signal_changed (WITH_SUBTITLES);
1145 }
1146
1147 void
1148 Film::set_subtitle_offset (int o)
1149 {
1150         {
1151                 boost::mutex::scoped_lock lm (_state_mutex);
1152                 _subtitle_offset = o;
1153         }
1154         signal_changed (SUBTITLE_OFFSET);
1155 }
1156
1157 void
1158 Film::set_subtitle_scale (float s)
1159 {
1160         {
1161                 boost::mutex::scoped_lock lm (_state_mutex);
1162                 _subtitle_scale = s;
1163         }
1164         signal_changed (SUBTITLE_SCALE);
1165 }
1166
1167 void
1168 Film::set_audio_language (string l)
1169 {
1170         {
1171                 boost::mutex::scoped_lock lm (_state_mutex);
1172                 _audio_language = l;
1173         }
1174         signal_changed (DCI_METADATA);
1175 }
1176
1177 void
1178 Film::set_subtitle_language (string l)
1179 {
1180         {
1181                 boost::mutex::scoped_lock lm (_state_mutex);
1182                 _subtitle_language = l;
1183         }
1184         signal_changed (DCI_METADATA);
1185 }
1186
1187 void
1188 Film::set_territory (string t)
1189 {
1190         {
1191                 boost::mutex::scoped_lock lm (_state_mutex);
1192                 _territory = t;
1193         }
1194         signal_changed (DCI_METADATA);
1195 }
1196
1197 void
1198 Film::set_rating (string r)
1199 {
1200         {
1201                 boost::mutex::scoped_lock lm (_state_mutex);
1202                 _rating = r;
1203         }
1204         signal_changed (DCI_METADATA);
1205 }
1206
1207 void
1208 Film::set_studio (string s)
1209 {
1210         {
1211                 boost::mutex::scoped_lock lm (_state_mutex);
1212                 _studio = s;
1213         }
1214         signal_changed (DCI_METADATA);
1215 }
1216
1217 void
1218 Film::set_facility (string f)
1219 {
1220         {
1221                 boost::mutex::scoped_lock lm (_state_mutex);
1222                 _facility = f;
1223         }
1224         signal_changed (DCI_METADATA);
1225 }
1226
1227 void
1228 Film::set_package_type (string p)
1229 {
1230         {
1231                 boost::mutex::scoped_lock lm (_state_mutex);
1232                 _package_type = p;
1233         }
1234         signal_changed (DCI_METADATA);
1235 }
1236
1237 void
1238 Film::set_thumbs (vector<int> t)
1239 {
1240         {
1241                 boost::mutex::scoped_lock lm (_state_mutex);
1242                 _thumbs = t;
1243         }
1244         signal_changed (THUMBS);
1245 }
1246
1247 void
1248 Film::set_size (Size s)
1249 {
1250         {
1251                 boost::mutex::scoped_lock lm (_state_mutex);
1252                 _size = s;
1253         }
1254         signal_changed (SIZE);
1255 }
1256
1257 void
1258 Film::set_length (int l)
1259 {
1260         {
1261                 boost::mutex::scoped_lock lm (_state_mutex);
1262                 _length = l;
1263         }
1264         signal_changed (LENGTH);
1265 }
1266
1267 void
1268 Film::set_audio_sample_rate (int r)
1269 {
1270         {
1271                 boost::mutex::scoped_lock lm (_state_mutex);
1272                 _audio_sample_rate = r;
1273         }
1274         signal_changed (AUDIO_SAMPLE_RATE);
1275 }
1276
1277 void
1278 Film::set_content_digest (string d)
1279 {
1280         {
1281                 boost::mutex::scoped_lock lm (_state_mutex);
1282                 _content_digest = d;
1283         }
1284         _dirty = true;
1285 }
1286
1287 void
1288 Film::set_has_subtitles (bool s)
1289 {
1290         {
1291                 boost::mutex::scoped_lock lm (_state_mutex);
1292                 _has_subtitles = s;
1293         }
1294         signal_changed (HAS_SUBTITLES);
1295 }
1296
1297 void
1298 Film::set_audio_streams (vector<AudioStream> s)
1299 {
1300         {
1301                 boost::mutex::scoped_lock lm (_state_mutex);
1302                 _audio_streams = s;
1303         }
1304         _dirty = true;
1305 }
1306
1307 void
1308 Film::set_subtitle_streams (vector<SubtitleStream> s)
1309 {
1310         {
1311                 boost::mutex::scoped_lock lm (_state_mutex);
1312                 _subtitle_streams = s;
1313         }
1314         _dirty = true;
1315 }
1316
1317 void
1318 Film::set_frames_per_second (float f)
1319 {
1320         {
1321                 boost::mutex::scoped_lock lm (_state_mutex);
1322                 _frames_per_second = f;
1323         }
1324         signal_changed (FRAMES_PER_SECOND);
1325 }
1326         
1327 void
1328 Film::signal_changed (Property p)
1329 {
1330         {
1331                 boost::mutex::scoped_lock lm (_state_mutex);
1332                 _dirty = true;
1333         }
1334         ui_signaller->emit (boost::bind (boost::ref (Changed), p));
1335 }
1336
1337 int
1338 Film::audio_channels () const
1339 {
1340         boost::mutex::scoped_lock lm (_state_mutex);
1341         if (_audio_stream == -1) {
1342                 return 0;
1343         }
1344         
1345         return _audio_streams[_audio_stream].channels ();
1346 }