Somewhat untested and sketchy basics of trimming.
authorCarl Hetherington <cth@carlh.net>
Tue, 30 Jul 2013 20:34:16 +0000 (21:34 +0100)
committerCarl Hetherington <cth@carlh.net>
Tue, 30 Jul 2013 20:34:16 +0000 (21:34 +0100)
20 files changed:
src/lib/content.cc
src/lib/content.h
src/lib/ffmpeg_content.cc
src/lib/ffmpeg_content.h
src/lib/film.cc
src/lib/moving_image_content.cc
src/lib/moving_image_content.h
src/lib/player.cc
src/lib/playlist.cc
src/lib/sndfile_content.cc
src/lib/sndfile_content.h
src/lib/still_image_content.cc
src/lib/still_image_content.h
src/wx/film_editor.cc
src/wx/timeline.cc
src/wx/timeline.h
src/wx/timing_panel.cc
src/wx/timing_panel.h
test/black_fill_test.cc
test/play_test.cc

index 372be30205fa2dfa857ff6f080383ed0c31a0e44..9508144913bc5af335358d54b1e20e806381898e 100644 (file)
@@ -30,12 +30,16 @@ using std::set;
 using boost::shared_ptr;
 using boost::lexical_cast;
 
-int const ContentProperty::START = 400;
+int const ContentProperty::POSITION = 400;
 int const ContentProperty::LENGTH = 401;
+int const ContentProperty::TRIM_START = 402;
+int const ContentProperty::TRIM_END = 403;
 
-Content::Content (shared_ptr<const Film> f, Time s)
+Content::Content (shared_ptr<const Film> f, Time p)
        : _film (f)
-       , _start (s)
+       , _position (p)
+       , _trim_start (0)
+       , _trim_end (0)
        , _change_signals_frequent (false)
 {
 
@@ -44,7 +48,9 @@ Content::Content (shared_ptr<const Film> f, Time s)
 Content::Content (shared_ptr<const Film> f, boost::filesystem::path p)
        : _film (f)
        , _path (p)
-       , _start (0)
+       , _position (0)
+       , _trim_start (0)
+       , _trim_end (0)
        , _change_signals_frequent (false)
 {
 
@@ -56,7 +62,9 @@ Content::Content (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node)
 {
        _path = node->string_child ("Path");
        _digest = node->string_child ("Digest");
-       _start = node->number_child<Time> ("Start");
+       _position = node->number_child<Time> ("Position");
+       _trim_start = node->number_child<Time> ("TrimStart");
+       _trim_end = node->number_child<Time> ("TrimEnd");
 }
 
 void
@@ -65,7 +73,9 @@ Content::as_xml (xmlpp::Node* node) const
        boost::mutex::scoped_lock lm (_mutex);
        node->add_child("Path")->add_child_text (_path.string());
        node->add_child("Digest")->add_child_text (_digest);
-       node->add_child("Start")->add_child_text (lexical_cast<string> (_start));
+       node->add_child("Position")->add_child_text (lexical_cast<string> (_position));
+       node->add_child("TrimStart")->add_child_text (lexical_cast<string> (_trim_start));
+       node->add_child("TrimEnd")->add_child_text (lexical_cast<string> (_trim_end));
 }
 
 void
@@ -91,16 +101,39 @@ Content::signal_changed (int p)
 }
 
 void
-Content::set_start (Time s)
+Content::set_position (Time p)
 {
        {
                boost::mutex::scoped_lock lm (_mutex);
-               _start = s;
+               _position = p;
        }
 
-       signal_changed (ContentProperty::START);
+       signal_changed (ContentProperty::POSITION);
 }
 
