Various range fixes.
[dcpomatic.git] / src / lib / film.cc
index 75dbfee3d145386ac8a272b1bb1e9dd3a7c331fd..26edc80b07a00bbd38d3d83af825cb56a88ebf38 100644 (file)
@@ -63,6 +63,7 @@ using std::ifstream;
 using std::ofstream;
 using std::setfill;
 using std::min;
+using std::make_pair;
 using boost::shared_ptr;
 using boost::lexical_cast;
 using boost::to_upper_copy;
@@ -82,7 +83,8 @@ Film::Film (string d, bool must_exist)
        , _dcp_content_type (0)
        , _format (0)
        , _scaler (Scaler::from_id ("bicubic"))
-       , _dcp_trim_action (CUT)
+       , _dcp_trim_start (0)
+       , _dcp_trim_end (0)
        , _dcp_ab (false)
        , _audio_stream (-1)
        , _audio_gain (0)
@@ -128,6 +130,7 @@ Film::Film (string d, bool must_exist)
        read_metadata ();
 
        _log = new FileLog (file ("log"));
+       set_dci_date_today ();
 }
 
 Film::Film (Film const & o)
@@ -141,8 +144,8 @@ Film::Film (Film const & o)
        , _crop              (o._crop)
        , _filters           (o._filters)
        , _scaler            (o._scaler)
-       , _dcp_frames        (o._dcp_frames)
-       , _dcp_trim_action   (o._dcp_trim_action)
+       , _dcp_trim_start    (o._dcp_trim_start)
+       , _dcp_trim_end      (o._dcp_trim_end)
        , _dcp_ab            (o._dcp_ab)
        , _audio_stream      (o._audio_stream)
        , _audio_gain        (o._audio_gain)
@@ -221,6 +224,8 @@ Film::j2k_dir () const
 void
 Film::make_dcp (bool transcode)
 {
+       set_dci_date_today ();
+       
        if (dcp_name().find ("/") != string::npos) {
                throw BadSettingError ("name", "cannot contain slashes");
        }
@@ -251,24 +256,13 @@ Film::make_dcp (bool transcode)
 
        shared_ptr<Options> o (new Options (j2k_dir(), ".j2c", dir ("wavs")));
        o->out_size = format()->dcp_size ();
-       if (!dcp_frames()) {
-               /* Decode the whole film, no blacking */
-               o->black_after = 0;
-       } else {
-               switch (dcp_trim_action()) {
-               case CUT:
-                       /* Decode only part of the film, no blacking */
-                       o->black_after = 0;
-                       break;
-               case BLACK_OUT:
-                       /* Decode the whole film, but black some frames out */
-                       o->black_after = dcp_frames().get ();
-               }
-       }
-       
        o->padding = format()->dcp_padding (shared_from_this ());
        o->ratio = format()->ratio_as_float (shared_from_this ());
+       if (dcp_length ()) {
+               o->decode_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
+       }
        o->decode_subtitles = with_subtitles ();
+       o->decode_video_skip = dcp_frame_rate (frames_per_second()).skip;
 
        shared_ptr<Job> r;
 
@@ -292,7 +286,7 @@ Film::examine_content ()
                return;
        }
 
-       set_thumbs (vector<int> ());
+       set_thumbs (vector<SourceFrame> ());
        boost::filesystem::remove_all (dir ("thumbs"));
 
        /* This call will recreate the directory */
@@ -416,18 +410,8 @@ Film::write_metadata () const
                f << "filter " << (*i)->id () << "\n";
        }
        f << "scaler " << _scaler->id () << "\n";
-       f << "dcp_frames " << _dcp_frames.get_value_or(0) << "\n";
-
-       f << "dcp_trim_action ";
-       switch (_dcp_trim_action) {
-       case CUT:
-               f << "cut\n";
-               break;
-       case BLACK_OUT:
-               f << "black_out\n";
-               break;
-       }
-       
+       f << "dcp_trim_start " << _dcp_trim_start << "\n";
+       f << "dcp_trim_end " << _dcp_trim_end << "\n";
        f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
        f << "selected_audio_stream " << _audio_stream << "\n";
        f << "audio_gain " << _audio_gain << "\n";
@@ -448,7 +432,7 @@ Film::write_metadata () const
        /* Cached stuff; this is information about our content; we could
           look it up each time, but that's slow.
        */
-       for (vector<int>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) {
+       for (vector<SourceFrame>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) {
                f << "thumb " << *i << "\n";
        }
        f << "width " << _size.width << "\n";
@@ -506,17 +490,10 @@ Film::read_metadata ()
                        _filters.push_back (Filter::from_id (v));
                } else if (k == "scaler") {
                        _scaler = Scaler::from_id (v);
-               } else if (k == "dcp_frames") {
-                       int const vv = atoi (v.c_str ());
-                       if (vv) {
-                               _dcp_frames = vv;
-                       }
-               } else if (k == "dcp_trim_action") {
-                       if (v == "cut") {
-                               _dcp_trim_action = CUT;
-                       } else if (v == "black_out") {
-                               _dcp_trim_action = BLACK_OUT;
-                       }
+               } 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 (k == "dcp_ab") {
                        _dcp_ab = (v == "1");
                } else if (k == "selected_audio_stream") {
@@ -594,16 +571,19 @@ Film::thumb_file (int n) const
        return thumb_file_for_frame (thumb_frame (n));
 }
 
