Pass around JPEG2000 data as a shared_ptr and hence avoid a copy
authorCarl Hetherington <cth@carlh.net>
Sun, 1 Nov 2020 22:51:19 +0000 (23:51 +0100)
committerCarl Hetherington <cth@carlh.net>
Mon, 2 Nov 2020 22:10:04 +0000 (23:10 +0100)
of 4MB of data for every JPEG2000 frame we decode.

src/lib/j2k_encoder.cc
src/lib/j2k_image_proxy.cc
src/lib/j2k_image_proxy.h
src/lib/player_video.cc
src/lib/player_video.h
src/lib/reel_writer.cc
src/lib/reel_writer.h
src/lib/writer.cc
src/lib/writer.h

index 64cd3814784306877628794326fdcec462cd88af..18bb27645ea72e209d0054323dd235570fe4bd45 100644 (file)
@@ -127,7 +127,7 @@ J2KEncoder::end ()
                LOG_GENERAL (N_("Encode left-over frame %1"), (*i)->index ());
                try {
                        _writer->write (
-                               (*i)->encode_locally(),
+                               shared_ptr<dcp::Data>(new dcp::ArrayData((*i)->encode_locally())),
                                (*i)->index(),
                                (*i)->eyes()
                                );
@@ -296,12 +296,12 @@ try
 
                        lock.unlock ();
 
-                       optional<Data> encoded;
+                       shared_ptr<Data> encoded;
 
                        /* We need to encode this input */
                        if (server) {
                                try {
-                                       encoded = vf->encode_remotely (server.get ());
+                                       encoded.reset(new dcp::ArrayData(vf->encode_remotely(server.get())));
 
                                        if (remote_backoff > 0) {
                                                LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
@@ -324,7 +324,7 @@ try
                        } else {
                                try {
                                        LOG_TIMING ("start-local-encode thread=%1 frame=%2", thread_id(), vf->index());
-                                       encoded = vf->encode_locally ();
+                                       encoded.reset(new dcp::ArrayData(vf->encode_locally()));
                                        LOG_TIMING ("finish-local-encode thread=%1 frame=%2", thread_id(), vf->index());
                                } catch (std::exception& e) {
                                        /* This is very bad, so don't cope with it, just pass it on */
@@ -334,7 +334,7 @@ try
                        }
 
                        if (encoded) {
-                               _writer->write (encoded.get(), vf->index (), vf->eyes ());
+                               _writer->write (encoded, vf->index(), vf->eyes());
                                frame_done ();
                        } else {
                                lock.lock ();
index 08ebc343c26ce7cf8e9407582d215f80f2914e0b..064bbec7bbc3632c7f7366a941521356812ae64c 100644 (file)
@@ -46,12 +46,12 @@ using std::make_pair;
 using boost::shared_ptr;
 using boost::optional;
 using boost::dynamic_pointer_cast;
-using dcp::Data;
+using dcp::ArrayData;
 using dcp::raw_convert;
 
 /** Construct a J2KImageProxy from a JPEG2000 file */
 J2KImageProxy::J2KImageProxy (boost::filesystem::path path, dcp::Size size, AVPixelFormat pixel_format)
-       : _data (path)
+       : _data (new dcp::ArrayData(path))
        , _size (size)
        , _pixel_format (pixel_format)
        , _error (false)
@@ -60,13 +60,14 @@ J2KImageProxy::J2KImageProxy (boost::filesystem::path path, dcp::Size size, AVPi
        DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
 }
 
+
 J2KImageProxy::J2KImageProxy (
        shared_ptr<const dcp::MonoPictureFrame> frame,
        dcp::Size size,
        AVPixelFormat pixel_format,
        optional<int> forced_reduction
        )
-       : _data (frame->j2k_size ())
+       : _data (frame)
        , _size (size)
        , _pixel_format (pixel_format)
        , _forced_reduction (forced_reduction)
@@ -74,9 +75,9 @@ J2KImageProxy::J2KImageProxy (
 {
        /* ::image assumes 16bpp */
        DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
-       memcpy (_data.data().get(), frame->j2k_data(), _data.size ());
 }
 
+
 J2KImageProxy::J2KImageProxy (
        shared_ptr<const dcp::StereoPictureFrame> frame,
        dcp::Size size,
@@ -84,7 +85,8 @@ J2KImageProxy::J2KImageProxy (
        AVPixelFormat pixel_format,
        optional<int> forced_reduction
        )
-       : _size (size)
+       : _data (eye ? frame->left() : frame->right())
+       , _size (size)
        , _eye (eye)
        , _pixel_format (pixel_format)
        , _forced_reduction (forced_reduction)
@@ -92,18 +94,9 @@ J2KImageProxy::J2KImageProxy (
 {
        /* ::image assumes 16bpp */
        DCPOMATIC_ASSERT (_pixel_format == AV_PIX_FMT_RGB48 || _pixel_format == AV_PIX_FMT_XYZ12LE);
-       switch (eye) {
-       case dcp::EYE_LEFT:
-               _data = Data (frame->left_j2k_size ());
-               memcpy (_data.data().get(), frame->left_j2k_data(), _data.size ());
-               break;
-       case dcp::EYE_RIGHT:
-               _data = Data (frame->right_j2k_size ());
-               memcpy (_data.data().get(), frame->right_j2k_data(), _data.size ());
-               break;
-       }
 }
 
+
 J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
        : _error (false)
 {
@@ -111,13 +104,14 @@ J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
        if (xml->optional_number_child<int> ("Eye")) {
                _eye = static_cast<dcp::Eye> (xml->number_child<int> ("Eye"));
        }
-       _data = Data (xml->number_child<int> ("Size"));
+       shared_ptr<ArrayData> data(new ArrayData(xml->number_child<int>("Size")));
        /* This only matters when we are using J2KImageProxy for the preview, which
           will never use this constructor (which is only used for passing data to
           encode servers).  So we can put anything in here.  It's a bit of a hack.
        */
        _pixel_format = AV_PIX_FMT_XYZ12LE;
-       socket->read (_data.data().get (), _data.size ());
+       socket->read (data->data(), data->size());
+       _data = data;
 }
 
 int
@@ -144,7 +138,8 @@ J2KImageProxy::prepare (optional<dcp::Size> target_size) const
        }
 
        try {
-               shared_ptr<dcp::OpenJPEGImage> decompressed = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), reduce);
+               /* XXX: should check that potentially trashing _data here doesn't matter */
+               shared_ptr<dcp::OpenJPEGImage> decompressed = dcp::decompress_j2k (const_cast<uint8_t*>(_data->data()), _data->size(), reduce);
                _image.reset (new Image (_pixel_format, decompressed->size(), true));
 
                int const shift = 16 - decompressed->precision (0);
@@ -202,13 +197,13 @@ J2KImageProxy::add_metadata (xmlpp::Node* node) const
        if (_eye) {
                node->add_child("Eye")->add_child_text (raw_convert<string> (static_cast<int> (_eye.get ())));
        }
-       node->add_child("Size")->add_child_text (raw_convert<string> (_data.size ()));
+       node->add_child("Size")->add_child_text (raw_convert<string>(_data->size()));
 }
 
 void
 J2KImageProxy::write_to_socket (shared_ptr<Socket> socket) const
 {
-       socket->write (_data.data().get(), _data.size());
+       socket->write (_data->data(), _data->size());
 }
 
 bool
@@ -219,15 +214,11 @@ J2KImageProxy::same (shared_ptr<const ImageProxy> other) const
                return false;
        }
 
-       if (_data.size() != jp->_data.size()) {
-               return false;
-       }
-
-       return memcmp (_data.data().get(), jp->_data.data().get(), _data.size()) == 0;
+       return *_data == *jp->_data;
 }
 
-J2KImageProxy::J2KImageProxy (Data data, dcp::Size size, AVPixelFormat pixel_format)
-       : _data (data)
+J2KImageProxy::J2KImageProxy (ArrayData data, dcp::Size size, AVPixelFormat pixel_format)
+       : _data (new ArrayData(data))
        , _size (size)
        , _pixel_format (pixel_format)
 {
@@ -238,7 +229,7 @@ J2KImageProxy::J2KImageProxy (Data data, dcp::Size size, AVPixelFormat pixel_for
 size_t
 J2KImageProxy::memory_used () const
 {
-       size_t m = _data.size();
+       size_t m = _data->size();
        if (_image) {
                /* 3 components, 16-bits per pixel */
                m += 3 * 2 * _image->size().width * _image->size().height;
index 71bcffb2c50754460736700310ad0af8f002cb3b..3eccc213da41c348a73f431f7e5ac3f992b50698 100644 (file)
@@ -19,8 +19,8 @@
 */
 
 #include "image_proxy.h"
+#include <dcp/array_data.h>
 #include <dcp/util.h>
-#include <dcp/data.h>
 #include <boost/thread/mutex.hpp>
 
 namespace dcp {
@@ -60,7 +60,7 @@ public:
        bool same (boost::shared_ptr<const ImageProxy>) const;
        int prepare (boost::optional<dcp::Size> = boost::optional<dcp::Size>()) const;
 
-       dcp::Data j2k () const {
+       boost::shared_ptr<const dcp::Data> j2k () const {
                return _data;
        }
 
@@ -74,9 +74,9 @@ private:
        friend struct client_server_test_j2k;
 
        /* For tests */
-       J2KImageProxy (dcp::Data data, dcp::Size size, AVPixelFormat pixel_format);
+       J2KImageProxy (dcp::ArrayData data, dcp::Size size, AVPixelFormat pixel_format);
 
-       dcp::Data _data;
+       boost::shared_ptr<const dcp::Data> _data;
        dcp::Size _size;
        boost::optional<dcp::Eye> _eye;
        mutable boost::shared_ptr<Image> _image;
index 10e798ed510644a86e03d11249d737139268d3b0..620245781afa3374e062cd018119b7c5b23ce677 100644 (file)
@@ -232,7 +232,7 @@ PlayerVideo::has_j2k () const
        return _crop == Crop () && _out_size == j2k->size() && !_text && !_fade && !_colour_conversion;
 }
 
-Data
+shared_ptr<const dcp::Data>
 PlayerVideo::j2k () const
 {
        shared_ptr<const J2KImageProxy> j2k = dynamic_pointer_cast<const J2KImageProxy> (_in);
index 1a4a01d58cd8af449647a7f222a66e83f16b7146..6043632c29000d45943e735ffb992810e12b0f9a 100644 (file)
@@ -79,7 +79,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;
-       dcp::Data j2k () const;
+       boost::shared_ptr<const dcp::Data> j2k () const;
 
        Eyes eyes () const {
                return _eyes;
index 097b9d84b376eba7224af4cbc37a4555e069d025..7ed79d818128ecf1a1f34553e2f57e0683ec1ae3 100644 (file)
@@ -67,6 +67,7 @@ using boost::dynamic_pointer_cast;
 #if BOOST_VERSION >= 106100
 using namespace boost::placeholders;
 #endif
+using dcp::ArrayData;
 using dcp::Data;
 using dcp::raw_convert;
 using namespace dcpomatic;
@@ -289,14 +290,14 @@ ReelWriter::check_existing_picture_asset (boost::filesystem::path asset)
 }
 
 void
-ReelWriter::write (optional<Data> encoded, Frame frame, Eyes eyes)
+ReelWriter::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes)
 {
        if (!_picture_asset_writer) {
                /* We're not writing any data */
                return;
        }
 
-       dcp::FrameInfo fin = _picture_asset_writer->write (encoded->data().get (), encoded->size());
+       dcp::FrameInfo fin = _picture_asset_writer->write (encoded->data(), encoded->size());
        write_frame_info (frame, eyes, fin);
        _last_written[eyes] = encoded;
 }
@@ -338,7 +339,7 @@ ReelWriter::repeat_write (Frame frame, Eyes eyes)
        }
 
        dcp::FrameInfo fin = _picture_asset_writer->write (
-               _last_written[eyes]->data().get(),
+               _last_written[eyes]->data(),
                _last_written[eyes]->size()
                );
        write_frame_info (frame, eyes, fin);
index 17bfc7ba2c191482e674bb0645b470bf965e6e3e..09c29adae05a1e7efacdf22448ed81ac0e6ce31e 100644 (file)
@@ -66,7 +66,7 @@ public:
                boost::optional<std::string> content_summary
                );
 
-       void write (boost::optional<dcp::Data> encoded, Frame frame, Eyes eyes);
+       void write (boost::shared_ptr<const dcp::Data> encoded, Frame frame, Eyes eyes);
        void fake_write (int size);
        void repeat_write (Frame frame, Eyes eyes);
        void write (boost::shared_ptr<const AudioBuffers> audio);
@@ -104,7 +104,7 @@ private:
        /** the first picture frame index that does not already exist in our MXF */
        int _first_nonexistant_frame;
        /** the data of the last written frame, if there is one */
-       boost::optional<dcp::Data> _last_written[EYES_COUNT];
+       boost::shared_ptr<const dcp::Data> _last_written[EYES_COUNT];
        /** index of this reel within the DCP (starting from 0) */
        int _reel_index;
        /** number of reels in the DCP */
index d3fdc5128611a2e6c5436ae8c6147be61c07034e..346cbb0c26cdbdd8ee03eda3c3683de58f1971cb 100644 (file)
@@ -66,6 +66,7 @@ using boost::optional;
 using namespace boost::placeholders;
 #endif
 using dcp::Data;
+using dcp::ArrayData;
 using namespace dcpomatic;
 
 Writer::Writer (shared_ptr<const Film> film, weak_ptr<Job> j)
@@ -130,7 +131,7 @@ Writer::~Writer ()
  *  @param eyes Eyes that this frame image is for.
  */
 void
-Writer::write (Data encoded, Frame frame, Eyes eyes)
+Writer::write (shared_ptr<const Data> encoded, Frame frame, Eyes eyes)
 {
        boost::mutex::scoped_lock lock (_state_mutex);
 
@@ -426,7 +427,7 @@ try
                        case QueueItem::FULL:
                                LOG_DEBUG_ENCODE (N_("Writer FULL-writes %1 (%2)"), qi.frame, (int) qi.eyes);
                                if (!qi.encoded) {
-                                       qi.encoded = Data (_film->j2c_path (qi.reel, qi.frame, qi.eyes, false));
+                                       qi.encoded.reset (new ArrayData(_film->j2c_path(qi.reel, qi.frame, qi.eyes, false)));
                                }
                                reel.write (qi.encoded, qi.frame, qi.eyes);
                                ++_full_written;
index 71e04df96053c7cf9e7b756421b9e658e925a797..459bc5a882e18e10bb541f4b33e91c7c65eda72c 100644 (file)
@@ -70,7 +70,7 @@ public:
        } type;
 
        /** encoded data for FULL */
-       boost::optional<dcp::Data> encoded;
+       boost::shared_ptr<const dcp::Data> encoded;
        /** size of data for FAKE */
        int size;
        /** reel index */
@@ -105,7 +105,7 @@ public:
 
        bool can_fake_write (Frame) const;
 
-       void write (dcp::Data, Frame, Eyes);
+       void write (boost::shared_ptr<const dcp::Data>, Frame, Eyes);
        void fake_write (Frame, Eyes);
        bool can_repeat (Frame) const;
        void repeat (Frame, Eyes);