Initial butler work.
authorCarl Hetherington <cth@carlh.net>
Tue, 7 Mar 2017 19:27:33 +0000 (19:27 +0000)
committerCarl Hetherington <cth@carlh.net>
Wed, 19 Apr 2017 22:04:32 +0000 (23:04 +0100)
src/lib/audio_ring_buffers.cc [new file with mode: 0644]
src/lib/audio_ring_buffers.h [new file with mode: 0644]
src/lib/butler.cc [new file with mode: 0644]
src/lib/butler.h [new file with mode: 0644]
src/lib/player_video.h
src/lib/video_ring_buffers.cc [new file with mode: 0644]
src/lib/video_ring_buffers.h [new file with mode: 0644]
src/lib/wscript
src/wx/film_viewer.cc
src/wx/film_viewer.h

diff --git a/src/lib/audio_ring_buffers.cc b/src/lib/audio_ring_buffers.cc
new file mode 100644 (file)
index 0000000..8107247
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+    Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "audio_ring_buffers.h"
+#include "dcpomatic_assert.h"
+#include "exceptions.h"
+#include <boost/foreach.hpp>
+
+using std::min;
+using std::cout;
+using boost::shared_ptr;
+
+AudioRingBuffers::AudioRingBuffers ()
+       : _used_in_head (0)
+{
+
+}
+
+void
+AudioRingBuffers::put (shared_ptr<const AudioBuffers> data, DCPTime time)
+{
+       boost::mutex::scoped_lock lm (_mutex);
+
+       if (!_buffers.empty ()) {
+               DCPOMATIC_ASSERT (_buffers.front()->channels() == data->channels());
+       }
+
+       _buffers.push_back (data);
+       _latest = time;
+}
+
+void
+AudioRingBuffers::get (float* out, int channels, int frames)
+{
+       boost::mutex::scoped_lock lm (_mutex);
+
+       while (frames > 0) {
+               if (_buffers.empty ()) {
+                       for (int i = 0; i < frames; ++i) {
+                               for (int j = 0; j < channels; ++j) {
+                                       *out++ = 0;
+                               }
+                       }
+                       cout << "audio underrun; missing " << frames << "!\n";
+                       return;
+               }
+
+               int const to_do = min (frames, _buffers.front()->frames() - _used_in_head);
+               float** p = _buffers.front()->data();
+               int const c = min (_buffers.front()->channels(), channels);
+               for (int i = 0; i < to_do; ++i) {
+                       for (int j = 0; j < c; ++j) {
+                               *out++ = p[j][i + _used_in_head];
+                       }
+                       for (int j = c; j < channels; ++j) {
+                               *out++ = 0;
+                       }
+               }
+               _used_in_head += to_do;
+               frames -= to_do;
+
+               if (_used_in_head == _buffers.front()->frames()) {
+                       _buffers.pop_front ();
+                       _used_in_head = 0;
+               }
+       }
+}
+
+void
+AudioRingBuffers::clear ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       _buffers.clear ();
+       _used_in_head = 0;
+       _latest = DCPTime ();
+}
+
+Frame
+AudioRingBuffers::size ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       Frame s = 0;
+       BOOST_FOREACH (shared_ptr<const AudioBuffers> i, _buffers) {
+               s += i->frames ();
+       }
+       return s - _used_in_head;
+}
diff --git a/src/lib/audio_ring_buffers.h b/src/lib/audio_ring_buffers.h
new file mode 100644 (file)
index 0000000..cec7c71
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+    Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#ifndef DCPOMATIC_AUDIO_RING_BUFFERS_H
+#define DCPOMATIC_AUDIO_RING_BUFFERS_H
+
+#include "audio_buffers.h"
+#include "types.h"
+#include "dcpomatic_time.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
+#include <list>
+
+class AudioRingBuffers : public boost::noncopyable
+{
+public:
+       AudioRingBuffers ();
+
+       void put (boost::shared_ptr<const AudioBuffers> data, DCPTime time);
+       void get (float* out, int channels, int frames);
+
+       void clear ();
+       Frame size ();
+
+       DCPTime latest () const {
+               boost::mutex::scoped_lock lm (_mutex);
+               return _latest;
+       }
+
+private:
+       mutable boost::mutex _mutex;
+       std::list<boost::shared_ptr<const AudioBuffers> > _buffers;
+       DCPTime _latest;
+       int _used_in_head;
+};
+
+#endif
diff --git a/src/lib/butler.cc b/src/lib/butler.cc
new file mode 100644 (file)
index 0000000..e40ae0b
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+    Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "butler.h"
+#include "player.h"
+#include <boost/weak_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+using std::cout;
+using std::pair;
+using std::make_pair;
+using boost::weak_ptr;
+using boost::shared_ptr;
+using boost::bind;
+using boost::optional;
+
+/** Video readahead in frames */
+#define VIDEO_READAHEAD 10
+
+Butler::Butler (weak_ptr<const Film> film, shared_ptr<Player> player)
+       : _film (film)
+       , _player (player)
+       , _pending_seek_accurate (false)
+       , _finished (false)
+{
+       _player->Video.connect (bind (&VideoRingBuffers::put, &_video, _1, _2));
+       _thread = new boost::thread (bind (&Butler::thread, this));
+}
+
+Butler::~Butler ()
+{
+       _thread->interrupt ();
+       try {
+               _thread->join ();
+       } catch (boost::thread_interrupted& e) {
+               /* No problem */
+       }
+       delete _thread;
+}
+
+void
+Butler::thread ()
+{
+       while (true) {
+               boost::mutex::scoped_lock lm (_mutex);
+
+               while (_video.size() > VIDEO_READAHEAD && !_pending_seek_position) {
+                       _summon.wait (lm);
+               }
+
+               if (_pending_seek_position) {
+                       _player->seek (*_pending_seek_position, _pending_seek_accurate);
+                       _pending_seek_position = optional<DCPTime> ();
+               }
+
+               while (_video.size() < VIDEO_READAHEAD) {
+                       _arrived.notify_all ();
+                       if (_player->pass ()) {
+                               _finished = true;
+                               break;
+                       }
+               }
+       }
+}
+
+pair<shared_ptr<PlayerVideo>, DCPTime>
+Butler::get_video ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       while (_video.size() == 0 && !_finished) {
+               _arrived.wait (lm);
+       }
+
+       if (_finished) {
+               return make_pair (shared_ptr<PlayerVideo>(), DCPTime());
+       }
+
+       return _video.get ();
+}
+
+void
+Butler::seek (DCPTime position, bool accurate)
+{
+       _video.clear ();
+
+       {
+               boost::mutex::scoped_lock lm (_mutex);
+               _finished = false;
+               _pending_seek_position = position;
+               _pending_seek_accurate = accurate;
+       }
+
+       _summon.notify_all ();
+}
diff --git a/src/lib/butler.h b/src/lib/butler.h
new file mode 100644 (file)
index 0000000..d801d6c
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+    Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "video_ring_buffers.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/signals2.hpp>
+
+class Film;
+class Player;
+class PlayerVideo;
+
+class Butler : public boost::noncopyable
+{
+public:
+       Butler (boost::weak_ptr<const Film> film, boost::shared_ptr<Player> player);
+       ~Butler ();
+
+       void seek (DCPTime position, bool accurate);
+       std::pair<boost::shared_ptr<PlayerVideo>, DCPTime> get_video ();
+
+private:
+       void thread ();
+       void video (boost::shared_ptr<PlayerVideo> video, DCPTime time);
+
+       boost::weak_ptr<const Film> _film;
+       boost::shared_ptr<Player> _player;
+       boost::thread* _thread;
+
+       VideoRingBuffers _video;
+
+       boost::mutex _mutex;
+       boost::condition _summon;
+       boost::condition _arrived;
+       boost::optional<DCPTime> _pending_seek_position;
+       bool _pending_seek_accurate;
+
+       bool _finished;
+
+       boost::signals2::scoped_connection _player_video_connection;
+};
index 60c9224b0e5254978397e271fb1206584c970c68..5017d0e3c8f8bc8a1358d4fcf1d8e75055fd06bf 100644 (file)
@@ -18,6 +18,9 @@
 
 */
 
