Reduce during J2K decode where possible for playback (#986).
authorCarl Hetherington <cth@carlh.net>
Sat, 26 Nov 2016 15:14:11 +0000 (15:14 +0000)
committerCarl Hetherington <cth@carlh.net>
Sat, 26 Nov 2016 15:14:11 +0000 (15:14 +0000)
ChangeLog
cscript
src/lib/image_proxy.h
src/lib/j2k_image_proxy.cc
src/lib/j2k_image_proxy.h
src/lib/magick_image_proxy.cc
src/lib/magick_image_proxy.h
src/lib/player_video.cc
src/lib/raw_image_proxy.cc
src/lib/raw_image_proxy.h

index d983bca8d85edbe3bf285957198aba9ffea9d0b1..fe2840ba2ab854db2edd41e767ee065431946914 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2016-11-26  Carl Hetherington  <cth@carlh.net>
+
+       * Speed up preview of exisiting DCPs when the preview size
+       is less than half the full DCP size (#986).
+
 2016-11-25  Carl Hetherington  <cth@carlh.net>
 
        * Allow multi-edit of content timing details (#1010).
diff --git a/cscript b/cscript
index 6203a1042b2237d31414e785a3ede1a070f3bb29..9064446f51e2f56d0bc77ac820387170712fff38 100644 (file)
--- a/cscript
+++ b/cscript
@@ -245,7 +245,7 @@ def dependencies(target):
         ffmpeg_options = {}
 
     return (('ffmpeg-cdist', 'c7df8d5', ffmpeg_options),
-            ('libdcp', 'v1.4.4'),
+            ('libdcp', '71f5d09'),
             ('libsub', 'v1.2.4'))
 
 def configure_options(target):
index b0ec161102d1f311e1e019c47793314c35166944..d7bd7b0e62811f9846ceb92a127a77be09d9b1f5 100644 (file)
@@ -60,8 +60,16 @@ class ImageProxy : public boost::noncopyable
 public:
        virtual ~ImageProxy () {}
 
-       /** @return Image (which must be aligned) */
-       virtual boost::shared_ptr<Image> image (boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> ()) const = 0;
+       /** @param note Handler for any notes that occur.
+        *  @param size Size that the returned image will be scaled to, in case this
+        *  can be used as an optimisation.
+        *  @return Image (which must be aligned)
+        */
+       virtual boost::shared_ptr<Image> image (
+               boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
+               boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
+               ) const = 0;
+
        virtual void add_metadata (xmlpp::Node *) const = 0;
        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 */
index fb38c9e999a9d04913f14823a67a0ed581429944..4e38213c8326ef1930da01cc3a3cf934efa96730 100644 (file)
 #include <dcp/j2k.h>
 #include <libcxml/cxml.h>
 #include <libxml++/libxml++.h>
+#include <Magick++.h>
 #include <iostream>
 
 #include "i18n.h"
 
 using std::string;
 using std::cout;
+using std::max;
 using boost::shared_ptr;
 using boost::optional;
 using boost::dynamic_pointer_cast;
@@ -91,46 +93,53 @@ J2KImageProxy::J2KImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
        socket->read (_data.data().get (), _data.size ());
 }
 
-void
-J2KImageProxy::ensure_j2k () const
-{
-       if (!_j2k) {
-               _j2k = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), 0);
-       }
-}
-
 shared_ptr<Image>