-/** @param n A frame index within the Film.
+/** @param n A frame index within the Film's source.
  *  @return The path to the thumb's image file for this frame;
  *  we assume that it exists.
  */
 string
-Film::thumb_file_for_frame (int n) const
+Film::thumb_file_for_frame (SourceFrame n) const
 {
        return thumb_base_for_frame(n) + ".png";
 }
 
+/** @param n Thumb index.
+ *  Must not be called with the _state_mutex locked.
+ */
 string
 Film::thumb_base (int n) const
 {
@@ -611,10 +591,8 @@ Film::thumb_base (int n) const
 }
 
 string
-Film::thumb_base_for_frame (int n) const
+Film::thumb_base_for_frame (SourceFrame n) const
 {
-       boost::mutex::scoped_lock lm (_state_mutex);
-
        stringstream s;
        s.width (8);
        s << setfill('0') << n;
@@ -627,9 +605,11 @@ Film::thumb_base_for_frame (int n) const
 }
 
 /** @param n A thumb index.
- *  @return The frame within the Film that it is for.
+ *  @return The frame within the Film's source that it is for.
+ *
+ *  Must not be called with the _state_mutex locked.
  */
-int
+SourceFrame
 Film::thumb_frame (int n) const
 {
        boost::mutex::scoped_lock lm (_state_mutex);
@@ -712,28 +692,26 @@ Film::target_audio_sample_rate () const
        /* Resample to a DCI-approved sample rate */
        double t = dcp_audio_sample_rate (audio_sample_rate());
 
+       DCPFrameRate dfr = dcp_frame_rate (frames_per_second ());
+
        /* Compensate for the fact that video will be rounded to the
           nearest integer number of frames per second.
        */
-       if (rint (frames_per_second()) != frames_per_second()) {
-               t *= _frames_per_second / rint (frames_per_second());
+       if (dfr.run_fast) {
+               t *= _frames_per_second * dfr.skip / dfr.frames_per_second;
        }
 
        return rint (t);
 }
 
-boost::optional<int>
+boost::optional<SourceFrame>
 Film::dcp_length () const
 {
        if (!length()) {
-               return boost::optional<int> ();
-       }
-
-       if (dcp_frames()) {
-               return min (dcp_frames().get(), length().get());
+               return boost::optional<SourceFrame> ();
        }
 
-       return length();
+       return length().get() - dcp_trim_start() - dcp_trim_end();
 }
 
 /** @return a DCI-compliant name for a DCP of this film */
@@ -806,8 +784,7 @@ Film::dci_name () const
                d << _studio << "_";
        }
 
-       boost::gregorian::date today = boost::gregorian::day_clock::local_day ();
-       d << boost::gregorian::to_iso_string (today) << "_";
+       d << boost::gregorian::to_iso_string (_dci_date) << "_";
 
        if (!_facility.empty ()) {
                d << _facility << "_";
@@ -1043,33 +1020,23 @@ Film::set_scaler (Scaler const * s)
 }
 
 void
-Film::set_dcp_frames (int f)
-{
-       {
-               boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_frames = f;
-       }
-       signal_changed (DCP_FRAMES);
-}
-
-void
-Film::unset_dcp_frames ()
+Film::set_dcp_trim_start (int t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_frames = boost::none;
+               _dcp_trim_start = t;
        }
-       signal_changed (DCP_FRAMES);
+       signal_changed (DCP_TRIM_START);
 }
 
 void
-Film::set_dcp_trim_action (TrimAction a)
+Film::set_dcp_trim_end (int t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
-               _dcp_trim_action = a;
+               _dcp_trim_end = t;
        }
-       signal_changed (DCP_TRIM_ACTION);
+       signal_changed (DCP_TRIM_END);
 }
 
 void
@@ -1233,7 +1200,7 @@ Film::set_package_type (string p)
 }
 
 void
-Film::set_thumbs (vector<int> t)
+Film::set_thumbs (vector<SourceFrame> t)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
@@ -1253,7 +1220,7 @@ Film::set_size (Size s)
 }
 
 void
-Film::set_length (int l)
+Film::set_length (SourceFrame l)
 {
        {
                boost::mutex::scoped_lock lm (_state_mutex);
@@ -1355,3 +1322,9 @@ Film::audio_channels () const
        
        return _audio_streams[_audio_stream].channels ();
 }
+
+void
+Film::set_dci_date_today ()
+{
+       _dci_date = boost::gregorian::day_clock::local_day ();
+}