Support for keeping video in sequence when changing lengths; tie selection in timelin...
authorCarl Hetherington <cth@carlh.net>
Mon, 27 May 2013 20:54:50 +0000 (21:54 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 27 May 2013 20:54:50 +0000 (21:54 +0100)
20 files changed:
src/lib/content.cc
src/lib/content.h
src/lib/ffmpeg_content.cc
src/lib/film.cc
src/lib/film.h
src/lib/imagemagick_content.cc
src/lib/player.cc
src/lib/playlist.cc
src/lib/playlist.h
src/lib/video_content.cc
src/lib/video_content.h
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/film_viewer.cc
src/wx/timecode.cc
src/wx/timecode.h
src/wx/timeline.cc
src/wx/timeline.h
src/wx/timeline_dialog.cc
src/wx/timeline_dialog.h

index aaf2e4f9c3341424581582eaad4921a8a4e06a8e..6a33e9f7e27ce8961ce6439d648138cf891af7ce 100644 (file)
@@ -28,6 +28,7 @@ using boost::shared_ptr;
 using boost::lexical_cast;
 
 int const ContentProperty::START = 400;
+int const ContentProperty::LENGTH = 401;
 
 Content::Content (shared_ptr<const Film> f, Time s)
        : _film (f)
index 321252c02f62ca9f9154dcab161b995ddfb3d795..5e8f98428dd1314b68f3551617a0f576c9e3a11e 100644 (file)
@@ -40,6 +40,7 @@ class ContentProperty
 {
 public:
        static int const START;
+       static int const LENGTH;
 };
 
 class Content : public boost::enable_shared_from_this<Content>
index d912ee418d2e85e34997dfab898587e9969abe4c..7a67ee5b8b5475cbb7e19b10baf02a37586b0393 100644 (file)
@@ -156,7 +156,7 @@ FFmpegContent::examine (shared_ptr<Job> job)
 
         take_from_video_decoder (decoder);
 
-        signal_changed (VideoContentProperty::VIDEO_LENGTH);
+        signal_changed (ContentProperty::LENGTH);
         signal_changed (FFmpegContentProperty::SUBTITLE_STREAMS);
         signal_changed (FFmpegContentProperty::SUBTITLE_STREAM);
         signal_changed (FFmpegContentProperty::AUDIO_STREAMS);
index 26b3962a1fe7c122fd7c03b2f5057afc2725168f..8eeb4fec5141ba1e936f49d2568277d1aee95e8c 100644 (file)
@@ -905,3 +905,9 @@ Film::dcp_audio_frame_rate () const
        /* XXX */
        return 48000;
 }
+
+void
+Film::set_sequence_video (bool s)
+{
+       _playlist->set_sequence_video (s);
+}
index 9d1b863742152961f6eb00080c87c0666dd1b2e1..cb970da4d0667f98f10cbec89d128697e750d982 100644 (file)
@@ -119,6 +119,8 @@ public:
        void set_loop (int);
        int loop () const;
 
+       void set_sequence_video (bool);
+
        /** Identifiers for the parts of our state;
            used for signalling changes.
        */
index ac04fb15b7588c44b20419bb932f38bba997025e..a4777e3cda11350c5823384fb5022c4b8c702e7b 100644 (file)
@@ -75,15 +75,8 @@ ImageMagickContent::examine (shared_ptr<Job> job)
        
        shared_ptr<ImageMagickDecoder> decoder (new ImageMagickDecoder (film, shared_from_this()));
 
-       {
-               boost::mutex::scoped_lock lm (_mutex);
-               /* Initial length */
-               _video_length = Config::instance()->default_still_length() * 24;
-       }
-       
+       set_video_length (Config::instance()->default_still_length() * 24);
        take_from_video_decoder (decoder);
-       
-        signal_changed (VideoContentProperty::VIDEO_LENGTH);
 }
 
 shared_ptr<Content>
