Spot repeated frames from single-image sources and optimise encoding.
authorCarl Hetherington <cth@carlh.net>
Thu, 11 Sep 2014 22:35:57 +0000 (23:35 +0100)
committerCarl Hetherington <cth@carlh.net>
Thu, 11 Sep 2014 22:35:57 +0000 (23:35 +0100)
14 files changed:
ChangeLog
src/lib/dcp_video.cc
src/lib/dcp_video.h
src/lib/encoder.cc
src/lib/image.cc
src/lib/image.h
src/lib/image_proxy.h
src/lib/magick_image_proxy.cc
src/lib/magick_image_proxy.h
src/lib/player_video.cc
src/lib/player_video.h
src/lib/position.h
src/lib/position_image.h
src/lib/wscript

index b1fab23cfd6904a7bd0b15550febb0f998e7d666..1829dbb0a26b7eb5089fb17d9881613b921a7096 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,7 @@
 2014-09-11  Carl Hetherington  <cth@carlh.net>
 
+       * Restore encoding optimisations for still-image sources.
+
        * Add option to re-make signing chain with specified organisation,
        common names etc. (#354)
 
index d849866512d4cdc0e983ca42201631fe3fe88d70..ccfc800c8389f275518b774d3e8e4daa7c1157dd 100644 (file)
@@ -315,3 +315,18 @@ DCPVideo::eyes () const
        return _frame->eyes ();
 }
 
+/** @return true if this DCPVideo is definitely the same as another;
+ *  (apart from the frame index), false if it is probably not.
+ */
+bool
+DCPVideo::same (shared_ptr<const DCPVideo> other) const
+{
+       if (_frames_per_second != other->_frames_per_second ||
+           _j2k_bandwidth != other->_j2k_bandwidth ||
+           _resolution != other->_resolution ||
+           _burn_subtitles != other->_burn_subtitles) {
+               return false;
+       }
+
+       return _frame->same (other->_frame);
+}
index e8e90260ce9955fd390be34a4135489187710a07..d517a8f0258ab47b3f5c1b1a0a3e6e7910c2b034 100644 (file)
@@ -57,6 +57,8 @@ public:
        }
 
        Eyes eyes () const;
+
+       bool same (boost::shared_ptr<const DCPVideo> other) const;
        
 private:
 
index 6848dc2b32315230533a49f3b70a3965e11866cc..0c9faa70d331bb722d70ffd17ba924456e344133 100644 (file)
@@ -176,6 +176,9 @@ Encoder::frame_done ()
        }
 }
 
