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