wip: hacks which at least get GPU-decoded image on screen
authorCarl Hetherington <cth@carlh.net>
Tue, 11 Aug 2020 23:12:31 +0000 (01:12 +0200)
committerCarl Hetherington <cth@carlh.net>
Sun, 13 Sep 2020 18:22:44 +0000 (20:22 +0200)
13 files changed:
src/lib/butler.cc
src/lib/butler.h
src/lib/cpu_player_video_preparer.cc [new file with mode: 0644]
src/lib/cpu_player_video_preparer.h [new file with mode: 0644]
src/lib/fastvideo.cc
src/lib/fastvideo_player_video_preparer.cc [new file with mode: 0644]
src/lib/fastvideo_player_video_preparer.h [new file with mode: 0644]
src/lib/j2k_image_proxy.cc
src/lib/j2k_image_proxy.h
src/lib/player_video.h
src/lib/player_video_preparer.h
src/lib/wscript
test/fastvideo_test.cc

index 39da0bd5c10434a04449eaf782098cff29cfe13e..6b730236dc06418df68ae4d462f1fb124dd9ca9e 100644 (file)
@@ -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 */
@@ -297,32 +288,6 @@ Butler::seek_unlocked (DCPTime position, bool accurate)
        _summon.notify_all ();
 }
 
-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)
 {
@@ -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);
 }
index e13843c90e9c14833704cde408e65de072e8e5c9..f389ebc88d7b61de0575faaf56a73f124a18c8f9 100644 (file)
@@ -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.
 
 #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 (file)
index 0000000..80cb5bc
--- /dev/null
@@ -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 (file)
index 0000000..7cb46dc
--- /dev/null
@@ -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
index 2b236bd9bf999aab861e635238ff35f985a1c447..8e67f4b26af02b9633c0e25e98d6646d852816b7 100644 (file)
@@ -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 (file)
index 0000000..9d694df
--- /dev/null
@@ -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 (file)
index 0000000..384d8ed
--- /dev/null
@@ -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;
+};
+
index c8a561d252c43457a1f79613e604de8fbbac9372..18d597e50f9fd839e5544a23acc0dd6a9c15e7ff 100644 (file)
@@ -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;
+}
index 78e0cbd9bafcd0868040c5c3b2945ba1168eec5a..e9cb69f7a1d0147c28bbf9d67c7282df3eba0a45 100644 (file)
@@ -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;
index 9fd313a15eb03c51911ada209487fc4d57a0af28..464bb4894c9e809aa1e967f804bd8299284ffcd5 100644 (file)
@@ -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;
 
index c164634771b0eb889a72be30d2535fb59f28f533..821d9794c2c1ec9feb521b0adb9ceacf4e5ad1ce 100644 (file)
@@ -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
 
index bbf72a03840a9a7331e6c21c356c2cb5606f1359..3b14f8738312d7df6fc3aefbc7dfc65b1e1525d9 100644 (file)
@@ -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'
 
index 65ea1bb7e6d22d60767aee28b96bb41766fb574b..ba78900cb10fed92a2b21de4c4975ec780a57fa2 100644 (file)
@@ -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");
 }