+void
+Content::set_trim_start (Time t)
+{
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               _trim_start = t;
+       }
+
+       signal_changed (ContentProperty::TRIM_START);
+}
+
+void
+Content::set_trim_end (Time t)
+{
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               _trim_end = t;
+       }
+
+       signal_changed (ContentProperty::TRIM_END);
+}
+
+
 shared_ptr<Content>
 Content::clone () const
 {
@@ -119,5 +152,20 @@ Content::clone () const
 string
 Content::technical_summary () const
 {
-       return String::compose ("%1 %2 %3", path(), digest(), start());
+       return String::compose ("%1 %2 %3", path(), digest(), position());
+}
+
+Time
+Content::length_after_trim () const
+{
+       return full_length () - _trim_start - _trim_end;
+}
+
+/** @param t A time relative to the start of this content (not the position).
+ *  @return true if this time is trimmed by our trim settings.
+ */
+bool
+Content::trimmed (Time t) const
+{
+       return (t < trim_start() || t > (full_length() - trim_end ()));
 }
index 78b80e2540ec35cdc5f03dfb0ea4c8679249a46f..e3f5597527fd240ee4ee4e4028f878adc0e1b7c7 100644 (file)
@@ -38,8 +38,10 @@ class Film;
 class ContentProperty
 {
 public:
-       static int const START;
+       static int const POSITION;
        static int const LENGTH;
+       static int const TRIM_START;
+       static int const TRIM_END;
 };
 
 class Content : public boost::enable_shared_from_this<Content>, public boost::noncopyable
@@ -55,7 +57,7 @@ public:
        virtual std::string technical_summary () const;
        virtual std::string information () const = 0;
        virtual void as_xml (xmlpp::Node *) const;
-       virtual Time length () const = 0;
+       virtual Time full_length () const = 0;
 
        boost::shared_ptr<Content> clone () const;
        
@@ -70,21 +72,42 @@ public:
                return _digest;
        }
 
-       void set_start (Time);
+       void set_position (Time);
 
-       Time start () const {
+       /** Time that this content starts; i.e. the time that the first
+        *  bit of the content (trimmed or not) will happen.
+        */
+       Time position () const {
                boost::mutex::scoped_lock lm (_mutex);
-               return _start;
+               return _position;
        }
 
+       void set_trim_start (Time);
+
+       Time trim_start () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _trim_start;
+       }
+
+       void set_trim_end (Time);
+       
+       Time trim_end () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _trim_end;
+       }
+       
        Time end () const {
-               return start() + length();
+               return position() + length_after_trim();
        }
 
+       Time length_after_trim () const;
+       
        void set_change_signals_frequent (bool f) {
                _change_signals_frequent = f;
        }
 
+       bool trimmed (Time) const;
+
        boost::signals2::signal<void (boost::weak_ptr<Content>, int, bool)> Changed;
 
 protected:
@@ -97,7 +120,9 @@ private:
        /** Path of a file or a directory containing files */
        boost::filesystem::path _path;
        std::string _digest;
-       Time _start;
+       Time _position;
+       Time _trim_start;
+       Time _trim_end;
        bool _change_signals_frequent;
 };
 
index 84be76d27ccf7b0a26406e00fc6a034528e6ba0f..de967c04554cb843c08a8cbb09330078d483e1a6 100644 (file)
@@ -346,7 +346,7 @@ FFmpegSubtitleStream::as_xml (xmlpp::Node* root) const
 }
 
 Time
-FFmpegContent::length () const
+FFmpegContent::full_length () const
 {
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
index 96d8c1727d24fc46e9b0d84a5e14a1ae6265386f..775cb92205c9f2b9a1660a3713d08c14ab5871f9 100644 (file)
@@ -107,7 +107,7 @@ public:
        std::string technical_summary () const;
        std::string information () const;
        void as_xml (xmlpp::Node *) const;
-       Time length () const;
+       Time full_length () const;
 
        std::string identifier () const;
        
index 13c1558e96a077b53d87058614dbffc6dd9ecc05..2f7e0787323210d93297ae36a3248512dfc619ba 100644 (file)
@@ -806,7 +806,7 @@ Film::add_content (shared_ptr<Content> c)
 {
        /* Add video content after any existing content */
        if (dynamic_pointer_cast<VideoContent> (c)) {
-               c->set_start (_playlist->video_end ());
+               c->set_position (_playlist->video_end ());
        }
 
        _playlist->add (c);
index 30030d9fd1b60bb31eecb6fef28022ecaabf09ce..63b4b9f249d5f794f1400df3695641d63c29f68c 100644 (file)
@@ -94,7 +94,7 @@ MovingImageContent::examine (shared_ptr<Job> job)
 }
 
 Time
-MovingImageContent::length () const
+MovingImageContent::full_length () const
 {
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
index 2b3c48dd114f02f00500007b3a9883705e5155c6..1a64750fe5692f8ae9f1212a0f338b190359adc9 100644 (file)
@@ -42,7 +42,7 @@ public:
        std::string summary () const;
        std::string technical_summary () const;
        void as_xml (xmlpp::Node *) const;
-       Time length () const;
+       Time full_length () const;
 
        std::string identifier () const;
 
index 33a5bbfef828a9b2712f822af210eedbd2955d24..63cf4ee7f8af3e9393d0215e7392d639c84c5988 100644 (file)
@@ -55,15 +55,15 @@ class Piece
 public:
        Piece (shared_ptr<Content> c)
                : content (c)
-               , video_position (c->start ())
-               , audio_position (c->start ())
+               , video_position (c->position ())
+               , audio_position (c->position ())
        {}
        
        Piece (shared_ptr<Content> c, shared_ptr<Decoder> d)
                : content (c)
                , decoder (d)
-               , video_position (c->start ())
-               , audio_position (c->start ())
+               , video_position (c->position ())
+               , audio_position (c->position ())
        {}
        
        shared_ptr<Content> content;
@@ -83,7 +83,7 @@ std::ostream& operator<<(std::ostream& s, Piece const & p)
                s << "\tsndfile    ";
        }
        
-       s << " at " << p.content->start() << " until " << p.content->end();
+       s << " at " << p.content->position() << " until " << p.content->end();
        
        return s;
 }
