Move audio bits into AudioContent.
[dcpomatic.git] / src / lib / film.cc
1 /* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
2
3 /*
4     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
5
6     This program is free software; you can redistribute it and/or modify
7     it under the terms of the GNU General Public License as published by
8     the Free Software Foundation; either version 2 of the License, or
9     (at your option) any later version.
10
11     This program is distributed in the hope that it will be useful,
12     but WITHOUT ANY WARRANTY; without even the implied warranty of
13     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14     GNU General Public License for more details.
15
16     You should have received a copy of the GNU General Public License
17     along with this program; if not, write to the Free Software
18     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19
20 */
21
22 #include <stdexcept>
23 #include <iostream>
24 #include <algorithm>
25 #include <fstream>
26 #include <cstdlib>
27 #include <sstream>
28 #include <iomanip>
29 #include <unistd.h>
30 #include <boost/filesystem.hpp>
31 #include <boost/algorithm/string.hpp>
32 #include <boost/lexical_cast.hpp>
33 #include <boost/date_time.hpp>
34 #include <libxml++/libxml++.h>
35 #include <libcxml/cxml.h>
36 #include "film.h"
37 #include "container.h"
38 #include "job.h"
39 #include "filter.h"
40 #include "util.h"
41 #include "job_manager.h"
42 #include "ab_transcode_job.h"
43 #include "transcode_job.h"
44 #include "scp_dcp_job.h"
45 #include "log.h"
46 #include "exceptions.h"
47 #include "examine_content_job.h"
48 #include "scaler.h"
49 #include "config.h"
50 #include "version.h"
51 #include "ui_signaller.h"
52 #include "analyse_audio_job.h"
53 #include "playlist.h"
54 #include "player.h"
55 #include "ffmpeg_content.h"
56 #include "imagemagick_content.h"
57 #include "sndfile_content.h"
58 #include "dcp_content_type.h"
59
60 #include "i18n.h"
61
62 using std::string;
63 using std::stringstream;
64 using std::multimap;
65 using std::pair;
66 using std::map;
67 using std::vector;
68 using std::ifstream;
69 using std::ofstream;
70 using std::setfill;
71 using std::min;
72 using std::make_pair;
73 using std::endl;
74 using std::cout;
75 using std::list;
76 using boost::shared_ptr;
77 using boost::lexical_cast;
78 using boost::to_upper_copy;
79 using boost::ends_with;
80 using boost::starts_with;
81 using boost::optional;
82 using libdcp::Size;
83
84 int const Film::state_version = 4;
85
86 /** Construct a Film object in a given directory, reading any metadata
87  *  file that exists in that directory.  An exception will be thrown if
88  *  must_exist is true and the specified directory does not exist.
89  *
90  *  @param d Film directory.
91  *  @param must_exist true to throw an exception if does not exist.
92  */
93
94 Film::Film (string d, bool must_exist)
95         : _playlist (new Playlist)
96         , _use_dci_name (true)
97         , _dcp_content_type (Config::instance()->default_dcp_content_type ())
98         , _container (Config::instance()->default_container ())
99         , _scaler (Scaler::from_id ("bicubic"))
100         , _ab (false)
101         , _with_subtitles (false)
102         , _subtitle_offset (0)
103         , _subtitle_scale (1)
104         , _colour_lut (0)
105         , _j2k_bandwidth (200000000)
106         , _dci_metadata (Config::instance()->default_dci_metadata ())
107         , _dcp_video_frame_rate (0)
108         , _dirty (false)
109 {
110         set_dci_date_today ();
111
112         _playlist->Changed.connect (bind (&Film::playlist_changed, this));
113         _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
114         
115         /* Make state.directory a complete path without ..s (where possible)
116            (Code swiped from Adam Bowen on stackoverflow)
117         */
118         
119         boost::filesystem::path p (boost::filesystem::system_complete (d));
120         boost::filesystem::path result;
121         for (boost::filesystem::path::iterator i = p.begin(); i != p.end(); ++i) {
122                 if (*i == "..") {
123                         if (boost::filesystem::is_symlink (result) || result.filename() == "..") {
124                                 result /= *i;
125                         } else {
126                                 result = result.parent_path ();
127                         }
128                 } else if (*i != ".") {
129                         result /= *i;
130                 }
131         }
132
133         set_directory (result.string ());
134         
135         if (!boost::filesystem::exists (directory())) {
136                 if (must_exist) {
137                         throw OpenFileError (directory());
138                 } else {
139                         boost::filesystem::create_directory (directory());
140                 }
141         }
142
143         if (must_exist) {
144                 read_metadata ();
145         } else {
146                 write_metadata ();
147         }
148
149         _log.reset (new FileLog (file ("log")));
150 }
151
152 Film::Film (Film const & o)
153         : boost::enable_shared_from_this<Film> (o)
154         /* note: the copied film shares the original's log */
155         , _log               (o._log)
156         , _playlist          (new Playlist (o._playlist))
157         , _directory         (o._directory)
158         , _name              (o._name)
159         , _use_dci_name      (o._use_dci_name)
160         , _dcp_content_type  (o._dcp_content_type)
161         , _container         (o._container)
162         , _filters           (o._filters)
163         , _scaler            (o._scaler)
164         , _ab                (o._ab)
165         , _with_subtitles    (o._with_subtitles)
166         , _subtitle_offset   (o._subtitle_offset)
167         , _subtitle_scale    (o._subtitle_scale)
168         , _colour_lut        (o._colour_lut)
169         , _j2k_bandwidth     (o._j2k_bandwidth)
170         , _dci_metadata      (o._dci_metadata)
171         , _dcp_video_frame_rate (o._dcp_video_frame_rate)
172         , _dci_date          (o._dci_date)
173         , _dirty             (o._dirty)
174 {
175         _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
176 }
177
178 string
179 Film::video_state_identifier () const
180 {
181         assert (container ());
182         LocaleGuard lg;
183
184         pair<string, string> f = Filter::ffmpeg_strings (filters());
185
186         stringstream s;
187         s << container()->id()
188           << "_" << _playlist->video_digest()
189           << "_" << _dcp_video_frame_rate
190           << "_" << f.first << "_" << f.second
191           << "_" << scaler()->id()
192           << "_" << j2k_bandwidth()
193           << "_" << boost::lexical_cast<int> (colour_lut());
194
195         if (ab()) {
196                 pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
197                 s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
198         }
199
200         return s.str ();
201 }
202           
203 /** @return The path to the directory to write video frame info files to */
204 string
205 Film::info_dir () const
206 {
207         boost::filesystem::path p;
208         p /= "info";
209         p /= video_state_identifier ();
210         return dir (p.string());
211 }
212
213 string
214 Film::internal_video_mxf_dir () const
215 {
216         boost::filesystem::path p;
217         return dir ("video");
218 }
219
220 string
221 Film::internal_video_mxf_filename () const
222 {
223         return video_state_identifier() + ".mxf";
224 }
225
226 string
227 Film::dcp_video_mxf_filename () const
228 {
229         return filename_safe_name() + "_video.mxf";
230 }
231
232 string
233 Film::dcp_audio_mxf_filename () const
234 {
235         return filename_safe_name() + "_audio.mxf";
236 }
237
238 string
239 Film::filename_safe_name () const
240 {
241         string const n = name ();
242         string o;
243         for (size_t i = 0; i < n.length(); ++i) {
244                 if (isalnum (n[i])) {
245                         o += n[i];
246                 } else {
247                         o += "_";
248                 }
249         }
250
251         return o;
252 }
253
254 string
255 Film::audio_analysis_path () const
256 {
257         boost::filesystem::path p;
258         p /= "analysis";
259         p /= _playlist->audio_digest();
260         return file (p.string ());
261 }
262
263 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
264 void
265 Film::make_dcp ()
266 {
267         set_dci_date_today ();
268         
269         if (dcp_name().find ("/") != string::npos) {
270                 throw BadSettingError (_("name"), _("cannot contain slashes"));
271         }
272         
273         log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary()));
274
275         {
276                 char buffer[128];
277                 gethostname (buffer, sizeof (buffer));
278                 log()->log (String::compose ("Starting to make DCP on %1", buffer));
279         }
280         
281 //      log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? _("still") : _("video"))));
282 //      if (length()) {
283 //              log()->log (String::compose ("Content length %1", length().get()));
284 //      }
285 //      log()->log (String::compose ("Content digest %1", content_digest()));
286 //      log()->log (String::compose ("Content at %1 fps, DCP at %2 fps", source_frame_rate(), dcp_frame_rate()));
287         log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
288         log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
289 #ifdef DCPOMATIC_DEBUG
290         log()->log ("DCP-o-matic built in debug mode.");
291 #else
292         log()->log ("DCP-o-matic built in optimised mode.");
293 #endif
294 #ifdef LIBDCP_DEBUG
295         log()->log ("libdcp built in debug mode.");
296 #else
297         log()->log ("libdcp built in optimised mode.");
298 #endif
299         pair<string, int> const c = cpu_info ();
300         log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
301         
302         if (container() == 0) {
303                 throw MissingSettingError (_("container"));
304         }
305
306         if (_playlist->content().empty ()) {
307                 throw StringError (_("You must add some content to the DCP before creating it"));
308         }
309
310         if (dcp_content_type() == 0) {
311                 throw MissingSettingError (_("content type"));
312         }
313
314         if (name().empty()) {
315                 throw MissingSettingError (_("name"));
316         }
317
318         shared_ptr<Job> r;
319
320         if (ab()) {
321                 r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this())));
322         } else {
323                 r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
324         }
325 }
326
327 /** Start a job to analyse the audio in our Playlist */
328 void
329 Film::analyse_audio ()
330 {
331         if (_analyse_audio_job) {
332                 return;
333         }
334
335         _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this()));
336         _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this));
337         JobManager::instance()->add (_analyse_audio_job);
338 }
339
340 /** Start a job to examine a piece of content */
341 void
342 Film::examine_content (shared_ptr<Content> c)
343 {
344         shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
345         JobManager::instance()->add (j);
346 }
347
348 void
349 Film::analyse_audio_finished ()
350 {
351         ensure_ui_thread ();
352
353         if (_analyse_audio_job->finished_ok ()) {
354                 AudioAnalysisSucceeded ();
355         }
356         
357         _analyse_audio_job.reset ();
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()));
365         JobManager::instance()->add (j);
366 }
367
368 /** Count the number of frames that have been encoded for this film.
369  *  @return frame count.
370  */
371 int
372 Film::encoded_frames () const
373 {
374         if (container() == 0) {
375                 return 0;
376         }
377
378         int N = 0;
379         for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (info_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
380                 ++N;
381                 boost::this_thread::interruption_point ();
382         }
383
384         return N;
385 }
386
387 /** Write state to our `metadata' file */
388 void
389 Film::write_metadata () const
390 {
391         boost::mutex::scoped_lock lm (_state_mutex);
392         LocaleGuard lg;
393
394         boost::filesystem::create_directories (directory());
395
396         xmlpp::Document doc;
397         xmlpp::Element* root = doc.create_root_node ("Metadata");
398
399         root->add_child("Version")->add_child_text (boost::lexical_cast<string> (state_version));
400         root->add_child("Name")->add_child_text (_name);
401         root->add_child("UseDCIName")->add_child_text (_use_dci_name ? "1" : "0");
402
403         if (_dcp_content_type) {
404                 root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ());
405         }
406
407         if (_container) {
408                 root->add_child("Container")->add_child_text (_container->id ());
409         }
410
411         for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
412                 root->add_child("Filter")->add_child_text ((*i)->id ());
413         }
414         
415         root->add_child("Scaler")->add_child_text (_scaler->id ());
416         root->add_child("AB")->add_child_text (_ab ? "1" : "0");
417         root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
418         root->add_child("SubtitleOffset")->add_child_text (boost::lexical_cast<string> (_subtitle_offset));
419         root->add_child("SubtitleScale")->add_child_text (boost::lexical_cast<string> (_subtitle_scale));
420         root->add_child("ColourLUT")->add_child_text (boost::lexical_cast<string> (_colour_lut));
421         root->add_child("J2KBandwidth")->add_child_text (boost::lexical_cast<string> (_j2k_bandwidth));
422         _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
423         root->add_child("DCPVideoFrameRate")->add_child_text (boost::lexical_cast<string> (_dcp_video_frame_rate));
424         root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
425         _playlist->as_xml (root->add_child ("Playlist"));
426
427         doc.write_to_file_formatted (file ("metadata.xml"));
428         
429         _dirty = false;
430 }
431
432 /** Read state from our metadata file */
433 void
434 Film::read_metadata ()
435 {
436         boost::mutex::scoped_lock lm (_state_mutex);
437         LocaleGuard lg;
438
439         if (boost::filesystem::exists (file ("metadata")) && !boost::filesystem::exists (file ("metadata.xml"))) {
440                 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!"));
441         }
442
443         cxml::File f (file ("metadata.xml"), "Metadata");
444         
445         _name = f.string_child ("Name");
446         _use_dci_name = f.bool_child ("UseDCIName");
447
448         {
449                 optional<string> c = f.optional_string_child ("DCPContentType");
450                 if (c) {
451                         _dcp_content_type = DCPContentType::from_dci_name (c.get ());
452                 }
453         }
454
455         {
456                 optional<string> c = f.optional_string_child ("Container");
457                 if (c) {
458                         _container = Container::from_id (c.get ());
459                 }
460         }
461
462         {
463                 list<shared_ptr<cxml::Node> > c = f.node_children ("Filter");
464                 for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
465                         _filters.push_back (Filter::from_id ((*i)->content ()));
466                 }
467         }
468
469         _scaler = Scaler::from_id (f.string_child ("Scaler"));
470         _ab = f.bool_child ("AB");
471         _with_subtitles = f.bool_child ("WithSubtitles");
472         _subtitle_offset = f.number_child<float> ("SubtitleOffset");
473         _subtitle_scale = f.number_child<float> ("SubtitleScale");
474         _colour_lut = f.number_child<int> ("ColourLUT");
475         _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
476         _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
477         _dcp_video_frame_rate = f.number_child<int> ("DCPVideoFrameRate");
478         _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
479
480         _playlist->set_from_xml (f.node_child ("Playlist"));
481
482         _dirty = false;
483 }
484
485 /** Given a directory name, return its full path within the Film's directory.
486  *  The directory (and its parents) will be created if they do not exist.
487  */
488 string
489 Film::dir (string d) const
490 {
491         boost::mutex::scoped_lock lm (_directory_mutex);
492         
493         boost::filesystem::path p;
494         p /= _directory;
495         p /= d;
496         
497         boost::filesystem::create_directories (p);
498         
499         return p.string ();
500 }
501
502 /** Given a file or directory name, return its full path within the Film's directory.
503  *  _directory_mutex must not be locked on entry.
504  *  Any required parent directories will be created.
505  */
506 string
507 Film::file (string f) const
508 {
509         boost::mutex::scoped_lock lm (_directory_mutex);
510
511         boost::filesystem::path p;
512         p /= _directory;
513         p /= f;
514
515         boost::filesystem::create_directories (p.parent_path ());
516         
517         return p.string ();
518 }
519
520 /** @return a DCI-compliant name for a DCP of this film */
521 string
522 Film::dci_name (bool if_created_now) const
523 {
524         stringstream d;
525
526         string fixed_name = to_upper_copy (name());
527         for (size_t i = 0; i < fixed_name.length(); ++i) {
528                 if (fixed_name[i] == ' ') {
529                         fixed_name[i] = '-';
530                 }
531         }
532
533         /* Spec is that the name part should be maximum 14 characters, as I understand it */
534         if (fixed_name.length() > 14) {
535                 fixed_name = fixed_name.substr (0, 14);
536         }
537
538         d << fixed_name;
539
540         if (dcp_content_type()) {
541                 d << "_" << dcp_content_type()->dci_name();
542         }
543
544         if (container()) {
545                 d << "_" << container()->dci_name();
546         }
547
548         DCIMetadata const dm = dci_metadata ();
549
550         if (!dm.audio_language.empty ()) {
551                 d << "_" << dm.audio_language;
552                 if (!dm.subtitle_language.empty()) {
553                         d << "-" << dm.subtitle_language;
554                 } else {
555                         d << "-XX";
556                 }
557         }
558
559         if (!dm.territory.empty ()) {
560                 d << "_" << dm.territory;
561                 if (!dm.rating.empty ()) {
562                         d << "-" << dm.rating;
563                 }
564         }
565
566         d << "_51_2K";
567
568         if (!dm.studio.empty ()) {
569                 d << "_" << dm.studio;
570         }
571
572         if (if_created_now) {
573                 d << "_" << boost::gregorian::to_iso_string (boost::gregorian::day_clock::local_day ());
574         } else {
575                 d << "_" << boost::gregorian::to_iso_string (_dci_date);
576         }
577
578         if (!dm.facility.empty ()) {
579                 d << "_" << dm.facility;
580         }
581
582         if (!dm.package_type.empty ()) {
583                 d << "_" << dm.package_type;
584         }
585
586         return d.str ();
587 }
588
589 /** @return name to give the DCP */
590 string
591 Film::dcp_name (bool if_created_now) const
592 {
593         if (use_dci_name()) {
594                 return dci_name (if_created_now);
595         }
596
597         return name();
598 }
599
600
601 void
602 Film::set_directory (string d)
603 {
604         boost::mutex::scoped_lock lm (_state_mutex);
605         _directory = d;
606         _dirty = true;
607 }
608
609 void
610 Film::set_name (string n)
611 {
612         {
613                 boost::mutex::scoped_lock lm (_state_mutex);
614                 _name = n;
615         }
616         signal_changed (NAME);
617 }
618
619 void
620 Film::set_use_dci_name (bool u)
621 {
622         {
623                 boost::mutex::scoped_lock lm (_state_mutex);
624                 _use_dci_name = u;
625         }
626         signal_changed (USE_DCI_NAME);
627 }
628
629 void
630 Film::set_dcp_content_type (DCPContentType const * t)
631 {
632         {
633                 boost::mutex::scoped_lock lm (_state_mutex);
634                 _dcp_content_type = t;
635         }
636         signal_changed (DCP_CONTENT_TYPE);
637 }
638
639 void
640 Film::set_container (Container const * c)
641 {
642         {
643                 boost::mutex::scoped_lock lm (_state_mutex);
644                 _container = c;
645         }
646         signal_changed (CONTAINER);
647 }
648
649 void
650 Film::set_filters (vector<Filter const *> f)
651 {
652         {
653                 boost::mutex::scoped_lock lm (_state_mutex);
654                 _filters = f;
655         }
656         signal_changed (FILTERS);
657 }
658
659 void
660 Film::set_scaler (Scaler const * s)
661 {
662         {
663                 boost::mutex::scoped_lock lm (_state_mutex);
664                 _scaler = s;
665         }
666         signal_changed (SCALER);
667 }
668
669 void
670 Film::set_ab (bool a)
671 {
672         {
673                 boost::mutex::scoped_lock lm (_state_mutex);
674                 _ab = a;
675         }
676         signal_changed (AB);
677 }
678
679 void
680 Film::set_with_subtitles (bool w)
681 {
682         {
683                 boost::mutex::scoped_lock lm (_state_mutex);
684                 _with_subtitles = w;
685         }
686         signal_changed (WITH_SUBTITLES);
687 }
688
689 void
690 Film::set_subtitle_offset (int o)
691 {
692         {
693                 boost::mutex::scoped_lock lm (_state_mutex);
694                 _subtitle_offset = o;
695         }
696         signal_changed (SUBTITLE_OFFSET);
697 }
698
699 void
700 Film::set_subtitle_scale (float s)
701 {
702         {
703                 boost::mutex::scoped_lock lm (_state_mutex);
704                 _subtitle_scale = s;
705         }
706         signal_changed (SUBTITLE_SCALE);
707 }
708
709 void
710 Film::set_colour_lut (int i)
711 {
712         {
713                 boost::mutex::scoped_lock lm (_state_mutex);
714                 _colour_lut = i;
715         }
716         signal_changed (COLOUR_LUT);
717 }
718
719 void
720 Film::set_j2k_bandwidth (int b)
721 {
722         {
723                 boost::mutex::scoped_lock lm (_state_mutex);
724                 _j2k_bandwidth = b;
725         }
726         signal_changed (J2K_BANDWIDTH);
727 }
728
729 void
730 Film::set_dci_metadata (DCIMetadata m)
731 {
732         {
733                 boost::mutex::scoped_lock lm (_state_mutex);
734                 _dci_metadata = m;
735         }
736         signal_changed (DCI_METADATA);
737 }
738
739
740 void
741 Film::set_dcp_video_frame_rate (int f)
742 {
743         {
744                 boost::mutex::scoped_lock lm (_state_mutex);
745                 _dcp_video_frame_rate = f;
746         }
747         signal_changed (DCP_VIDEO_FRAME_RATE);
748 }
749
750 void
751 Film::signal_changed (Property p)
752 {
753         {
754                 boost::mutex::scoped_lock lm (_state_mutex);
755                 _dirty = true;
756         }
757
758         switch (p) {
759         case Film::CONTENT:
760                 set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
761                 break;
762         default:
763                 break;
764         }
765
766         if (ui_signaller) {
767                 ui_signaller->emit (boost::bind (boost::ref (Changed), p));
768         }
769 }
770
771 void
772 Film::set_dci_date_today ()
773 {
774         _dci_date = boost::gregorian::day_clock::local_day ();
775 }
776
777 string
778 Film::info_path (int f) const
779 {
780         boost::filesystem::path p;
781         p /= info_dir ();
782
783         stringstream s;
784         s.width (8);
785         s << setfill('0') << f << ".md5";
786
787         p /= s.str();
788
789         /* info_dir() will already have added any initial bit of the path,
790            so don't call file() on this.
791         */
792         return p.string ();
793 }
794
795 string
796 Film::j2c_path (int f, bool t) const
797 {
798         boost::filesystem::path p;
799         p /= "j2c";
800         p /= video_state_identifier ();
801
802         stringstream s;
803         s.width (8);
804         s << setfill('0') << f << ".j2c";
805
806         if (t) {
807                 s << ".tmp";
808         }
809
810         p /= s.str();
811         return file (p.string ());
812 }
813
814 /** Make an educated guess as to whether we have a complete DCP
815  *  or not.
816  *  @return true if we do.
817  */
818
819 bool
820 Film::have_dcp () const
821 {
822         try {
823                 libdcp::DCP dcp (dir (dcp_name()));
824                 dcp.read ();
825         } catch (...) {
826                 return false;
827         }
828
829         return true;
830 }
831
832 shared_ptr<Player>
833 Film::player () const
834 {
835         boost::mutex::scoped_lock lm (_state_mutex);
836         return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
837 }
838
839 shared_ptr<Playlist>
840 Film::playlist () const
841 {
842         boost::mutex::scoped_lock lm (_state_mutex);
843         return _playlist;
844 }
845
846 Playlist::ContentList
847 Film::content () const
848 {
849         return _playlist->content ();
850 }
851
852 void
853 Film::add_content (shared_ptr<Content> c)
854 {
855         _playlist->add (c);
856         examine_content (c);
857 }
858
859 void
860 Film::remove_content (shared_ptr<Content> c)
861 {
862         _playlist->remove (c);
863 }
864
865 Time
866 Film::length () const
867 {
868         return _playlist->length (shared_from_this ());
869 }
870
871 bool
872 Film::has_subtitles () const
873 {
874         return _playlist->has_subtitles ();
875 }
876
877 OutputVideoFrame
878 Film::best_dcp_video_frame_rate () const
879 {
880         return _playlist->best_dcp_frame_rate ();
881 }
882
883 void
884 Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
885 {
886         if (p == VideoContentProperty::VIDEO_FRAME_RATE) {
887                 set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
888         } 
889
890         if (ui_signaller) {
891                 ui_signaller->emit (boost::bind (boost::ref (ContentChanged), c, p));
892         }
893 }
894
895 void
896 Film::playlist_changed ()
897 {
898         signal_changed (CONTENT);
899 }       
900
901 int
902 Film::loop () const
903 {
904         return _playlist->loop ();
905 }
906
907 void
908 Film::set_loop (int c)
909 {
910         _playlist->set_loop (c);
911 }
912
913 OutputAudioFrame
914 Film::time_to_audio_frames (Time t) const
915 {
916         return t * dcp_audio_frame_rate () / TIME_HZ;
917 }
918
919 OutputVideoFrame
920 Film::time_to_video_frames (Time t) const
921 {
922         return t * dcp_video_frame_rate () / TIME_HZ;
923 }
924
925 Time
926 Film::audio_frames_to_time (OutputAudioFrame f) const
927 {
928         return f * TIME_HZ / dcp_audio_frame_rate ();
929 }
930
931 Time
932 Film::video_frames_to_time (OutputVideoFrame f) const
933 {
934         return f * TIME_HZ / dcp_video_frame_rate ();
935 }
936
937 OutputAudioFrame
938 Film::dcp_audio_frame_rate () const
939 {
940         /* XXX */
941         return 48000;
942 }