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)
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);
+}
}
Eyes eyes () const;
+
+ bool same (boost::shared_ptr<const DCPVideo> other) const;
private:
}
}
+/** 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)
{
encodings.
*/
int remote_backoff = 0;
+ shared_ptr<DCPVideo> last_dcp_video;
+ shared_ptr<EncodedData> last_encoded;
while (true) {
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 ();
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;
+}
};
extern PositionImage merge (std::list<PositionImage> images);
+extern bool operator== (Image const & a, Image const & b);
#endif
* 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
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;
#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)
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;
+}
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;
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);
+}
return _inter_size;
}
+ bool same (boost::shared_ptr<const PlayerVideo> other) const;
+
private:
boost::shared_ptr<const ImageProxy> _in;
DCPTime _time;
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
#define DCPOMATIC_POSITION_IMAGE_H
#include "position.h"
+#include <boost/shared_ptr.hpp>
class Image;
boost::shared_ptr<Image> image;
Position<int> position;
+
+ bool same (PositionImage const & other) const;
};
#endif
player.cc
player_video.cc
playlist.cc
+ position_image.cc
ratio.cc
raw_image_proxy.cc
render_subtitles.cc