Use plain git hash for VERSION when there is no exact tag.
[dcpomatic.git] / src / lib / playlist.cc
index 20534e467778b58246e87cb0ec5ab12033cfc09a..85957e106803be886e4f5a85016f15cc21282282 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2013-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2013-2021 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
 
 */
 
-#include "playlist.h"
-#include "video_content.h"
-#include "text_content.h"
-#include "ffmpeg_decoder.h"
-#include "ffmpeg_content.h"
-#include "image_decoder.h"
+
 #include "audio_content.h"
+#include "compose.hpp"
+#include "config.h"
 #include "content_factory.h"
 #include "dcp_content.h"
+#include "digester.h"
+#include "ffmpeg_content.h"
+#include "ffmpeg_decoder.h"
+#include "image_decoder.h"
 #include "job.h"
-#include "config.h"
+#include "playlist.h"
+#include "text_content.h"
 #include "util.h"
-#include "digester.h"
-#include "compose.hpp"
+#include "video_content.h"
 #include <libcxml/cxml.h>
 #include <libxml++/libxml++.h>
 #include <boost/bind/placeholders.hpp>
 
 #include "i18n.h"
 
-using std::list;
+
 using std::cout;
-using std::vector;
-using std::min;
+using std::dynamic_pointer_cast;
+using std::list;
 using std::max;
-using std::string;
+using std::min;
 using std::pair;
-using boost::optional;
 using std::shared_ptr;
+using std::string;
+using std::vector;
 using std::weak_ptr;
-using std::dynamic_pointer_cast;
+using boost::optional;
 using namespace dcpomatic;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
 #endif
 
-Playlist::Playlist ()
-       : _sequence (true)
-       , _sequencing (false)
+
+class ContentSorter
 {
+public:
+       bool operator()(shared_ptr<Content> a, shared_ptr<Content> b)
+       {
+               if (a->position() != b->position()) {
+                       return a->position() < b->position();
+               }
+
+               /* Put video before audio if they start at the same time */
+               if (a->video && !b->video) {
+                       return true;
+               } else if (!a->video && b->video) {
+                       return false;
+               }
+
+               /* Last resort */
+               return a->digest() < b->digest();
+       }
+};
 
-}
 
 Playlist::~Playlist ()
 {
@@ -69,6 +87,7 @@ Playlist::~Playlist ()
        disconnect ();
 }
 
+
 void
 Playlist::content_change (weak_ptr<const Film> weak_film, ChangeType type, weak_ptr<Content> content, int property, bool frequent)
 {
@@ -117,6 +136,7 @@ Playlist::content_change (weak_ptr<const Film> weak_film, ChangeType type, weak_
        ContentChange (type, content, property, frequent);
 }
 
+
 void
 Playlist::maybe_sequence (shared_ptr<const Film> film)
 {
@@ -126,7 +146,7 @@ Playlist::maybe_sequence (shared_ptr<const Film> film)
 
        _sequencing = true;
 
-       ContentList cont = content ();
+       auto cont = content ();
 
        /* Keep track of the content that we've set the position of so that we don't
           do it twice.
@@ -171,6 +191,7 @@ Playlist::maybe_sequence (shared_ptr<const Film> film)
        _sequencing = false;
 }
 
+
 string
 Playlist::video_identifier () const
 {
@@ -193,6 +214,7 @@ Playlist::video_identifier () const
        return digester.get ();
 }
 
+
 /** @param film Film that this Playlist is for.
  *  @param node &lt;Playlist&gt; node.
  *  @param version Metadata version number.
@@ -227,8 +249,8 @@ Playlist::set_from_xml (shared_ptr<const Film> film, cxml::ConstNodePtr node, in
                }
 
                /* ...or have a start trim which is an integer number of frames */
-               ContentTime const old_trim = content->trim_start();
-               content->set_trim_start(old_trim);
+               auto const old_trim = content->trim_start();
+               content->set_trim_start(film, old_trim);
                if (old_trim != content->trim_start()) {
                        string note = _("Your project contains video content whose trim was not aligned to a frame boundary.");
                        note += "  ";
@@ -255,6 +277,7 @@ Playlist::set_from_xml (shared_ptr<const Film> film, cxml::ConstNodePtr node, in
        reconnect (film);
 }
 
+
 /** @param node &lt;Playlist&gt; node.
  *  @param with_content_paths true to include &lt;Path&gt; nodes in &lt;Content&gt; nodes, false to omit them.
  */
@@ -266,6 +289,7 @@ Playlist::as_xml (xmlpp::Node* node, bool with_content_paths)
        }
 }
 
+
 void
 Playlist::add (shared_ptr<const Film> film, shared_ptr<Content> c)
 {
@@ -283,6 +307,7 @@ Playlist::add (shared_ptr<const Film> film, shared_ptr<Content> c)
        LengthChange ();
 }
 
+
 void
 Playlist::remove (shared_ptr<Content> c)
 {
@@ -316,6 +341,7 @@ Playlist::remove (shared_ptr<Content> c)
        LengthChange ();
 }
 
+
 void
 Playlist::remove (ContentList c)
 {
@@ -325,7 +351,7 @@ Playlist::remove (ContentList c)
                boost::mutex::scoped_lock lm (_mutex);
 
                for (auto i: c) {
-                       ContentList::iterator j = _content.begin ();
+                       auto j = _content.begin ();
                        while (j != _content.end() && *j != i) {
                                ++j;
                        }
@@ -343,6 +369,7 @@ Playlist::remove (ContentList c)
        LengthChange ();
 }
 
+
 class FrameRateCandidate
 {
 public:
@@ -355,6 +382,7 @@ public:
        int dcp;
 };
 
+
 /** @return the best frame rate from Config::_allowed_dcp_frame_rates for the content in this list */
 int
 Playlist::best_video_frame_rate () const
@@ -412,6 +440,7 @@ Playlist::best_video_frame_rate () const
        return best->dcp;
 }
 
+
 /** @return length of the playlist from time 0 to the last thing on the playlist */
 DCPTime
 Playlist::length (shared_ptr<const Film> film) const
@@ -424,6 +453,7 @@ Playlist::length (shared_ptr<const Film> film) const
        return len;
 }
 