@@ -100,7 +93,7 @@ ImageMagickContent::set_video_length (ContentVideoFrame len)
                _video_length = len;
        }
 
-       signal_changed (VideoContentProperty::VIDEO_LENGTH);
+       signal_changed (ContentProperty::LENGTH);
 }
 
 Time
index 786180a6a5f27d6e50331a1530a43b70e253f22f..34894ff0e1f2a8bb0977a974b11c4968682380df 100644 (file)
@@ -251,14 +251,6 @@ Player::add_silent_piece (Time s, Time len)
 }
 
 
-struct ContentSorter
-{
-       bool operator() (shared_ptr<Content> a, shared_ptr<Content> b)
-       {
-               return a->start() < b->start();
-       }
-};
-
 void
 Player::setup_pieces ()
 {
@@ -358,7 +350,7 @@ Player::content_changed (weak_ptr<Content> w, int p)
                return;
        }
 
-       if (p == ContentProperty::START || p == VideoContentProperty::VIDEO_LENGTH) {
+       if (p == ContentProperty::START || p == ContentProperty::LENGTH) {
                _have_valid_pieces = false;
        }
 }
index d2df75a09a4a9e045c73c7f4d7f14803518630a0..d32ec6ba8ac911e70295d70840a1ce67347be4b7 100644 (file)
@@ -1,5 +1,3 @@
-/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
-
 /*
     Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
 
@@ -51,6 +49,8 @@ using boost::lexical_cast;
 
 Playlist::Playlist ()
        : _loop (1)
+       , _sequence_video (true)
+       , _sequencing_video (false)
 {
 
 }
@@ -72,6 +72,26 @@ Playlist::~Playlist ()
 void
 Playlist::content_changed (weak_ptr<Content> c, int p)
 {
+       if (p == ContentProperty::LENGTH && _sequence_video && !_sequencing_video) {
+               cout << "sequencing.\n";
+               _sequencing_video = true;
+
+               ContentList cl = _content;
+               sort (cl.begin(), cl.end(), ContentSorter ());
+               Time last = 0;
+               for (ContentList::iterator i = cl.begin(); i != cl.end(); ++i) {
+                       if (!dynamic_pointer_cast<VideoContent> (*i)) {
+                               continue;
+                       }
+
+                       (*i)->set_start (last);
+                       cout << (*i)->file() << " -> " << last << "\n";
+                       last = (*i)->end ();
+               }
+
+               _sequencing_video = false;
+       }
+       
        ContentChanged (c, p);
 }
 
@@ -143,6 +163,7 @@ Playlist::set_from_xml (shared_ptr<const Film> film, shared_ptr<const cxml::Node
 
        reconnect ();
        _loop = node->number_child<int> ("Loop");
+       _sequence_video = node->bool_child ("SequenceVideo");
 }
 
 /** @param node <Playlist> node */
@@ -154,6 +175,7 @@ Playlist::as_xml (xmlpp::Node* node)
        }
 
        node->add_child("Loop")->add_child_text(lexical_cast<string> (_loop));
+       node->add_child("SequenceVideo")->add_child_text(_sequence_video ? "1" : "0");
 }
 
 void
@@ -297,3 +319,15 @@ Playlist::video_end () const
 
        return end;
 }
+
+void
+Playlist::set_sequence_video (bool s)
+{
+       _sequence_video = s;
+}
+
+bool
+ContentSorter::operator() (shared_ptr<Content> a, shared_ptr<Content> b)
+{
+       return a->start() < b->start();
+}
index f75b4ba63ea9e3e27447de2d52942257fe1c0cb3..3a7ca73bf3dcf4f3e772c024ab41c8c92439e5b6 100644 (file)
@@ -51,6 +51,11 @@ class Region;
  * are played simultaneously (i.e. they can be split up into multiple files for different channels)
  */
 
