Merge master branch.
[dcpomatic.git] / src / lib / film.cc
index 5a11b0ca969ec5bfb262168158fb3be52e52514f..ae9edbfdb0a93a954a6e7c13bc967738598430e4 100644 (file)
@@ -39,7 +39,6 @@
 #include "ab_transcode_job.h"
 #include "transcode_job.h"
 #include "scp_dcp_job.h"
-#include "make_dcp_job.h"
 #include "log.h"
 #include "options.h"
 #include "exceptions.h"
@@ -72,8 +71,9 @@ using boost::to_upper_copy;
 using boost::ends_with;
 using boost::starts_with;
 using boost::optional;
+using libdcp::Size;
 
-int const Film::state_version = 1;
+int const Film::state_version = 2;
 
 /** Construct a Film object in a given directory, reading any metadata
  *  file that exists in that directory.  An exception will be thrown if
@@ -89,8 +89,8 @@ Film::Film (string d, bool must_exist)
        , _dcp_content_type (0)
        , _format (0)
        , _scaler (Scaler::from_id ("bicubic"))
-       , _dcp_trim_start (0)
-       , _dcp_trim_end (0)
+       , _trim_start (0)
+       , _trim_end (0)
        , _dcp_ab (false)
        , _use_content_audio (true)
        , _audio_gain (0)
@@ -155,8 +155,8 @@ Film::Film (Film const & o)
        , _crop              (o._crop)
        , _filters           (o._filters)
        , _scaler            (o._scaler)
-       , _dcp_trim_start    (o._dcp_trim_start)
-       , _dcp_trim_end      (o._dcp_trim_end)
+       , _trim_start        (o._trim_start)
+       , _trim_end          (o._trim_end)
        , _dcp_ab            (o._dcp_ab)
        , _content_audio_stream (o._content_audio_stream)
        , _external_audio    (o._external_audio)
@@ -179,6 +179,7 @@ Film::Film (Film const & o)
        , _package_type      (o._package_type)
        , _size              (o._size)
        , _length            (o._length)
+       , _dcp_intrinsic_duration (o._dcp_intrinsic_duration)
        , _content_digest    (o._content_digest)
        , _content_audio_streams (o._content_audio_streams)
        , _external_audio_stream (o._external_audio_stream)
@@ -254,7 +255,9 @@ Film::make_dcp (bool transcode)
        }
        
        log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? "still" : "video")));
-       log()->log (String::compose ("Content length %1", length().get()));
+       if (length()) {
+               log()->log (String::compose ("Content length %1", length().get()));
+       }
        log()->log (String::compose ("Content digest %1", content_digest()));
        log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
        log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
@@ -287,47 +290,20 @@ Film::make_dcp (bool transcode)
                throw MissingSettingError ("name");
        }
 
-       shared_ptr<EncodeOptions> oe (new EncodeOptions (j2k_dir(), ".j2c", dir ("wavs")));
-       oe->out_size = format()->dcp_size ();
-       oe->padding = format()->dcp_padding (shared_from_this ());
-       if (dcp_length ()) {
-               oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
-               if (audio_stream()) {
-                       oe->audio_range = make_pair (
-
-                               video_frames_to_audio_frames (
-                                       oe->video_range.get().first,
-                                       dcp_audio_sample_rate (audio_stream()->sample_rate()),
-                                       dcp_frame_rate (frames_per_second()).frames_per_second
-                                       ),
-                               
-                               video_frames_to_audio_frames (
-                                       oe->video_range.get().second,
-                                       dcp_audio_sample_rate (audio_stream()->sample_rate()),
-                                       dcp_frame_rate (frames_per_second()).frames_per_second
-                                       )
-                               );
-               }
-                       
-       }
-       
-       oe->video_skip = dcp_frame_rate (frames_per_second()).skip;
-
-       shared_ptr<DecodeOptions> od (new DecodeOptions);
-       od->decode_subtitles = with_subtitles ();
+       DecodeOptions od;
+       od.decode_subtitles = with_subtitles ();
 
        shared_ptr<Job> r;
 
        if (transcode) {
                if (dcp_ab()) {
-                       r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, shared_ptr<Job> ())));
                } else {
-                       r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+                       r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, shared_ptr<Job> ())));
                }
        }
 
-       r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, oe, r)));
-       JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), oe, r)));
+       // r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, r)));
 }
 
 /** Start a job to examine our content file */
@@ -411,8 +387,8 @@ Film::write_metadata () const
                f << "filter " << (*i)->id () << "\n";
        }
        f << "scaler " << _scaler->id () << "\n";
-       f << "dcp_trim_start " << _dcp_trim_start << "\n";
-       f << "dcp_trim_end " << _dcp_trim_end << "\n";
+       f << "trim_start " << _trim_start << "\n";
+       f << "trim_end " << _trim_end << "\n";
        f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
        if (_content_audio_stream) {
                f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n";
@@ -443,6 +419,7 @@ Film::write_metadata () const
        f << "width " << _size.width << "\n";
        f << "height " << _size.height << "\n";
        f << "length " << _length.get_value_or(0) << "\n";
+       f << "dcp_intrinsic_duration " << _dcp_intrinsic_duration.get_value_or(0) << "\n";
        f << "content_digest " << _content_digest << "\n";
 
        for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
@@ -523,10 +500,10 @@ Film::read_metadata ()
                        _filters.push_back (Filter::from_id (v));
                } else if (k == "scaler") {
                        _scaler = Scaler::from_id (v);
-               } else if (k == "dcp_trim_start") {
-                       _dcp_trim_start = atoi (v.c_str ());
-               } else if (k == "dcp_trim_end") {
-                       _dcp_trim_end = atoi (v.c_str ());
+               } else if ( ((!version || version < 2) && k == "trim_start") || k == "trim_start") {
+                       _trim_start = atoi (v.c_str ());
+               } else if ( ((!version || version < 2) && k == "trim_end") || k == "trim_end") {
+                       _trim_end = atoi (v.c_str ());
                } else if (k == "dcp_ab") {
                        _dcp_ab = (v == "1");
                } else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
