Put log in the right place.
[dcpomatic.git] / src / lib / film.cc
index ef29d35fdde57806a2d0ef8f0a5f9a3def734789..e9c60429d08da7ede88480140fe844aa9119cc86 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
 
     This program is free software; you can redistribute it and/or modify
     it under the terms of the GNU General Public License as published by
 #include <libcxml/cxml.h>
 #include "film.h"
 #include "job.h"
-#include "filter.h"
 #include "util.h"
 #include "job_manager.h"
-#include "ab_transcode_job.h"
 #include "transcode_job.h"
 #include "scp_dcp_job.h"
 #include "log.h"
 #include "config.h"
 #include "version.h"
 #include "ui_signaller.h"
-#include "analyse_audio_job.h"
 #include "playlist.h"
 #include "player.h"
-#include "ffmpeg_content.h"
-#include "imagemagick_content.h"
-#include "sndfile_content.h"
 #include "dcp_content_type.h"
 #include "ratio.h"
+#include "cross.h"
 
 #include "i18n.h"
 
@@ -72,6 +67,7 @@ using std::endl;
 using std::cout;
 using std::list;
 using boost::shared_ptr;
+using boost::weak_ptr;
 using boost::lexical_cast;
 using boost::dynamic_pointer_cast;
 using boost::to_upper_copy;
@@ -92,16 +88,15 @@ Film::Film (string d)
        , _use_dci_name (true)
        , _dcp_content_type (Config::instance()->default_dcp_content_type ())
        , _container (Config::instance()->default_container ())
+       , _resolution (RESOLUTION_2K)
        , _scaler (Scaler::from_id ("bicubic"))
-       , _ab (false)
        , _with_subtitles (false)
-       , _subtitle_offset (0)
-       , _subtitle_scale (1)
-       , _colour_lut (0)
-       , _j2k_bandwidth (200000000)
+       , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
        , _dci_metadata (Config::instance()->default_dci_metadata ())
-       , _dcp_video_frame_rate (0)
+       , _dcp_video_frame_rate (24)
        , _dcp_audio_channels (MAX_AUDIO_CHANNELS)
+       , _dcp_3d (false)
+       , _sequence_video (true)
        , _dirty (false)
 {
        set_dci_date_today ();
@@ -129,50 +124,26 @@ Film::Film (string d)
 
        set_directory (result.string ());
        _log.reset (new FileLog (file ("log")));
-}
 
-Film::Film (Film const & o)
-       : boost::enable_shared_from_this<Film> (o)
-       /* note: the copied film shares the original's log */
-       , _log               (o._log)
-       , _playlist          (new Playlist (o._playlist))
-       , _directory         (o._directory)
-       , _name              (o._name)
-       , _use_dci_name      (o._use_dci_name)
-       , _dcp_content_type  (o._dcp_content_type)
-       , _container         (o._container)
-       , _scaler            (o._scaler)
-       , _ab                (o._ab)
-       , _with_subtitles    (o._with_subtitles)
-       , _subtitle_offset   (o._subtitle_offset)
-       , _subtitle_scale    (o._subtitle_scale)
-       , _colour_lut        (o._colour_lut)
-       , _j2k_bandwidth     (o._j2k_bandwidth)
-       , _dci_metadata      (o._dci_metadata)
-       , _dcp_video_frame_rate (o._dcp_video_frame_rate)
-       , _dci_date          (o._dci_date)
-       , _dirty             (o._dirty)
-{
-       _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
+       _playlist->set_sequence_video (_sequence_video);
 }
 
 string
