Untested merge of master.
[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 <libxml++/libxml++.h>
33 #include <libcxml/cxml.h>
34 #include "film.h"
35 #include "format.h"
36 #include "job.h"
37 #include "filter.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 "log.h"
44 #include "exceptions.h"
45 #include "examine_content_job.h"
46 #include "scaler.h"
47 #include "config.h"
48 #include "version.h"
49 #include "ui_signaller.h"
50 #include "analyse_audio_job.h"
51 #include "playlist.h"
52 #include "player.h"
53 #include "ffmpeg_content.h"
54 #include "imagemagick_content.h"
55 #include "sndfile_content.h"
56 #include "dcp_content_type.h"
57
58 #include "i18n.h"
59
60 using std::string;
61 using std::stringstream;
62 using std::multimap;
63 using std::pair;
64 using std::map;
65 using std::vector;
66 using std::ifstream;
67 using std::ofstream;
68 using std::setfill;
69 using std::min;
70 using std::make_pair;
71 using std::endl;
72 using std::cout;
73 using std::list;
74 using boost::shared_ptr;
75 using boost::lexical_cast;
76 using boost::to_upper_copy;
77 using boost::ends_with;
78 using boost::starts_with;
79 using boost::optional;
80 using libdcp::Size;
81
82 int const Film::state_version = 4;
83
84 /** Construct a Film object in a given directory, reading any metadata
85  *  file that exists in that directory.  An exception will be thrown if
86  *  must_exist is true and the specified directory does not exist.
87  *
88  *  @param d Film directory.
89  *  @param must_exist true to throw an exception if does not exist.
90  */
91
92 Film::Film (string d, bool must_exist)
93         : _playlist (new Playlist)
94         , _use_dci_name (true)
95         , _trust_content_headers (true)
96         , _dcp_content_type (0)
97         , _format (Format::from_id ("185"))
98         , _scaler (Scaler::from_id ("bicubic"))
99         , _trim_start (0)
100         , _trim_end (0)
101         , _trim_type (CPL)
102         , _ab (false)
103         , _audio_gain (0)
104         , _audio_delay (0)
105         , _with_subtitles (false)
106         , _subtitle_offset (0)
107         , _subtitle_scale (1)
108         , _colour_lut (0)
109         , _j2k_bandwidth (200000000)
110         , _dci_metadata (Config::instance()->default_dci_metadata ())
111         , _dcp_frame_rate (0)
112         , _dirty (false)
113 {
114         set_dci_date_today ();
115
116         _playlist->ContentChanged.connect (bind (&Film::content_changed, this, _1, _2));
117         
118         /* Make state.directory a complete path without ..s (where possible)
119            (Code swiped from Adam Bowen on stackoverflow)
120         */
121         
122         boost::filesystem::path p (boost::filesystem::system_complete (d));
123         boost::filesystem::path result;
124         for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
125                 if (*i == "..") {
126                         if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
127                                 result /= *i;
128                         } else {
129                                 result = result.parent_path ();
130                         }
131                 } else if (*i != ".") {
132                         result /= *i;
133                 }
134         }
135
136         set_directory (result.string ());
137         
138         if (!boost::filesystem::exists (directory())) {
139                 if (must_exist) {
140                         throw OpenFileError (directory());
141                 } else {
142                         boost::filesystem::create_directory (directory());
143                 }
144         }
145
146         if (must_exist) {
147                 read_metadata ();
148         } else {
149                 write_metadata ();
150         }
151
152         _log.reset (new FileLog (file ("log")));
153 }
154
155 Film::Film (Film const & o)
156         : boost::enable_shared_from_this<Film> (o)
157         /* note: the copied film shares the original's log */
158         , _log               (o._log)
159         , _playlist          (new Playlist)
160         , _directory         (o._directory)
161         , _name              (o._name)
162         , _use_dci_name      (o._use_dci_name)
163         , _trust_content_headers (o._trust_content_headers)
164         , _dcp_content_type  (o._dcp_content_type)
165         , _format            (o._format)
166         , _crop              (o._crop)
167         , _filters           (o._filters)
168         , _scaler            (o._scaler)
169         , _trim_start        (o._trim_start)
170         , _trim_end          (o._trim_end)
171         , _trim_type         (o._trim_type)
172         , _ab                (o._ab)
173         , _audio_gain        (o._audio_gain)
174         , _audio_delay       (o._audio_delay)
175         , _with_subtitles    (o._with_subtitles)
176         , _subtitle_offset   (o._subtitle_offset)
177         , _subtitle_scale    (o._subtitle_scale)
178         , _colour_lut        (o._colour_lut)
179         , _j2k_bandwidth     (o._j2k_bandwidth)
180         , _dci_metadata      (o._dci_metadata)
181         , _dcp_frame_rate    (o._dcp_frame_rate)
182         , _dci_date          (o._dci_date)
183         , _dirty             (o._dirty)
184 {
185         for (ContentList::const_iterator i = o._content.begin(); i != o._content.end(); ++i) {
186                 _content.push_back ((*i)->clone ());
187         }
188         
189         _playlist->ContentChanged.connect (bind (&Film::content_changed, this, _1, _2));
190         
191         _playlist->setup (_content);
192 }
193
194 string
195 Film::video_state_identifier () const
196 {
197         assert (format ());
198         LocaleGuard lg;
199
200         pair<string, string> f = Filter::ffmpeg_strings (filters());
201
202         stringstream s;
203         s << format()->id()
204           << "_" << _playlist->video_digest()
205           << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
206           << "_" << _dcp_frame_rate
207           << "_" << f.first << "_" << f.second
208           << "_" << scaler()->id()
209           << "_" << j2k_bandwidth()
210           << "_" << boost::lexical_cast<int> (colour_lut());
211
212         if (ab()) {
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         }
216
217         return s.str ();
218 }
219           
220 /** @return The path to the directory to write video frame info files to */
221 string
222 Film::info_dir () const
223 {
224         boost::filesystem::path p;
225         p /= "info";
226         p /= video_state_identifier ();
227         return dir (p.string());
228 }
229
230 string
231 Film::internal_video_mxf_dir () const
232 {
233         boost::filesystem::path p;
234         return dir ("video");
235 }
236
237 string
238 Film::internal_video_mxf_filename () const
239 {
240         return video_state_identifier() + ".mxf";
241 }
242
243 string
244 Film::dcp_video_mxf_filename () const
245 {
246         return filename_safe_name() + "_video.mxf";
247 }
248
249 string
250 Film::dcp_audio_mxf_filename () const
251 {
252         return filename_safe_name() + "_audio.mxf";
253 }
254
255 string
256 Film::filename_safe_name () const
257 {
258         string const n = name ();
259         string o;
260         for (size_t i = 0; i < n.length(); ++i) {
261                 if (isalnum (n[i])) {
262                         o += n[i];
263                 } else {
264                         o += "_";
265                 }
266         }
267
268         return o;
269 }
270
271 string
272 Film::audio_analysis_path () const
273 {
274         boost::filesystem::path p;
275         p /= "analysis";
276         p /= _playlist->audio_digest();
277         return file (p.string ());
278 }
279
280 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
281 void
282 Film::make_dcp ()
283 {
284         set_dci_date_today ();
285         
286         if (dcp_name().find ("/") != string::npos) {
287                 throw BadSettingError (_("name"), _("cannot contain slashes"));
288         }
289         
290         log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary()));
291
292         {
293                 char buffer[128];
294                 gethostname (buffer, sizeof (buffer));
295                 log()->log (String::compose ("Starting to make DCP on %1", buffer));
296         }
297         
298 //      log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video"))));
299 //      if (length()) {
300 //              log()->log (String::compose ("Content length %1", length().get()));
301 //      }
302 //      log()->log (String::compose ("Content digest %1", content_digest()));
303 //      log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate()));
304         log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
305         log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
306 #ifdef DCPOMATIC_DEBUG
307         log()->log ("DCP-o-matic built in debug mode.");
308 #else
309         log()->log ("DCP-o-matic built in optimised mode.");
310 #endif
311 #ifdef LIBDCP_DEBUG
312         log()->log ("libdcp built in debug mode.");
313 #else
314         log()->log ("libdcp built in optimised mode.");
315 #endif
316         pair<string, int> const c = cpu_info ();
317         log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
318         
319         if (format() == 0) {
320                 throw MissingSettingError (_("format"));
321         }
322
323         if (content().empty ()) {
324                 throw MissingSettingError (_("content"));
325         }
326
327         if (dcp_content_type() == 0) {
328                 throw MissingSettingError (_("content type"));
329         }
330
331         if (name().empty()) {
332                 throw MissingSettingError (_("name"));
333         }
334
335         shared_ptr<Job> r;
336
337         if (ab()) {
338                 r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this())));
339         } else {
340                 r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
341         }
342 }
343
344 /** Start a job to analyse the audio in our Playlist */
345 void
346 Film::analyse_audio ()
347 {
348         if (_analyse_audio_job) {
349                 return;
350         }
351
352         _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this()));
353         _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this));
354         JobManager::instance()->add (_analyse_audio_job);
355 }
356
357 /** Start a job to examine a piece of content */
358 void
359 Film::examine_content (shared_ptr<Content> c)
360 {
361         shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c, trust_content_headers ()));
362         JobManager::instance()->add (j);
363 }
364
365 void
366 Film::analyse_audio_finished ()
367 {
368         ensure_ui_thread ();
369
370         if (_analyse_audio_job->finished_ok ()) {
371                 AudioAnalysisSucceeded ();
372         }
373         
374         _analyse_audio_job.reset ();
375 }
376
377 /** Start a job to send our DCP to the configured TMS */
378 void
379 Film::send_dcp_to_tms ()
380 {
381         shared_ptr<Job> j (new SCPDCPJob (shared_from_this()));
382         JobManager::instance()->add (j);
383 }
384
385 /** Count the number of frames that have been encoded for this film.
386  *  @return frame count.
387  */
388 int
389 Film::encoded_frames () const
390 {
391         if (format() == 0) {
392                 return 0;
393         }
394
395         int N = 0;
396         for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (info_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
397                 ++N;
398                 boost::this_thread::interruption_point ();
399         }
400
401         return N;
402 }
403
404 /** Write state to our `metadata' file */
405 void
406 Film::write_metadata () const
407 {
408         ContentList the_content = content ();
409         
410         boost::mutex::scoped_lock lm (_state_mutex);
411         LocaleGuard lg;
412
413         boost::filesystem::create_directories (directory());
414
415         xmlpp::Document doc;
416         xmlpp::Element* root = doc.create_root_node ("Metadata");
417
418         root->add_child("Version")->add_child_text (boost::lexical_cast<string> (state_version));
419         root->add_child("Name")->add_child_text (_name);
420         root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0");
421         root->add_child("TrustContentHeaders")->add_child_text (_trust_content_headers ? "1" : "0");
422
423         if (_dcp_content_type) {
424                 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ());
425         }
426
427         if (_format) {
428                 root->add_child("Format")->add_child_text (_format->id ());
429         }
430
431         switch (_trim_type) {
432         case CPL:
433                 root->add_child("TrimType")->add_child_text ("CPL");
434                 break;
435         case ENCODE:
436                 root->add_child("TrimType")->add_child_text ("Encode");
437         }
438                         
439         root->add_child("LeftCrop")->add_child_text (boost::lexical_cast<string> (_crop.left));
440         root->add_child("RightCrop")->add_child_text (boost::lexical_cast<string> (_crop.right));
441         root->add_child("TopCrop")->add_child_text (boost::lexical_cast<string> (_crop.top));
442         root->add_child("BottomCrop")->add_child_text (boost::lexical_cast<string> (_crop.bottom));
443
444         for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
445                 root->add_child("Filter")->add_child_text ((*i)->id ());
446         }
447         
448         root->add_child("Scaler")->add_child_text (_scaler->id ());
449         root->add_child("TrimStart")->add_child_text (boost::lexical_cast<string> (_trim_start));
450         root->add_child("TrimEnd")->add_child_text (boost::lexical_cast<string> (_trim_end));
451         root->add_child("AB")->add_child_text (_ab ? "1" : "0");
452         root->add_child("AudioGain")->add_child_text (boost::lexical_cast<string> (_audio_gain));
453         root->add_child("AudioDelay")->add_child_text (boost::lexical_cast<string> (_audio_delay));
454         root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
455         root->add_child("SubtitleOffset")->add_child_text (boost::lexical_cast<string> (_subtitle_offset));
456         root->add_child("SubtitleScale")->add_child_text (boost::lexical_cast<string> (_subtitle_scale));
457         root->add_child("ColourLUT")->add_child_text (boost::lexical_cast<string> (_colour_lut));
458         root->add_child("J2KBandwidth")->add_child_text (boost::lexical_cast<string> (_j2k_bandwidth));
459         _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
460         root->add_child("DCPFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_frame_rate));
461         root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
462         _audio_mapping.as_xml (root->add_child("AudioMapping"));
463
464         for (ContentList::iterator i = the_content.begin(); i != the_content.end(); ++i) {
465                 (*i)->as_xml (root->add_child ("Content"));
466         }
467
468         doc.write_to_file_formatted (file ("metadata.xml"));
469         
470         _dirty = false;
471 }
472
473 /** Read state from our metadata file */
474 void
475 Film::read_metadata ()
476 {
477         boost::mutex::scoped_lock lm (_state_mutex);
478         LocaleGuard lg;
479
480         if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
481                 throw StringError (_("This film was created with an older version of DCP-o-matic, and unfortunately it cannot be loaded into this version.  You will need to create a new Film, re-add your content and set it up again.  Sorry!"));
482         }
483
484         cxml::File f (file ("metadata.xml"), "Metadata");
485         
486         _name = f.string_child ("Name");
487         _use_dci_name = f.bool_child ("UseDCIName");
488         _trust_content_headers = f.bool_child ("TrustContentHeaders");
489
490         {
491                 optional<string> c = f.optional_string_child ("DCPContentType");
492                 if (c) {
493                         _dcp_content_type = DCPContentType::from_dci_name (c.get ());
494                 }
495         }
496
497         {
498                 optional<string> c = f.optional_string_child ("Format");
499                 if (c) {
500                         _format = Format::from_id (c.get ());
501                 }
502         }
503
504         {
505                 optional<string> c = f.optional_string_child ("TrimType");
506                 if (!c || c.get() == "CPL") {
507                         _trim_type = CPL;
508                 } else if (c && c.get() == "Encode") {
509                         _trim_type = ENCODE;
510                 }
511         }
512
513         _crop.left = f.number_child<int> ("LeftCrop");
514         _crop.right = f.number_child<int> ("RightCrop");
515         _crop.top = f.number_child<int> ("TopCrop");
516         _crop.bottom = f.number_child<int> ("BottomCrop");
517
518         {
519                 list<shared_ptr<cxml::Node> > c = f.node_children ("Filter");
520                 for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
521                         _filters.push_back (Filter::from_id ((*i)->content ()));
522                 }
523         }
524
525         _scaler = Scaler::from_id (f.string_child ("Scaler"));
526         _trim_start = f.number_child<int> ("TrimStart");
527         _trim_end = f.number_child<int> ("TrimEnd");
528         _ab = f.bool_child ("AB");
529         _audio_gain = f.number_child<float> ("AudioGain");
530         _audio_delay = f.number_child<int> ("AudioDelay");
531         _with_subtitles = f.bool_child ("WithSubtitles");
532         _subtitle_offset = f.number_child<float> ("SubtitleOffset");
533         _subtitle_scale = f.number_child<float> ("SubtitleScale");
534         _colour_lut = f.number_child<int> ("ColourLUT");
535         _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
536         _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
537         _dcp_frame_rate = f.number_child<int> ("DCPFrameRate");
538         _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
539
540         list<shared_ptr<cxml::Node> > c = f.node_children ("Content");
541         for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
542
543                 string const type = (*i)->string_child ("Type");
544                 boost::shared_ptr<Content> c;
545                 
546                 if (type == "FFmpeg") {
547                         c.reset (new FFmpegContent (*i));
548                 } else if (type == "ImageMagick") {
549                         c.reset (new ImageMagickContent (*i));
550                 } else if (type == "Sndfile") {
551                         c.reset (new SndfileContent (*i));
552                 }
553
554                 _content.push_back (c);
555         }
556
557         /* This must come after we've loaded the content, as we're looking things up in _content */
558         _audio_mapping.set_from_xml (_content, f.node_child ("AudioMapping"));
559
560         _dirty = false;
561
562         _playlist->setup (_content);
563 }
564
565 libdcp::Size
566 Film::cropped_size (libdcp::Size s) const
567 {
568         boost::mutex::scoped_lock lm (_state_mutex);
569         s.width -= _crop.left + _crop.right;
570         s.height -= _crop.top + _crop.bottom;
571         return s;
572 }
573
574 /** Given a directory name, return its full path within the Film's directory.
575  *  The directory (and its parents) will be created if they do not exist.
576  */
577 string
578 Film::dir (string d) const
579 {
580         boost::mutex::scoped_lock lm (_directory_mutex);
581         
582         boost::filesystem::path p;
583         p /= _directory;
584         p /= d;
585         
586         boost::filesystem::create_directories (p);
587         
588         return p.string ();
589 }
590
591 /** Given a file or directory name, return its full path within the Film's directory.
592  *  _directory_mutex must not be locked on entry.
593  *  Any required parent directories will be created.
594  */
595 string
596 Film::file (string f) const
597 {
598         boost::mutex::scoped_lock lm (_directory_mutex);
599
600         boost::filesystem::path p;
601         p /= _directory;
602         p /= f;
603
604         boost::filesystem::create_directories (p.parent_path ());
605         
606         return p.string ();
607 }
608
609 /** @return The sampling rate that we will resample the audio to */
610 int
611 Film::target_audio_sample_rate () const
612 {
613         if (!has_audio ()) {
614                 return 0;
615         }
616         
617         /* Resample to a DCI-approved sample rate */
618         double t = dcp_audio_sample_rate (audio_frame_rate());
619
620         FrameRateConversion frc (video_frame_rate(), dcp_frame_rate());
621
622         /* Compensate if the DCP is being run at a different frame rate
623            to the source; that is, if the video is run such that it will
624            look different in the DCP compared to the source (slower or faster).
625            skip/repeat doesn't come into effect here.
626         */
627
628         if (frc.change_speed) {
629                 t *= video_frame_rate() * frc.factor() / dcp_frame_rate();
630         }
631
632         return rint (t);
633 }
634
635 /** @return a DCI-compliant name for a DCP of this film */
636 string
637 Film::dci_name (bool if_created_now) const
638 {
639         stringstream d;
640
641         string fixed_name = to_upper_copy (name());
642         for (size_t i = 0; i < fixed_name.length(); ++i) {
643                 if (fixed_name[i] == ' ') {
644                         fixed_name[i] = '-';
645                 }
646         }
647
648         /* Spec is that the name part should be maximum 14 characters, as I understand it */
649         if (fixed_name.length() > 14) {
650                 fixed_name = fixed_name.substr (0, 14);
651         }
652
653         d << fixed_name;
654
655         if (dcp_content_type()) {
656                 d << "_" << dcp_content_type()->dci_name();
657         }
658
659         if (format()) {
660                 d << "_" << format()->dci_name();
661         }
662
663         DCIMetadata const dm = dci_metadata ();
664
665         if (!dm.audio_language.empty ()) {
666                 d << "_" << dm.audio_language;
667                 if (!dm.subtitle_language.empty()) {
668                         d << "-" << dm.subtitle_language;
669                 } else {
670                         d << "-XX";
671                 }
672         }
673
674         if (!dm.territory.empty ()) {
675                 d << "_" << dm.territory;
676                 if (!dm.rating.empty ()) {
677                         d << "-" << dm.rating;
678                 }
679         }
680
681         switch (audio_channels ()) {
682         case 1:
683                 d << "_10";
684                 break;
685         case 2:
686                 d << "_20";
687                 break;
688         case 6:
689                 d << "_51";
690                 break;
691         case 8:
692                 d << "_71";
693                 break;
694         }
695
696         d << "_2K";
697
698         if (!dm.studio.empty ()) {
699                 d << "_" << dm.studio;
700         }
701
702         if (if_created_now) {
703                 d << "_" << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
704         } else {
705                 d << "_" << boost::gregorian::to_iso_string (_dci_date);
706         }
707
708         if (!dm.facility.empty ()) {
709                 d << "_" << dm.facility;
710         }
711
712         if (!dm.package_type.empty ()) {
713                 d << "_" << dm.package_type;
714         }
715
716         return d.str ();
717 }
718
719 /** @return name to give the DCP */
720 string
721 Film::dcp_name (bool if_created_now) const
722 {
723         if (use_dci_name()) {
724                 return dci_name (if_created_now);
725         }
726
727         return name();
728 }
729
730
731 void
732 Film::set_directory (string d)
733 {
734         boost::mutex::scoped_lock lm (_state_mutex);
735         _directory = d;
736         _dirty = true;
737 }
738
739 void
740 Film::set_name (string n)
741 {
742         {
743                 boost::mutex::scoped_lock lm (_state_mutex);
744                 _name = n;
745         }
746         signal_changed (NAME);
747 }
748
749 void
750 Film::set_use_dci_name (bool u)
751 {
752         {
753                 boost::mutex::scoped_lock lm (_state_mutex);
754                 _use_dci_name = u;
755         }
756         signal_changed (USE_DCI_NAME);
757 }
758
759 void
760 Film::set_trust_content_headers (bool t)
761 {
762         {
763                 boost::mutex::scoped_lock lm (_state_mutex);
764                 _trust_content_headers = t;
765         }
766         
767         signal_changed (TRUST_CONTENT_HEADERS);
768
769         if (!_trust_content_headers && !content().empty()) {
770                 /* We just said that we don't trust the content's header */
771                 ContentList c = content ();
772                 for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
773                         examine_content (*i);
774                 }
775         }
776 }
777                
778 void
779 Film::set_dcp_content_type (DCPContentType const * t)
780 {
781         {
782                 boost::mutex::scoped_lock lm (_state_mutex);
783                 _dcp_content_type = t;
784         }
785         signal_changed (DCP_CONTENT_TYPE);
786 }
787
788 void
789 Film::set_format (Format const * f)
790 {
791         {
792                 boost::mutex::scoped_lock lm (_state_mutex);
793                 _format = f;
794         }
795         signal_changed (FORMAT);
796 }
797
798 void
799 Film::set_crop (Crop c)
800 {
801         {
802                 boost::mutex::scoped_lock lm (_state_mutex);
803                 _crop = c;
804         }
805         signal_changed (CROP);
806 }
807
808 void
809 Film::set_left_crop (int c)
810 {
811         {
812                 boost::mutex::scoped_lock lm (_state_mutex);
813                 
814                 if (_crop.left == c) {
815                         return;
816                 }
817                 
818                 _crop.left = c;
819         }
820         signal_changed (CROP);
821 }
822
823 void
824 Film::set_right_crop (int c)
825 {
826         {
827                 boost::mutex::scoped_lock lm (_state_mutex);
828                 if (_crop.right == c) {
829                         return;
830                 }
831                 
832                 _crop.right = c;
833         }
834         signal_changed (CROP);
835 }
836
837 void
838 Film::set_top_crop (int c)
839 {
840         {
841                 boost::mutex::scoped_lock lm (_state_mutex);
842                 if (_crop.top == c) {
843                         return;
844                 }
845                 
846                 _crop.top = c;
847         }
848         signal_changed (CROP);
849 }
850
851 void
852 Film::set_bottom_crop (int c)
853 {
854         {
855                 boost::mutex::scoped_lock lm (_state_mutex);
856                 if (_crop.bottom == c) {
857                         return;
858                 }
859                 
860                 _crop.bottom = c;
861         }
862         signal_changed (CROP);
863 }
864
865 void
866 Film::set_filters (vector<Filter const *> f)
867 {
868         {
869                 boost::mutex::scoped_lock lm (_state_mutex);
870                 _filters = f;
871         }
872         signal_changed (FILTERS);
873 }
874
875 void
876 Film::set_scaler (Scaler const * s)
877 {
878         {
879                 boost::mutex::scoped_lock lm (_state_mutex);
880                 _scaler = s;
881         }
882         signal_changed (SCALER);
883 }
884
885 void
886 Film::set_trim_start (int t)
887 {
888         {
889                 boost::mutex::scoped_lock lm (_state_mutex);
890                 _trim_start = t;
891         }
892         signal_changed (TRIM_START);
893 }
894
895 void
896 Film::set_trim_end (int t)
897 {
898         {
899                 boost::mutex::scoped_lock lm (_state_mutex);
900                 _trim_end = t;
901         }
902         signal_changed (TRIM_END);
903 }
904
905 void
906 Film::set_trim_type (TrimType t)
907 {
908         {
909                 boost::mutex::scoped_lock lm (_state_mutex);
910                 _trim_type = t;
911         }
912         signal_changed (TRIM_TYPE);
913 }
914
915 void
916 Film::set_ab (bool a)
917 {
918         {
919                 boost::mutex::scoped_lock lm (_state_mutex);
920                 _ab = a;
921         }
922         signal_changed (AB);
923 }
924
925 void
926 Film::set_audio_gain (float g)
927 {
928         {
929                 boost::mutex::scoped_lock lm (_state_mutex);
930                 _audio_gain = g;
931         }
932         signal_changed (AUDIO_GAIN);
933 }
934
935 void
936 Film::set_audio_delay (int d)
937 {
938         {
939                 boost::mutex::scoped_lock lm (_state_mutex);
940                 _audio_delay = d;
941         }
942         signal_changed (AUDIO_DELAY);
943 }
944
945 void
946 Film::set_with_subtitles (bool w)
947 {
948         {
949                 boost::mutex::scoped_lock lm (_state_mutex);
950                 _with_subtitles = w;
951         }
952         signal_changed (WITH_SUBTITLES);
953 }
954
955 void
956 Film::set_subtitle_offset (int o)
957 {
958         {
959                 boost::mutex::scoped_lock lm (_state_mutex);
960                 _subtitle_offset = o;
961         }
962         signal_changed (SUBTITLE_OFFSET);
963 }
964
965 void
966 Film::set_subtitle_scale (float s)
967 {
968         {
969                 boost::mutex::scoped_lock lm (_state_mutex);
970                 _subtitle_scale = s;
971         }
972         signal_changed (SUBTITLE_SCALE);
973 }
974
975 void
976 Film::set_colour_lut (int i)
977 {
978         {
979                 boost::mutex::scoped_lock lm (_state_mutex);
980                 _colour_lut = i;
981         }
982         signal_changed (COLOUR_LUT);
983 }
984
985 void
986 Film::set_j2k_bandwidth (int b)
987 {
988         {
989                 boost::mutex::scoped_lock lm (_state_mutex);
990                 _j2k_bandwidth = b;
991         }
992         signal_changed (J2K_BANDWIDTH);
993 }
994
995 void
996 Film::set_dci_metadata (DCIMetadata m)
997 {
998         {
999                 boost::mutex::scoped_lock lm (_state_mutex);
1000                 _dci_metadata = m;
1001         }
1002         signal_changed (DCI_METADATA);
1003 }
1004
1005
1006 void
1007 Film::set_dcp_frame_rate (int f)
1008 {
1009         {
1010                 boost::mutex::scoped_lock lm (_state_mutex);
1011                 _dcp_frame_rate = f;
1012         }
1013         signal_changed (DCP_FRAME_RATE);
1014 }
1015
1016 void
1017 Film::signal_changed (Property p)
1018 {
1019         {
1020                 boost::mutex::scoped_lock lm (_state_mutex);
1021                 _dirty = true;
1022         }
1023
1024         switch (p) {
1025         case Film::CONTENT:
1026                 _playlist->setup (content ());
1027                 set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
1028                 set_audio_mapping (_playlist->default_audio_mapping ());
1029                 break;
1030         default:
1031                 break;
1032         }
1033
1034         if (ui_signaller) {
1035                 ui_signaller->emit (boost::bind (boost::ref (Changed), p));
1036         }
1037 }
1038
1039 void
1040 Film::set_dci_date_today ()
1041 {
1042         _dci_date = boost::gregorian::day_clock::local_day ();
1043 }
1044
1045 string
1046 Film::info_path (int f) const
1047 {
1048         boost::filesystem::path p;
1049         p /= info_dir ();
1050
1051         stringstream s;
1052         s.width (8);
1053         s << setfill('0') << f << ".md5";
1054
1055         p /= s.str();
1056
1057         /* info_dir() will already have added any initial bit of the path,
1058            so don't call file() on this.
1059         */
1060         return p.string ();
1061 }
1062
1063 string
1064 Film::j2c_path (int f, bool t) const
1065 {
1066         boost::filesystem::path p;
1067         p /= "j2c";
1068         p /= video_state_identifier ();
1069
1070         stringstream s;
1071         s.width (8);
1072         s << setfill('0') << f << ".j2c";
1073
1074         if (t) {
1075                 s << ".tmp";
1076         }
1077
1078         p /= s.str();
1079         return file (p.string ());
1080 }
1081
1082 /** Make an educated guess as to whether we have a complete DCP
1083  *  or not.
1084  *  @return true if we do.
1085  */
1086
1087 bool
1088 Film::have_dcp () const
1089 {
1090         try {
1091                 libdcp::DCP dcp (dir (dcp_name()));
1092                 dcp.read ();
1093         } catch (...) {
1094                 return false;
1095         }
1096
1097         return true;
1098 }
1099
1100 shared_ptr<Player>
1101 Film::player () const
1102 {
1103         boost::mutex::scoped_lock lm (_state_mutex);
1104         return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
1105 }
1106
1107 void
1108 Film::add_content (shared_ptr<Content> c)
1109 {
1110         {
1111                 boost::mutex::scoped_lock lm (_state_mutex);
1112                 _content.push_back (c);
1113         }
1114
1115         signal_changed (CONTENT);
1116
1117         examine_content (c);
1118 }
1119
1120 void
1121 Film::remove_content (shared_ptr<Content> c)
1122 {
1123         {
1124                 boost::mutex::scoped_lock lm (_state_mutex);
1125                 ContentList::iterator i = find (_content.begin(), _content.end(), c);
1126                 if (i != _content.end ()) {
1127                         _content.erase (i);
1128                 }
1129         }
1130
1131         signal_changed (CONTENT);
1132 }
1133
1134 void
1135 Film::move_content_earlier (shared_ptr<Content> c)
1136 {
1137         {
1138                 boost::mutex::scoped_lock lm (_state_mutex);
1139                 ContentList::iterator i = find (_content.begin(), _content.end(), c);
1140                 if (i == _content.begin () || i == _content.end()) {
1141                         return;
1142                 }
1143
1144                 ContentList::iterator j = i;
1145                 --j;
1146
1147                 swap (*i, *j);
1148         }
1149
1150         signal_changed (CONTENT);
1151 }
1152
1153 void
1154 Film::move_content_later (shared_ptr<Content> c)
1155 {
1156         {
1157                 boost::mutex::scoped_lock lm (_state_mutex);
1158                 ContentList::iterator i = find (_content.begin(), _content.end(), c);
1159                 if (i == _content.end()) {
1160                         return;
1161                 }
1162
1163                 ContentList::iterator j = i;
1164                 ++j;
1165                 if (j == _content.end ()) {
1166                         return;
1167                 }
1168
1169                 swap (*i, *j);
1170         }
1171
1172         signal_changed (CONTENT);
1173
1174 }
1175
1176 ContentAudioFrame
1177 Film::audio_length () const
1178 {
1179         return _playlist->audio_length ();
1180 }
1181
1182 int
1183 Film::audio_channels () const
1184 {
1185         return _playlist->audio_channels ();
1186 }
1187
1188 int
1189 Film::audio_frame_rate () const
1190 {
1191         return _playlist->audio_frame_rate ();
1192 }
1193
1194 bool
1195 Film::has_audio () const
1196 {
1197         return _playlist->has_audio ();
1198 }
1199
1200 float
1201 Film::video_frame_rate () const
1202 {
1203         return _playlist->video_frame_rate ();
1204 }
1205
1206 libdcp::Size
1207 Film::video_size () const
1208 {
1209         return _playlist->video_size ();
1210 }
1211
1212 ContentVideoFrame
1213 Film::video_length () const
1214 {
1215         return _playlist->video_length ();
1216 }
1217
1218 /** Unfortunately this is needed as the GUI has FFmpeg-specific controls */
1219 shared_ptr<FFmpegContent>
1220 Film::ffmpeg () const
1221 {
1222         boost::mutex::scoped_lock lm (_state_mutex);
1223         
1224         for (ContentList::const_iterator i = _content.begin (); i != _content.end(); ++i) {
1225                 shared_ptr<FFmpegContent> f = boost::dynamic_pointer_cast<FFmpegContent> (*i);
1226                 if (f) {
1227                         return f;
1228                 }
1229         }
1230
1231         return shared_ptr<FFmpegContent> ();
1232 }
1233
1234 vector<FFmpegSubtitleStream>
1235 Film::ffmpeg_subtitle_streams () const
1236 {
1237         shared_ptr<FFmpegContent> f = ffmpeg ();
1238         if (f) {
1239                 return f->subtitle_streams ();
1240         }
1241
1242         return vector<FFmpegSubtitleStream> ();
1243 }
1244
1245 boost::optional<FFmpegSubtitleStream>
1246 Film::ffmpeg_subtitle_stream () const
1247 {
1248         shared_ptr<FFmpegContent> f = ffmpeg ();
1249         if (f) {
1250                 return f->subtitle_stream ();
1251         }
1252
1253         return boost::none;
1254 }
1255
1256 vector<FFmpegAudioStream>
1257 Film::ffmpeg_audio_streams () const
1258 {
1259         shared_ptr<FFmpegContent> f = ffmpeg ();
1260         if (f) {
1261                 return f->audio_streams ();
1262         }
1263
1264         return vector<FFmpegAudioStream> ();
1265 }
1266
1267 boost::optional<FFmpegAudioStream>
1268 Film::ffmpeg_audio_stream () const
1269 {
1270         shared_ptr<FFmpegContent> f = ffmpeg ();
1271         if (f) {
1272                 return f->audio_stream ();
1273         }
1274
1275         return boost::none;
1276 }
1277
1278 void
1279 Film::set_ffmpeg_subtitle_stream (FFmpegSubtitleStream s)
1280 {
1281         shared_ptr<FFmpegContent> f = ffmpeg ();
1282         if (f) {
1283                 f->set_subtitle_stream (s);
1284         }
1285 }
1286
1287 void
1288 Film::set_ffmpeg_audio_stream (FFmpegAudioStream s)
1289 {
1290         shared_ptr<FFmpegContent> f = ffmpeg ();
1291         if (f) {
1292                 f->set_audio_stream (s);
1293         }
1294 }
1295
1296 void
1297 Film::set_audio_mapping (AudioMapping m)
1298 {
1299         {
1300                 boost::mutex::scoped_lock lm (_state_mutex);
1301                 _audio_mapping = m;
1302         }
1303
1304         signal_changed (AUDIO_MAPPING);
1305 }
1306
1307 void
1308 Film::content_changed (boost::weak_ptr<Content> c, int p)
1309 {
1310         if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
1311                 set_dcp_frame_rate (best_dcp_frame_rate (video_frame_rate ()));
1312         } else if (p == AudioContentProperty::AUDIO_CHANNELS) {
1313                 set_audio_mapping (_playlist->default_audio_mapping ());
1314         }               
1315
1316         if (ui_signaller) {
1317                 ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
1318         }
1319 }