+struct ContentSorter
+{
+       bool operator() (boost::shared_ptr<Content> a, boost::shared_ptr<Content> b);
+};
+
 class Playlist
 {
 public:
@@ -85,6 +90,8 @@ public:
        int best_dcp_frame_rate () const;
        Time video_end () const;
 
+       void set_sequence_video (bool);
+
        mutable boost::signals2::signal<void ()> Changed;
        mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged;
        
@@ -94,6 +101,8 @@ private:
 
        ContentList _content;
        int _loop;
+       bool _sequence_video;
+       bool _sequencing_video;
        std::list<boost::signals2::connection> _content_connections;
 };
 
index 2e413678f5d0aa219a91157b8162b8a0594fd3f5..ae799dad383f0de8a90411d3b288b5393c60e667 100644 (file)
 
 #include "i18n.h"
 
-int const VideoContentProperty::VIDEO_LENGTH = 0;
-int const VideoContentProperty::VIDEO_SIZE = 1;
-int const VideoContentProperty::VIDEO_FRAME_RATE = 2;
-int const VideoContentProperty::VIDEO_CROP = 3;
+int const VideoContentProperty::VIDEO_SIZE = 0;
+int const VideoContentProperty::VIDEO_FRAME_RATE = 1;
+int const VideoContentProperty::VIDEO_CROP = 2;
 
 using std::string;
 using std::stringstream;
index 7dde927af0bd14d8103fb96c4c3430c4f7a08c54..ce2550d12d8f2ca4059d3f5860402482d1e1c65e 100644 (file)
@@ -28,7 +28,6 @@ class VideoDecoder;
 class VideoContentProperty
 {
 public:
-       static int const VIDEO_LENGTH;
        static int const VIDEO_SIZE;
        static int const VIDEO_FRAME_RATE;
        static int const VIDEO_CROP;
index d036f318ecf7d0b25966dbbca8a48b423ccf0e37..75867d1d57839c84afac3eed713e31367b1a8965 100644 (file)
@@ -222,7 +222,9 @@ FilmEditor::connect_to_widgets ()
        _audio_delay->Connect            (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED,     wxCommandEventHandler (FilmEditor::audio_delay_changed), 0, this);
        _audio_stream->Connect           (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this);
        _subtitle_stream->Connect        (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED,      wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this);
-       _audio_mapping->Changed.connect  (bind (&FilmEditor::audio_mapping_changed, this, _1));
+       _audio_mapping->Changed.connect  (boost::bind (&FilmEditor::audio_mapping_changed, this, _1));
+       _start->Changed.connect          (boost::bind (&FilmEditor::start_changed, this));
+       _length->Changed.connect         (boost::bind (&FilmEditor::length_changed, this));
 }
 
 void
@@ -693,7 +695,12 @@ FilmEditor::film_content_changed (weak_ptr<Content> weak_content, int property)
                } else {
                        _start->set (0, 24);
                }
-
+       } else if (property == ContentProperty::LENGTH) {
+               if (content) {
+                       _length->set (content->length (), _film->dcp_video_frame_rate ());
+               } else {
+                       _length->set (0, 24);
+               }
        } else if (property == VideoContentProperty::VIDEO_CROP) {
                checked_set (_left_crop,   video_content ? video_content->crop().left :   0);
                checked_set (_right_crop,  video_content ? video_content->crop().right :  0);
@@ -848,6 +855,7 @@ FilmEditor::set_film (shared_ptr<Film> f)
        film_changed (Film::DCP_VIDEO_FRAME_RATE);
 
        film_content_changed (boost::shared_ptr<Content> (), ContentProperty::START);
+       film_content_changed (boost::shared_ptr<Content> (), ContentProperty::LENGTH);
        film_content_changed (boost::shared_ptr<Content> (), VideoContentProperty::VIDEO_CROP);
        film_content_changed (boost::shared_ptr<Content> (), AudioContentProperty::AUDIO_GAIN);
        film_content_changed (boost::shared_ptr<Content> (), AudioContentProperty::AUDIO_DELAY);
@@ -872,7 +880,6 @@ FilmEditor::set_things_sensitive (bool s)
        _edit_dci_button->Enable (s);
        _format->Enable (s);
        _content->Enable (s);
-       _content->Enable (s);
        _left_crop->Enable (s);
        _right_crop->Enable (s);
        _top_crop->Enable (s);
@@ -1175,6 +1182,7 @@ FilmEditor::content_selection_changed (wxListEvent &)
         setup_content_sensitivity ();
        shared_ptr<Content> s = selected_content ();
        film_content_changed (s, ContentProperty::START);
+       film_content_changed (s, ContentProperty::LENGTH);
        film_content_changed (s, VideoContentProperty::VIDEO_CROP);
        film_content_changed (s, AudioContentProperty::AUDIO_GAIN);
        film_content_changed (s, AudioContentProperty::AUDIO_DELAY);
@@ -1412,3 +1420,41 @@ FilmEditor::audio_mapping_changed (AudioMapping m)
 
        ac->set_audio_mapping (m);
 }