-J2KImageProxy::image (optional<dcp::NoteHandler>) const
+J2KImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size> target_size) const
 {
-       ensure_j2k ();
-
-       if (_j2k->precision(0) < 12) {
-               int const shift = 12 - _j2k->precision (0);
-               for (int c = 0; c < 3; ++c) {
-                       int* p = _j2k->data (c);
-                       for (int y = 0; y < _j2k->size().height; ++y) {
-                               for (int x = 0; x < _j2k->size().width; ++x) {
-                                       *p++ <<= shift;
+       if (!_j2k || target_size != _j2k_target_size) {
+               int reduce = 0;
+
+               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);
+               _j2k = dcp::decompress_j2k (const_cast<uint8_t*> (_data.data().get()), _data.size (), reduce);
+
+               if (_j2k->precision(0) < 12) {
+                       int const shift = 12 - _j2k->precision (0);
+                       for (int c = 0; c < 3; ++c) {
+                               int* p = _j2k->data (c);
+                               for (int y = 0; y < _j2k->size().height; ++y) {
+                                       for (int x = 0; x < _j2k->size().width; ++x) {
+                                               *p++ <<= shift;
+                                       }
                                }
                        }
                }
+
+               _j2k_target_size = target_size;
        }
 
-       shared_ptr<Image> image (new Image (_pixel_format, _size, true));
+       shared_ptr<Image> image (new Image (_pixel_format, _j2k->size(), true));
 
        /* Copy data in whatever format (sRGB or XYZ) into our Image; I'm assuming
           the data is 12-bit either way.
        */
 
+       int const width = _j2k->size().width;
+
        int p = 0;
        for (int y = 0; y < _j2k->size().height; ++y) {
                uint16_t* q = (uint16_t *) (image->data()[0] + y * image->stride()[0]);
-               for (int x = 0; x < _j2k->size().width; ++x) {
+               for (int x = 0; x < width; ++x) {
                        for (int c = 0; c < 3; ++c) {
                                *q++ = _j2k->data(c)[p] << 4;
                        }
                        ++p;
                }
+               p += _j2k->factor(0) * width;
        }
 
        return image;
index 96a776f2a5333bc6100a41629a4cf2919f24f7c6..8fc9040d5a8eda0eccf621fbaeddcce1c1ded1f5 100644 (file)
@@ -35,7 +35,11 @@ public:
        J2KImageProxy (boost::shared_ptr<const dcp::StereoPictureFrame> frame, dcp::Size, dcp::Eye, AVPixelFormat pixel_format);
        J2KImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
 
-       boost::shared_ptr<Image> image (boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> ()) const;
+       boost::shared_ptr<Image> image (
+               boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
+               boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
+               ) const;
+
        void add_metadata (xmlpp::Node *) const;
        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 */
@@ -57,11 +61,11 @@ private:
 
        /* For tests */
        J2KImageProxy (dcp::Data data, dcp::Size size, AVPixelFormat pixel_format);
-       void ensure_j2k () const;
 
        dcp::Data _data;
        dcp::Size _size;
        boost::optional<dcp::Eye> _eye;
        mutable boost::shared_ptr<dcp::OpenJPEGImage> _j2k;
+       mutable boost::optional<dcp::Size> _j2k_target_size;
        AVPixelFormat _pixel_format;
 };
index 2d1867fcc14a972c2296465178c07f50e7a4321f..b8255c9ad92b70f3706d4b3801a25b744a8572e0 100644 (file)
@@ -67,7 +67,7 @@ MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> s
 }
 
 shared_ptr<Image>
-MagickImageProxy::image (optional<dcp::NoteHandler>) const
+MagickImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size>) const
 {
        boost::mutex::scoped_lock lm (_mutex);
 
index e255b1336fcf512ad2eb70596749d17d5911f371..5c4532add9b1e8ae67bad08546e37087b50aa769 100644 (file)
@@ -29,7 +29,11 @@ public:
        MagickImageProxy (boost::filesystem::path);
        MagickImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
 
-       boost::shared_ptr<Image> image (boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> ()) const;
+       boost::shared_ptr<Image> image (
+               boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
+               boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
+               ) const;
+
        void add_metadata (xmlpp::Node *) const;
        void send_binary (boost::shared_ptr<Socket>) const;
        bool same (boost::shared_ptr<const ImageProxy> other) const;
index 8075b7b7d437dee5f6cf94c27fb6f2ab78d1a249..5e34ddf97fa0879868fb92d5e84e7cf5156bc607 100644 (file)
@@ -106,7 +106,7 @@ PlayerVideo::set_subtitle (PositionImage image)
 shared_ptr<Image>
 PlayerVideo::image (dcp::NoteHandler note, function<AVPixelFormat (AVPixelFormat)> pixel_format, bool aligned, bool fast) const
 {
-       shared_ptr<Image> im = _in->image (optional<dcp::NoteHandler> (note));
+       shared_ptr<Image> im = _in->image (optional<dcp::NoteHandler> (note), _inter_size);
 
        Crop total_crop = _crop;
        switch (_part) {
index 6ebcee60c9d1c135822a8426fd3d49d7f5cec98a..ea702f138594a9217709786934c5b3ae76051b52 100644 (file)
@@ -53,7 +53,7 @@ RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> soc
 }
 
 shared_ptr<Image>
-RawImageProxy::image (optional<dcp::NoteHandler>) const
+RawImageProxy::image (optional<dcp::NoteHandler>, optional<dcp::Size>) const
 {
        return _image;
 }
index 379c06f32786722c9e9c2c9a387a2f242fadb6f5..28fd7f26362090241526ae92eaf2a7fe78d70be3 100644 (file)
@@ -29,7 +29,11 @@ public:
        RawImageProxy (boost::shared_ptr<Image>);
        RawImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
 
-       boost::shared_ptr<Image> image (boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> ()) const;
+       boost::shared_ptr<Image> image (
+               boost::optional<dcp::NoteHandler> note = boost::optional<dcp::NoteHandler> (),
+               boost::optional<dcp::Size> size = boost::optional<dcp::Size> ()
+               ) const;
+
        void add_metadata (xmlpp::Node *) const;
        void send_binary (boost::shared_ptr<Socket>) const;
        bool same (boost::shared_ptr<const ImageProxy>) const;