Add trim type property to Content; by content or by playlist.
[dcpomatic.git] / src / lib / content.cc
index ca76b01a6c8ad627f851fa7503c2eb9e18eaaa85..3ac2bc05183b1228809323c2ffb03319df24b92f 100644 (file)
@@ -52,18 +52,21 @@ using boost::shared_ptr;
 using boost::optional;
 using dcp::raw_convert;
 using dcp::locale_convert;
+using namespace dcpomatic;
 
 int const ContentProperty::PATH = 400;
 int const ContentProperty::POSITION = 401;
 int const ContentProperty::LENGTH = 402;
 int const ContentProperty::TRIM_START = 403;
 int const ContentProperty::TRIM_END = 404;
-int const ContentProperty::VIDEO_FRAME_RATE = 405;
+int const ContentProperty::TRIM_BEHAVIOUR = 405;
+int const ContentProperty::VIDEO_FRAME_RATE = 406;
 
 Content::Content ()
        : _position (0)
        , _trim_start (0)
        , _trim_end (0)
+       , _trim_behaviour (TRIM_CONTENT)
        , _change_signals_frequent (false)
 {
 
@@ -73,6 +76,7 @@ Content::Content (DCPTime p)
        : _position (p)
        , _trim_start (0)
        , _trim_end (0)
+       , _trim_behaviour (TRIM_CONTENT)
        , _change_signals_frequent (false)
 {
 
@@ -82,6 +86,7 @@ Content::Content (boost::filesystem::path p)
        : _position (0)
        , _trim_start (0)
        , _trim_end (0)
+       , _trim_behaviour (TRIM_CONTENT)
        , _change_signals_frequent (false)
 {
        add_path (p);
@@ -106,6 +111,7 @@ Content::Content (cxml::ConstNodePtr node)
        _position = DCPTime (node->number_child<DCPTime::Type> ("Position"));
        _trim_start = ContentTime (node->number_child<ContentTime::Type> ("TrimStart"));
        _trim_end = ContentTime (node->number_child<ContentTime::Type> ("TrimEnd"));
+       _trim_behaviour = node->optional_string_child("TrimBehaviour").get_value_or("content") == "content" ? TRIM_CONTENT : TRIM_PLAYLIST;
        _video_frame_rate = node->optional_number_child<double> ("VideoFrameRate");
 }
 
@@ -125,6 +131,10 @@ Content::Content (vector<shared_ptr<Content> > c)
                        throw JoinError (_("Only the last piece of content to be joined can have an end trim."));
                }
 
+               if (_trim_behaviour != c[i]->_trim_behaviour) {
+                       throw JoinError (_("Content to be joined must ahve the same trim behaviour setting"));
+               }
+
                if (
                        (_video_frame_rate && !c[i]->_video_frame_rate) ||
                        (!_video_frame_rate && c[i]->_video_frame_rate)
@@ -159,6 +169,7 @@ Content::as_xml (xmlpp::Node* node, bool with_paths) const
        node->add_child("Position")->add_child_text (raw_convert<string> (_position.get ()));
        node->add_child("TrimStart")->add_child_text (raw_convert<string> (_trim_start.get ()));
        node->add_child("TrimEnd")->add_child_text (raw_convert<string> (_trim_end.get ()));
+       node->add_child("TrimBehaviour")->add_child_text (_trim_behaviour == TRIM_CONTENT ? N_("content") : N_("playlist"));
        if (_video_frame_rate) {
                node->add_child("VideoFrameRate")->add_child_text (raw_convert<string> (_video_frame_rate.get()));
        }
@@ -211,7 +222,7 @@ Content::signal_change (ChangeType c, int p)
 }
 
 void
-Content::set_position (shared_ptr<const Film> film, DCPTime p)
+Content::set_position (shared_ptr<const Film> film, DCPTime p, bool force_emit)
 {
        /* video and audio content can modify its position */
 
@@ -219,7 +230,13 @@ Content::set_position (shared_ptr<const Film> film, DCPTime p)
                video->modify_position (film, p);
        }
 
-       if (audio) {
+       /* Only allow the audio to modify if we have no video;
+          sometimes p can't be on an integer video AND audio frame,
+          and in these cases we want the video constraint to be
+          satisfied since (I think) the audio code is better able to
+          cope.
+       */
+       if (!video && audio) {
                audio->modify_position (film, p);
        }
 
@@ -227,7 +244,7 @@ Content::set_position (shared_ptr<const Film> film, DCPTime p)
 
        {
                boost::mutex::scoped_lock lm (_mutex);
-               if (p == _position) {
+               if (p == _position && !force_emit) {
                        cc.abort ();
                        return;
                }
@@ -245,7 +262,8 @@ Content::set_trim_start (ContentTime t)
                video->modify_trim_start (t);
        }
 
-       if (audio) {
+       /* See note in ::set_position */
+       if (!video && audio) {
                audio->modify_trim_start (t);
        }
 
@@ -269,6 +287,18 @@ Content::set_trim_end (ContentTime t)
 }
 
 
+void
+Content::set_trim_behaviour (TrimBehaviour t)
+{
+       ChangeSignaller<Content> cc (this, ContentProperty::TRIM_BEHAVIOUR);
+
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               _trim_behaviour = t;
+       }
+}
+
+
 shared_ptr<Content>
 Content::clone () const
 {
@@ -295,7 +325,11 @@ Content::technical_summary () const
 DCPTime
 Content::length_after_trim (shared_ptr<const Film> film) const
 {
-       return max (DCPTime(), full_length(film) - DCPTime(trim_start() + trim_end(), film->active_frame_rate_change(position())));
+       DCPTime length = max(DCPTime(), full_length(film) - DCPTime(trim_start() + trim_end(), film->active_frame_rate_change(position())));
+       if (video) {
+               length = length.round(film->video_frame_rate());
+       }
+       return length;
 }
 
 /** @return string which changes when something about this content changes which affects