+
+void
+FilmEditor::start_changed ()
+{
+       shared_ptr<Content> c = selected_content ();
+       if (!c) {
+               return;
+       }
+
+       c->set_start (_start->get (_film->dcp_video_frame_rate ()));
+}
+
+void
+FilmEditor::length_changed ()
+{
+       shared_ptr<Content> c = selected_content ();
+       if (!c) {
+               return;
+       }
+
+       shared_ptr<ImageMagickContent> ic = dynamic_pointer_cast<ImageMagickContent> (c);
+       if (ic) {
+               ic->set_video_length (_length->get(_film->dcp_video_frame_rate()) * ic->video_frame_rate() / TIME_HZ);
+       }
+}
+
+void
+FilmEditor::set_selection (weak_ptr<Content> wc)
+{
+       Playlist::ContentList content = _film->content ();
+       for (size_t i = 0; i < content.size(); ++i) {
+               if (content[i] == wc.lock ()) {
+                       _content->SetItemState (i, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
+               } else {
+                       _content->SetItemState (i, 0, wxLIST_STATE_SELECTED | wxLIST_STATE_FOCUSED);
+               }
+       }
+}
index b169aee7f47456ed6210e786abaaa89e538510d7..c34cd73e005804dad78abfe21916ff55e9ec0354 100644 (file)
@@ -47,6 +47,7 @@ public:
        FilmEditor (boost::shared_ptr<Film>, wxWindow *);
 
        void set_film (boost::shared_ptr<Film>);
+       void set_selection (boost::weak_ptr<Content>);
 
        boost::signals2::signal<void (std::string)> FileChanged;
 
@@ -93,6 +94,8 @@ private:
        void audio_stream_changed (wxCommandEvent &);
        void subtitle_stream_changed (wxCommandEvent &);
        void audio_mapping_changed (AudioMapping);
+       void start_changed ();
+       void length_changed ();
 
        /* Handle changes to the model */
        void film_changed (Film::Property);
index 4b1fb442e12ba802e42eb32a70affcd81dfd68c6..97185ca94ba5b5cfc117926324b29c7f1a4583eb 100644 (file)
@@ -464,7 +464,7 @@ FilmViewer::active_jobs_changed (bool a)
 void
 FilmViewer::film_content_changed (weak_ptr<Content>, int p)
 {
-       if (p == VideoContentProperty::VIDEO_LENGTH) {
+       if (p == ContentProperty::LENGTH) {
                /* Force an update to our frame */
                wxScrollEvent ev;
                slider_moved (ev);
index 460f7423b0d066feb9c8be54a125dec0925bc69f..f8cdccbe2698eacf535bb84a2c3153c4f8a079ae 100644 (file)
 #include "wx_util.h"
 
 using std::string;
+using std::cout;
 using boost::lexical_cast;
 
 Timecode::Timecode (wxWindow* parent)
        : wxPanel (parent)
+       , _in_set (false)
 {
        wxClientDC dc (parent);
        wxSize size = dc.GetTextExtent (wxT ("9999"));
@@ -58,12 +60,19 @@ Timecode::Timecode (wxWindow* parent)
        _frames->SetMaxLength (2);
        sizer->Add (_frames);
 
+       _hours->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this);
+       _minutes->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this);
+       _seconds->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this);
+       _frames->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (Timecode::changed), 0, this);
+
        SetSizerAndFit (sizer);
 }
 
 void
 Timecode::set (Time t, int fps)
 {
+       _in_set = true;
+       
        int const h = t / (3600 * TIME_HZ);
        t -= h * 3600 * TIME_HZ;
        int const m = t / (60 * TIME_HZ);
@@ -72,19 +81,35 @@ Timecode::set (Time t, int fps)
        t -= s * TIME_HZ;
        int const f = t * fps / TIME_HZ;
 
-       _hours->SetValue (wxString::Format ("%02d", h));
-       _minutes->SetValue (wxString::Format ("%02d", m));
-       _seconds->SetValue (wxString::Format ("%02d", s));
-       _frames->SetValue (wxString::Format ("%02d", f));
+       _hours->SetValue (wxString::Format (wxT ("%d"), h));
+       _minutes->SetValue (wxString::Format (wxT ("%d"), m));
+       _seconds->SetValue (wxString::Format (wxT ("%d"), s));
+       _frames->SetValue (wxString::Format (wxT ("%d"), f));
+
+       _in_set = false;
 }
 
 Time
 Timecode::get (int fps) const
 {
        Time t = 0;
-       t += lexical_cast<int> (wx_to_std (_hours->GetValue())) * 3600 * TIME_HZ;
-       t += lexical_cast<int> (wx_to_std (_minutes->GetValue())) * 60 * TIME_HZ;
-       t += lexical_cast<int> (wx_to_std (_seconds->GetValue())) * TIME_HZ;
-       t += lexical_cast<int> (wx_to_std (_frames->GetValue())) * TIME_HZ / fps;
+       string const h = wx_to_std (_hours->GetValue ());
+       t += lexical_cast<int> (h.empty() ? "0" : h) * 3600 * TIME_HZ;
+       string const m = wx_to_std (_minutes->GetValue());
+       t += lexical_cast<int> (m.empty() ? "0" : m) * 60 * TIME_HZ;
+       string const s = wx_to_std (_seconds->GetValue());
+       t += lexical_cast<int> (s.empty() ? "0" : s) * TIME_HZ;
+       string const f = wx_to_std (_frames->GetValue());
+       t += lexical_cast<int> (f.empty() ? "0" : f) * TIME_HZ / fps;
        return t;
 }