+#ifndef DCPOMATIC_PLAYER_VIDEO_H
+#define DCPOMATIC_PLAYER_VIDEO_H
+
 #include "types.h"
 #include "position.h"
 #include "dcpomatic_time.h"
@@ -98,3 +101,5 @@ private:
        boost::optional<ColourConversion> _colour_conversion;
        boost::optional<PositionImage> _subtitle;
 };
+
+#endif
diff --git a/src/lib/video_ring_buffers.cc b/src/lib/video_ring_buffers.cc
new file mode 100644 (file)
index 0000000..2542854
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+    Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "video_ring_buffers.h"
+#include "player_video.h"
+#include <boost/foreach.hpp>
+#include <list>
+#include <iostream>
+
+using std::list;
+using std::make_pair;
+using std::cout;
+using std::pair;
+using boost::shared_ptr;
+using boost::optional;
+
+void
+VideoRingBuffers::put (shared_ptr<PlayerVideo> frame, DCPTime time)
+{
+       cout << "put " << to_string(time) << "\n";
+       boost::mutex::scoped_lock lm (_mutex);
+       _data.push_back (make_pair (frame, time));
+}
+
+pair<shared_ptr<PlayerVideo>, DCPTime>
+VideoRingBuffers::get ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       if (_data.empty ()) {
+               cout << "get: no data.\n";
+               return make_pair(shared_ptr<PlayerVideo>(), DCPTime());
+       }
+       pair<shared_ptr<PlayerVideo>, DCPTime> const r = _data.front ();
+       cout << "get: here we go! " << to_string(r.second) << "\n";
+       _data.pop_front ();
+       return r;
+}
+
+Frame
+VideoRingBuffers::size () const
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       return _data.size ();
+}
+
+void
+VideoRingBuffers::clear ()
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       _data.clear ();
+}
+
+optional<DCPTime>
+VideoRingBuffers::latest () const
+{
+       boost::mutex::scoped_lock lm (_mutex);
+       if (_data.empty ()) {
+               return optional<DCPTime> ();
+       }
+
+       return _data.back().second;
+}
diff --git a/src/lib/video_ring_buffers.h b/src/lib/video_ring_buffers.h
new file mode 100644 (file)
index 0000000..f728247
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+    Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+
+    This file is part of DCP-o-matic.
+
+    DCP-o-matic is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    DCP-o-matic is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with DCP-o-matic.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "dcpomatic_time.h"
+#include "player_video.h"
+#include "types.h"
+#include <boost/noncopyable.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/shared_ptr.hpp>
+#include <utility>
+
+class PlayerVideo;
+
+class VideoRingBuffers : public boost::noncopyable
+{
+public:
+       void put (boost::shared_ptr<PlayerVideo> frame, DCPTime time);
+       std::pair<boost::shared_ptr<PlayerVideo>, DCPTime> get ();
+
+       void clear ();
+       Frame size () const;
+
+       boost::optional<DCPTime> latest () const;
+
+private:
+       mutable boost::mutex _mutex;
+       std::list<std::pair<boost::shared_ptr<PlayerVideo>, DCPTime> > _data;
+};
index 2c34577d94d5ded4e5f0b1c246e8675bbabb28ee..23e8468123f6b366568631a06b85fa0055bdcc3b 100644 (file)
@@ -35,6 +35,7 @@ sources = """
           audio_point.cc
           audio_processor.cc
           audio_stream.cc
+          butler.cc
           case_insensitive_sorter.cc
           cinema.cc
           cinema_kdms.cc
@@ -144,6 +145,7 @@ sources = """
           video_mxf_content.cc
           video_mxf_decoder.cc
           video_mxf_examiner.cc