@@ -587,6 +564,11 @@ Film::read_metadata ()
                        if (vv) {
                                _length = vv;
                        }
+               } else if (k == "dcp_intrinsic_duration") {
+                       int const vv = atoi (v.c_str ());
+                       if (vv) {
+                               _dcp_intrinsic_duration = vv;
+                       }
                } else if (k == "content_digest") {
                        _content_digest = v;
                } else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
@@ -698,30 +680,25 @@ Film::target_audio_sample_rate () const
        /* Resample to a DCI-approved sample rate */
        double t = dcp_audio_sample_rate (audio_stream()->sample_rate());
 
-       DCPFrameRate dfr = dcp_frame_rate (frames_per_second ());
+       DCPFrameRate dfr (frames_per_second ());
 
-       /* Compensate for the fact that video will be rounded to the
-          nearest integer number of frames per second.
+       /* Compensate if the DCP is being run at a different frame rate
+          to the source; that is, if the video is run such that it will
+          look different in the DCP compared to the source (slower or faster).
+          skip/repeat doesn't come into effect here.
        */
-       if (dfr.run_fast) {
-               t *= _frames_per_second * dfr.skip / dfr.frames_per_second;
+
+       if (dfr.change_speed) {
+               t *= _frames_per_second * dfr.factor() / dfr.frames_per_second;
        }
 
        return rint (t);
 }
 
-boost::optional<int>
-Film::dcp_length () const
+int
+Film::still_duration_in_frames () const
 {
-       if (content_type() == STILL) {
-               return _still_duration * frames_per_second();
-       }
-       
-       if (!length()) {
-               return boost::optional<int> ();
-       }
-
-       return length().get() - dcp_trim_start() - dcp_trim_end();
+       return still_duration() * frames_per_second();
 }
 
 /** @return a DCI-compliant name for a DCP of this film */
@@ -893,8 +870,7 @@ Film::set_content (string c)
        */
 
        try {
-               shared_ptr<DecodeOptions> o (new DecodeOptions);
-               Decoders d = decoder_factory (shared_from_this(), o, 0);
+               Decoders d = decoder_factory (shared_from_this(), DecodeOptions(), 0);
                
                set_size (d.video->native_size ());
                set_frames_per_second (d.video->frames_per_second ());
@@ -1069,23 +1045,23 @@ Film::set_scaler (Scaler const * s)
 }
 
 void
-Film::set_dcp_trim_start (int t)
+Film::set_trim_start (int t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_trim_start = t;
+               _trim_start = t;
        }
-       signal_changed (DCP_TRIM_START);
+       signal_changed (TRIM_START);
 }
 
 void
-Film::set_dcp_trim_end (int t)
+Film::set_trim_end (int t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_trim_end = t;
+               _trim_end = t;
        }
-       signal_changed (DCP_TRIM_END);
+       signal_changed (TRIM_END);
 }
 
 void
@@ -1116,8 +1092,7 @@ Film::set_external_audio (vector<string> a)
                _external_audio = a;
        }
 
-       shared_ptr<DecodeOptions> o (new DecodeOptions);
-       shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0));
+       shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), DecodeOptions(), 0));
        if (decoder->audio_stream()) {
                _external_audio_stream = decoder->audio_stream ();
        }
@@ -1324,7 +1299,17 @@ Film::unset_length ()
                _length = boost::none;
        }
        signal_changed (LENGTH);
-}      
+}
+
+void
+Film::set_dcp_intrinsic_duration (int d)
+{
+       {
+               boost::mutex::scoped_lock lm (_state_mutex);
+               _dcp_intrinsic_duration = d;
+       }
+       signal_changed (DCP_INTRINSIC_DURATION);
+}
 
 void
 Film::set_content_digest (string d)
@@ -1405,3 +1390,45 @@ Film::audio_stream () const
 
        return _external_audio_stream;
 }
+
+/** @param f DCP frame index.
+ *  @param t true to return a temporary file path, otherwise a permanent one.
+ *  @return The path to write this video frame to.
+ */
+string
+Film::frame_out_path (int f, bool t) const
+{
+       stringstream s;
+       s << j2k_dir() << "/";
+       s.width (8);
+       s << std::setfill('0') << f << ".j2c";
+
+       if (t) {
+               s << ".tmp";
+       }
+
+       return s.str ();
+}
+
+string
+Film::hash_out_path (int f, bool t) const
+{
+       return frame_out_path (f, t) + ".md5";
+}
+
+/** @param c Audio channel index.
+ *  @param t true to return a temporary file path, otherwise a permanent one.
+ *  @return The path to write this audio file to.
+ */
+string
+Film::multichannel_audio_out_path (int c, bool t) const
+{
+       stringstream s;
+       s << dir ("wavs") << "/" << (c + 1) << ".wav";
+       if (t) {
+               s << ".tmp";
+       }
+       
+       return s.str ();
+}
+