@@ -248,14 +248,19 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image
                return;
        }
 
+       Time const relative_time = (frame * frc.factor() * TIME_HZ / _film->video_frame_rate());
+       if (content->trimmed (relative_time)) {
+               return;
+       }
+       
        shared_ptr<Image> work_image = image->crop (content->crop(), true);
 
        libdcp::Size const image_size = content->ratio()->size (_video_container_size);
        
        work_image = work_image->scale_and_convert_to_rgb (image_size, _film->scaler(), true);
 
-       Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->video_frame_rate());
-       
+       Time time = content->position() + relative_time - content->trim_start ();
+           
        if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) {
                work_image->alpha_blend (_out_subtitle.image, _out_subtitle.position);
        }
@@ -297,6 +302,15 @@ Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers
        shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (piece->content);
        assert (content);
 
+       Time const relative_time = _film->audio_frames_to_time (frame)
+               + (content->audio_delay() * TIME_HZ / 1000);
+
+       if (content->trimmed (relative_time)) {
+               return;
+       }
+
+       Time time = content->position() + relative_time;
+       
        /* Resample */
        if (content->content_audio_frame_rate() != content->output_audio_frame_rate()) {
                shared_ptr<Resampler> r = resampler (content, true);
@@ -317,10 +331,6 @@ Player::process_audio (weak_ptr<Piece> weak_piece, shared_ptr<const AudioBuffers
 
        audio = dcp_mapped;
 
-       Time time = content->start()
-               + _film->audio_frames_to_time (frame)
-               + (content->audio_delay() * TIME_HZ / 1000);
-
        /* We must cut off anything that comes before the start of all time */
        if (time < 0) {
                int const frames = - time * _film->audio_frame_rate() / TIME_HZ;
@@ -380,18 +390,18 @@ Player::seek (Time t, bool accurate)
                        continue;
                }
                
-               Time s = t - vc->start ();
+               Time s = t - vc->position ();
                s = max (static_cast<Time> (0), s);
-               s = min (vc->length(), s);
+               s = min (vc->length_after_trim(), s);
 
-               (*i)->video_position = (*i)->audio_position = vc->start() + s;
+               (*i)->video_position = (*i)->audio_position = vc->position() + s;
 
                FrameRateConversion frc (vc->video_frame_rate(), _film->video_frame_rate());
                /* Here we are converting from time (in the DCP) to a frame number in the content.
                   Hence we need to use the DCP's frame rate and the double/skip correction, not
                   the source's rate.
                */
-               VideoContent::Frame f = s * _film->video_frame_rate() / (frc.factor() * TIME_HZ);
+               VideoContent::Frame f = (s + vc->trim_start ()) * _film->video_frame_rate() / (frc.factor() * TIME_HZ);
                dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate);
        }
 
@@ -487,7 +497,8 @@ Player::content_changed (weak_ptr<Content> w, int property, bool frequent)
        }
 
        if (
-               property == ContentProperty::START || property == ContentProperty::LENGTH ||
+               property == ContentProperty::POSITION || property == ContentProperty::LENGTH ||
+               property == ContentProperty::TRIM_START || property == ContentProperty::TRIM_END ||
                property == VideoContentProperty::VIDEO_CROP || property == VideoContentProperty::VIDEO_RATIO
                ) {
                
@@ -622,6 +633,6 @@ Player::update_subtitle ()
        _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2)));
        
        _out_subtitle.image = _in_subtitle.image->scale (libdcp::Size (scaled_size.width, scaled_size.height), Scaler::from_id ("bicubic"), true);
