From: Carl Hetherington Date: Thu, 11 Sep 2014 22:35:57 +0000 (+0100) Subject: Spot repeated frames from single-image sources and optimise encoding. X-Git-Tag: v2.0.48~596 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=88065ad7e9070c7c6a0f9b15202c392084e9e8ba Spot repeated frames from single-image sources and optimise encoding. --- diff --git a/ChangeLog b/ChangeLog index b1fab23cf..1829dbb0a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2014-09-11 Carl Hetherington + * Restore encoding optimisations for still-image sources. + * Add option to re-make signing chain with specified organisation, common names etc. (#354) diff --git a/src/lib/dcp_video.cc b/src/lib/dcp_video.cc index d84986651..ccfc800c8 100644 --- a/src/lib/dcp_video.cc +++ b/src/lib/dcp_video.cc @@ -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 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); +} diff --git a/src/lib/dcp_video.h b/src/lib/dcp_video.h index e8e90260c..d517a8f02 100644 --- a/src/lib/dcp_video.h +++ b/src/lib/dcp_video.h @@ -57,6 +57,8 @@ public: } Eyes eyes () const; + + bool same (boost::shared_ptr other) const; private: diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 6848dc2b3..0c9faa70d 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -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 pv) { @@ -265,6 +268,8 @@ try encodings. */ int remote_backoff = 0; + shared_ptr last_dcp_video; + shared_ptr last_encoded; while (true) { @@ -287,38 +292,47 @@ try shared_ptr 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 (); diff --git a/src/lib/image.cc b/src/lib/image.cc index 2eb2dbe28..0b06d39b1 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -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; +} diff --git a/src/lib/image.h b/src/lib/image.h index 6c5391641..e1f8d0ddd 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -94,5 +94,6 @@ private: }; extern PositionImage merge (std::list images); +extern bool operator== (Image const & a, Image const & b); #endif diff --git a/src/lib/image_proxy.h b/src/lib/image_proxy.h index 0fdea48ee..7ff28e174 100644 --- a/src/lib/image_proxy.h +++ b/src/lib/image_proxy.h @@ -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 () const = 0; virtual void add_metadata (xmlpp::Node *) const = 0; virtual void send_binary (boost::shared_ptr) 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 { + return false; + } protected: boost::shared_ptr _log; diff --git a/src/lib/magick_image_proxy.cc b/src/lib/magick_image_proxy.cc index 4adf8047f..c3cfc422c 100644 --- a/src/lib/magick_image_proxy.cc +++ b/src/lib/magick_image_proxy.cc @@ -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) : ImageProxy (log) @@ -133,3 +135,18 @@ MagickImageProxy::send_binary (shared_ptr socket) const socket->write (_blob.length ()); socket->write ((uint8_t *) _blob.data (), _blob.length ()); } + +bool +MagickImageProxy::same (shared_ptr other) const +{ + shared_ptr mp = dynamic_pointer_cast (other); + if (!mp) { + return false; + } + + if (_blob.length() != mp->_blob.length()) { + return false; + } + + return memcmp (_blob.data(), mp->_blob.data(), _blob.length()) == 0; +} diff --git a/src/lib/magick_image_proxy.h b/src/lib/magick_image_proxy.h index a2635236f..8b43d0a00 100644 --- a/src/lib/magick_image_proxy.h +++ b/src/lib/magick_image_proxy.h @@ -28,6 +28,7 @@ public: boost::shared_ptr image () const; void add_metadata (xmlpp::Node *) const; void send_binary (boost::shared_ptr) const; + bool same (boost::shared_ptr other) const; private: Magick::Blob _blob; diff --git a/src/lib/player_video.cc b/src/lib/player_video.cc index 8fd966e5f..aab90a806 100644 --- a/src/lib/player_video.cc +++ b/src/lib/player_video.cc @@ -176,4 +176,23 @@ PlayerVideo::inter_position () const return Position ((_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 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); +} diff --git a/src/lib/player_video.h b/src/lib/player_video.h index 74e05d1e9..59894a227 100644 --- a/src/lib/player_video.h +++ b/src/lib/player_video.h @@ -70,6 +70,8 @@ public: return _inter_size; } + bool same (boost::shared_ptr other) const; + private: boost::shared_ptr _in; DCPTime _time; diff --git a/src/lib/position.h b/src/lib/position.h index 345f7ab4a..3c561d85c 100644 --- a/src/lib/position.h +++ b/src/lib/position.h @@ -64,4 +64,11 @@ operator== (Position const & a, Position const & b) return a.x == b.x && a.y == b.y; } +template +bool +operator!= (Position const & a, Position const & b) +{ + return a.x != b.x || a.y != b.y; +} + #endif diff --git a/src/lib/position_image.h b/src/lib/position_image.h index dbd3c600e..c0c65d1da 100644 --- a/src/lib/position_image.h +++ b/src/lib/position_image.h @@ -21,6 +21,7 @@ #define DCPOMATIC_POSITION_IMAGE_H #include "position.h" +#include class Image; @@ -36,6 +37,8 @@ public: boost::shared_ptr image; Position position; + + bool same (PositionImage const & other) const; }; #endif diff --git a/src/lib/wscript b/src/lib/wscript index 3f77a5ceb..4e62206f1 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -61,6 +61,7 @@ sources = """ player.cc player_video.cc playlist.cc + position_image.cc ratio.cc raw_image_proxy.cc render_subtitles.cc