+
+void
+Timecode::changed (wxCommandEvent &)
+{
+       if (_in_set) {
+               return;
+       }
+       
+       Changed ();
+}
index f243ee0b8d219c8ebeb94d68e9a0be956b44bcaa..9b6fe66542e9fec1c1bc8d15d26ccd1c7133fbc6 100644 (file)
@@ -17,6 +17,7 @@
 
 */
 
+#include <boost/signals2.hpp>
 #include <wx/wx.h>
 #include "lib/types.h"
 
@@ -28,9 +29,15 @@ public:
        void set (Time, int);
        Time get (int) const;
 
+       boost::signals2::signal<void ()> Changed;
+
 private:
+       void changed (wxCommandEvent &);
+       
        wxTextCtrl* _hours;
        wxTextCtrl* _minutes;
        wxTextCtrl* _seconds;
        wxTextCtrl* _frames;
+
+       bool _in_set;
 };
index 54f3d75cf7f3143ee80ac18efee98ea0a16ab7ec..42f489c2f5d438c4cdb96944ab821511b9a84793 100644 (file)
 #include <wx/graphics.h>
 #include <boost/weak_ptr.hpp>
 #include "film.h"
+#include "film_editor.h"
 #include "timeline.h"
 #include "wx_util.h"
-#include "playlist.h"
+#include "lib/playlist.h"
 
 using std::list;
 using std::cout;