-Film::video_state_identifier () const
+Film::video_identifier () const
 {
        assert (container ());
        LocaleGuard lg;
 
        stringstream s;
        s << container()->id()
-         << "_" << _playlist->video_digest()
+         << "_" << resolution_to_string (_resolution)
+         << "_" << _playlist->video_identifier()
          << "_" << _dcp_video_frame_rate
          << "_" << scaler()->id()
-         << "_" << j2k_bandwidth()
-         << "_" << lexical_cast<int> (colour_lut());
+         << "_" << j2k_bandwidth();
 
-       if (ab()) {
-               pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
-               s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
+       if (_dcp_3d) {
+               s << "_3D";
        }
 
        return s.str ();
@@ -184,21 +155,20 @@ Film::info_dir () const
 {
        boost::filesystem::path p;
        p /= "info";
-       p /= video_state_identifier ();
+       p /= video_identifier ();
        return dir (p.string());
 }
 
 string
 Film::internal_video_mxf_dir () const
 {
-       boost::filesystem::path p;
        return dir ("video");
 }
 
 string
 Film::internal_video_mxf_filename () const
 {
-       return video_state_identifier() + ".mxf";
+       return video_identifier() + ".mxf";
 }
 
 string
@@ -229,13 +199,12 @@ Film::filename_safe_name () const
        return o;
 }
 
-string
-Film::audio_analysis_path () const
+boost::filesystem::path
+Film::audio_analysis_path (shared_ptr<const AudioContent> c) const
 {
-       boost::filesystem::path p;
-       p /= "analysis";
-       p /= _playlist->audio_digest();
-       return file (p.string ());
+       boost::filesystem::path p = dir ("analysis");
+       p /= c->digest();
+       return p;
 }
 
 /** Add suitable Jobs to the JobManager to create a DCP for this Film */
@@ -276,12 +245,16 @@ Film::make_dcp ()
 #endif
        pair<string, int> const c = cpu_info ();
        log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
+       list<pair<string, string> > const m = mount_info ();
+       for (list<pair<string, string> >::const_iterator i = m.begin(); i != m.end(); ++i) {
+               log()->log (String::compose ("Mount: %1 %2", i->first, i->second));
+       }
        
        if (container() == 0) {
                throw MissingSettingError (_("container"));
        }
 
-       if (_playlist->content().empty ()) {
+       if (content().empty()) {
                throw StringError (_("You must add some content to the DCP before creating it"));
        }
 
@@ -293,38 +266,7 @@ Film::make_dcp ()
                throw MissingSettingError (_("name"));
        }
 
-       shared_ptr<Job> r;
-
-       if (ab()) {
-               r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this())));
-       } else {
-               r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
-       }
-}
-
-/** Start a job to analyse the audio in our Playlist */
-void
-Film::analyse_audio ()
-{
-       if (_analyse_audio_job) {
-               return;
-       }
-
-       _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this()));
-       _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this));
-       JobManager::instance()->add (_analyse_audio_job);
-}
-
-void
-Film::analyse_audio_finished ()
-{
-       ensure_ui_thread ();
-
-       if (_analyse_audio_job->finished_ok ()) {
-               AudioAnalysisSucceeded ();
-       }
-       
-       _analyse_audio_job.reset ();
+       JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
 }
 
 /** Start a job to send our DCP to the configured TMS */
@@ -382,17 +324,16 @@ Film::write_metadata () const
                root->add_child("Container")->add_child_text (_container->id ());
        }
 
+       root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
        root->add_child("Scaler")->add_child_text (_scaler->id ());
-       root->add_child("AB")->add_child_text (_ab ? "1" : "0");
        root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
-       root->add_child("SubtitleOffset")->add_child_text (lexical_cast<string> (_subtitle_offset));
-       root->add_child("SubtitleScale")->add_child_text (lexical_cast<string> (_subtitle_scale));
-       root->add_child("ColourLUT")->add_child_text (lexical_cast<string> (_colour_lut));
        root->add_child("J2KBandwidth")->add_child_text (lexical_cast<string> (_j2k_bandwidth));
        _dci_metadata.as_xml (root->add_child ("DCIMetadata"));
        root->add_child("DCPVideoFrameRate")->add_child_text (lexical_cast<string> (_dcp_video_frame_rate));
        root->add_child("DCIDate")->add_child_text (boost::gregorian::to_iso_string (_dci_date));
        root->add_child("DCPAudioChannels")->add_child_text (lexical_cast<string> (_dcp_audio_channels));
+       root->add_child("DCP3D")->add_child_text (_dcp_3d ? "1" : "0");
+       root->add_child("SequenceVideo")->add_child_text (_sequence_video ? "1" : "0");
        _playlist->as_xml (root->add_child ("Playlist"));
 
        doc.write_to_file_formatted (file ("metadata.xml"));
@@ -430,17 +371,16 @@ Film::read_metadata ()
                }
        }
 
+       _resolution = string_to_resolution (f.string_child ("Resolution"));
        _scaler = Scaler::from_id (f.string_child ("Scaler"));