-       _out_subtitle.from = _in_subtitle.from + piece->content->start ();
-       _out_subtitle.to = _in_subtitle.to + piece->content->start ();
+       _out_subtitle.from = _in_subtitle.from + piece->content->position ();
+       _out_subtitle.to = _in_subtitle.to + piece->content->position ();
 }
index e9ea3e3c7143179afbb82e3ee4dfb331a75b7dcf..de48ff5f5cd0d49bb31a2691a437c4f825eddf38 100644 (file)
@@ -72,7 +72,6 @@ Playlist::content_changed (weak_ptr<Content> content, int property, bool frequen
        ContentChanged (content, property, frequent);
 }
 
-
 void
 Playlist::maybe_sequence_video ()
 {
@@ -90,7 +89,7 @@ Playlist::maybe_sequence_video ()
                        continue;
                }
                
-               (*i)->set_start (last);
+               (*i)->set_position (last);
                last = (*i)->end ();
        }
        
@@ -295,7 +294,7 @@ Playlist::set_sequence_video (bool s)
 bool
 ContentSorter::operator() (shared_ptr<Content> a, shared_ptr<Content> b)
 {
-       return a->start() < b->start();
+       return a->position() < b->position();
 }
 
 /** @return content in an undefined order */
@@ -310,8 +309,8 @@ Playlist::repeat (ContentList c, int n)
 {
        pair<Time, Time> range (TIME_MAX, 0);
        for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
-               range.first = min (range.first, (*i)->start ());
-               range.second = max (range.second, (*i)->start ());
+               range.first = min (range.first, (*i)->position ());
+               range.second = max (range.second, (*i)->position ());
                range.first = min (range.first, (*i)->end ());
                range.second = max (range.second, (*i)->end ());
        }
@@ -320,7 +319,7 @@ Playlist::repeat (ContentList c, int n)
        for (int i = 0; i < n; ++i) {
                for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
                        shared_ptr<Content> copy = (*i)->clone ();
-                       copy->set_start (pos + copy->start() - range.first);
+                       copy->set_position (pos + copy->position() - range.first);
                        _content.push_back (copy);
                }
                pos += range.second - range.first;
index 2ca00cf6dc6c508812cc08feb9fd5365a8d5130c..fc6f45d001005bf939c22539acb7b67df7dd239a 100644 (file)
@@ -136,7 +136,7 @@ SndfileContent::as_xml (xmlpp::Node* node) const
 }
 
 Time
-SndfileContent::length () const
+SndfileContent::full_length () const
 {
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
index 27e0ca21d30a197f9e805cef6f3d7e2a7b5b0aab..191d625277307ea933fa13f1877ffa7494eab965 100644 (file)
@@ -44,7 +44,7 @@ public:
        std::string technical_summary () const;
        std::string information () const;
        void as_xml (xmlpp::Node *) const;
-       Time length () const;
+       Time full_length () const;
 
        /* AudioContent */
        int audio_channels () const {
index 5a3fed1b3988f52f1539f09831c2ddd7d9034b67..0cf80b54682d248dc1175accfa4d1de5cd88cb02 100644 (file)
@@ -94,7 +94,7 @@ StillImageContent::set_video_length (VideoContent::Frame len)
 }
 
 Time
-StillImageContent::length () const
+StillImageContent::full_length () const
 {
        shared_ptr<const Film> film = _film.lock ();
        assert (film);
index 24d5174a098b2f2bc2eee28a84ffbdd4e1bd80df..ccd7fbc03b1c10e6826812558a1e3c4f2cbe653d 100644 (file)
@@ -42,7 +42,7 @@ public:
        std::string summary () const;
        std::string technical_summary () const;
        void as_xml (xmlpp::Node *) const;
-       Time length () const;
+       Time full_length () const;
 
        std::string identifier () const;
        
index 914dc61361254d8b0e8539dc37fa311dd789ea17..c0e169835f8f2cb9834a8e1aa5000a1d67fa1a4b 100644 (file)
@@ -733,8 +733,10 @@ FilmEditor::content_selection_changed ()
        /* All other sensitivity in content panels should be triggered by
           one of these.
        */
-       film_content_changed (s, ContentProperty::START);
+       film_content_changed (s, ContentProperty::POSITION);
        film_content_changed (s, ContentProperty::LENGTH);
+       film_content_changed (s, ContentProperty::TRIM_START);
+       film_content_changed (s, ContentProperty::TRIM_END);
        film_content_changed (s, VideoContentProperty::VIDEO_CROP);
        film_content_changed (s, VideoContentProperty::VIDEO_RATIO);
        film_content_changed (s, VideoContentProperty::VIDEO_FRAME_TYPE);
index f6e0dabb6a8d7f79aa401de97a8e047e5415a5ff..7c0690084341b02a15507df31f566c7d7ec042c3 100644 (file)
@@ -92,9 +92,9 @@ public:
                }
                
                return dcpomatic::Rect<int> (
-                       time_x (content->start ()) - 8,
+                       time_x (content->position ()) - 8,
                        y_pos (_track) - 8,
-                       content->length () * _timeline.pixels_per_time_unit() + 16,
+                       content->length_after_trim () * _timeline.pixels_per_time_unit() + 16,
                        _timeline.track_height() + 16
                        );
        }
