summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2020-08-12 01:12:31 +0200
committerCarl Hetherington <cth@carlh.net>2020-09-13 20:22:44 +0200
commitf5f14a6422ddd68a52dd14686c1bd49159dbaa74 (patch)
tree69b5ca05de568ae133d221484af7c463d68b903d
parentfba17cbd0be154e7e89b7fe9a1d5a68b4a5ff279 (diff)
wip: hacks which at least get GPU-decoded image on screen
-rw-r--r--src/lib/butler.cc53
-rw-r--r--src/lib/butler.h10
-rw-r--r--src/lib/cpu_player_video_preparer.cc58
-rw-r--r--src/lib/cpu_player_video_preparer.h33
-rw-r--r--src/lib/fastvideo.cc2
-rw-r--r--src/lib/fastvideo_player_video_preparer.cc221
-rw-r--r--src/lib/fastvideo_player_video_preparer.h49
-rw-r--r--src/lib/j2k_image_proxy.cc30
-rw-r--r--src/lib/j2k_image_proxy.h5
-rw-r--r--src/lib/player_video.h5
-rw-r--r--src/lib/player_video_preparer.h17
-rw-r--r--src/lib/wscript3
-rw-r--r--test/fastvideo_test.cc59
13 files changed, 479 insertions, 66 deletions
diff --git a/src/lib/butler.cc b/src/lib/butler.cc
index 39da0bd5c..6b730236d 100644
--- a/src/lib/butler.cc
+++ b/src/lib/butler.cc
@@ -26,6 +26,8 @@
#include "cross.h"
#include "compose.hpp"
#include "exceptions.h"
+#include "cpu_player_video_preparer.h"
+#include "fastvideo_player_video_preparer.h"
#include <boost/weak_ptr.hpp>
#include <boost/shared_ptr.hpp>
@@ -44,9 +46,9 @@ using namespace boost::placeholders;
#endif
/** Minimum video readahead in frames */
-#define MINIMUM_VIDEO_READAHEAD 10
+#define MINIMUM_VIDEO_READAHEAD 64
/** Maximum video readahead in frames; should never be exceeded (by much) unless there are bugs in Player */
-#define MAXIMUM_VIDEO_READAHEAD 48
+#define MAXIMUM_VIDEO_READAHEAD 128
/** Minimum audio readahead in frames */
#define MINIMUM_AUDIO_READAHEAD (48000 * MINIMUM_VIDEO_READAHEAD / 24)
/** Maximum audio readahead in frames; should never be exceeded (by much) unless there are bugs in Player */
@@ -66,7 +68,6 @@ Butler::Butler (
bool fast
)
: _player (player)
- , _prepare_work (new boost::asio::io_service::work (_prepare_service))
, _pending_seek_accurate (false)
, _suspended (0)
, _finished (false)
@@ -79,6 +80,8 @@ Butler::Butler (
, _aligned (aligned)
, _fast (fast)
{
+ _preparer.reset (new FastvideoPlayerVideoPreparer(pixel_format, aligned, fast));
+
_player_video_connection = _player->Video.connect (bind (&Butler::video, this, _1, _2));
_player_audio_connection = _player->Audio.connect (bind (&Butler::audio, this, _1, _2, _3));
_player_text_connection = _player->Text.connect (bind (&Butler::text, this, _1, _2, _3, _4));
@@ -90,16 +93,6 @@ Butler::Butler (
#ifdef DCPOMATIC_LINUX
pthread_setname_np (_thread.native_handle(), "butler");
#endif
-
- /* Create some threads to do work on the PlayerVideos we are creating; at present this is used to
- multi-thread JPEG2000 decoding.
- */
-
- LOG_TIMING("start-prepare-threads %1", boost::thread::hardware_concurrency() * 2);
-
- for (size_t i = 0; i < boost::thread::hardware_concurrency() * 2; ++i) {
- _prepare_pool.create_thread (bind (&boost::asio::io_service::run, &_prepare_service));
- }
}
Butler::~Butler ()
@@ -111,14 +104,12 @@ Butler::~Butler ()
_stop_thread = true;
}
- _prepare_work.reset ();
- _prepare_pool.join_all ();
- _prepare_service.stop ();
-
_thread.interrupt ();
try {
_thread.join ();
} catch (...) {}
+
+ _preparer.reset ();
}
/** Caller must hold a lock on _mutex */
@@ -298,32 +289,6 @@ Butler::seek_unlocked (DCPTime position, bool accurate)
}
void
-Butler::prepare (weak_ptr<PlayerVideo> weak_video)
-try
-{
- shared_ptr<PlayerVideo> video = weak_video.lock ();
- /* If the weak_ptr cannot be locked the video obviously no longer requires any work */
- if (video) {
- LOG_TIMING("start-prepare in %1", thread_id());
- video->prepare (_pixel_format, _aligned, _fast);
- LOG_TIMING("finish-prepare in %1", thread_id());
- }
-}
-catch (std::exception& e)
-{
- store_current ();
- boost::mutex::scoped_lock lm (_mutex);
- _died = true;
- _died_message = e.what ();
-}
-catch (...)
-{
- store_current ();
- boost::mutex::scoped_lock lm (_mutex);
- _died = true;
-}
-
-void
Butler::video (shared_ptr<PlayerVideo> video, DCPTime time)
{
boost::mutex::scoped_lock lm (_mutex);
@@ -333,7 +298,7 @@ Butler::video (shared_ptr<PlayerVideo> video, DCPTime time)
return;
}
- _prepare_service.post (bind (&Butler::prepare, this, weak_ptr<PlayerVideo>(video)));
+ _preparer->request (video);
_video.put (video, time);
}
diff --git a/src/lib/butler.h b/src/lib/butler.h
index e13843c90..f389ebc88 100644
--- a/src/lib/butler.h
+++ b/src/lib/butler.h
@@ -1,5 +1,5 @@
/*
- Copyright (C) 2016-2017 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2016-2020 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
@@ -28,10 +28,10 @@
#include <boost/thread.hpp>
#include <boost/thread/condition.hpp>
#include <boost/signals2.hpp>
-#include <boost/asio.hpp>
class Player;
class PlayerVideo;
+class PlayerVideoPreparer;
class Butler : public ExceptionStore, public boost::noncopyable
{
@@ -93,10 +93,6 @@ private:
AudioRingBuffers _audio;
TextRingBuffers _closed_caption;
- 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_accurate, _finished, _died, _stop_thread */
boost::mutex _mutex;
boost::condition _summon;
@@ -123,6 +119,8 @@ private:
*/
boost::optional<dcpomatic::DCPTime> _awaiting;
+ boost::shared_ptr<PlayerVideoPreparer> _preparer;
+
boost::signals2::scoped_connection _player_video_connection;
boost::signals2::scoped_connection _player_audio_connection;
boost::signals2::scoped_connection _player_text_connection;
diff --git a/src/lib/cpu_player_video_preparer.cc b/src/lib/cpu_player_video_preparer.cc
new file mode 100644
index 000000000..80cb5bc91
--- /dev/null
+++ b/src/lib/cpu_player_video_preparer.cc
@@ -0,0 +1,58 @@
+#include "cpu_player_video_preparer.h"
+#include "cross.h"
+#include "dcpomatic_log.h"
+#include "player_video.h"
+#include <boost/thread.hpp>
+
+
+using boost::bind;
+using boost::shared_ptr;
+using boost::weak_ptr;
+
+
+CPUPlayerVideoPreparer::CPUPlayerVideoPreparer (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast)
+ : _work (new boost::asio::io_service::work(_service))
+ , _pixel_format (pixel_format)
+ , _aligned (aligned)
+ , _fast (fast)
+{
+ LOG_TIMING("start-prepare-threads %1", boost::thread::hardware_concurrency() * 2);
+
+ for (size_t i = 0; i < boost::thread::hardware_concurrency() * 2; ++i) {
+ _pool.create_thread (bind (&boost::asio::io_service::run, &_service));
+ }
+}
+
+
+CPUPlayerVideoPreparer::~CPUPlayerVideoPreparer ()
+{
+ _work.reset ();
+ _pool.join_all ();
+ _service.stop ();
+}
+
+
+void
+CPUPlayerVideoPreparer::prepare (weak_ptr<PlayerVideo> weak_video)
+try
+{
+ shared_ptr<PlayerVideo> video = weak_video.lock ();
+ /* If the weak_ptr cannot be locked the video obviously no longer requires any work */
+ if (video) {
+ LOG_TIMING("start-prepare in %1", thread_id());
+ video->prepare (_pixel_format, _aligned, _fast);
+ LOG_TIMING("finish-prepare in %1", thread_id());
+ }
+}
+catch (...)
+{
+ store_current ();
+}
+
+
+void
+CPUPlayerVideoPreparer::request (shared_ptr<PlayerVideo> pv)
+{
+ _service.post (bind(&CPUPlayerVideoPreparer::prepare, this, weak_ptr<PlayerVideo>(pv)));
+}
+
diff --git a/src/lib/cpu_player_video_preparer.h b/src/lib/cpu_player_video_preparer.h
new file mode 100644
index 000000000..7cb46dc10
--- /dev/null
+++ b/src/lib/cpu_player_video_preparer.h
@@ -0,0 +1,33 @@
+#ifndef DCPOMATIC_CPU_PLAYER_VIDEO_PREPARER_H
+#define DCPOMATIC_CPU_PLAYER_VIDEO_PREPARER_H
+
+#include "player_video_preparer.h"
+extern "C" {
+#include <libavutil/pixfmt.h>
+}
+#include <boost/asio.hpp>
+#include <boost/thread.hpp>
+#include <boost/weak_ptr.hpp>
+
+
+class CPUPlayerVideoPreparer : public PlayerVideoPreparer
+{
+public:
+ CPUPlayerVideoPreparer (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast);
+ ~CPUPlayerVideoPreparer ();
+
+ void request (boost::shared_ptr<PlayerVideo> pv);
+
+private:
+ void prepare (boost::weak_ptr<PlayerVideo> pv);
+
+ boost::thread_group _pool;
+ boost::asio::io_service _service;
+ boost::shared_ptr<boost::asio::io_service::work> _work;
+
+ boost::function<AVPixelFormat (AVPixelFormat)> _pixel_format;
+ bool _aligned;
+ bool _fast;
+};
+
+#endif
diff --git a/src/lib/fastvideo.cc b/src/lib/fastvideo.cc
index 2b236bd9b..8e67f4b26 100644
--- a/src/lib/fastvideo.cc
+++ b/src/lib/fastvideo.cc
@@ -129,7 +129,7 @@ fastvideo_decompress_j2k (dcp::Data data, int reduce)
&decoder,
&parameters,
FAST_RGB8, info.width, info.height,
- max_batch_si\e,
+ max_batch_size,
&buffer
);
if (r != FAST_OK) {
diff --git a/src/lib/fastvideo_player_video_preparer.cc b/src/lib/fastvideo_player_video_preparer.cc
new file mode 100644
index 000000000..9d694df14
--- /dev/null
+++ b/src/lib/fastvideo_player_video_preparer.cc
@@ -0,0 +1,221 @@
+#include "dcpomatic_assert.h"
+#include "exceptions.h"
+#include "image.h"
+#include "image_proxy.h"
+#include "fastvideo_player_video_preparer.h"
+#include "j2k_image_proxy.h"
+#include "player_video.h"
+#include <fastvideo_decoder_j2k.h>
+#include <fastvideo_sdk.h>
+#include <boost/bind.hpp>
+
+
+using boost::bind;
+using boost::const_pointer_cast;
+using boost::dynamic_pointer_cast;
+using boost::shared_ptr;
+using boost::weak_ptr;
+
+
+FastvideoPlayerVideoPreparer::FastvideoPlayerVideoPreparer (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast)
+ : _stop_thread (false)
+ , _decoder (0)
+ , _setup_done (false)
+ , _cpu (pixel_format, aligned, fast)
+{
+ fastSdkParametersHandle_t sdk_parameters;
+ fastStatus_t r = fastGetSdkParametersHandle(&sdk_parameters);
+ if (r != FAST_OK) {
+ throw FastvideoError ("GetSdkParametersHandle", r);
+ }
+ r = fastDecoderJ2kLibraryInit(sdk_parameters);
+ if (r != FAST_OK) {
+ throw FastvideoError ("DecoderJ2kLibraryInit", r);
+ }
+
+ _thread = boost::thread (bind(&FastvideoPlayerVideoPreparer::thread, this));
+}
+
+
+FastvideoPlayerVideoPreparer::~FastvideoPlayerVideoPreparer ()
+{
+ _stop_thread = true;
+ _work.notify_all ();
+ try {
+ _thread.join ();
+ } catch (...) {}
+
+ if (_setup_done) {
+ fastDecoderJ2kDestroy(_decoder);
+ fastExportToHostDestroy(_adapter);
+ }
+}
+
+
+void
+FastvideoPlayerVideoPreparer::request (shared_ptr<PlayerVideo> pv)
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ _queue.push (weak_ptr<PlayerVideo>(pv));
+ _work.notify_all ();
+}
+
+
+void
+FastvideoPlayerVideoPreparer::thread ()
+{
+ while (true) {
+ boost::mutex::scoped_lock lm (_mutex);
+ while (!_stop_thread && _queue.empty()) {
+ _work.wait (lm);
+ }
+
+ if (_stop_thread) {
+ return;
+ }
+
+ weak_ptr<PlayerVideo> weak = _queue.front ();
+ _queue.pop ();
+
+ lm.unlock ();
+ shared_ptr<PlayerVideo> pv = weak.lock ();
+ if (pv) {
+ /* We're going to "prepare" just the image proxy here (if it interests us i.e. if it's J2K,
+ * then hand over the rest of the work to a CPUPlayerVideoPreparer.
+ */
+ shared_ptr<const ImageProxy> ip = pv->image_proxy ();
+ shared_ptr<const J2KImageProxy> jp = dynamic_pointer_cast<const J2KImageProxy> (ip);
+ if (jp) {
+ send_to_gpu (pv, jp);
+ } else {
+ _cpu.request (pv);
+ }
+ }
+ }
+}
+
+
+void
+FastvideoPlayerVideoPreparer::send_to_gpu (shared_ptr<PlayerVideo> pv, shared_ptr<const J2KImageProxy> proxy)
+{
+ if (!_setup_done) {
+ std::cout << "setup.\n";
+ setup (proxy->j2k());
+ _setup_done = true;
+ }
+
+ std::cout << "add to batch.\n";
+ fastDecoderJ2kAddImageToBatch(_decoder, proxy->j2k().data().get(), proxy->j2k().size());
+ _batch.push_back (pv);
+
+ int free_slots = 0;
+ fastDecoderJ2kFreeSlotsInBatch(_decoder, &free_slots);
+ std::cout << free_slots << " left in batch.\n";
+ if (free_slots == 0) {
+ /* Do some decoding */
+ transform_and_extract ();
+ }
+
+ /* XXX: maybe should hoover up any left-overs at some point */
+}
+
+
+void
+FastvideoPlayerVideoPreparer::transform_and_extract ()
+{
+ fastDecoderJ2kReport_t report;
+ fastStatus_t r = fastDecoderJ2kTransformBatch(_decoder, &report);
+ if (r != FAST_OK) {
+ throw FastvideoError ("DecoderJ2kTransformBatch", r);
+ }
+
+ size_t index = 0;
+ int images_left;
+ do {
+ DCPOMATIC_ASSERT (index <= _batch.size());
+ shared_ptr<PlayerVideo> pv = _batch[index].lock();
+ if (pv) {
+ std::cout << "got a pv for batch number " << index << "\n";
+ /* XXX: this should be memlocked or whatever fastMalloc does */
+ shared_ptr<J2KImageProxy> proxy = const_pointer_cast<J2KImageProxy>(dynamic_pointer_cast<const J2KImageProxy>(pv->image_proxy()));
+ DCPOMATIC_ASSERT (proxy);
+ dcp::Size const size = proxy->size();
+ shared_ptr<dcpomatic::Image> image(new dcpomatic::Image(AV_PIX_FMT_RGB24, size, true));
+ fastExportParameters_t export_parameters;
+ export_parameters.convert = FAST_CONVERT_NONE;
+ fastStatus_t r = fastExportToHostCopy(_adapter, image->data()[0], size.width, image->stride()[0], size.height, &export_parameters);
+ if (r != FAST_OK) {
+ throw FastvideoError ("ExportToHostCopy", r);
+ }
+
+ std::cout << "setting the proxy's image.\n";
+ proxy->set_image (image);
+
+ _cpu.request (pv);
+ }
+
+ fastStatus_t r = fastDecoderJ2kGetNextDecodedImage(_decoder, &report, &images_left);
+ std::cout << images_left << " images left.\n";
+ if (r != FAST_OK) {
+ throw FastvideoError ("DecoderJ2kGetNextDecodedImage", r);
+ }
+
+ ++index;
+
+ } while (images_left);
+
+ _batch.clear ();
+}
+
+
+void
+FastvideoPlayerVideoPreparer::setup (dcp::Data sample)
+{
+ fastJ2kImageInfo_t info;
+ fastStatus_t r = fastDecoderJ2kPredecode(&info, sample.data().get(), sample.size());
+ if (r != FAST_OK) {
+ throw FastvideoError ("DecoderJ2kPredecode", r);
+ }
+
+ fastDecoderJ2kStaticParameters_t parameters;
+ memset(&parameters, 0, sizeof(fastDecoderJ2kStaticParameters_t));
+
+ parameters.ResolutionLevels = 0;
+ parameters.verboseLevel = 1;
+ parameters.enableROI = 0;
+
+ parameters.maxTileWidth = info.width;
+ parameters.maxTileHeight = info.height;
+
+ parameters.windowX0 = 0;
+ parameters.windowY0 = 0;
+ parameters.windowWidth = info.width;
+ parameters.windowHeight = info.height;
+
+ parameters.truncationLength = 0;
+ parameters.truncationMode = 0;
+ parameters.truncationRate = 0;
+
+ parameters.DecodePasses = 0;
+ parameters.imageInfo = &info;
+ parameters.maxStreamSize = max_stream_size;
+
+ r = fastDecoderJ2kCreate(
+ &_decoder,
+ &parameters,
+ FAST_RGB8, info.width, info.height,
+ max_batch_size,
+ &_buffer
+ );
+
+ if (r != FAST_OK) {
+ fastTraceClose ();
+ throw FastvideoError ("J2kCreate", r);
+ }
+
+ fastSurfaceFormat_t surface_format = FAST_RGB8;
+ r = fastExportToHostCreate(&_adapter, &surface_format, _buffer);
+ if (r != FAST_OK) {
+ throw FastvideoError ("ExportToHostCreate");
+ }
+}
diff --git a/src/lib/fastvideo_player_video_preparer.h b/src/lib/fastvideo_player_video_preparer.h
new file mode 100644
index 000000000..384d8edeb
--- /dev/null
+++ b/src/lib/fastvideo_player_video_preparer.h
@@ -0,0 +1,49 @@
+#include "cpu_player_video_preparer.h"
+#include "player_video_preparer.h"
+#include <dcp/data.h>
+#include <fastvideo_decoder_j2k.h>
+#include <fastvideo_sdk.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/weak_ptr.hpp>
+#include <queue>
+
+
+class J2KImageProxy;
+
+
+class FastvideoPlayerVideoPreparer : public PlayerVideoPreparer
+{
+public:
+ FastvideoPlayerVideoPreparer (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast);
+ ~FastvideoPlayerVideoPreparer ();
+
+ void request (boost::shared_ptr<PlayerVideo> pv);
+
+private:
+ void thread ();
+ void setup (dcp::Data sample);
+ void send_to_gpu (boost::shared_ptr<PlayerVideo> pv, boost::shared_ptr<const J2KImageProxy> proxy);
+ void transform_and_extract ();
+
+ std::queue<boost::weak_ptr<PlayerVideo> > _queue;
+ boost::mutex _mutex;
+ boost::condition _work;
+
+ boost::atomic<bool> _stop_thread;
+ boost::thread _thread;
+
+ fastDecoderJ2kHandle_t _decoder;
+ fastDeviceSurfaceBufferHandle_t _buffer;
+ fastExportToHostHandle_t _adapter;
+ bool _setup_done;
+
+ std::vector<boost::weak_ptr<PlayerVideo> > _batch;
+
+ CPUPlayerVideoPreparer _cpu;
+
+ static const int max_batch_size = 16;
+ static const int max_stream_size = 1302083;
+};
+
diff --git a/src/lib/j2k_image_proxy.cc b/src/lib/j2k_image_proxy.cc
index c8a561d25..18d597e50 100644
--- a/src/lib/j2k_image_proxy.cc
+++ b/src/lib/j2k_image_proxy.cc
@@ -127,9 +127,23 @@ J2KImageProxy::prepare (optional<dcp::Size> target_size) const
{
boost::mutex::scoped_lock lm (_mutex);
- if (_image && target_size == _target_size) {
- DCPOMATIC_ASSERT (_reduce);
- return *_reduce;
+ if (target_size) {
+ std::cout << "target_size is " << target_size->width << " " << target_size->height << "\n";
+ } else {
+ std::cout << "no target size.\n";
+ }
+
+ if (_target_size) {
+ std::cout << "_target_size is " << _target_size->width << " " << _target_size->height << "\n";
+ } else {
+ std::cout << "no _target size.\n";
+ }
+
+ if (_image) {// && target_size == _target_size) {
+ std::cout << "hit.\n";
+ return 0;
+ //DCPOMATIC_ASSERT (_reduce);
+ //return *_reduce;
}
int reduce = 0;
@@ -146,6 +160,7 @@ J2KImageProxy::prepare (optional<dcp::Size> target_size) const
}
try {
+ std::cout << "cpu decompress.\n";
shared_ptr<dcp::OpenJPEGImage> decompressed = dcp::decompress_j2k (_data, reduce);
_image.reset (new Image (_pixel_format, decompressed->size(), true));
@@ -247,3 +262,12 @@ J2KImageProxy::memory_used () const
}
return m;
}
+
+
+void
+J2KImageProxy::set_image (shared_ptr<dcpomatic::Image> image)
+{
+ // XXX need _target_size to be set up
+ boost::mutex::scoped_lock lm (_mutex);
+ _image = image;
+}
diff --git a/src/lib/j2k_image_proxy.h b/src/lib/j2k_image_proxy.h
index 78e0cbd9b..e9cb69f7a 100644
--- a/src/lib/j2k_image_proxy.h
+++ b/src/lib/j2k_image_proxy.h
@@ -58,7 +58,12 @@ public:
void write_to_socket (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;
+
+ /* these are kind of related; one prepares by calculating the image in-place,
+ * one by being given the image; they could be renamed, perhaps.
+ */
int prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const;
+ void set_image (boost::shared_ptr<dcpomatic::Image> image);
dcp::Data j2k () const {
return _data;
diff --git a/src/lib/player_video.h b/src/lib/player_video.h
index 9fd313a15..464bb4894 100644
--- a/src/lib/player_video.h
+++ b/src/lib/player_video.h
@@ -82,6 +82,7 @@ public:
bool reset_metadata (boost::shared_ptr<const Film> film, dcp::Size video_container_size, dcp::Size film_frame_size);
bool has_j2k () const;
+ /* XXX: who's using this?! */
dcp::Data j2k () const;
Eyes eyes () const {
@@ -116,6 +117,10 @@ public:
return _error;
}
+ boost::shared_ptr<const ImageProxy> image_proxy () const {
+ return _in;
+ }
+
private:
void make_image (boost::function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const;
diff --git a/src/lib/player_video_preparer.h b/src/lib/player_video_preparer.h
index c16463477..821d9794c 100644
--- a/src/lib/player_video_preparer.h
+++ b/src/lib/player_video_preparer.h
@@ -1,14 +1,17 @@
-class PlayerVideoPreparer
-{
-public:
- virtual void request (boost::shared_ptr<PlayerVideo> pv) = 0;
-};
+#ifndef DCPOMATIC_PLAYER_VIDEO_PREPARER_H
+#define DCPOMATIC_PLAYER_VIDEO_PREPARER_H
+
+#include "exception_store.h"
+#include <boost/shared_ptr.hpp>
+class PlayerVideo;
-class CPUPlayerVideoPreparer : public PlayerVideoPreparer
+class PlayerVideoPreparer : public ExceptionStore
{
public:
- void request (boost::shared_ptr<PlayerVideo> pv);
+ virtual ~PlayerVideoPreparer () {}
+ virtual void request (boost::shared_ptr<PlayerVideo> pv) = 0;
};
+#endif
diff --git a/src/lib/wscript b/src/lib/wscript
index bbf72a038..3b14f8738 100644
--- a/src/lib/wscript
+++ b/src/lib/wscript
@@ -56,6 +56,7 @@ sources = """
content.cc
content_factory.cc
copy_dcp_details_to_film.cc
+ cpu_player_video_preparer.cc
create_cli.cc
cross_common.cc
crypto.cc
@@ -229,7 +230,7 @@ def build(bld):
if bld.env.STATIC_DCPOMATIC:
obj.uselib += ' XMLPP'
if bld.env.ENABLE_FASTVIDEO:
- obj.source += ' fastvideo.cc'
+ obj.source += ' fastvideo_player_video_preparer.cc'
obj.target = 'dcpomatic2'
diff --git a/test/fastvideo_test.cc b/test/fastvideo_test.cc
index 65ea1bb7e..ba78900cb 100644
--- a/test/fastvideo_test.cc
+++ b/test/fastvideo_test.cc
@@ -1,10 +1,61 @@
-#include "lib/fastvideo.h"
+#include "lib/colour_conversion.h"
+#include "lib/content.h"
+#include "lib/cross.h"
+#include "lib/image.h"
+#include "lib/fastvideo_player_video_preparer.h"
+#include "lib/j2k_image_proxy.h"
+#include "lib/player_video.h"
+#include "lib/types.h"
#include "test.h"
+#include <boost/optional.hpp>
+#include <boost/shared_ptr.hpp>
#include <boost/test/unit_test.hpp>
+#include <vector>
-BOOST_AUTO_TEST_CASE (fastvideo_decoder_test)
+
+using std::vector;
+using boost::optional;
+using boost::shared_ptr;
+using boost::weak_ptr;
+
+
+static
+AVPixelFormat
+pixel_format (AVPixelFormat)
{
- dcp::Data data ("test/data/sizing_card_flat.j2k");
- fastvideo_decompress_j2k (data, 0);
+ return AV_PIX_FMT_RGB24;
+}
+
+
+BOOST_AUTO_TEST_CASE (fastvideo_preparer_setup_teardown_test)
+{
+ FastvideoPlayerVideoPreparer preparer (&pixel_format, true, true);
+}
+
+
+BOOST_AUTO_TEST_CASE (fastvideo_preparer_simple_decode_test)
+{
+ FastvideoPlayerVideoPreparer preparer (&pixel_format, true, true);
+
+ shared_ptr<J2KImageProxy> proxy(new J2KImageProxy("test/data/sizing_card_flat.j2k", dcp::Size(1998, 1080), AV_PIX_FMT_XYZ12));
+
+ vector<shared_ptr<PlayerVideo> > videos;
+
+ for (int i = 0; i < 16; ++i) {
+ shared_ptr<PlayerVideo> pv(
+ new PlayerVideo(
+ proxy, Crop(), optional<double>(), dcp::Size(1998, 1080), dcp::Size(1998, 1080),
+ EYES_BOTH, PART_WHOLE, optional<ColourConversion>(), VIDEO_RANGE_FULL, weak_ptr<Content>(), optional<Frame>(), false
+ )
+ );
+
+ preparer.request (pv);
+ videos.push_back (pv);
+ }
+
+ dcpomatic_sleep_seconds (5);
+
+ std::cout << "____-> get an image.\n";
+ videos[0]->image_proxy()->image().image->convert_pixel_format(dcp::YUV_TO_RGB_REC709, AV_PIX_FMT_RGBA, true, true)->as_png().write("/home/carl/foo.png");
}