-       _ab = f.bool_child ("AB");
        _with_subtitles = f.bool_child ("WithSubtitles");
-       _subtitle_offset = f.number_child<float> ("SubtitleOffset");
-       _subtitle_scale = f.number_child<float> ("SubtitleScale");
-       _colour_lut = f.number_child<int> ("ColourLUT");
        _j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
        _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
        _dcp_video_frame_rate = f.number_child<int> ("DCPVideoFrameRate");
        _dci_date = boost::gregorian::from_undelimited_string (f.string_child ("DCIDate"));
        _dcp_audio_channels = f.number_child<int> ("DCPAudioChannels");
+       _sequence_video = f.bool_child ("SequenceVideo");
+       _dcp_3d = f.bool_child ("DCP3D");
 
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"));
 
@@ -506,6 +446,14 @@ Film::dci_name (bool if_created_now) const
                d << "_" << dcp_content_type()->dci_name();
        }
 
+       if (dcp_3d ()) {
+               d << "-3D";
+       }
+
+       if (dcp_video_frame_rate() != 24) {
+               d << "-" << dcp_video_frame_rate();
+       }
+
        if (container()) {
                d << "_" << container()->dci_name();
        }
@@ -514,7 +462,7 @@ Film::dci_name (bool if_created_now) const
 
        if (!dm.audio_language.empty ()) {
                d << "_" << dm.audio_language;
-               if (!dm.subtitle_language.empty()) {
+               if (!dm.subtitle_language.empty() && with_subtitles()) {
                        d << "-" << dm.subtitle_language;
                } else {
                        d << "-XX";
@@ -528,7 +476,28 @@ Film::dci_name (bool if_created_now) const
                }
        }
 
-       d << "_51_2K";
+       switch (dcp_audio_channels ()) {
+       case 1:
+               d << "_10";
+               break;
+       case 2:
+               d << "_20";
+               break;
+       case 3:
+               d << "_30";
+               break;
+       case 4:
+               d << "_40";
+               break;
+       case 5:
+               d << "_50";
+               break;
+       case 6:
+               d << "_51";
+               break;
+       }
+
+       d << "_" << resolution_to_string (_resolution);
 
        if (!dm.studio.empty ()) {
                d << "_" << dm.studio;
@@ -612,23 +581,23 @@ Film::set_container (Ratio const * c)
 }
 
 void
-Film::set_scaler (Scaler const * s)
+Film::set_resolution (Resolution r)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _scaler = s;
+               _resolution = r;
        }
-       signal_changed (SCALER);
+       signal_changed (RESOLUTION);
 }
 
 void
-Film::set_ab (bool a)
+Film::set_scaler (Scaler const * s)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _ab = a;
+               _scaler = s;
        }
-       signal_changed (AB);
+       signal_changed (SCALER);
 }
 
 void
@@ -642,64 +611,53 @@ Film::set_with_subtitles (bool w)
 }
 
 void
-Film::set_subtitle_offset (int o)
-{
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               _subtitle_offset = o;
-       }
-       signal_changed (SUBTITLE_OFFSET);
-}
-
-void
-Film::set_subtitle_scale (float s)
+Film::set_j2k_bandwidth (int b)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _subtitle_scale = s;
+               _j2k_bandwidth = b;
        }
-       signal_changed (SUBTITLE_SCALE);
+       signal_changed (J2K_BANDWIDTH);
 }
 
 void
-Film::set_colour_lut (int i)
+Film::set_dci_metadata (DCIMetadata m)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _colour_lut = i;
+               _dci_metadata = m;
        }
-       signal_changed (COLOUR_LUT);
+       signal_changed (DCI_METADATA);
 }
 
 void
-Film::set_j2k_bandwidth (int b)
+Film::set_dcp_video_frame_rate (int f)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _j2k_bandwidth = b;
+               _dcp_video_frame_rate = f;
        }
-       signal_changed (J2K_BANDWIDTH);
+       signal_changed (DCP_VIDEO_FRAME_RATE);
 }
 
 void
-Film::set_dci_metadata (DCIMetadata m)
+Film::set_dcp_audio_channels (int c)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dci_metadata = m;
+               _dcp_audio_channels = c;
        }
-       signal_changed (DCI_METADATA);
+       signal_changed (DCP_AUDIO_CHANNELS);
 }
 
