summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2017-07-26 15:47:52 +0100
committerCarl Hetherington <cth@carlh.net>2017-07-26 15:47:52 +0100
commit3e230d3785f19bc707fd7ea2b1f55321b93f536f (patch)
tree2773a105d2cf218d9c038418e388ff50f9b46236 /src/lib
parentb395478cbb0706de2b6afa9a34fb33e49c61ee67 (diff)
Multi-threaded decode of DCP when previewing.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/butler.cc23
-rw-r--r--src/lib/butler.h6
-rw-r--r--src/lib/image_proxy.h4
-rw-r--r--src/lib/j2k_image_proxy.cc50
-rw-r--r--src/lib/j2k_image_proxy.h5
-rw-r--r--src/lib/player_video.cc6
-rw-r--r--src/lib/player_video.h1
7 files changed, 74 insertions, 21 deletions
diff --git a/src/lib/butler.cc b/src/lib/butler.cc
index 771c4e633..053d7c3bc 100644
--- a/src/lib/butler.cc
+++ b/src/lib/butler.cc
@@ -48,6 +48,7 @@ using boost::optional;
Butler::Butler (weak_ptr<const Film> film, shared_ptr<Player> player, AudioMapping audio_mapping, int audio_channels)
: _film (film)
, _player (player)
+ , _prepare_work (new boost::asio::io_service::work (_prepare_service))
, _pending_seek_accurate (false)
, _finished (false)
, _died (false)
@@ -60,6 +61,13 @@ Butler::Butler (weak_ptr<const Film> film, shared_ptr<Player> player, AudioMappi
_player_audio_connection = _player->Audio.connect (bind (&Butler::audio, this, _1));
_player_changed_connection = _player->Changed.connect (bind (&Butler::player_changed, this));
_thread = new boost::thread (bind (&Butler::thread, this));
+
+ /* Create some threads to do work on the PlayerVideos we are creating; at present this is used to
+ multi-thread JPEG2000 decoding.
+ */
+ for (int i = 0; i < boost::thread::hardware_concurrency(); ++i) {
+ _prepare_pool.create_thread (bind (&boost::asio::io_service::run, &_prepare_service));
+ }
}
Butler::~Butler ()
@@ -69,6 +77,10 @@ Butler::~Butler ()
_stop_thread = true;
}
+ _prepare_work.reset ();
+ _prepare_pool.join_all ();
+ _prepare_service.stop ();
+
_thread->interrupt ();
try {
_thread->join ();
@@ -181,6 +193,16 @@ Butler::seek (DCPTime position, bool accurate)
}
void
+Butler::prepare (weak_ptr<PlayerVideo> weak_video) const
+{
+ shared_ptr<PlayerVideo> video = weak_video.lock ();
+ /* If the weak_ptr cannot be locked the video obviously no longer requires any work */
+ if (video) {
+ video->prepare ();
+ }
+}
+
+void
Butler::video (shared_ptr<PlayerVideo> video, DCPTime time)
{
{
@@ -191,6 +213,7 @@ Butler::video (shared_ptr<PlayerVideo> video, DCPTime time)
}
}
+ _prepare_service.post (bind (&Butler::prepare, this, weak_ptr<PlayerVideo>(video)));
_video.put (video, time);
}
diff --git a/src/lib/butler.h b/src/lib/butler.h
index 9949ab79b..1e8947f9d 100644
--- a/src/lib/butler.h
+++ b/src/lib/butler.h
@@ -27,6 +27,7 @@
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include <boost/signals2.hpp>
+#include <boost/asio.hpp>
class Film;
class Player;
@@ -50,6 +51,7 @@ private:
void audio (boost::shared_ptr<AudioBuffers> audio);
void player_changed ();
bool should_run () const;
+ void prepare (boost::weak_ptr<PlayerVideo> video) const;
boost::weak_ptr<const Film> _film;
boost::shared_ptr<Player> _player;
@@ -58,6 +60,10 @@ private:
VideoRingBuffers _video;
AudioRingBuffers _audio;
+ boost::thread_group _prepare_pool;
+ boost::asio::io_service _prepare_service;
+ boost::shared_ptr<boost::asio::io_service::work> _prepare_work;
+
/** mutex to protect _pending_seek_position, _pending_seek_acurate, _finished, _died, _stop_thread */
boost::mutex _mutex;
boost::condition _summon;
diff --git a/src/lib/image_proxy.h b/src/lib/image_proxy.h
index d7bd7b0e6..f9a06d1b7 100644
--- a/src/lib/image_proxy.h
+++ b/src/lib/image_proxy.h
@@ -74,6 +74,10 @@ public:
virtual void send_binary (boost::shared_ptr<Socket>) const = 0;
/** @return true if our image is definitely the same as another, false if it is probably not */
virtual bool same (boost::shared_ptr<const ImageProxy>) const = 0;
+ /** Do any useful work that would speed up a subsequent call to ::image().
+ * This method may be called in a different thread to image().
+ */
+ virtual void prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const {}
virtual AVPixelFormat pixel_format () const = 0;
};
diff --git a/src/lib/j2k_image_proxy.cc b/src/lib/j2k_image_proxy.cc
index f7e6dd5f4..9e68951e9 100644
--- a/src/lib/j2k_image_proxy.cc
+++ b/src/lib/j2k_image_proxy.cc
@@ -93,35 +93,45 @@ J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
socket->read (_data.data().get (), _data.size ());
}
-shared_ptr<Image>
-J2KImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size> target_size) const
+void
+J2KImageProxy::prepare (optional<dcp::Size> target_size) const
{
- if (!_decompressed || target_size != _target_size) {
- int reduce = 0;
+ boost::mutex::scoped_lock lm (_mutex);
- while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
- ++reduce;
- }
+ if (_decompressed && target_size == _target_size) {
+ return;
+ }
- --reduce;
- reduce = max (0, reduce);
- _decompressed = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), reduce);
+ int reduce = 0;
- if (_decompressed->precision(0) < 12) {
- int const shift = 12 - _decompressed->precision (0);
- for (int c = 0; c < 3; ++c) {
- int* p = _decompressed->data (c);
- for (int y = 0; y < _decompressed->size().height; ++y) {
- for (int x = 0; x < _decompressed->size().width; ++x) {
- *p++ <<= shift;
- }
+ while (target_size && (_size.width / pow(2, reduce)) > target_size->width && (_size.height / pow(2, reduce)) > target_size->height) {
+ ++reduce;
+ }
+
+ --reduce;
+ reduce = max (0, reduce);
+ _decompressed = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), reduce);
+
+ if (_decompressed->precision(0) < 12) {
+ int const shift = 12 - _decompressed->precision (0);
+ for (int c = 0; c < 3; ++c) {
+ int* p = _decompressed->data (c);
+ for (int y = 0; y < _decompressed->size().height; ++y) {
+ for (int x = 0; x < _decompressed->size().width; ++x) {
+ *p++ <<= shift;
}
}
}
-
- _target_size = target_size;
}
+ _target_size = target_size;
+}
+
+shared_ptr<Image>
+J2KImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size> target_size) const
+{
+ prepare (target_size);
+
shared_ptr<Image> image (new Image (_pixel_format, _decompressed->size(), true));
/* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
diff --git a/src/lib/j2k_image_proxy.h b/src/lib/j2k_image_proxy.h
index 41d68588f..3133aac20 100644
--- a/src/lib/j2k_image_proxy.h
+++ b/src/lib/j2k_image_proxy.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2015 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2015-2017 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
@@ -21,6 +21,7 @@
#include "image_proxy.h"
#include <dcp/util.h>
#include <dcp/data.h>
+#include <boost/thread/mutex.hpp>
namespace dcp {
class MonoPictureFrame;
@@ -44,6 +45,7 @@ public:
void send_binary (boost::shared_ptr<Socket>) const;
/** @return true if our image is definitely the same as another, false if it is probably not */
bool same (boost::shared_ptr<const ImageProxy>) const;
+ void prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const;
AVPixelFormat pixel_format () const {
return _pixel_format;
}
@@ -68,4 +70,5 @@ private:
mutable boost::shared_ptr<dcp::OpenJPEGImage> _decompressed;
mutable boost::optional<dcp::Size> _target_size;
AVPixelFormat _pixel_format;
+ mutable boost::mutex _mutex;
};
diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc
index 6f0977e62..14291fc35 100644
--- a/src/lib/player_video.cc
+++ b/src/lib/player_video.cc
@@ -246,3 +246,9 @@ PlayerVideo::keep_xyz_or_rgb (AVPixelFormat p)
{
return p == AV_PIX_FMT_XYZ12LE ? AV_PIX_FMT_XYZ12LE : AV_PIX_FMT_RGB48LE;
}
+
+void
+PlayerVideo::prepare ()
+{
+ _in->prepare (_inter_size);
+}
diff --git a/src/lib/player_video.h b/src/lib/player_video.h
index 5017d0e3c..040145559 100644
--- a/src/lib/player_video.h
+++ b/src/lib/player_video.h
@@ -57,6 +57,7 @@ public:
void set_subtitle (PositionImage);
+ void prepare ();
boost::shared_ptr<Image> image (dcp::NoteHandler note, boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
static AVPixelFormat always_rgb (AVPixelFormat);