+/** Called in order, so each time this is called the supplied frame is the one
+ *  after the previous one.
+ */
 void
 Encoder::enqueue (shared_ptr<PlayerVideo> pv)
 {
@@ -265,6 +268,8 @@ try
           encodings.
        */
        int remote_backoff = 0;
+       shared_ptr<DCPVideo> last_dcp_video;
+       shared_ptr<EncodedData> last_encoded;
        
        while (true) {
 
@@ -287,38 +292,47 @@ try
 
                shared_ptr<EncodedData> encoded;
 
-               if (server) {
-                       try {
-                               encoded = vf->encode_remotely (server.get ());
-
-                               if (remote_backoff > 0) {
-                                       LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
+               if (last_dcp_video && vf->same (last_dcp_video)) {
+                       /* We already have encoded data for the same input as this one, so take a short-cut */
+                       encoded = last_encoded;
+               } else {
+                       /* We need to encode this input */
+                       if (server) {
+                               try {
+                                       encoded = vf->encode_remotely (server.get ());
+                                       
+                                       if (remote_backoff > 0) {
+                                               LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
+                                       }
+                                       
+                                       /* This job succeeded, so remove any backoff */
+                                       remote_backoff = 0;
+                                       
+                               } catch (std::exception& e) {
+                                       if (remote_backoff < 60) {
+                                               /* back off more */
+                                               remote_backoff += 10;
+                                       }
+                                       LOG_ERROR (
+                                               N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
+                                               vf->index(), server->host_name(), e.what(), remote_backoff
+                                               );
                                }
                                
-                               /* This job succeeded, so remove any backoff */
-                               remote_backoff = 0;
-                               
-                       } catch (std::exception& e) {
-                               if (remote_backoff < 60) {
-                                       /* back off more */
-                                       remote_backoff += 10;
+                       } else {
+                               try {
+                                       LOG_TIMING ("[%1] encoder thread begins local encode of %2", boost::this_thread::get_id(), vf->index());
+                                       encoded = vf->encode_locally ();
+                                       LOG_TIMING ("[%1] encoder thread finishes local encode of %2", boost::this_thread::get_id(), vf->index());
+                               } catch (std::exception& e) {
+                                       LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
                                }
-                               LOG_ERROR (
-                                       N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
-                                       vf->index(), server->host_name(), e.what(), remote_backoff
-                                       );
-                       }
-                               
-               } else {
-                       try {
-                               LOG_TIMING ("[%1] encoder thread begins local encode of %2", boost::this_thread::get_id(), vf->index());
-                               encoded = vf->encode_locally ();
-                               LOG_TIMING ("[%1] encoder thread finishes local encode of %2", boost::this_thread::get_id(), vf->index());
-                       } catch (std::exception& e) {
-                               LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
                        }
                }
 
+               last_dcp_video = vf;
+               last_encoded = encoded;
+
                if (encoded) {
                        _writer->write (encoded, vf->index (), vf->eyes ());
                        frame_done ();
index 2eb2dbe28b013363e0852684761202ec550613d1..0b06d39b1f3f215698d850452cf2f951371bab48 100644 (file)
@@ -690,3 +690,30 @@ Image::digest () const
 
        return digester.get ();
 }
+
+bool
+operator== (Image const & a, Image const & b)
+{
+       if (a.components() != b.components() || a.pixel_format() != b.pixel_format() || a.aligned() != b.aligned()) {
+               return false;
+       }
+
+       for (int c = 0; c < a.components(); ++c) {
+               if (a.lines(c) != b.lines(c) || a.line_size()[c] != b.line_size()[c] || a.stride()[c] != b.stride()[c]) {
+                       return false;
+               }
+
+               uint8_t* p = a.data()[c];
+               uint8_t* q = b.data()[c];
+               for (int y = 0; y < a.lines(c); ++y) {
+                       if (memcmp (p, q, a.line_size()[c]) != 0) {
+                               return false;
+                       }
+
+                       p += a.stride()[c];
+                       q += b.stride()[c];
+               }
+       }
+
+       return true;
+}
index 6c539164109a883e40b864a108a2a90810fd9fc3..e1f8d0ddd4c1d7597b17c78ce8449479e3e9f44a 100644 (file)
@@ -94,5 +94,6 @@ private:
 };
 
 extern PositionImage merge (std::list<PositionImage> images);
+extern bool operator== (Image const & a, Image const & b);
 
 #endif
index 0fdea48ee06e04d2610332d259c442d53ab9f8ba..7ff28e174ec092afdbc832ec6b07fb40c7f47c37 100644 (file)
@@ -50,7 +50,7 @@ namespace dcp {
  *  of happening in a single-threaded decoder.
  *
  *  For example, large TIFFs are slow to decode, so this class will keep
- *  the TIFF data TIFF until such a time that the actual image is needed.
+ *  the TIFF data compressed until the decompressed image is needed.
  *  At this point, the class decodes the TIFF to an Image.
  */
 class ImageProxy : public boost::noncopyable
@@ -63,6 +63,10 @@ public:
        virtual boost::shared_ptr<Image> image () 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 */
+       virtual bool same (boost::shared_ptr<const ImageProxy>) const {
+               return false;
+       }
 
 protected:
        boost::shared_ptr<Log> _log;
index 4adf8047f6ae8af8200375b21e79f5cd88ef461a..c3cfc422c3183141d023e83665f9239cd6d52279 100644 (file)
@@ -31,7 +31,9 @@
 #define LOG_TIMING(...) _log->microsecond_log (String::compose (__VA_ARGS__), Log::TYPE_TIMING);
 
 using std::string;
+using std::cout;
 using boost::shared_ptr;
+using boost::dynamic_pointer_cast;
 
 MagickImageProxy::MagickImageProxy (boost::filesystem::path path, shared_ptr<Log> log)
        : ImageProxy (log)
@@ -133,3 +135,18 @@ MagickImageProxy::send_binary (shared_ptr<Socket> socket) const
        socket->write (_blob.length ());
        socket->write ((uint8_t *) _blob.data (), _blob.length ());
 }
+
+bool
+MagickImageProxy::same (shared_ptr<const ImageProxy> other) const
+{
+       shared_ptr<const MagickImageProxy> mp = dynamic_pointer_cast<const MagickImageProxy> (other);
+       if (!mp) {
+               return false;
+       }
+
+       if (_blob.length() != mp->_blob.length()) {
+               return false;
+       }
+       
+       return memcmp (_blob.data(), mp->_blob.data(), _blob.length()) == 0;
+}
index a2635236fc26014b7281ba8228133d35d7aab7b2..8b43d0a000233c051613e5b14f0ea2247716fe34 100644 (file)
@@ -28,6 +28,7 @@ public:
        boost::shared_ptr<Image> image () const;
        void add_metadata (xmlpp::Node *) const;
        void send_binary (boost::shared_ptr<Socket>) const;
+       bool same (boost::shared_ptr<const ImageProxy> other) const;
 
 private:       
        Magick::Blob _blob;
index 8fd966e5fc57a73ff2c0297fe0c893b07ec35e81..aab90a806c59116cea62f149cd155e302bb7ea20 100644 (file)
@@ -176,4 +176,23 @@ PlayerVideo::inter_position () const
        return Position<int> ((_out_size.width - _inter_size.width) / 2, (_out_size.height - _inter_size.height) / 2);
 }
 
+/** @return true if this PlayerVideo is definitely the same as another
+ * (apart from _time), false if it is probably not
+ */
+bool
+PlayerVideo::same (shared_ptr<const PlayerVideo> other) const
+{
+       if (_in != other->_in ||
+           _crop != other->_crop ||
+           _inter_size != other->_inter_size ||
+           _out_size != other->_out_size ||
+           _scaler != other->_scaler ||
+           _eyes != other->_eyes ||
+           _part != other->_part ||
+           _colour_conversion != other->_colour_conversion ||
+           !_subtitle.same (other->_subtitle)) {
+               return false;
+       }
 
+       return _in->same (other->_in);
+}
index 74e05d1e91b46ea654b31148f7439576d0c065e1..59894a22780b6bbe36c3286cc0859087f0cdf1f3 100644 (file)
@@ -70,6 +70,8 @@ public:
                return _inter_size;
        }
 
+       bool same (boost::shared_ptr<const PlayerVideo> other) const;
+
 private:
        boost::shared_ptr<const ImageProxy> _in;
        DCPTime _time;
index 345f7ab4a85bcd8b0cd7565543e4736f26e90968..3c561d85c2b14b909b2f7e1fd2445bed662ff189 100644 (file)
@@ -64,4 +64,11 @@ operator== (Position<T> const & a, Position<T> const & b)
        return a.x == b.x && a.y == b.y;
 }
 
+template<class T>
+bool
+operator!= (Position<T> const & a, Position<T> const & b)
+{
+       return a.x != b.x || a.y != b.y;
+}
+
 #endif
index dbd3c600e46ffcf8183a20ad9bf5b227947c0df6..c0c65d1da9bc250bbc341be153ffc6b9035ef2f0 100644 (file)
@@ -21,6 +21,7 @@
 #define DCPOMATIC_POSITION_IMAGE_H
 
 #include "position.h"
+#include <boost/shared_ptr.hpp>
 
 class Image;
 
@@ -36,6 +37,8 @@ public:
        
        boost::shared_ptr<Image> image;
        Position<int> position;
+
+       bool same (PositionImage const & other) const;
 };
 
 #endif
index 3f77a5cebb9707b0575a8e8c29c30878d2f5d5e5..4e62206f129d0217fbc20e159c0909e329a016cd 100644 (file)
@@ -61,6 +61,7 @@ sources = """
           player.cc
           player_video.cc
           playlist.cc
+          position_image.cc
           ratio.cc
           raw_image_proxy.cc
           render_subtitles.cc