+
 /** @return position of the first thing on the playlist, if it's not empty */
 optional<DCPTime>
 Playlist::start () const
@@ -441,6 +471,7 @@ Playlist::start () const
        return start;
 }
 
+
 /** Must be called with a lock held on _mutex */
 void
 Playlist::disconnect ()
@@ -452,6 +483,7 @@ Playlist::disconnect ()
        _content_connections.clear ();
 }
 
+
 /** Must be called with a lock held on _mutex */
 void
 Playlist::reconnect (shared_ptr<const Film> film)
@@ -463,6 +495,7 @@ Playlist::reconnect (shared_ptr<const Film> film)
        }
 }
 
+
 DCPTime
 Playlist::video_end (shared_ptr<const Film> film) const
 {
@@ -476,6 +509,7 @@ Playlist::video_end (shared_ptr<const Film> film) const
        return end;
 }
 
+
 DCPTime
 Playlist::text_end (shared_ptr<const Film> film) const
 {
@@ -489,10 +523,11 @@ Playlist::text_end (shared_ptr<const Film> film) const
        return end;
 }
 
+
 FrameRateChange
 Playlist::active_frame_rate_change (DCPTime t, int dcp_video_frame_rate) const
 {
-       ContentList cont = content ();
+       auto cont = content ();
        for (ContentList::const_reverse_iterator i = cont.rbegin(); i != cont.rend(); ++i) {
                if (!(*i)->video) {
                        continue;
@@ -515,29 +550,13 @@ Playlist::active_frame_rate_change (DCPTime t, int dcp_video_frame_rate) const
        return FrameRateChange (dcp_video_frame_rate, dcp_video_frame_rate);
 }
 
+
 void
 Playlist::set_sequence (bool s)
 {
        _sequence = s;
 }
 
-bool
-ContentSorter::operator() (shared_ptr<Content> a, shared_ptr<Content> b)
-{
-       if (a->position() != b->position()) {
-               return a->position() < b->position();
-       }
-
-       /* Put video before audio if they start at the same time */
-       if (a->video && !b->video) {
-               return true;
-       } else if (!a->video && b->video) {
-               return false;
-       }
-
-       /* Last resort */
-       return a->digest() < b->digest();
-}
 
 /** @return content in ascending order of position */
 ContentList
@@ -547,10 +566,11 @@ Playlist::content () const
        return _content;
 }
 
+
 void
 Playlist::repeat (shared_ptr<const Film> film, ContentList c, int n)
 {
-       pair<DCPTime, DCPTime> range (DCPTime::max (), DCPTime ());
+       pair<DCPTime, DCPTime> range (DCPTime::max(), DCPTime());
        for (auto i: c) {
                range.first = min (range.first, i->position ());
                range.second = max (range.second, i->position ());
@@ -580,6 +600,7 @@ Playlist::repeat (shared_ptr<const Film> film, ContentList c, int n)
        Change (ChangeType::DONE);
 }
 
+
 void
 Playlist::move_earlier (shared_ptr<const Film> film, shared_ptr<Content> c)
 {
@@ -603,35 +624,31 @@ Playlist::move_earlier (shared_ptr<const Film> film, shared_ptr<Content> c)
        c->set_position (film, p);
 }
 
+
 void
 Playlist::move_later (shared_ptr<const Film> film, shared_ptr<Content> c)
 {
        auto cont = content ();
-       auto i = cont.begin();
-       while (i != cont.end() && *i != c) {
-               ++i;
-       }
-
-       DCPOMATIC_ASSERT (i != cont.end());
 
-       ContentList::iterator next = i;
-       ++next;
+       auto iter = std::find(cont.begin(), cont.end(), c);
+       DCPOMATIC_ASSERT(iter != cont.end());
 
+       ContentList::iterator next = std::next(iter);
        if (next == cont.end()) {
+               /* This content is already at the end */
                return;
        }
 
-       auto next_c = *next;
-
-       next_c->set_position (film, c->position());
-       c->set_position (film, c->position() + next_c->length_after_trim(film));
+       (*next)->set_position(film, c->position());
+       c->set_position(film, c->position() + (*next)->length_after_trim(film));
 }
 
+
 int64_t
 Playlist::required_disk_space (shared_ptr<const Film> film, int j2k_bandwidth, int audio_channels, int audio_frame_rate) const
 {
-       int64_t video = uint64_t (j2k_bandwidth / 8) * length(film).seconds();
-       int64_t audio = uint64_t (audio_channels * audio_frame_rate * 3) * length(film).seconds();
+       int64_t video = uint64_t(j2k_bandwidth / 8) * length(film).seconds();
+       int64_t audio = uint64_t(audio_channels) * audio_frame_rate * 3 * length(film).seconds();
 
        for (auto i: content()) {
                auto d = dynamic_pointer_cast<DCPContent> (i);
@@ -640,7 +657,7 @@ Playlist::required_disk_space (shared_ptr<const Film> film, int j2k_bandwidth, i
                                video -= uint64_t (j2k_bandwidth / 8) * d->length_after_trim(film).seconds();
                        }
                        if (d->reference_audio()) {
-                               audio -= uint64_t (audio_channels * audio_frame_rate * 3) * d->length_after_trim(film).seconds();
+                               audio -= uint64_t(audio_channels) * audio_frame_rate * 3 * d->length_after_trim(film).seconds();
                        }
                }
        }
@@ -649,6 +666,7 @@ Playlist::required_disk_space (shared_ptr<const Film> film, int j2k_bandwidth, i
        return video + audio + 65536;
 }
 
+
 string
 Playlist::content_summary (shared_ptr<const Film> film, DCPTimePeriod period) const
 {
@@ -656,7 +674,7 @@ Playlist::content_summary (shared_ptr<const Film> film, DCPTimePeriod period) co
        int best_score = -1;
        for (auto i: content()) {
                int score = 0;
-               auto const o = DCPTimePeriod(i->position(), i->end(film)).overlap (period);
+               auto const o = i->period(film).overlap(period);
                if (o) {
                        score += 100 * o.get().duration().get() / period.duration().get();
                }
@@ -666,7 +684,7 @@ Playlist::content_summary (shared_ptr<const Film> film, DCPTimePeriod period) co
                }
 
                if (score > best_score) {
-                       best_summary = i->path(0).leaf().string();
+                       best_summary = i->path(0).filename().string();
                        best_score = score;
                }
        }
@@ -674,6 +692,7 @@ Playlist::content_summary (shared_ptr<const Film> film, DCPTimePeriod period) co
        return best_summary;
 }
 
+
 pair<double, double>
 Playlist::speed_up_range (int dcp_video_frame_rate) const
 {