@@ -183,7 +184,7 @@ private:
 
        void content_changed (int p)
        {
-               if (p == ContentProperty::START || p == VideoContentProperty::VIDEO_LENGTH) {
+               if (p == ContentProperty::START || p == ContentProperty::LENGTH) {
                        force_redraw ();
                }
        }
@@ -312,8 +313,9 @@ private:
        int _y;
 };
 
-Timeline::Timeline (wxWindow* parent, shared_ptr<const Film> film)
+Timeline::Timeline (wxWindow* parent, FilmEditor* ed, shared_ptr<Film> film)
        : wxPanel (parent, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
+       , _film_editor (ed)
        , _film (film)
        , _tracks (0)
        , _pixels_per_time_unit (0)
@@ -482,6 +484,9 @@ Timeline::left_down (wxMouseEvent& ev)
                shared_ptr<ContentView> cv = dynamic_pointer_cast<ContentView> (*j);
                if (cv) {
                        cv->set_selected (i == j);
+                       if (i == j) {
+                               _film_editor->set_selection (cv->content ());
+                       }
                }
        }
 
@@ -518,6 +523,10 @@ Timeline::mouse_moved (wxMouseEvent& ev)
                shared_ptr<Content> c = _down_view->content().lock();
                if (c) {
                        c->set_start (max (static_cast<Time> (0), _down_view_start + time_diff));
+
+                       shared_ptr<Film> film = _film.lock ();
+                       assert (film);
+                       film->set_sequence_video (false);
                }
        }
 }
index 566ca060a45174fe3539cbe50dd91ff3f44a961b..348286db8588c97397b16db8a8f92bddacaf361b 100644 (file)
 class Film;
 class View;
 class ContentView;
+class FilmEditor;
 
 class Timeline : public wxPanel
 {
 public:
-       Timeline (wxWindow *, boost::shared_ptr<const Film>);
+       Timeline (wxWindow *, FilmEditor *, boost::shared_ptr<Film>);
 
        boost::shared_ptr<const Film> film () const;
 
@@ -68,7 +69,8 @@ private:
        void resized (wxSizeEvent &);
        void assign_tracks ();
 
-       boost::weak_ptr<const Film> _film;
+       FilmEditor* _film_editor;
+       boost::weak_ptr<Film> _film;
        std::list<boost::shared_ptr<View> > _views;
        int _tracks;
        double _pixels_per_time_unit;
index 91d1f7b07d3652ecd67e0251dc75e9c68a08f837..35d5eec2105b5190ccaedd91973706c29895aa1f 100644 (file)
@@ -19,6 +19,7 @@
 
 #include <list>
 #include <wx/graphics.h>
+#include "film_editor.h"
 #include "timeline_dialog.h"
 #include "wx_util.h"
 #include "playlist.h"
@@ -27,9 +28,9 @@ using std::list;
 using std::cout;
 using boost::shared_ptr;
 
-TimelineDialog::TimelineDialog (wxWindow* parent, shared_ptr<const Film> film)
-       : wxDialog (parent, wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
-       , _timeline (this, film)
+TimelineDialog::TimelineDialog (FilmEditor* ed, shared_ptr<Film> film)
+       : wxDialog (ed, wxID_ANY, _("Timeline"), wxDefaultPosition, wxSize (640, 512), wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE)
+       , _timeline (this, ed, film)
 {
        wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL);
        
index bc6b83eb56810bd6c3e964afed150df34de5af96..17ca22c493281ad8dfc3b3a211e78959c295d96b 100644 (file)
@@ -27,7 +27,7 @@ class Playlist;
 class TimelineDialog : public wxDialog
 {
 public:
-       TimelineDialog (wxWindow *, boost::shared_ptr<const Film>);
+       TimelineDialog (FilmEditor *, boost::shared_ptr<Film>);
 
 private:
        Timeline _timeline;