@@ -133,8 +133,8 @@ private:
                        return;
                }
 
-               Time const start = cont->start ();
-               Time const len = cont->length ();
+               Time const position = cont->position ();
+               Time const len = cont->length_after_trim ();
 
                wxColour selected (colour().Red() / 2, colour().Green() / 2, colour().Blue() / 2);
 
@@ -148,11 +148,11 @@ private:
                }
 
                wxGraphicsPath path = gc->CreatePath ();
-               path.MoveToPoint    (time_x (start),       y_pos (_track) + 4);
-               path.AddLineToPoint (time_x (start + len), y_pos (_track) + 4);
-               path.AddLineToPoint (time_x (start + len), y_pos (_track + 1) - 4);
-               path.AddLineToPoint (time_x (start),       y_pos (_track + 1) - 4);
-               path.AddLineToPoint (time_x (start),       y_pos (_track) + 4);
+               path.MoveToPoint    (time_x (position),       y_pos (_track) + 4);
+               path.AddLineToPoint (time_x (position + len), y_pos (_track) + 4);
+               path.AddLineToPoint (time_x (position + len), y_pos (_track + 1) - 4);
+               path.AddLineToPoint (time_x (position),       y_pos (_track + 1) - 4);
+               path.AddLineToPoint (time_x (position),       y_pos (_track) + 4);
                gc->StrokePath (path);
                gc->FillPath (path);
 
@@ -163,8 +163,8 @@ private:
                wxDouble name_leading;
                gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading);
                
-               gc->Clip (wxRegion (time_x (start), y_pos (_track), len * _timeline.pixels_per_time_unit(), _timeline.track_height()));
-               gc->DrawText (name, time_x (start) + 12, y_pos (_track + 1) - name_height - 4);
+               gc->Clip (wxRegion (time_x (position), y_pos (_track), len * _timeline.pixels_per_time_unit(), _timeline.track_height()));
+               gc->DrawText (name, time_x (position) + 12, y_pos (_track + 1) - name_height - 4);
                gc->ResetClip ();
        }
 
@@ -177,7 +177,7 @@ private:
        {
                ensure_ui_thread ();
                
-               if (p == ContentProperty::START || p == ContentProperty::LENGTH) {
+               if (p == ContentProperty::POSITION || p == ContentProperty::LENGTH) {
                        force_redraw ();
                }
 
@@ -327,7 +327,7 @@ Timeline::Timeline (wxWindow* parent, FilmEditor* ed, shared_ptr<Film> film)
        , _tracks (0)
        , _pixels_per_time_unit (0)
        , _left_down (false)
-       , _down_view_start (0)
+       , _down_view_position (0)
        , _first_move (false)
        , _menu (film, this)
 {
@@ -430,8 +430,8 @@ Timeline::assign_tracks ()
                                        
                                if (test && test->track() == t) {
                                        bool const no_overlap =
-                                               (acv_content->start() < test_content->start() && acv_content->end() < test_content->start()) ||
-                                               (acv_content->start() > test_content->end()   && acv_content->end() > test_content->end());
+                                               (acv_content->position() < test_content->position() && acv_content->end() < test_content->position()) ||
+                                               (acv_content->position() > test_content->end()      && acv_content->end() > test_content->end());
                                        
                                        if (!no_overlap) {
                                                /* we have an overlap on track `t' */
@@ -499,7 +499,7 @@ Timeline::left_down (wxMouseEvent& ev)
 
        if (content_view) {
                _down_view = content_view;
-               _down_view_start = content_view->content()->start ();
+               _down_view_position = content_view->content()->position ();
        }
 
        for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
@@ -539,7 +539,7 @@ Timeline::left_up (wxMouseEvent& ev)
                _down_view->content()->set_change_signals_frequent (false);
        }
 
-       set_start_from_event (ev);
+       set_position_from_event (ev);
 }
 
 void
@@ -549,7 +549,7 @@ Timeline::mouse_moved (wxMouseEvent& ev)
                return;
        }
 
-       set_start_from_event (ev);
+       set_position_from_event (ev);
 }
 
 void
@@ -570,7 +570,7 @@ Timeline::right_down (wxMouseEvent& ev)
 }
 
 void