-
 void
-Film::set_dcp_video_frame_rate (int f)
+Film::set_dcp_3d (bool t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_video_frame_rate = f;
+               _dcp_3d = t;
        }
-       signal_changed (DCP_VIDEO_FRAME_RATE);
+       signal_changed (DCP_3D);
 }
 
 void
@@ -714,6 +672,10 @@ Film::signal_changed (Property p)
        case Film::CONTENT:
                set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
                break;
+       case Film::DCP_VIDEO_FRAME_RATE:
+       case Film::SEQUENCE_VIDEO:
+               _playlist->maybe_sequence_video ();
+               break;
        default:
                break;
        }
@@ -730,15 +692,23 @@ Film::set_dci_date_today ()
 }
 
 string
-Film::info_path (int f) const
+Film::info_path (int f, Eyes e) const
 {
        boost::filesystem::path p;
        p /= info_dir ();
 
        stringstream s;
        s.width (8);
-       s << setfill('0') << f << ".md5";
+       s << setfill('0') << f;
+
+       if (e == EYES_LEFT) {
+               s << ".L";
+       } else if (e == EYES_RIGHT) {
+               s << ".R";
+       }
 
+       s << ".md5";
+       
        p /= s.str();
 
        /* info_dir() will already have added any initial bit of the path,
@@ -748,15 +718,23 @@ Film::info_path (int f) const
 }
 
 string
-Film::j2c_path (int f, bool t) const
+Film::j2c_path (int f, Eyes e, bool t) const
 {
        boost::filesystem::path p;
        p /= "j2c";
-       p /= video_state_identifier ();
+       p /= video_identifier ();
 
        stringstream s;
        s.width (8);
-       s << setfill('0') << f << ".j2c";
+       s << setfill('0') << f;
+
+       if (e == EYES_LEFT) {
+               s << ".L";
+       } else if (e == EYES_RIGHT) {
+               s << ".R";
+       }
+       
+       s << ".j2c";
 
        if (t) {
                s << ".tmp";
@@ -785,7 +763,7 @@ Film::have_dcp () const
 }
 
 shared_ptr<Player>
-Film::player () const
+Film::make_player () const
 {
        return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
 }
@@ -797,7 +775,7 @@ Film::playlist () const
        return _playlist;
 }
 
-Playlist::ContentList
+ContentList
 Film::content () const
 {
        return _playlist->content ();
@@ -807,9 +785,24 @@ void
 Film::examine_and_add_content (shared_ptr<Content> c)
 {
        shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
+       j->Finished.connect (bind (&Film::maybe_add_content, this, boost::weak_ptr<Job> (j), boost::weak_ptr<Content> (c)));
        JobManager::instance()->add (j);
 }
 
+void
+Film::maybe_add_content (weak_ptr<Job> j, weak_ptr<Content> c)
+{
+       shared_ptr<Job> job = j.lock ();
+       if (!job || !job->finished_ok ()) {
+               return;
+       }
+       
+       shared_ptr<Content> content = c.lock ();
+       if (content) {
+               add_content (content);
+       }
+}
+
 void
 Film::add_content (shared_ptr<Content> c)
 {
@@ -863,18 +856,6 @@ Film::playlist_changed ()
        signal_changed (CONTENT);
 }      
 
-int
-Film::loop () const
-{
-       return _playlist->loop ();
-}
-
-void
-Film::set_loop (int c)
-{
-       _playlist->set_loop (c);
-}
-
 OutputAudioFrame
 Film::time_to_audio_frames (Time t) const
 {
@@ -909,11 +890,25 @@ Film::dcp_audio_frame_rate () const
 void
 Film::set_sequence_video (bool s)
 {
-       _playlist->set_sequence_video (s);
+       {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               _sequence_video = s;
+               _playlist->set_sequence_video (s);
+       }
+       
+       signal_changed (SEQUENCE_VIDEO);
 }
 
 libdcp::Size
 Film::full_frame () const
 {
-       return libdcp::Size (2048, 1080);
+       switch (_resolution) {
+       case RESOLUTION_2K:
+               return libdcp::Size (2048, 1080);
+       case RESOLUTION_4K:
+               return libdcp::Size (4096, 2160);
+       }
+
+       assert (false);
+       return libdcp::Size ();
 }