+          video_ring_buffers.cc
           writer.cc
           """
 
index a5371fcf0adcdffbe48b6523104629f7dd278c45..8fff213454b2193c4fa8d218cf2e798cfe4a7980 100644 (file)
@@ -39,6 +39,7 @@
 #include "lib/video_content.h"
 #include "lib/video_decoder.h"
 #include "lib/timer.h"
+#include "lib/butler.h"
 #include "lib/log.h"
 #include "lib/config.h"
 extern "C" {
@@ -179,9 +180,9 @@ FilmViewer::set_film (shared_ptr<Film> film)
        _player->set_play_referenced ();
 
        _film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1));
-
        _player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1));
-       _player->Video.connect (boost::bind (&FilmViewer::video, this, _1, _2));
+
+       recreate_butler ();
 
        calculate_sizes ();
        refresh ();
@@ -189,6 +190,18 @@ FilmViewer::set_film (shared_ptr<Film> film)
        setup_sensitivity ();
 }
 
+void
+FilmViewer::recreate_butler ()
+{
+       _butler.reset ();
+
+       if (!_film) {
+               return;
+       }
+
+       _butler.reset (new Butler (_film, _player));
+}
+
 void
 FilmViewer::refresh_panel ()
 {
@@ -197,16 +210,15 @@ FilmViewer::refresh_panel ()
 }
 
 void
-FilmViewer::video (shared_ptr<PlayerVideo> pv, DCPTime time)
+FilmViewer::get ()
 {
-       if (!_player) {
-               return;
-       }
+       pair<shared_ptr<PlayerVideo>, DCPTime> video;
+       do {
+               video = _butler->get_video ();
+       } while (_film->three_d() && ((_left_eye->GetValue() && video.first->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && video.first->eyes() == EYES_LEFT)));
 
-       if (_film->three_d ()) {
-               if ((_left_eye->GetValue() && pv->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && pv->eyes() == EYES_LEFT)) {
-                       return;
-               }
+       if (!video.first) {
+               return;
        }
 
        /* In an ideal world, what we would do here is:
@@ -227,28 +239,21 @@ FilmViewer::video (shared_ptr<PlayerVideo> pv, DCPTime time)
         * image and convert it (from whatever the user has said it is) to RGB.
         */
 
-       _frame = pv->image (
+       _frame = video.first->image (
                bind (&Log::dcp_log, _film->log().get(), _1, _2),
                bind (&PlayerVideo::always_rgb, _1),
                false, true
                );
 
-       ImageChanged (pv);
+       ImageChanged (video.first);
 
-       _position = time;
-       _inter_position = pv->inter_position ();
-       _inter_size = pv->inter_size ();
+       _position = video.second;
+       _inter_position = video.first->inter_position ();
+       _inter_size = video.first->inter_size ();
 
        refresh_panel ();
 }
 
-void
-FilmViewer::get ()
-{
-       Image const * current = _frame.get ();
-       while (!_player->pass() && _frame.get() == current) {}
-}
-
 void
 FilmViewer::timer ()
 {
@@ -334,7 +339,7 @@ FilmViewer::panel_sized (wxSizeEvent& ev)
 void
 FilmViewer::calculate_sizes ()
 {
-       if (!_film || !_player) {
+       if (!_film) {
                return;
        }
 
@@ -570,11 +575,11 @@ FilmViewer::jump_to_selected_clicked ()
 void
 FilmViewer::seek (DCPTime t, bool accurate)
 {
-       if (!_player) {
+       if (!_butler) {
                return;
        }
 
-       _player->seek (t, accurate);
+       _butler->seek (t, accurate);
        _last_seek_accurate = accurate;
        get ();
 }
index 9fc21fc9ce765e2dd57e82e669826781eb1cfc4b..d50cc562d565dd03912a675e46e90ddf1276c6ee 100644 (file)
@@ -22,8 +22,8 @@
  *  @brief A wx widget to view `thumbnails' of a Film.
  */
 
-#include <wx/wx.h>
 #include "lib/film.h"
+#include <wx/wx.h>
 
 class wxToggleButton;
 class FFmpegPlayer;
@@ -31,6 +31,7 @@ class Image;
 class RGBPlusAlphaImage;
 class PlayerVideo;
 class Player;
+class Butler;
 
 /** @class FilmViewer
  *  @brief A wx widget to view a preview of a Film.
@@ -68,7 +69,6 @@ private:
        void player_changed (bool);
        void update_position_label ();
        void update_position_slider ();
-       void video (boost::shared_ptr<PlayerVideo>, DCPTime time);
        void get ();
        void seek (DCPTime t, bool accurate);
        void refresh_panel ();
@@ -79,6 +79,7 @@ private:
        void frame_number_clicked ();
        void go_to (DCPTime t);
        void jump_to_selected_clicked ();
+       void recreate_butler ();
 
        boost::shared_ptr<Film> _film;
        boost::shared_ptr<Player> _player;
@@ -113,4 +114,6 @@ private:
         *  can get the same one that we got last time.
         */
        bool _last_seek_accurate;
+
+       boost::shared_ptr<Butler> _butler;
 };