-Timeline::set_start_from_event (wxMouseEvent& ev)
+Timeline::set_position_from_event (wxMouseEvent& ev)
 {
        wxPoint const p = ev.GetPosition();
 
@@ -584,7 +584,7 @@ Timeline::set_start_from_event (wxMouseEvent& ev)
 
        Time const time_diff = (p.x - _down_point.x) / _pixels_per_time_unit;
        if (_down_view) {
-               _down_view->content()->set_start (max (static_cast<Time> (0), _down_view_start + time_diff));
+               _down_view->content()->set_position (max (static_cast<Time> (0), _down_view_position + time_diff));
 
                shared_ptr<Film> film = _film.lock ();
                assert (film);
index db33dbbdc70e161003eaf9df40ebbaf46c64eefa..0217373b9c3bbbf6a6a6986d77c3f74e1ecc99d7 100644 (file)
@@ -73,7 +73,7 @@ private:
        void playlist_changed ();
        void resized ();
        void assign_tracks ();
-       void set_start_from_event (wxMouseEvent &);
+       void set_position_from_event (wxMouseEvent &);
        void clear_selection ();
 
        typedef std::vector<boost::shared_ptr<View> > ViewList;
@@ -92,7 +92,7 @@ private:
        bool _left_down;
        wxPoint _down_point;
        boost::shared_ptr<ContentView> _down_view;
-       Time _down_view_start;
+       Time _down_view_position;
        bool _first_move;
        ContentMenu _menu;
 
index 313543d2d0cccddacd3598f4bc3c6bcfedbe1988..f11a4e5e69c553a60bcc1e1119f8ec90e6fb07ed 100644 (file)
@@ -34,46 +34,66 @@ TimingPanel::TimingPanel (FilmEditor* e)
        wxFlexGridSizer* grid = new wxFlexGridSizer (2, 4, 4);
        _sizer->Add (grid, 0, wxALL, 8);
 
-       add_label_to_sizer (grid, this, _("Start time"), true);
-       _start = new Timecode (this);
-       grid->Add (_start);
+       add_label_to_sizer (grid, this, _("Position"), true);
+       _position = new Timecode (this);
+       grid->Add (_position);
        add_label_to_sizer (grid, this, _("Length"), true);
        _length = new Timecode (this);
        grid->Add (_length);
+       add_label_to_sizer (grid, this, _("Trim from start"), true);
+       _trim_start = new Timecode (this);
+       grid->Add (_trim_start);
+       add_label_to_sizer (grid, this, _("Trim from end"), true);
+       _trim_end = new Timecode (this);
+       grid->Add (_trim_end);
 
-       _start->Changed.connect  (boost::bind (&TimingPanel::start_changed, this));
-       _length->Changed.connect (boost::bind (&TimingPanel::length_changed, this));
+       _position->Changed.connect   (boost::bind (&TimingPanel::position_changed, this));
+       _length->Changed.connect     (boost::bind (&TimingPanel::length_changed, this));
+       _trim_start->Changed.connect (boost::bind (&TimingPanel::trim_start_changed, this));
+       _trim_end->Changed.connect   (boost::bind (&TimingPanel::trim_end_changed, this));
 }
 
 void
 TimingPanel::film_content_changed (shared_ptr<Content> content, int property)
 {
-       if (property == ContentProperty::START) {
+       if (property == ContentProperty::POSITION) {
                if (content) {
-                       _start->set (content->start (), _editor->film()->video_frame_rate ());
+                       _position->set (content->position (), _editor->film()->video_frame_rate ());
                } else {
-                       _start->set (0, 24);
+                       _position->set (0, 24);
                }
        } else if (property == ContentProperty::LENGTH) {
                if (content) {
-                       _length->set (content->length (), _editor->film()->video_frame_rate ());
+                       _length->set (content->full_length (), _editor->film()->video_frame_rate ());
                } else {
                        _length->set (0, 24);
                }
-       }
+       } else if (property == ContentProperty::TRIM_START) {
+               if (content) {
+                       _trim_start->set (content->trim_start (), _editor->film()->video_frame_rate ());
+               } else {
+                       _trim_start->set (0, 24);
+               }
+       } else if (property == ContentProperty::TRIM_END) {
+               if (content) {
+                       _trim_end->set (content->trim_end (), _editor->film()->video_frame_rate ());
+               } else {
+                       _trim_end->set (0, 24);
+               }
+       }       
 
        _length->set_editable (dynamic_pointer_cast<StillImageContent> (content));
 }
 
 void
-TimingPanel::start_changed ()
+TimingPanel::position_changed ()
 {
        shared_ptr<Content> c = _editor->selected_content ();
        if (!c) {
                return;
        }
 
-       c->set_start (_start->get (_editor->film()->video_frame_rate ()));
+       c->set_position (_position->get (_editor->film()->video_frame_rate ()));
 }
 
 void
@@ -89,3 +109,26 @@ TimingPanel::length_changed ()
                ic->set_video_length (_length->get (_editor->film()->video_frame_rate()) * ic->video_frame_rate() / TIME_HZ);
        }
 }
+
+void
+TimingPanel::trim_start_changed ()
+{
+       shared_ptr<Content> c = _editor->selected_content ();
+       if (!c) {
+               return;
+       }
+
+       c->set_trim_start (_trim_start->get (_editor->film()->video_frame_rate ()));
+}
+
+
+void
+TimingPanel::trim_end_changed ()
+{
+       shared_ptr<Content> c = _editor->selected_content ();
+       if (!c) {
+               return;
+       }
+
+       c->set_trim_end (_trim_end->get (_editor->film()->video_frame_rate ()));
+}
index b5c9ef026b5130581428cc596d91d5a77ef92e10..b84ea52be644030be6b852958ce3e1899f7d3cf8 100644 (file)
@@ -29,9 +29,13 @@ public:
        void film_content_changed (boost::shared_ptr<Content>, int);
        
 private:
-       void start_changed ();
+       void position_changed ();
        void length_changed ();
+       void trim_start_changed ();
+       void trim_end_changed ();
        
-       Timecode* _start;
+       Timecode* _position;
        Timecode* _length;
+       Timecode* _trim_start;
+       Timecode* _trim_end;
 };
index 4bd76c07ff8ac2cc77f3cfa6c1adddf74329f902..2c239e767fcf99bc4856224e5cedb42895b1b332 100644 (file)
@@ -47,9 +47,9 @@ BOOST_AUTO_TEST_CASE (black_fill_test)
        wait_for_jobs ();
 
        contentA->set_video_length (3);
-       contentA->set_start (film->video_frames_to_time (2));
+       contentA->set_position (film->video_frames_to_time (2));
        contentB->set_video_length (1);
-       contentB->set_start (film->video_frames_to_time (7));
+       contentB->set_position (film->video_frames_to_time (7));
 
        film->make_dcp ();
 
index 52b54408b826adb8908e5860bc505b76336056d6..e1fffa1d86422ba1f6dd7f160e632a3e4dcfe5f7 100644 (file)
@@ -100,9 +100,9 @@ BOOST_AUTO_TEST_CASE (play_test)
        /* Film should have been set to 25fps */
        BOOST_CHECK_EQUAL (film->video_frame_rate(), 25);
 
-       BOOST_CHECK_EQUAL (A->start(), 0);
+       BOOST_CHECK_EQUAL (A->position(), 0);
        /* A is 16 frames long at 25 fps */
-       BOOST_CHECK_EQUAL (B->start(), 16 * TIME_HZ / 25);
+       BOOST_CHECK_EQUAL (B->position(), 16 * TIME_HZ / 25);
 
        shared_ptr<Player> player = film->make_player ();
        PlayerWrapper wrap (player);